diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/qe/Makefile | 43 | ||||
-rw-r--r-- | drivers/qe/qe.c | 254 | ||||
-rw-r--r-- | drivers/qe/qe.h | 237 | ||||
-rw-r--r-- | drivers/qe/uccf.c | 404 | ||||
-rw-r--r-- | drivers/qe/uccf.h | 130 | ||||
-rw-r--r-- | drivers/qe/uec.c | 1266 | ||||
-rw-r--r-- | drivers/qe/uec.h | 716 | ||||
-rw-r--r-- | drivers/qe/uec_phy.c | 604 | ||||
-rw-r--r-- | drivers/qe/uec_phy.h | 256 |
9 files changed, 3910 insertions, 0 deletions
diff --git a/drivers/qe/Makefile b/drivers/qe/Makefile new file mode 100644 index 0000000..4844181 --- /dev/null +++ b/drivers/qe/Makefile @@ -0,0 +1,43 @@ +# +# Copyright (C) 2006 Freescale Semiconductor, Inc. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)qe.a + +COBJS := qe.o uccf.o uec.o uec_phy.o + +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c new file mode 100644 index 0000000..5f20962 --- /dev/null +++ b/drivers/qe/qe.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "common.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" + +#if defined(CONFIG_QE) +qe_map_t *qe_immr = NULL; +static qe_snum_t snums[QE_NUM_OF_SNUM]; + +void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data) +{ + u32 cecr; + + if (cmd == QE_RESET) { + out_be32(&qe_immr->cp.cecr,(u32) (cmd | QE_CR_FLG)); + } else { + out_be32(&qe_immr->cp.cecdr, cmd_data); + out_be32(&qe_immr->cp.cecr, (sbc | QE_CR_FLG | + ((u32) mcn<<QE_CR_PROTOCOL_SHIFT) | cmd)); + } + /* Wait for the QE_CR_FLG to clear */ + do { + cecr = in_be32(&qe_immr->cp.cecr); + } while (cecr & QE_CR_FLG); + + return; +} + +uint qe_muram_alloc(uint size, uint align) +{ + DECLARE_GLOBAL_DATA_PTR; + + uint retloc; + uint align_mask, off; + uint savebase; + + align_mask = align - 1; + savebase = gd->mp_alloc_base; + + if ((off = (gd->mp_alloc_base & align_mask)) != 0) + gd->mp_alloc_base += (align - off); + + if ((off = size & align_mask) != 0) + size += (align - off); + + if ((gd->mp_alloc_base + size) >= gd->mp_alloc_top) { + gd->mp_alloc_base = savebase; + printf("%s: ran out of ram.\n", __FUNCTION__); + } + + retloc = gd->mp_alloc_base; + gd->mp_alloc_base += size; + + memset((void *)&qe_immr->muram[retloc], 0, size); + + __asm__ __volatile__("sync"); + + return retloc; +} + +void *qe_muram_addr(uint offset) +{ + return (void *)&qe_immr->muram[offset]; +} + +static void qe_sdma_init(void) +{ + volatile sdma_t *p; + uint sdma_buffer_base; + + p = (volatile sdma_t *)&qe_immr->sdma; + + /* All of DMA transaction in bus 1 */ + out_be32(&p->sdaqr, 0); + out_be32(&p->sdaqmr, 0); + + /* Allocate 2KB temporary buffer for sdma */ + sdma_buffer_base = qe_muram_alloc(2048, 64); + out_be32(&p->sdwbcr, sdma_buffer_base & QE_SDEBCR_BA_MASK); + + /* Clear sdma status */ + out_be32(&p->sdsr, 0x03000000); + + /* Enable global mode on bus 1, and 2KB buffer size */ + out_be32(&p->sdmr, QE_SDMR_GLB_1_MSK | (0x3 << QE_SDMR_CEN_SHIFT)); +} + +static u8 thread_snum[QE_NUM_OF_SNUM] = { + 0x04, 0x05, 0x0c, 0x0d, + 0x14, 0x15, 0x1c, 0x1d, + 0x24, 0x25, 0x2c, 0x2d, + 0x34, 0x35, 0x88, 0x89, + 0x98, 0x99, 0xa8, 0xa9, + 0xb8, 0xb9, 0xc8, 0xc9, + 0xd8, 0xd9, 0xe8, 0xe9 +}; + +static void qe_snums_init(void) +{ + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + snums[i].state = QE_SNUM_STATE_FREE; + snums[i].num = thread_snum[i]; + } +} + +int qe_get_snum(void) +{ + int snum = -EBUSY; + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + if (snums[i].state == QE_SNUM_STATE_FREE) { + snums[i].state = QE_SNUM_STATE_USED; + snum = snums[i].num; + break; + } + } + + return snum; +} + +void qe_put_snum(u8 snum) +{ + int i; + + for (i = 0; i < QE_NUM_OF_SNUM; i++) { + if (snums[i].num == snum) { + snums[i].state = QE_SNUM_STATE_FREE; + break; + } + } +} + +void qe_init(uint qe_base) +{ + DECLARE_GLOBAL_DATA_PTR; + + /* Init the QE IMMR base */ + qe_immr = (qe_map_t *)qe_base; + + gd->mp_alloc_base = QE_DATAONLY_BASE; + gd->mp_alloc_top = gd->mp_alloc_base + QE_DATAONLY_SIZE; + + qe_sdma_init(); + qe_snums_init(); +} + +void qe_reset(void) +{ + qe_issue_cmd(QE_RESET, QE_CR_SUBBLOCK_INVALID, + (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0); +} + +void qe_assign_page(uint snum, uint para_ram_base) +{ + u32 cecr; + + out_be32(&qe_immr->cp.cecdr, para_ram_base); + out_be32(&qe_immr->cp.cecr, ((u32) snum<<QE_CR_ASSIGN_PAGE_SNUM_SHIFT) + | QE_CR_FLG | QE_ASSIGN_PAGE); + + /* Wait for the QE_CR_FLG to clear */ + do { + cecr = in_be32(&qe_immr->cp.cecr); + } while (cecr & QE_CR_FLG ); + + return; +} + +/* + * brg: 0~15 as BRG1~BRG16 + rate: baud rate + * BRG input clock comes from the BRGCLK (internal clock generated from + the QE clock, it is one-half of the QE clock), If need the clock source + from CLKn pin, we have te change the function. + */ + +#define BRG_CLK (gd->brg_clk) + +int qe_set_brg(uint brg, uint rate) +{ + DECLARE_GLOBAL_DATA_PTR; + volatile uint *bp; + u32 divisor; + int div16 = 0; + + if (brg >= QE_NUM_OF_BRGS) + return -EINVAL; + bp = (uint *)&qe_immr->brg.brgc1; + bp += brg; + + divisor = (BRG_CLK / rate); + if (divisor > QE_BRGC_DIVISOR_MAX + 1) { + div16 = 1; + divisor /= 16; + } + + *bp = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) | QE_BRGC_ENABLE; + __asm__ __volatile__("sync"); + + if (div16) { + *bp |= QE_BRGC_DIV16; + __asm__ __volatile__("sync"); + } + + return 0; +} + +/* Set ethernet MII clock master +*/ +int qe_set_mii_clk_src(int ucc_num) +{ + u32 cmxgcr; + + /* check if the UCC number is in range. */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) { + printf("%s: ucc num not in ranges\n", __FUNCTION__); + return -EINVAL; + } + + cmxgcr = in_be32(&qe_immr->qmx.cmxgcr); + cmxgcr &= ~QE_CMXGCR_MII_ENET_MNG_MASK; + cmxgcr |= (ucc_num <<QE_CMXGCR_MII_ENET_MNG_SHIFT); + out_be32(&qe_immr->qmx.cmxgcr, cmxgcr); + + return 0; +} + +#endif /* CONFIG_QE */ diff --git a/drivers/qe/qe.h b/drivers/qe/qe.h new file mode 100644 index 0000000..f7f8ed0 --- /dev/null +++ b/drivers/qe/qe.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __QE_H__ +#define __QE_H__ + +#include "common.h" + +#define QE_NUM_OF_SNUM 28 +#define QE_NUM_OF_BRGS 16 +#define UCC_MAX_NUM 8 + +#define QE_DATAONLY_BASE (uint)(128) +#define QE_DATAONLY_SIZE ((uint)(0xc000) - QE_DATAONLY_BASE) + +/* QE threads SNUM +*/ +typedef enum qe_snum_state { + QE_SNUM_STATE_USED, /* used */ + QE_SNUM_STATE_FREE /* free */ +} qe_snum_state_e; + +typedef struct qe_snum { + u8 num; /* snum */ + qe_snum_state_e state; /* state */ +} qe_snum_t; + +/* QE RISC allocation +*/ +typedef enum qe_risc_allocation { + QE_RISC_ALLOCATION_RISC1 = 1, /* RISC 1 */ + QE_RISC_ALLOCATION_RISC2 = 2, /* RISC 2 */ + QE_RISC_ALLOCATION_RISC1_AND_RISC2 = 3 /* RISC 1 or RISC 2 */ +} qe_risc_allocation_e; + +/* QE CECR commands for UCC fast. +*/ +#define QE_CR_FLG 0x00010000 +#define QE_RESET 0x80000000 +#define QE_INIT_TX_RX 0x00000000 +#define QE_INIT_RX 0x00000001 +#define QE_INIT_TX 0x00000002 +#define QE_ENTER_HUNT_MODE 0x00000003 +#define QE_STOP_TX 0x00000004 +#define QE_GRACEFUL_STOP_TX 0x00000005 +#define QE_RESTART_TX 0x00000006 +#define QE_SWITCH_COMMAND 0x00000007 +#define QE_SET_GROUP_ADDRESS 0x00000008 +#define QE_INSERT_CELL 0x00000009 +#define QE_ATM_TRANSMIT 0x0000000a +#define QE_CELL_POOL_GET 0x0000000b +#define QE_CELL_POOL_PUT 0x0000000c +#define QE_IMA_HOST_CMD 0x0000000d +#define QE_ATM_MULTI_THREAD_INIT 0x00000011 +#define QE_ASSIGN_PAGE 0x00000012 +#define QE_START_FLOW_CONTROL 0x00000014 +#define QE_STOP_FLOW_CONTROL 0x00000015 +#define QE_ASSIGN_PAGE_TO_DEVICE 0x00000016 +#define QE_GRACEFUL_STOP_RX 0x0000001a +#define QE_RESTART_RX 0x0000001b + +/* QE CECR Sub Block Code - sub block code of QE command. +*/ +#define QE_CR_SUBBLOCK_INVALID 0x00000000 +#define QE_CR_SUBBLOCK_USB 0x03200000 +#define QE_CR_SUBBLOCK_UCCFAST1 0x02000000 +#define QE_CR_SUBBLOCK_UCCFAST2 0x02200000 +#define QE_CR_SUBBLOCK_UCCFAST3 0x02400000 +#define QE_CR_SUBBLOCK_UCCFAST4 0x02600000 +#define QE_CR_SUBBLOCK_UCCFAST5 0x02800000 +#define QE_CR_SUBBLOCK_UCCFAST6 0x02a00000 +#define QE_CR_SUBBLOCK_UCCFAST7 0x02c00000 +#define QE_CR_SUBBLOCK_UCCFAST8 0x02e00000 +#define QE_CR_SUBBLOCK_UCCSLOW1 0x00000000 +#define QE_CR_SUBBLOCK_UCCSLOW2 0x00200000 +#define QE_CR_SUBBLOCK_UCCSLOW3 0x00400000 +#define QE_CR_SUBBLOCK_UCCSLOW4 0x00600000 +#define QE_CR_SUBBLOCK_UCCSLOW5 0x00800000 +#define QE_CR_SUBBLOCK_UCCSLOW6 0x00a00000 +#define QE_CR_SUBBLOCK_UCCSLOW7 0x00c00000 +#define QE_CR_SUBBLOCK_UCCSLOW8 0x00e00000 +#define QE_CR_SUBBLOCK_MCC1 0x03800000 +#define QE_CR_SUBBLOCK_MCC2 0x03a00000 +#define QE_CR_SUBBLOCK_MCC3 0x03000000 +#define QE_CR_SUBBLOCK_IDMA1 0x02800000 +#define QE_CR_SUBBLOCK_IDMA2 0x02a00000 +#define QE_CR_SUBBLOCK_IDMA3 0x02c00000 +#define QE_CR_SUBBLOCK_IDMA4 0x02e00000 +#define QE_CR_SUBBLOCK_HPAC 0x01e00000 +#define QE_CR_SUBBLOCK_SPI1 0x01400000 +#define QE_CR_SUBBLOCK_SPI2 0x01600000 +#define QE_CR_SUBBLOCK_RAND 0x01c00000 +#define QE_CR_SUBBLOCK_TIMER 0x01e00000 +#define QE_CR_SUBBLOCK_GENERAL 0x03c00000 + +/* QE CECR Protocol - For non-MCC, specifies mode for QE CECR command. +*/ +#define QE_CR_PROTOCOL_UNSPECIFIED 0x00 /* For all other protocols */ +#define QE_CR_PROTOCOL_HDLC_TRANSPARENT 0x00 +#define QE_CR_PROTOCOL_ATM_POS 0x0A +#define QE_CR_PROTOCOL_ETHERNET 0x0C +#define QE_CR_PROTOCOL_L2_SWITCH 0x0D +#define QE_CR_PROTOCOL_SHIFT 6 + +/* QE ASSIGN PAGE command +*/ +#define QE_CR_ASSIGN_PAGE_SNUM_SHIFT 17 + +/* Communication Direction. +*/ +typedef enum comm_dir { + COMM_DIR_NONE = 0, + COMM_DIR_RX = 1, + COMM_DIR_TX = 2, + COMM_DIR_RX_AND_TX = 3 +} comm_dir_e; + +/* Clocks and BRG's +*/ +typedef enum qe_clock { + QE_CLK_NONE = 0, + QE_BRG1, /* Baud Rate Generator 1 */ + QE_BRG2, /* Baud Rate Generator 2 */ + QE_BRG3, /* Baud Rate Generator 3 */ + QE_BRG4, /* Baud Rate Generator 4 */ + QE_BRG5, /* Baud Rate Generator 5 */ + QE_BRG6, /* Baud Rate Generator 6 */ + QE_BRG7, /* Baud Rate Generator 7 */ + QE_BRG8, /* Baud Rate Generator 8 */ + QE_BRG9, /* Baud Rate Generator 9 */ + QE_BRG10, /* Baud Rate Generator 10 */ + QE_BRG11, /* Baud Rate Generator 11 */ + QE_BRG12, /* Baud Rate Generator 12 */ + QE_BRG13, /* Baud Rate Generator 13 */ + QE_BRG14, /* Baud Rate Generator 14 */ + QE_BRG15, /* Baud Rate Generator 15 */ + QE_BRG16, /* Baud Rate Generator 16 */ + QE_CLK1, /* Clock 1 */ + QE_CLK2, /* Clock 2 */ + QE_CLK3, /* Clock 3 */ + QE_CLK4, /* Clock 4 */ + QE_CLK5, /* Clock 5 */ + QE_CLK6, /* Clock 6 */ + QE_CLK7, /* Clock 7 */ + QE_CLK8, /* Clock 8 */ + QE_CLK9, /* Clock 9 */ + QE_CLK10, /* Clock 10 */ + QE_CLK11, /* Clock 11 */ + QE_CLK12, /* Clock 12 */ + QE_CLK13, /* Clock 13 */ + QE_CLK14, /* Clock 14 */ + QE_CLK15, /* Clock 15 */ + QE_CLK16, /* Clock 16 */ + QE_CLK17, /* Clock 17 */ + QE_CLK18, /* Clock 18 */ + QE_CLK19, /* Clock 19 */ + QE_CLK20, /* Clock 20 */ + QE_CLK21, /* Clock 21 */ + QE_CLK22, /* Clock 22 */ + QE_CLK23, /* Clock 23 */ + QE_CLK24, /* Clock 24 */ + QE_CLK_DUMMY +} qe_clock_e; + +/* QE CMXGCR register +*/ +#define QE_CMXGCR_MII_ENET_MNG_MASK 0x00007000 +#define QE_CMXGCR_MII_ENET_MNG_SHIFT 12 + +/* QE CMXUCR registers + */ +#define QE_CMXUCR_TX_CLK_SRC_MASK 0x0000000F + +/* QE BRG configuration register +*/ +#define QE_BRGC_ENABLE 0x00010000 +#define QE_BRGC_DIVISOR_SHIFT 1 +#define QE_BRGC_DIVISOR_MAX 0xFFF +#define QE_BRGC_DIV16 1 + +/* QE SDMA registers +*/ +#define QE_SDSR_BER1 0x02000000 +#define QE_SDSR_BER2 0x01000000 + +#define QE_SDMR_GLB_1_MSK 0x80000000 +#define QE_SDMR_ADR_SEL 0x20000000 +#define QE_SDMR_BER1_MSK 0x02000000 +#define QE_SDMR_BER2_MSK 0x01000000 +#define QE_SDMR_EB1_MSK 0x00800000 +#define QE_SDMR_ER1_MSK 0x00080000 +#define QE_SDMR_ER2_MSK 0x00040000 +#define QE_SDMR_CEN_MASK 0x0000E000 +#define QE_SDMR_SBER_1 0x00000200 +#define QE_SDMR_SBER_2 0x00000200 +#define QE_SDMR_EB1_PR_MASK 0x000000C0 +#define QE_SDMR_ER1_PR 0x00000008 + +#define QE_SDMR_CEN_SHIFT 13 +#define QE_SDMR_EB1_PR_SHIFT 6 + +#define QE_SDTM_MSNUM_SHIFT 24 + +#define QE_SDEBCR_BA_MASK 0x01FFFFFF + +void qe_config_iopin(u8 port, u8 pin, int dir, int open_drain, int assign); +void qe_issue_cmd(uint cmd, uint sbc, u8 mcn, u32 cmd_data); +uint qe_muram_alloc(uint size, uint align); +void *qe_muram_addr(uint offset); +int qe_get_snum(void); +void qe_put_snum(u8 snum); +void qe_init(uint qe_base); +void qe_reset(void); +void qe_assign_page(uint snum, uint para_ram_base); +int qe_set_brg(uint brg, uint rate); +int qe_set_mii_clk_src(int ucc_num); + +#endif /* __QE_H__ */ diff --git a/drivers/qe/uccf.c b/drivers/qe/uccf.c new file mode 100644 index 0000000..25f7482 --- /dev/null +++ b/drivers/qe/uccf.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "common.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" +#include "uccf.h" + +#if defined(CONFIG_QE) +void ucc_fast_transmit_on_demand(ucc_fast_private_t *uccf) +{ + out_be16(&uccf->uf_regs->utodr, UCC_FAST_TOD); +} + +u32 ucc_fast_get_qe_cr_subblock(int ucc_num) +{ + switch (ucc_num) { + case 0: return QE_CR_SUBBLOCK_UCCFAST1; + case 1: return QE_CR_SUBBLOCK_UCCFAST2; + case 2: return QE_CR_SUBBLOCK_UCCFAST3; + case 3: return QE_CR_SUBBLOCK_UCCFAST4; + case 4: return QE_CR_SUBBLOCK_UCCFAST5; + case 5: return QE_CR_SUBBLOCK_UCCFAST6; + case 6: return QE_CR_SUBBLOCK_UCCFAST7; + case 7: return QE_CR_SUBBLOCK_UCCFAST8; + default: return QE_CR_SUBBLOCK_INVALID; + } +} + +static void ucc_get_cmxucr_reg(int ucc_num, volatile u32 **p_cmxucr, + u8 *reg_num, u8 *shift) +{ + switch (ucc_num) { + case 0: /* UCC1 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr1); + *reg_num = 1; + *shift = 16; + break; + case 2: /* UCC3 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr1); + *reg_num = 1; + *shift = 0; + break; + case 4: /* UCC5 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr2); + *reg_num = 2; + *shift = 16; + break; + case 6: /* UCC7 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr2); + *reg_num = 2; + *shift = 0; + break; + case 1: /* UCC2 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr3); + *reg_num = 3; + *shift = 16; + break; + case 3: /* UCC4 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr3); + *reg_num = 3; + *shift = 0; + break; + case 5: /* UCC6 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr4); + *reg_num = 4; + *shift = 16; + break; + case 7: /* UCC8 */ + *p_cmxucr = &(qe_immr->qmx.cmxucr4); + *reg_num = 4; + *shift = 0; + break; + default: + break; + } +} + +static int ucc_set_clk_src(int ucc_num, qe_clock_e clock, comm_dir_e mode) +{ + volatile u32 *p_cmxucr; + u8 reg_num; + u8 shift; + u32 clockBits; + u32 clockMask; + int source = -1; + + /* check if the UCC number is in range. */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) + return -EINVAL; + + if (! ((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) { + printf("%s: bad comm mode type passed\n", __FUNCTION__); + return -EINVAL; + } + + ucc_get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); + + switch (reg_num) { + case 1: + switch (clock) { + case QE_BRG1: source = 1; break; + case QE_BRG2: source = 2; break; + case QE_BRG7: source = 3; break; + case QE_BRG8: source = 4; break; + case QE_CLK9: source = 5; break; + case QE_CLK10: source = 6; break; + case QE_CLK11: source = 7; break; + case QE_CLK12: source = 8; break; + case QE_CLK15: source = 9; break; + case QE_CLK16: source = 10; break; + default: source = -1; break; + } + break; + case 2: + switch (clock) { + case QE_BRG5: source = 1; break; + case QE_BRG6: source = 2; break; + case QE_BRG7: source = 3; break; + case QE_BRG8: source = 4; break; + case QE_CLK13: source = 5; break; + case QE_CLK14: source = 6; break; + case QE_CLK19: source = 7; break; + case QE_CLK20: source = 8; break; + case QE_CLK15: source = 9; break; + case QE_CLK16: source = 10; break; + default: source = -1; break; + } + break; + case 3: + switch (clock) { + case QE_BRG9: source = 1; break; + case QE_BRG10: source = 2; break; + case QE_BRG15: source = 3; break; + case QE_BRG16: source = 4; break; + case QE_CLK3: source = 5; break; + case QE_CLK4: source = 6; break; + case QE_CLK17: source = 7; break; + case QE_CLK18: source = 8; break; + case QE_CLK7: source = 9; break; + case QE_CLK8: source = 10; break; + case QE_CLK16: source = 11; break; + default: source = -1; break; + } + break; + case 4: + switch (clock) { + case QE_BRG13: source = 1; break; + case QE_BRG14: source = 2; break; + case QE_BRG15: source = 3; break; + case QE_BRG16: source = 4; break; + case QE_CLK5: source = 5; break; + case QE_CLK6: source = 6; break; + case QE_CLK21: source = 7; break; + case QE_CLK22: source = 8; break; + case QE_CLK7: source = 9; break; + case QE_CLK8: source = 10; break; + case QE_CLK16: source = 11; break; + default: source = -1; break; + } + break; + default: + source = -1; + break; + } + + if (source == -1) { + printf("%s: Bad combination of clock and UCC\n", __FUNCTION__); + return -ENOENT; + } + + clockBits = (u32) source; + clockMask = QE_CMXUCR_TX_CLK_SRC_MASK; + if (mode == COMM_DIR_RX) { + clockBits <<= 4; /* Rx field is 4 bits to left of Tx field */ + clockMask <<= 4; /* Rx field is 4 bits to left of Tx field */ + } + clockBits <<= shift; + clockMask <<= shift; + + out_be32(p_cmxucr, (in_be32(p_cmxucr) & ~clockMask) | clockBits); + + return 0; +} + +static uint ucc_get_reg_baseaddr(int ucc_num) +{ + uint base = 0; + + /* check if the UCC number is in range */ + if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) { + printf("%s: the UCC num not in ranges\n", __FUNCTION__); + return 0; + } + + switch (ucc_num) { + case 0: base = 0x00002000; break; + case 1: base = 0x00003000; break; + case 2: base = 0x00002200; break; + case 3: base = 0x00003200; break; + case 4: base = 0x00002400; break; + case 5: base = 0x00003400; break; + case 6: base = 0x00002600; break; + case 7: base = 0x00003600; break; + default: break; + } + + base = (uint)qe_immr + base; + return base; +} + +void ucc_fast_enable(ucc_fast_private_t *uccf, comm_dir_e mode) +{ + ucc_fast_t *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Enable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr |= UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 1; + } + if (mode & COMM_DIR_RX) { + gumr |= UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 1; + } + out_be32(&uf_regs->gumr, gumr); +} + +void ucc_fast_disable(ucc_fast_private_t *uccf, comm_dir_e mode) +{ + ucc_fast_t *uf_regs; + u32 gumr; + + uf_regs = uccf->uf_regs; + + /* Disable reception and/or transmission on this UCC. */ + gumr = in_be32(&uf_regs->gumr); + if (mode & COMM_DIR_TX) { + gumr &= ~UCC_FAST_GUMR_ENT; + uccf->enabled_tx = 0; + } + if (mode & COMM_DIR_RX) { + gumr &= ~UCC_FAST_GUMR_ENR; + uccf->enabled_rx = 0; + } + out_be32(&uf_regs->gumr, gumr); +} + +int ucc_fast_init(ucc_fast_info_t *uf_info, ucc_fast_private_t **uccf_ret) +{ + ucc_fast_private_t *uccf; + ucc_fast_t *uf_regs; + + if (!uf_info) + return -EINVAL; + + if ((uf_info->ucc_num < 0) || (uf_info->ucc_num > UCC_MAX_NUM - 1)) { + printf("%s: Illagal UCC number!\n", __FUNCTION__); + return -EINVAL; + } + + uccf = (ucc_fast_private_t *)malloc(sizeof(ucc_fast_private_t)); + if (!uccf) { + printf("%s: No memory for UCC fast data structure!\n", + __FUNCTION__); + return -ENOMEM; + } + memset(uccf, 0, sizeof(ucc_fast_private_t)); + + /* Save fast UCC structure */ + uccf->uf_info = uf_info; + uccf->uf_regs = (ucc_fast_t *)ucc_get_reg_baseaddr(uf_info->ucc_num); + + if (uccf->uf_regs == NULL) { + printf("%s: No memory map for UCC fast controller!\n", + __FUNCTION__); + return -ENOMEM; + } + + uccf->enabled_tx = 0; + uccf->enabled_rx = 0; + + uf_regs = uccf->uf_regs; + uccf->p_ucce = (u32 *) &(uf_regs->ucce); + uccf->p_uccm = (u32 *) &(uf_regs->uccm); + + /* Init GUEMR register, UCC both Rx and Tx is Fast protocol */ + out_8(&uf_regs->guemr, UCC_GUEMR_SET_RESERVED3 | UCC_GUEMR_MODE_FAST_RX + | UCC_GUEMR_MODE_FAST_TX); + + /* Set GUMR, disable UCC both Rx and Tx, Ethernet protocol */ + out_be32(&uf_regs->gumr, UCC_FAST_GUMR_ETH); + + /* Set the Giga ethernet VFIFO stuff */ + if (uf_info->eth_type == GIGA_ETH) { + /* Allocate memory for Tx Virtual Fifo */ + uccf->ucc_fast_tx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_UTFS_GIGA_INIT, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* Allocate memory for Rx Virtual Fifo */ + uccf->ucc_fast_rx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_URFS_GIGA_INIT + + UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, + uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, + uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, UCC_GETH_URFS_GIGA_INIT); + out_be16(&uf_regs->urfet, UCC_GETH_URFET_GIGA_INIT); + out_be16(&uf_regs->urfset, UCC_GETH_URFSET_GIGA_INIT); + out_be16(&uf_regs->utfs, UCC_GETH_UTFS_GIGA_INIT); + out_be16(&uf_regs->utfet, UCC_GETH_UTFET_GIGA_INIT); + out_be16(&uf_regs->utftt, UCC_GETH_UTFTT_GIGA_INIT); + } + + /* Set the Fast ethernet VFIFO stuff */ + if (uf_info->eth_type == FAST_ETH) { + /* Allocate memory for Tx Virtual Fifo */ + uccf->ucc_fast_tx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_UTFS_INIT, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* Allocate memory for Rx Virtual Fifo */ + uccf->ucc_fast_rx_virtual_fifo_base_offset = + qe_muram_alloc(UCC_GETH_URFS_INIT + + UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD, + UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT); + + /* utfb, urfb are offsets from MURAM base */ + out_be32(&uf_regs->utfb, + uccf->ucc_fast_tx_virtual_fifo_base_offset); + out_be32(&uf_regs->urfb, + uccf->ucc_fast_rx_virtual_fifo_base_offset); + + /* Set Virtual Fifo registers */ + out_be16(&uf_regs->urfs, UCC_GETH_URFS_INIT); + out_be16(&uf_regs->urfet, UCC_GETH_URFET_INIT); + out_be16(&uf_regs->urfset, UCC_GETH_URFSET_INIT); + out_be16(&uf_regs->utfs, UCC_GETH_UTFS_INIT); + out_be16(&uf_regs->utfet, UCC_GETH_UTFET_INIT); + out_be16(&uf_regs->utftt, UCC_GETH_UTFTT_INIT); + } + + /* Rx clock routing */ + if (uf_info->rx_clock != QE_CLK_NONE) { + if (ucc_set_clk_src(uf_info->ucc_num, + uf_info->rx_clock, COMM_DIR_RX)) { + printf("%s: Illegal value for parameter 'RxClock'.\n", + __FUNCTION__); + return -EINVAL; + } + } + + /* Tx clock routing */ + if (uf_info->tx_clock != QE_CLK_NONE) { + if (ucc_set_clk_src(uf_info->ucc_num, + uf_info->tx_clock, COMM_DIR_TX)) { + printf("%s: Illegal value for parameter 'TxClock'.\n", + __FUNCTION__); + return -EINVAL; + } + } + + /* Clear interrupt mask register to disable all of interrupts */ + out_be32(&uf_regs->uccm, 0x0); + + /* Writing '1' to clear all of envents */ + out_be32(&uf_regs->ucce, 0xffffffff); + + *uccf_ret = uccf; + return 0; +} +#endif /* CONFIG_QE */ diff --git a/drivers/qe/uccf.h b/drivers/qe/uccf.h new file mode 100644 index 0000000..1ff9e1d --- /dev/null +++ b/drivers/qe/uccf.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __UCCF_H__ +#define __UCCF_H__ + +#include "common.h" +#include "qe.h" + +/* Fast or Giga ethernet +*/ +typedef enum enet_type { + FAST_ETH, + GIGA_ETH, +} enet_type_e; + +/* General UCC Extended Mode Register +*/ +#define UCC_GUEMR_MODE_MASK_RX 0x02 +#define UCC_GUEMR_MODE_MASK_TX 0x01 +#define UCC_GUEMR_MODE_FAST_RX 0x02 +#define UCC_GUEMR_MODE_FAST_TX 0x01 +#define UCC_GUEMR_MODE_SLOW_RX 0x00 +#define UCC_GUEMR_MODE_SLOW_TX 0x00 +#define UCC_GUEMR_SET_RESERVED3 0x10 /* Bit 3 must be set 1 */ + +/* General UCC FAST Mode Register +*/ +#define UCC_FAST_GUMR_TCI 0x20000000 +#define UCC_FAST_GUMR_TRX 0x10000000 +#define UCC_FAST_GUMR_TTX 0x08000000 +#define UCC_FAST_GUMR_CDP 0x04000000 +#define UCC_FAST_GUMR_CTSP 0x02000000 +#define UCC_FAST_GUMR_CDS 0x01000000 +#define UCC_FAST_GUMR_CTSS 0x00800000 +#define UCC_FAST_GUMR_TXSY 0x00020000 +#define UCC_FAST_GUMR_RSYN 0x00010000 +#define UCC_FAST_GUMR_RTSM 0x00002000 +#define UCC_FAST_GUMR_REVD 0x00000400 +#define UCC_FAST_GUMR_ENR 0x00000020 +#define UCC_FAST_GUMR_ENT 0x00000010 + +/* GUMR [MODE] bit maps +*/ +#define UCC_FAST_GUMR_HDLC 0x00000000 +#define UCC_FAST_GUMR_QMC 0x00000002 +#define UCC_FAST_GUMR_UART 0x00000004 +#define UCC_FAST_GUMR_BISYNC 0x00000008 +#define UCC_FAST_GUMR_ATM 0x0000000a +#define UCC_FAST_GUMR_ETH 0x0000000c + +/* Transmit On Demand (UTORD) +*/ +#define UCC_SLOW_TOD 0x8000 +#define UCC_FAST_TOD 0x8000 + +/* Fast Ethernet (10/100 Mbps) +*/ +#define UCC_GETH_URFS_INIT 512 /* Rx virtual FIFO size */ +#define UCC_GETH_URFET_INIT 256 /* 1/2 urfs */ +#define UCC_GETH_URFSET_INIT 384 /* 3/4 urfs */ +#define UCC_GETH_UTFS_INIT 512 /* Tx virtual FIFO size */ +#define UCC_GETH_UTFET_INIT 256 /* 1/2 utfs */ +#define UCC_GETH_UTFTT_INIT 128 + +/* Gigabit Ethernet (1000 Mbps) +*/ +#define UCC_GETH_URFS_GIGA_INIT 4096/*2048*/ /* Rx virtual FIFO size */ +#define UCC_GETH_URFET_GIGA_INIT 2048/*1024*/ /* 1/2 urfs */ +#define UCC_GETH_URFSET_GIGA_INIT 3072/*1536*/ /* 3/4 urfs */ +#define UCC_GETH_UTFS_GIGA_INIT 8192/*2048*/ /* Tx virtual FIFO size */ +#define UCC_GETH_UTFET_GIGA_INIT 4096/*1024*/ /* 1/2 utfs */ +#define UCC_GETH_UTFTT_GIGA_INIT 0x400/*0x40*/ /* */ + +/* UCC fast alignment +*/ +#define UCC_FAST_RX_ALIGN 4 +#define UCC_FAST_MRBLR_ALIGNMENT 4 +#define UCC_FAST_VIRT_FIFO_REGS_ALIGNMENT 8 + +/* Sizes +*/ +#define UCC_FAST_RX_VIRTUAL_FIFO_SIZE_PAD 8 + +/* UCC fast structure. +*/ +typedef struct ucc_fast_info { + int ucc_num; + qe_clock_e rx_clock; + qe_clock_e tx_clock; + enet_type_e eth_type; +} ucc_fast_info_t; + +typedef struct ucc_fast_private { + ucc_fast_info_t *uf_info; + ucc_fast_t *uf_regs; /* a pointer to memory map of UCC regs */ + u32 *p_ucce; /* a pointer to the event register */ + u32 *p_uccm; /* a pointer to the mask register */ + int enabled_tx; /* whether UCC is enabled for Tx (ENT) */ + int enabled_rx; /* whether UCC is enabled for Rx (ENR) */ + u32 ucc_fast_tx_virtual_fifo_base_offset; + u32 ucc_fast_rx_virtual_fifo_base_offset; +} ucc_fast_private_t; + +void ucc_fast_transmit_on_demand(ucc_fast_private_t *uccf); +u32 ucc_fast_get_qe_cr_subblock(int ucc_num); +void ucc_fast_enable(ucc_fast_private_t *uccf, comm_dir_e mode); +void ucc_fast_disable(ucc_fast_private_t *uccf, comm_dir_e mode); +int ucc_fast_init(ucc_fast_info_t *uf_info, ucc_fast_private_t **uccf_ret); + +#endif /* __UCCF_H__ */ diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c new file mode 100644 index 0000000..f640c81 --- /dev/null +++ b/drivers/qe/uec.c @@ -0,0 +1,1266 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "common.h" +#include "net.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/io.h" +#include "asm/immap_qe.h" +#include "qe.h" +#include "uccf.h" +#include "uec.h" +#include "uec_phy.h" + +#if defined(CONFIG_QE) + +#ifdef CONFIG_UEC_ETH1 +static uec_info_t eth1_uec_info = { + .uf_info = { + .ucc_num = CFG_UEC1_UCC_NUM, + .rx_clock = CFG_UEC1_RX_CLK, + .tx_clock = CFG_UEC1_TX_CLK, + .eth_type = CFG_UEC1_ETH_TYPE, + }, + .num_threads_tx = UEC_NUM_OF_THREADS_4, + .num_threads_rx = UEC_NUM_OF_THREADS_4, + .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, + .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, + .tx_bd_ring_len = 16, + .rx_bd_ring_len = 16, + .phy_address = CFG_UEC1_PHY_ADDR, + .enet_interface = CFG_UEC1_INTERFACE_MODE, +}; +#endif +#ifdef CONFIG_UEC_ETH2 +static uec_info_t eth2_uec_info = { + .uf_info = { + .ucc_num = CFG_UEC2_UCC_NUM, + .rx_clock = CFG_UEC2_RX_CLK, + .tx_clock = CFG_UEC2_TX_CLK, + .eth_type = CFG_UEC2_ETH_TYPE, + }, + .num_threads_tx = UEC_NUM_OF_THREADS_4, + .num_threads_rx = UEC_NUM_OF_THREADS_4, + .riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, + .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2, + .tx_bd_ring_len = 16, + .rx_bd_ring_len = 16, + .phy_address = CFG_UEC2_PHY_ADDR, + .enet_interface = CFG_UEC2_INTERFACE_MODE, +}; +#endif + +static int uec_mac_enable(uec_private_t *uec, comm_dir_e mode) +{ + uec_t *uec_regs; + u32 maccfg1; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + maccfg1 = in_be32(&uec_regs->maccfg1); + + if (mode & COMM_DIR_TX) { + maccfg1 |= MACCFG1_ENABLE_TX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_tx_enabled = 1; + } + + if (mode & COMM_DIR_RX) { + maccfg1 |= MACCFG1_ENABLE_RX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_rx_enabled = 1; + } + + return 0; +} + +static int uec_mac_disable(uec_private_t *uec, comm_dir_e mode) +{ + uec_t *uec_regs; + u32 maccfg1; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + maccfg1 = in_be32(&uec_regs->maccfg1); + + if (mode & COMM_DIR_TX) { + maccfg1 &= ~MACCFG1_ENABLE_TX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_tx_enabled = 0; + } + + if (mode & COMM_DIR_RX) { + maccfg1 &= ~MACCFG1_ENABLE_RX; + out_be32(&uec_regs->maccfg1, maccfg1); + uec->mac_rx_enabled = 0; + } + + return 0; +} + +static int uec_graceful_stop_tx(uec_private_t *uec) +{ + ucc_fast_t *uf_regs; + u32 cecr_subblock; + u32 ucce; + + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + uf_regs = uec->uccf->uf_regs; + + /* Clear the grace stop event */ + out_be32(&uf_regs->ucce, UCCE_GRA); + + /* Issue host command */ + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + /* Wait for command to complete */ + do { + ucce = in_be32(&uf_regs->ucce); + } while (! (ucce & UCCE_GRA)); + + uec->grace_stopped_tx = 1; + + return 0; +} + +static int uec_graceful_stop_rx(uec_private_t *uec) +{ + u32 cecr_subblock; + u8 ack; + + if (!uec) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + if (!uec->p_rx_glbl_pram) { + printf("%s: No init rx global parameter\n", __FUNCTION__); + return -EINVAL; + } + + /* Clear acknowledge bit */ + ack = uec->p_rx_glbl_pram->rxgstpack; + ack &= ~GRACEFUL_STOP_ACKNOWLEDGE_RX; + uec->p_rx_glbl_pram->rxgstpack = ack; + + /* Keep issuing cmd and checking ack bit until it is asserted */ + do { + /* Issue host command */ + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + ack = uec->p_rx_glbl_pram->rxgstpack; + } while (! (ack & GRACEFUL_STOP_ACKNOWLEDGE_RX )); + + uec->grace_stopped_rx = 1; + + return 0; +} + +static int uec_restart_tx(uec_private_t *uec) +{ + u32 cecr_subblock; + + if (!uec || !uec->uec_info) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_RESTART_TX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + uec->grace_stopped_tx = 0; + + return 0; +} + +static int uec_restart_rx(uec_private_t *uec) +{ + u32 cecr_subblock; + + if (!uec || !uec->uec_info) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + + cecr_subblock = + ucc_fast_get_qe_cr_subblock(uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(QE_RESTART_RX, cecr_subblock, + (u8)QE_CR_PROTOCOL_ETHERNET, 0); + + uec->grace_stopped_rx = 0; + + return 0; +} + +static int uec_open(uec_private_t *uec, comm_dir_e mode) +{ + ucc_fast_private_t *uccf; + + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + uccf = uec->uccf; + + /* check if the UCC number is in range. */ + if (uec->uec_info->uf_info.ucc_num >= UCC_MAX_NUM) { + printf("%s: ucc_num out of range.\n", __FUNCTION__); + return -EINVAL; + } + + /* Enable MAC */ + uec_mac_enable(uec, mode); + + /* Enable UCC fast */ + ucc_fast_enable(uccf, mode); + + /* RISC microcode start */ + if ((mode & COMM_DIR_TX) && uec->grace_stopped_tx) { + uec_restart_tx(uec); + } + if ((mode & COMM_DIR_RX) && uec->grace_stopped_rx) { + uec_restart_rx(uec); + } + + return 0; +} + +static int uec_stop(uec_private_t *uec, comm_dir_e mode) +{ + ucc_fast_private_t *uccf; + + if (!uec || !uec->uccf) { + printf("%s: No handle passed.\n", __FUNCTION__); + return -EINVAL; + } + uccf = uec->uccf; + + /* check if the UCC number is in range. */ + if (uec->uec_info->uf_info.ucc_num >= UCC_MAX_NUM) { + printf("%s: ucc_num out of range.\n", __FUNCTION__); + return -EINVAL; + } + /* Stop any transmissions */ + if ((mode & COMM_DIR_TX) && !uec->grace_stopped_tx) { + uec_graceful_stop_tx(uec); + } + /* Stop any receptions */ + if ((mode & COMM_DIR_RX) && !uec->grace_stopped_rx) { + uec_graceful_stop_rx(uec); + } + + /* Disable the UCC fast */ + ucc_fast_disable(uec->uccf, mode); + + /* Disable the MAC */ + uec_mac_disable(uec, mode); + + return 0; +} + +static int uec_set_mac_duplex(uec_private_t *uec, int duplex) +{ + uec_t *uec_regs; + u32 maccfg2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + uec_regs = uec->uec_regs; + + if (duplex == DUPLEX_HALF) { + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 &= ~MACCFG2_FDX; + out_be32(&uec_regs->maccfg2, maccfg2); + } + + if (duplex == DUPLEX_FULL) { + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 |= MACCFG2_FDX; + out_be32(&uec_regs->maccfg2, maccfg2); + } + + return 0; +} + +static int uec_set_mac_if_mode(uec_private_t *uec, enet_interface_e if_mode) +{ + enet_interface_e enet_if_mode; + uec_info_t *uec_info; + uec_t *uec_regs; + u32 upsmr; + u32 maccfg2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_info = uec->uec_info; + uec_regs = uec->uec_regs; + enet_if_mode = if_mode; + + maccfg2 = in_be32(&uec_regs->maccfg2); + maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK; + + upsmr = in_be32(&uec->uccf->uf_regs->upsmr); + upsmr &= ~(UPSMR_RPM | UPSMR_TBIM | UPSMR_R10M | UPSMR_RMM); + + switch (enet_if_mode) { + case ENET_100_MII: + case ENET_10_MII: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + break; + case ENET_1000_GMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + break; + case ENET_1000_TBI: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + upsmr |= UPSMR_TBIM; + break; + case ENET_1000_RTBI: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + upsmr |= (UPSMR_RPM | UPSMR_TBIM); + break; + case ENET_1000_RGMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; + upsmr |= UPSMR_RPM; + break; + case ENET_100_RGMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + upsmr |= UPSMR_RPM; + break; + case ENET_10_RGMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + upsmr |= (UPSMR_RPM | UPSMR_R10M); + break; + case ENET_100_RMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + upsmr |= UPSMR_RMM; + break; + case ENET_10_RMII: + maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; + upsmr |= (UPSMR_R10M | UPSMR_RMM); + break; + default: + return -EINVAL; + break; + } + out_be32(&uec_regs->maccfg2, maccfg2); + out_be32(&uec->uccf->uf_regs->upsmr, upsmr); + + return 0; +} + +static int init_mii_management_configuration(uec_t *uec_regs) +{ + uint timeout = 0x1000; + u32 miimcfg = 0; + + miimcfg = in_be32(&uec_regs->miimcfg); + miimcfg |= MIIMCFG_MNGMNT_CLC_DIV_INIT_VALUE; + out_be32(&uec_regs->miimcfg, miimcfg); + + /* Wait until the bus is free */ + while ((in_be32(&uec_regs->miimcfg) & MIIMIND_BUSY) && timeout--); + if (timeout <= 0) { + printf("%s: The MII Bus is stuck!", __FUNCTION__); + return -ETIMEDOUT; + } + + return 0; +} + +static int init_phy(struct eth_device *dev) +{ + uec_private_t *uec; + uec_t *uec_regs; + struct uec_mii_info *mii_info; + struct phy_info *curphy; + int err; + + uec = (uec_private_t *)dev->priv; + uec_regs = uec->uec_regs; + + uec->oldlink = 0; + uec->oldspeed = 0; + uec->oldduplex = -1; + + mii_info = malloc(sizeof(*mii_info)); + if (!mii_info) { + printf("%s: Could not allocate mii_info", dev->name); + return -ENOMEM; + } + memset(mii_info, 0, sizeof(*mii_info)); + + mii_info->speed = SPEED_1000; + mii_info->duplex = DUPLEX_FULL; + mii_info->pause = 0; + mii_info->link = 1; + + mii_info->advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + mii_info->autoneg = 1; + mii_info->mii_id = uec->uec_info->phy_address; + mii_info->dev = dev; + + mii_info->mdio_read = &read_phy_reg; + mii_info->mdio_write = &write_phy_reg; + + uec->mii_info = mii_info; + + if (init_mii_management_configuration(uec_regs)) { + printf("%s: The MII Bus is stuck!", dev->name); + err = -1; + goto bus_fail; + } + + /* get info for this PHY */ + curphy = get_phy_info(uec->mii_info); + if (!curphy) { + printf("%s: No PHY found", dev->name); + err = -1; + goto no_phy; + } + + mii_info->phyinfo = curphy; + + /* Run the commands which initialize the PHY */ + if (curphy->init) { + err = curphy->init(uec->mii_info); + if (err) + goto phy_init_fail; + } + + return 0; + +phy_init_fail: +no_phy: +bus_fail: + free(mii_info); + return err; +} + +static void adjust_link(struct eth_device *dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + uec_t *uec_regs; + struct uec_mii_info *mii_info = uec->mii_info; + + extern void change_phy_interface_mode(struct eth_device *dev, + enet_interface_e mode); + uec_regs = uec->uec_regs; + + if (mii_info->link) { + /* Now we make sure that we can be in full duplex mode. + * If not, we operate in half-duplex mode. */ + if (mii_info->duplex != uec->oldduplex) { + if (!(mii_info->duplex)) { + uec_set_mac_duplex(uec, DUPLEX_HALF); + printf("%s: Half Duplex\n", dev->name); + } else { + uec_set_mac_duplex(uec, DUPLEX_FULL); + printf("%s: Full Duplex\n", dev->name); + } + uec->oldduplex = mii_info->duplex; + } + + if (mii_info->speed != uec->oldspeed) { + switch (mii_info->speed) { + case 1000: + break; + case 100: + printf ("switching to rgmii 100\n"); + /* change phy to rgmii 100 */ + change_phy_interface_mode(dev, + ENET_100_RGMII); + /* change the MAC interface mode */ + uec_set_mac_if_mode(uec,ENET_100_RGMII); + break; + case 10: + printf ("switching to rgmii 10\n"); + /* change phy to rgmii 10 */ + change_phy_interface_mode(dev, + ENET_10_RGMII); + /* change the MAC interface mode */ + uec_set_mac_if_mode(uec,ENET_10_RGMII); + break; + default: + printf("%s: Ack,Speed(%d)is illegal\n", + dev->name, mii_info->speed); + break; + } + + printf("%s: Speed %dBT\n", dev->name, mii_info->speed); + uec->oldspeed = mii_info->speed; + } + + if (!uec->oldlink) { + printf("%s: Link is up\n", dev->name); + uec->oldlink = 1; + } + + } else { /* if (mii_info->link) */ + if (uec->oldlink) { + printf("%s: Link is down\n", dev->name); + uec->oldlink = 0; + uec->oldspeed = 0; + uec->oldduplex = -1; + } + } +} + +static void phy_change(struct eth_device *dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + uec_t *uec_regs; + int result = 0; + + uec_regs = uec->uec_regs; + + /* Delay 5s to give the PHY a chance to change the register state */ + udelay(5000000); + + /* Update the link, speed, duplex */ + result = uec->mii_info->phyinfo->read_status(uec->mii_info); + + /* Adjust the interface according to speed */ + if ((0 == result) || (uec->mii_info->link == 0)) { + adjust_link(dev); + } +} + +static int uec_set_mac_address(uec_private_t *uec, u8 *mac_addr) +{ + uec_t *uec_regs; + u32 mac_addr1; + u32 mac_addr2; + + if (!uec) { + printf("%s: uec not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_regs = uec->uec_regs; + + /* if a station address of 0x12345678ABCD, perform a write to + MACSTNADDR1 of 0xCDAB7856, + MACSTNADDR2 of 0x34120000 */ + + mac_addr1 = (mac_addr[5] << 24) | (mac_addr[4] << 16) | \ + (mac_addr[3] << 8) | (mac_addr[2]); + out_be32(&uec_regs->macstnaddr1, mac_addr1); + + mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000; + out_be32(&uec_regs->macstnaddr2, mac_addr2); + + return 0; +} + +static int uec_convert_threads_num(uec_num_of_threads_e threads_num, + int *threads_num_ret) +{ + int num_threads_numerica; + + switch (threads_num) { + case UEC_NUM_OF_THREADS_1: + num_threads_numerica = 1; + break; + case UEC_NUM_OF_THREADS_2: + num_threads_numerica = 2; + break; + case UEC_NUM_OF_THREADS_4: + num_threads_numerica = 4; + break; + case UEC_NUM_OF_THREADS_6: + num_threads_numerica = 6; + break; + case UEC_NUM_OF_THREADS_8: + num_threads_numerica = 8; + break; + default: + printf("%s: Bad number of threads value.", + __FUNCTION__); + return -EINVAL; + } + + *threads_num_ret = num_threads_numerica; + + return 0; +} + +static void uec_init_tx_parameter(uec_private_t *uec, int num_threads_tx) +{ + uec_info_t *uec_info; + u32 end_bd; + u8 bmrx = 0; + int i; + + uec_info = uec->uec_info; + + /* Alloc global Tx parameter RAM page */ + uec->tx_glbl_pram_offset = qe_muram_alloc( + sizeof(uec_tx_global_pram_t), + UEC_TX_GLOBAL_PRAM_ALIGNMENT); + uec->p_tx_glbl_pram = (uec_tx_global_pram_t *) + qe_muram_addr(uec->tx_glbl_pram_offset); + + /* Zero the global Tx prameter RAM */ + memset(uec->p_tx_glbl_pram, 0, sizeof(uec_tx_global_pram_t)); + + /* Init global Tx parameter RAM */ + + /* TEMODER, RMON statistics disable, one Tx queue */ + out_be16(&uec->p_tx_glbl_pram->temoder, TEMODER_INIT_VALUE); + + /* SQPTR */ + uec->send_q_mem_reg_offset = qe_muram_alloc( + sizeof(uec_send_queue_qd_t), + UEC_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT); + uec->p_send_q_mem_reg = (uec_send_queue_mem_region_t *) + qe_muram_addr(uec->send_q_mem_reg_offset); + out_be32(&uec->p_tx_glbl_pram->sqptr, uec->send_q_mem_reg_offset); + + /* Setup the table with TxBDs ring */ + end_bd = (u32)uec->p_tx_bd_ring + (uec_info->tx_bd_ring_len - 1) + * SIZEOFBD; + out_be32(&uec->p_send_q_mem_reg->sqqd[0].bd_ring_base, + (u32)(uec->p_tx_bd_ring)); + out_be32(&uec->p_send_q_mem_reg->sqqd[0].last_bd_completed_address, + end_bd); + + /* Scheduler Base Pointer, we have only one Tx queue, no need it */ + out_be32(&uec->p_tx_glbl_pram->schedulerbasepointer, 0); + + /* TxRMON Base Pointer, TxRMON disable, we don't need it */ + out_be32(&uec->p_tx_glbl_pram->txrmonbaseptr, 0); + + /* TSTATE, global snooping, big endian, the CSB bus selected */ + bmrx = BMR_INIT_VALUE; + out_be32(&uec->p_tx_glbl_pram->tstate, ((u32)(bmrx) << BMR_SHIFT)); + + /* IPH_Offset */ + for (i = 0; i < MAX_IPH_OFFSET_ENTRY; i++) { + out_8(&uec->p_tx_glbl_pram->iphoffset[i], 0); + } + + /* VTAG table */ + for (i = 0; i < UEC_TX_VTAG_TABLE_ENTRY_MAX; i++) { + out_be32(&uec->p_tx_glbl_pram->vtagtable[i], 0); + } + + /* TQPTR */ + uec->thread_dat_tx_offset = qe_muram_alloc( + num_threads_tx * sizeof(uec_thread_data_tx_t) + + 32 *(num_threads_tx == 1), UEC_THREAD_DATA_ALIGNMENT); + + uec->p_thread_data_tx = (uec_thread_data_tx_t *) + qe_muram_addr(uec->thread_dat_tx_offset); + out_be32(&uec->p_tx_glbl_pram->tqptr, uec->thread_dat_tx_offset); +} + +static void uec_init_rx_parameter(uec_private_t *uec, int num_threads_rx) +{ + u8 bmrx = 0; + int i; + uec_82xx_address_filtering_pram_t *p_af_pram; + + /* Allocate global Rx parameter RAM page */ + uec->rx_glbl_pram_offset = qe_muram_alloc( + sizeof(uec_rx_global_pram_t), UEC_RX_GLOBAL_PRAM_ALIGNMENT); + uec->p_rx_glbl_pram = (uec_rx_global_pram_t *) + qe_muram_addr(uec->rx_glbl_pram_offset); + + /* Zero Global Rx parameter RAM */ + memset(uec->p_rx_glbl_pram, 0, sizeof(uec_rx_global_pram_t)); + + /* Init global Rx parameter RAM */ + /* REMODER, Extended feature mode disable, VLAN disable, + LossLess flow control disable, Receive firmware statisic disable, + Extended address parsing mode disable, One Rx queues, + Dynamic maximum/minimum frame length disable, IP checksum check + disable, IP address alignment disable + */ + out_be32(&uec->p_rx_glbl_pram->remoder, REMODER_INIT_VALUE); + + /* RQPTR */ + uec->thread_dat_rx_offset = qe_muram_alloc( + num_threads_rx * sizeof(uec_thread_data_rx_t), + UEC_THREAD_DATA_ALIGNMENT); + uec->p_thread_data_rx = (uec_thread_data_rx_t *) + qe_muram_addr(uec->thread_dat_rx_offset); + out_be32(&uec->p_rx_glbl_pram->rqptr, uec->thread_dat_rx_offset); + + /* Type_or_Len */ + out_be16(&uec->p_rx_glbl_pram->typeorlen, 3072); + + /* RxRMON base pointer, we don't need it */ + out_be32(&uec->p_rx_glbl_pram->rxrmonbaseptr, 0); + + /* IntCoalescingPTR, we don't need it, no interrupt */ + out_be32(&uec->p_rx_glbl_pram->intcoalescingptr, 0); + + /* RSTATE, global snooping, big endian, the CSB bus selected */ + bmrx = BMR_INIT_VALUE; + out_8(&uec->p_rx_glbl_pram->rstate, bmrx); + + /* MRBLR */ + out_be16(&uec->p_rx_glbl_pram->mrblr, MAX_RXBUF_LEN); + + /* RBDQPTR */ + uec->rx_bd_qs_tbl_offset = qe_muram_alloc( + sizeof(uec_rx_bd_queues_entry_t) + \ + sizeof(uec_rx_prefetched_bds_t), + UEC_RX_BD_QUEUES_ALIGNMENT); + uec->p_rx_bd_qs_tbl = (uec_rx_bd_queues_entry_t *) + qe_muram_addr(uec->rx_bd_qs_tbl_offset); + + /* Zero it */ + memset(uec->p_rx_bd_qs_tbl, 0, sizeof(uec_rx_bd_queues_entry_t) + \ + sizeof(uec_rx_prefetched_bds_t)); + out_be32(&uec->p_rx_glbl_pram->rbdqptr, uec->rx_bd_qs_tbl_offset); + out_be32(&uec->p_rx_bd_qs_tbl->externalbdbaseptr, + (u32)uec->p_rx_bd_ring); + + /* MFLR */ + out_be16(&uec->p_rx_glbl_pram->mflr, MAX_FRAME_LEN); + /* MINFLR */ + out_be16(&uec->p_rx_glbl_pram->minflr, MIN_FRAME_LEN); + /* MAXD1 */ + out_be16(&uec->p_rx_glbl_pram->maxd1, MAX_DMA1_LEN); + /* MAXD2 */ + out_be16(&uec->p_rx_glbl_pram->maxd2, MAX_DMA2_LEN); + /* ECAM_PTR */ + out_be32(&uec->p_rx_glbl_pram->ecamptr, 0); + /* L2QT */ + out_be32(&uec->p_rx_glbl_pram->l2qt, 0); + /* L3QT */ + for (i = 0; i < 8; i++) { + out_be32(&uec->p_rx_glbl_pram->l3qt[i], 0); + } + + /* VLAN_TYPE */ + out_be16(&uec->p_rx_glbl_pram->vlantype, 0x8100); + /* TCI */ + out_be16(&uec->p_rx_glbl_pram->vlantci, 0); + + /* Clear PQ2 style address filtering hash table */ + p_af_pram = (uec_82xx_address_filtering_pram_t *) \ + uec->p_rx_glbl_pram->addressfiltering; + + p_af_pram->iaddr_h = 0; + p_af_pram->iaddr_l = 0; + p_af_pram->gaddr_h = 0; + p_af_pram->gaddr_l = 0; +} + +static int uec_issue_init_enet_rxtx_cmd(uec_private_t *uec, + int thread_tx, int thread_rx) +{ + uec_init_cmd_pram_t *p_init_enet_param; + u32 init_enet_param_offset; + uec_info_t *uec_info; + int i; + int snum; + u32 init_enet_offset; + u32 entry_val; + u32 command; + u32 cecr_subblock; + + uec_info = uec->uec_info; + + /* Allocate init enet command parameter */ + uec->init_enet_param_offset = qe_muram_alloc( + sizeof(uec_init_cmd_pram_t), 4); + init_enet_param_offset = uec->init_enet_param_offset; + uec->p_init_enet_param = (uec_init_cmd_pram_t *) + qe_muram_addr(uec->init_enet_param_offset); + + /* Zero init enet command struct */ + memset((void *)uec->p_init_enet_param, 0, sizeof(uec_init_cmd_pram_t)); + + /* Init the command struct */ + p_init_enet_param = uec->p_init_enet_param; + p_init_enet_param->resinit0 = ENET_INIT_PARAM_MAGIC_RES_INIT0; + p_init_enet_param->resinit1 = ENET_INIT_PARAM_MAGIC_RES_INIT1; + p_init_enet_param->resinit2 = ENET_INIT_PARAM_MAGIC_RES_INIT2; + p_init_enet_param->resinit3 = ENET_INIT_PARAM_MAGIC_RES_INIT3; + p_init_enet_param->resinit4 = ENET_INIT_PARAM_MAGIC_RES_INIT4; + p_init_enet_param->largestexternallookupkeysize = 0; + + p_init_enet_param->rgftgfrxglobal |= ((u32)uec_info->num_threads_rx) + << ENET_INIT_PARAM_RGF_SHIFT; + p_init_enet_param->rgftgfrxglobal |= ((u32)uec_info->num_threads_tx) + << ENET_INIT_PARAM_TGF_SHIFT; + + /* Init Rx global parameter pointer */ + p_init_enet_param->rgftgfrxglobal |= uec->rx_glbl_pram_offset | + (u32)uec_info->riscRx; + + /* Init Rx threads */ + for (i = 0; i < (thread_rx + 1); i++) { + if ((snum = qe_get_snum()) < 0) { + printf("%s can not get snum\n", __FUNCTION__); + return -ENOMEM; + } + + if (i==0) { + init_enet_offset = 0; + } else { + init_enet_offset = qe_muram_alloc( + sizeof(uec_thread_rx_pram_t), + UEC_THREAD_RX_PRAM_ALIGNMENT); + } + + entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | + init_enet_offset | (u32)uec_info->riscRx; + p_init_enet_param->rxthread[i] = entry_val; + } + + /* Init Tx global parameter pointer */ + p_init_enet_param->txglobal = uec->tx_glbl_pram_offset | + (u32)uec_info->riscTx; + + /* Init Tx threads */ + for (i = 0; i < thread_tx; i++) { + if ((snum = qe_get_snum()) < 0) { + printf("%s can not get snum\n", __FUNCTION__); + return -ENOMEM; + } + + init_enet_offset = qe_muram_alloc(sizeof(uec_thread_tx_pram_t), + UEC_THREAD_TX_PRAM_ALIGNMENT); + + entry_val = ((u32)snum << ENET_INIT_PARAM_SNUM_SHIFT) | + init_enet_offset | (u32)uec_info->riscTx; + p_init_enet_param->txthread[i] = entry_val; + } + + __asm__ __volatile__("sync"); + + /* Issue QE command */ + command = QE_INIT_TX_RX; + cecr_subblock = ucc_fast_get_qe_cr_subblock( + uec->uec_info->uf_info.ucc_num); + qe_issue_cmd(command, cecr_subblock, (u8) QE_CR_PROTOCOL_ETHERNET, + init_enet_param_offset); + + return 0; +} + +static int uec_startup(uec_private_t *uec) +{ + uec_info_t *uec_info; + ucc_fast_info_t *uf_info; + ucc_fast_private_t *uccf; + ucc_fast_t *uf_regs; + uec_t *uec_regs; + int num_threads_tx; + int num_threads_rx; + u32 utbipar; + enet_interface_e enet_interface; + u32 length; + u32 align; + qe_bd_t *bd; + u8 *buf; + int i; + + if (!uec || !uec->uec_info) { + printf("%s: uec or uec_info not initial\n", __FUNCTION__); + return -EINVAL; + } + + uec_info = uec->uec_info; + uf_info = &(uec_info->uf_info); + + /* Check if Rx BD ring len is illegal */ + if ((uec_info->rx_bd_ring_len < UEC_RX_BD_RING_SIZE_MIN) || \ + (uec_info->rx_bd_ring_len % UEC_RX_BD_RING_SIZE_ALIGNMENT)) { + printf("%s: Rx BD ring len must be multiple of 4, and > 8.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Check if Tx BD ring len is illegal */ + if (uec_info->tx_bd_ring_len < UEC_TX_BD_RING_SIZE_MIN) { + printf("%s: Tx BD ring length must not be smaller than 2.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Check if MRBLR is illegal */ + if ((MAX_RXBUF_LEN == 0) || (MAX_RXBUF_LEN % UEC_MRBLR_ALIGNMENT)) { + printf("%s: max rx buffer length must be mutliple of 128.\n", + __FUNCTION__); + return -EINVAL; + } + + /* Both Rx and Tx are stopped */ + uec->grace_stopped_rx = 1; + uec->grace_stopped_tx = 1; + + /* Init UCC fast */ + if (ucc_fast_init(uf_info, &uccf)) { + printf("%s: failed to init ucc fast\n", __FUNCTION__); + return -ENOMEM; + } + + /* Save uccf */ + uec->uccf = uccf; + + /* Convert the Tx threads number */ + if (uec_convert_threads_num(uec_info->num_threads_tx, + &num_threads_tx)) { + return -EINVAL; + } + + /* Convert the Rx threads number */ + if (uec_convert_threads_num(uec_info->num_threads_rx, + &num_threads_rx)) { + return -EINVAL; + } + + uf_regs = uccf->uf_regs; + + /* UEC register is following UCC fast registers */ + uec_regs = (uec_t *)(&uf_regs->ucc_eth); + + /* Save the UEC register pointer to UEC private struct */ + uec->uec_regs = uec_regs; + + /* Init UPSMR, enable hardware statistics (UCC) */ + out_be32(&uec->uccf->uf_regs->upsmr, UPSMR_INIT_VALUE); + + /* Init MACCFG1, flow control disable, disable Tx and Rx */ + out_be32(&uec_regs->maccfg1, MACCFG1_INIT_VALUE); + + /* Init MACCFG2, length check, MAC PAD and CRC enable */ + out_be32(&uec_regs->maccfg2, MACCFG2_INIT_VALUE); + + /* Setup MAC interface mode */ + uec_set_mac_if_mode(uec, uec_info->enet_interface); + + /* Setup MII master clock source */ + qe_set_mii_clk_src(uec_info->uf_info.ucc_num); + + /* Setup UTBIPAR */ + utbipar = in_be32(&uec_regs->utbipar); + utbipar &= ~UTBIPAR_PHY_ADDRESS_MASK; + enet_interface = uec->uec_info->enet_interface; + if (enet_interface == ENET_1000_TBI || + enet_interface == ENET_1000_RTBI) { + utbipar |= (uec_info->phy_address + uec_info->uf_info.ucc_num) + << UTBIPAR_PHY_ADDRESS_SHIFT; + } else { + utbipar |= (0x10 + uec_info->uf_info.ucc_num) + << UTBIPAR_PHY_ADDRESS_SHIFT; + } + + out_be32(&uec_regs->utbipar, utbipar); + + /* Allocate Tx BDs */ + length = ((uec_info->tx_bd_ring_len * SIZEOFBD) / + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) * + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; + if ((uec_info->tx_bd_ring_len * SIZEOFBD) % + UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) { + length += UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; + } + + align = UEC_TX_BD_RING_ALIGNMENT; + uec->tx_bd_ring_offset = (u32)malloc((u32)(length + align)); + if (uec->tx_bd_ring_offset != 0) { + uec->p_tx_bd_ring = (u8 *)((uec->tx_bd_ring_offset + align) + & ~(align - 1)); + } + + /* Zero all of Tx BDs */ + memset((void *)(uec->tx_bd_ring_offset), 0, length + align); + + /* Allocate Rx BDs */ + length = uec_info->rx_bd_ring_len * SIZEOFBD; + align = UEC_RX_BD_RING_ALIGNMENT; + uec->rx_bd_ring_offset = (u32)(malloc((u32)(length + align))); + if (uec->rx_bd_ring_offset != 0) { + uec->p_rx_bd_ring = (u8 *)((uec->rx_bd_ring_offset + align) + & ~(align - 1)); + } + + /* Zero all of Rx BDs */ + memset((void *)(uec->rx_bd_ring_offset), 0, length + align); + + /* Allocate Rx buffer */ + length = uec_info->rx_bd_ring_len * MAX_RXBUF_LEN; + align = UEC_RX_DATA_BUF_ALIGNMENT; + uec->rx_buf_offset = (u32)malloc(length + align); + if (uec->rx_buf_offset != 0) { + uec->p_rx_buf = (u8 *)((uec->rx_buf_offset + align) + & ~(align - 1)); + } + + /* Zero all of the Rx buffer */ + memset((void *)(uec->rx_buf_offset), 0, length + align); + + /* Init TxBD ring */ + bd = (qe_bd_t *)uec->p_tx_bd_ring; + uec->txBd = bd; + + for (i = 0; i < uec_info->tx_bd_ring_len; i++) { + BD_DATA_CLEAR(bd); + BD_STATUS_SET(bd, 0); + BD_LENGTH_SET(bd, 0); + bd ++; + } + BD_STATUS_SET((--bd), TxBD_WRAP); + + /* Init RxBD ring */ + bd = (qe_bd_t *)uec->p_rx_bd_ring; + uec->rxBd = bd; + buf = uec->p_rx_buf; + for (i = 0; i < uec_info->rx_bd_ring_len; i++) { + BD_DATA_SET(bd, buf); + BD_LENGTH_SET(bd, 0); + BD_STATUS_SET(bd, RxBD_EMPTY); + buf += MAX_RXBUF_LEN; + bd ++; + } + BD_STATUS_SET((--bd), RxBD_WRAP | RxBD_EMPTY); + + /* Init global Tx parameter RAM */ + uec_init_tx_parameter(uec, num_threads_tx); + + /* Init global Rx parameter RAM */ + uec_init_rx_parameter(uec, num_threads_rx); + + /* Init ethernet Tx and Rx parameter command */ + if (uec_issue_init_enet_rxtx_cmd(uec, num_threads_tx, + num_threads_rx)) { + printf("%s issue init enet cmd failed\n", __FUNCTION__); + return -ENOMEM; + } + + return 0; +} + +static int uec_init(struct eth_device* dev, bd_t *bd) +{ + uec_private_t *uec; + int err; + + uec = (uec_private_t *)dev->priv; + + if (uec->the_first_run == 0) { + /* Set up the MAC address */ + if (dev->enetaddr[0] & 0x01) { + printf("%s: MacAddress is multcast address\n", + __FUNCTION__); + return -EINVAL; + } + uec_set_mac_address(uec, dev->enetaddr); + uec->the_first_run = 1; + } + + err = uec_open(uec, COMM_DIR_RX_AND_TX); + if (err) { + printf("%s: cannot enable UEC device\n", dev->name); + return err; + } + + return 0; +} + +static void uec_halt(struct eth_device* dev) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + uec_stop(uec, COMM_DIR_RX_AND_TX); +} + +static int uec_send(struct eth_device* dev, volatile void *buf, int len) +{ + uec_private_t *uec; + ucc_fast_private_t *uccf; + volatile qe_bd_t *bd; + volatile u16 status; + int i; + int result = 0; + + uec = (uec_private_t *)dev->priv; + uccf = uec->uccf; + bd = uec->txBd; + + /* Find an empty TxBD */ + for (i = 0; BD_STATUS(bd) & TxBD_READY; i++) { + if (i > 0x100000) { + printf("%s: tx buffer not ready\n", dev->name); + return result; + } + } + + /* Init TxBD */ + BD_DATA_SET(bd, buf); + BD_LENGTH_SET(bd, len); + status = BD_STATUS(bd); + status &= BD_WRAP; + status |= (TxBD_READY | TxBD_LAST); + BD_STATUS_SET(bd, status); + + /* Tell UCC to transmit the buffer */ + ucc_fast_transmit_on_demand(uccf); + + /* Wait for buffer to be transmitted */ + status = BD_STATUS(bd); + for (i = 0; status & TxBD_READY; i++) { + if (i > 0x100000) { + printf("%s: tx error\n", dev->name); + return result; + } + status = BD_STATUS(bd); + } + + /* Ok, the buffer be transimitted */ + BD_ADVANCE(bd, status, uec->p_tx_bd_ring); + uec->txBd = bd; + result = 1; + + return result; +} + +static int uec_recv(struct eth_device* dev) +{ + uec_private_t *uec = dev->priv; + volatile qe_bd_t *bd; + volatile u16 status; + u16 len; + u8 *data; + + bd = uec->rxBd; + status = BD_STATUS(bd); + + while (!(status & RxBD_EMPTY)) { + if (!(status & RxBD_ERROR)) { + data = BD_DATA(bd); + len = BD_LENGTH(bd); + NetReceive(data, len); + } else { + printf("%s: Rx error\n", dev->name); + } + status &= BD_CLEAN; + BD_LENGTH_SET(bd, 0); + BD_STATUS_SET(bd, status | RxBD_EMPTY); + BD_ADVANCE(bd, status, uec->p_rx_bd_ring); + status = BD_STATUS(bd); + } + uec->rxBd = bd; + + return 1; +} + +int uec_initialize(int index) +{ + struct eth_device *dev; + int i; + uec_private_t *uec; + uec_info_t *uec_info; + int err; + + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); + if (!dev) + return 0; + memset(dev, 0, sizeof(struct eth_device)); + + /* Allocate the UEC private struct */ + uec = (uec_private_t *)malloc(sizeof(uec_private_t)); + if (!uec) { + return -ENOMEM; + } + memset(uec, 0, sizeof(uec_private_t)); + + /* Init UEC private struct, they come from board.h */ + if (index == 0) { +#ifdef CONFIG_UEC_ETH1 + uec_info = ð1_uec_info; +#endif + } else if (index == 1) { +#ifdef CONFIG_UEC_ETH2 + uec_info = ð2_uec_info; +#endif + } else { + printf("%s: index is illegal.\n", __FUNCTION__); + return -EINVAL; + } + + uec->uec_info = uec_info; + + sprintf(dev->name, "FSL UEC%d", index); + dev->iobase = 0; + dev->priv = (void *)uec; + dev->init = uec_init; + dev->halt = uec_halt; + dev->send = uec_send; + dev->recv = uec_recv; + + /* Clear the ethnet address */ + for (i = 0; i < 6; i++) + dev->enetaddr[i] = 0; + + eth_register(dev); + + err = uec_startup(uec); + if (err) { + printf("%s: Cannot configure net device, aborting.",dev->name); + return err; + } + + err = init_phy(dev); + if (err) { + printf("%s: Cannot initialize PHY, aborting.\n", dev->name); + return err; + } + + phy_change(dev); + + return 1; +} +#endif /* CONFIG_QE */ diff --git a/drivers/qe/uec.h b/drivers/qe/uec.h new file mode 100644 index 0000000..0495026 --- /dev/null +++ b/drivers/qe/uec.h @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2006 Freescale Semiconductor, Inc. + * + * Dave Liu <daveliu@freescale.com> + * based on source code of Shlomi Gridish + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __UEC_H__ +#define __UEC_H__ + +#define MAX_TX_THREADS 8 +#define MAX_RX_THREADS 8 +#define MAX_TX_QUEUES 8 +#define MAX_RX_QUEUES 8 +#define MAX_PREFETCHED_BDS 4 +#define MAX_IPH_OFFSET_ENTRY 8 +#define MAX_ENET_INIT_PARAM_ENTRIES_RX 9 +#define MAX_ENET_INIT_PARAM_ENTRIES_TX 8 + +/* UEC UPSMR (Protocol Specific Mode Register) + */ +#define UPSMR_ECM 0x04000000 /* Enable CAM Miss */ +#define UPSMR_HSE 0x02000000 /* Hardware Statistics Enable */ +#define UPSMR_PRO 0x00400000 /* Promiscuous */ +#define UPSMR_CAP 0x00200000 /* CAM polarity */ +#define UPSMR_RSH 0x00100000 /* Receive Short Frames */ +#define UPSMR_RPM 0x00080000 /* Reduced Pin Mode interfaces */ +#define UPSMR_R10M 0x00040000 /* RGMII/RMII 10 Mode */ +#define UPSMR_RLPB 0x00020000 /* RMII Loopback Mode */ +#define UPSMR_TBIM 0x00010000 /* Ten-bit Interface Mode */ +#define UPSMR_RMM 0x00001000 /* RMII/RGMII Mode */ +#define UPSMR_CAM 0x00000400 /* CAM Address Matching */ +#define UPSMR_BRO 0x00000200 /* Broadcast Address */ +#define UPSMR_RES1 0x00002000 /* Reserved feild - must be 1 */ + +#define UPSMR_INIT_VALUE (UPSMR_HSE | UPSMR_RES1) + +/* UEC MACCFG1 (MAC Configuration 1 Register) + */ +#define MACCFG1_FLOW_RX 0x00000020 /* Flow Control Rx */ +#define MACCFG1_FLOW_TX 0x00000010 /* Flow Control Tx */ +#define MACCFG1_ENABLE_SYNCHED_RX 0x00000008 /* Enable Rx Sync */ +#define MACCFG1_ENABLE_RX 0x00000004 /* Enable Rx */ +#define MACCFG1_ENABLE_SYNCHED_TX 0x00000002 /* Enable Tx Sync */ +#define MACCFG1_ENABLE_TX 0x00000001 /* Enable Tx */ + +#define MACCFG1_INIT_VALUE (0) + +/* UEC MACCFG2 (MAC Configuration 2 Register) + */ +#define MACCFG2_PREL 0x00007000 +#define MACCFG2_PREL_SHIFT (31 - 19) +#define MACCFG2_PREL_MASK 0x0000f000 +#define MACCFG2_SRP 0x00000080 +#define MACCFG2_STP 0x00000040 +#define MACCFG2_RESERVED_1 0x00000020 /* must be set */ +#define MACCFG2_LC 0x00000010 /* Length Check */ +#define MACCFG2_MPE 0x00000008 +#define MACCFG2_FDX 0x00000001 /* Full Duplex */ +#define MACCFG2_FDX_MASK 0x00000001 +#define MACCFG2_PAD_CRC 0x00000004 +#define MACCFG2_CRC_EN 0x00000002 +#define MACCFG2_PAD_AND_CRC_MODE_NONE 0x00000000 +#define MACCFG2_PAD_AND_CRC_MODE_CRC_ONLY 0x00000002 +#define MACCFG2_PAD_AND_CRC_MODE_PAD_AND_CRC 0x00000004 +#define MACCFG2_INTERFACE_MODE_NIBBLE 0x00000100 +#define MACCFG2_INTERFACE_MODE_BYTE 0x00000200 +#define MACCFG2_INTERFACE_MODE_MASK 0x00000300 + +#define MACCFG2_INIT_VALUE (MACCFG2_PREL | MACCFG2_RESERVED_1 | \ + MACCFG2_LC | MACCFG2_PAD_CRC | MACCFG2_FDX) + +/* UEC Event Register +*/ +#define UCCE_MPD 0x80000000 +#define UCCE_SCAR 0x40000000 +#define UCCE_GRA 0x20000000 +#define UCCE_CBPR 0x10000000 +#define UCCE_BSY 0x08000000 +#define UCCE_RXC 0x04000000 +#define UCCE_TXC 0x02000000 +#define UCCE_TXE 0x01000000 +#define UCCE_TXB7 0x00800000 +#define UCCE_TXB6 0x00400000 +#define UCCE_TXB5 0x00200000 +#define UCCE_TXB4 0x00100000 +#define UCCE_TXB3 0x00080000 +#define UCCE_TXB2 0x00040000 +#define UCCE_TXB1 0x00020000 +#define UCCE_TXB0 0x00010000 +#define UCCE_RXB7 0x00008000 +#define UCCE_RXB6 0x00004000 +#define UCCE_RXB5 0x00002000 +#define UCCE_RXB4 0x00001000 +#define UCCE_RXB3 0x00000800 +#define UCCE_RXB2 0x00000400 +#define UCCE_RXB1 0x00000200 +#define UCCE_RXB0 0x00000100 +#define UCCE_RXF7 0x00000080 +#define UCCE_RXF6 0x00000040 +#define UCCE_RXF5 0x00000020 +#define UCCE_RXF4 0x00000010 +#define UCCE_RXF3 0x00000008 +#define UCCE_RXF2 0x00000004 +#define UCCE_RXF1 0x00000002 +#define UCCE_RXF0 0x00000001 + +#define UCCE_TXB (UCCE_TXB7 | UCCE_TXB6 | UCCE_TXB5 | UCCE_TXB4 | \ + UCCE_TXB3 | UCCE_TXB2 | UCCE_TXB1 | UCCE_TXB0) +#define UCCE_RXB (UCCE_RXB7 | UCCE_RXB6 | UCCE_RXB5 | UCCE_RXB4 | \ + UCCE_RXB3 | UCCE_RXB2 | UCCE_RXB1 | UCCE_RXB0) +#define UCCE_RXF (UCCE_RXF7 | UCCE_RXF6 | UCCE_RXF5 | UCCE_RXF4 | \ + UCCE_RXF3 | UCCE_RXF2 | UCCE_RXF1 | UCCE_RXF0) +#define UCCE_OTHER (UCCE_SCAR | UCCE_GRA | UCCE_CBPR | UCCE_BSY | \ + UCCE_RXC | UCCE_TXC | UCCE_TXE) + +/* UEC TEMODR Register +*/ +#define TEMODER_SCHEDULER_ENABLE 0x2000 +#define TEMODER_IP_CHECKSUM_GENERATE 0x0400 +#define TEMODER_PERFORMANCE_OPTIMIZATION_MODE1 0x0200 +#define TEMODER_RMON_STATISTICS 0x0100 +#define TEMODER_NUM_OF_QUEUES_SHIFT (15-15) + +#define TEMODER_INIT_VALUE 0xc000 + +/* UEC REMODR Register +*/ +#define REMODER_RX_RMON_STATISTICS_ENABLE 0x00001000 +#define REMODER_RX_EXTENDED_FEATURES 0x80000000 +#define REMODER_VLAN_OPERATION_TAGGED_SHIFT (31-9 ) +#define REMODER_VLAN_OPERATION_NON_TAGGED_SHIFT (31-10) +#define REMODER_RX_QOS_MODE_SHIFT (31-15) +#define REMODER_RMON_STATISTICS 0x00001000 +#define REMODER_RX_EXTENDED_FILTERING 0x00000800 +#define REMODER_NUM_OF_QUEUES_SHIFT (31-23) +#define REMODER_DYNAMIC_MAX_FRAME_LENGTH 0x00000008 +#define REMODER_DYNAMIC_MIN_FRAME_LENGTH 0x00000004 +#define REMODER_IP_CHECKSUM_CHECK 0x00000002 +#define REMODER_IP_ADDRESS_ALIGNMENT 0x00000001 + +#define REMODER_INIT_VALUE 0 + +/* BMRx - Bus Mode Register */ +#define BMR_GLB 0x20 +#define BMR_BO_BE 0x10 +#define BMR_DTB_SECONDARY_BUS 0x02 +#define BMR_BDB_SECONDARY_BUS 0x01 + +#define BMR_SHIFT 24 +#define BMR_INIT_VALUE (BMR_GLB | BMR_BO_BE) + +/* UEC UCCS (Ethernet Status Register) + */ +#define UCCS_BPR 0x02 +#define UCCS_PAU 0x02 +#define UCCS_MPD 0x01 + +/* UEC MIIMCFG (MII Management Configuration Register) + */ +#define MIIMCFG_RESET_MANAGEMENT 0x80000000 +#define MIIMCFG_NO_PREAMBLE 0x00000010 +#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) +#define MIIMCFG_CLOCK_DIVIDE_MASK 0x0000000f +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 + +#define MIIMCFG_MNGMNT_CLC_DIV_INIT_VALUE \ + MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 + +/* UEC MIIMCOM (MII Management Command Register) + */ +#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ +#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ + +/* UEC MIIMADD (MII Management Address Register) + */ +#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) +#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) + +/* UEC MIIMCON (MII Management Control Register) + */ +#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) +#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) + +/* UEC MIIMIND (MII Management Indicator Register) + */ +#define MIIMIND_NOT_VALID 0x00000004 +#define MIIMIND_SCAN 0x00000002 +#define MIIMIND_BUSY 0x00000001 + +/* UEC UTBIPAR (Ten Bit Interface Physical Address Register) + */ +#define UTBIPAR_PHY_ADDRESS_SHIFT (31 - 31) +#define UTBIPAR_PHY_ADDRESS_MASK 0x0000001f + +/* UEC UESCR (Ethernet Statistics Control Register) + */ +#define UESCR_AUTOZ 0x8000 +#define UESCR_CLRCNT 0x4000 +#define UESCR_MAXCOV_SHIFT (15 - 7) +#define UESCR_SCOV_SHIFT (15 - 15) + +/****** Tx data struct collection ******/ +/* Tx thread data, each Tx thread has one this struct. +*/ +typedef struct uec_thread_data_tx { + u8 res0[136]; +} __attribute__ ((packed)) uec_thread_data_tx_t; + +/* Tx thread parameter, each Tx thread has one this struct. +*/ +typedef struct uec_thread_tx_pram { + u8 res0[64]; +} __attribute__ ((packed)) uec_thread_tx_pram_t; + +/* Send queue queue-descriptor, each Tx queue has one this QD +*/ +typedef struct uec_send_queue_qd { + u32 bd_ring_base; /* pointer to BD ring base address */ + u8 res0[0x8]; + u32 last_bd_completed_address; /* last entry in BD ring */ + u8 res1[0x30]; +} __attribute__ ((packed)) uec_send_queue_qd_t; + +/* Send queue memory region */ +typedef struct uec_send_queue_mem_region { + uec_send_queue_qd_t sqqd[MAX_TX_QUEUES]; +} __attribute__ ((packed)) uec_send_queue_mem_region_t; + +/* Scheduler struct +*/ +typedef struct uec_scheduler { + u16 cpucount0; /* CPU packet counter */ + u16 cpucount1; /* CPU packet counter */ + u16 cecount0; /* QE packet counter */ + u16 cecount1; /* QE packet counter */ + u16 cpucount2; /* CPU packet counter */ + u16 cpucount3; /* CPU packet counter */ + u16 cecount2; /* QE packet counter */ + u16 cecount3; /* QE packet counter */ + u16 cpucount4; /* CPU packet counter */ + u16 cpucount5; /* CPU packet counter */ + u16 cecount4; /* QE packet counter */ + u16 cecount5; /* QE packet counter */ + u16 cpucount6; /* CPU packet counter */ + u16 cpucount7; /* CPU packet counter */ + u16 cecount6; /* QE packet counter */ + u16 cecount7; /* QE packet counter */ + u32 weightstatus[MAX_TX_QUEUES]; /* accumulated weight factor */ + u32 rtsrshadow; /* temporary variable handled by QE */ + u32 time; /* temporary variable handled by QE */ + u32 ttl; /* temporary variable handled by QE */ + u32 mblinterval; /* max burst length interval */ + u16 nortsrbytetime; /* normalized value of byte time in tsr units */ + u8 fracsiz; + u8 res0[1]; + u8 strictpriorityq; /* Strict Priority Mask register */ + u8 txasap; /* Transmit ASAP register */ + u8 extrabw; /* Extra BandWidth register */ + u8 oldwfqmask; /* temporary variable handled by QE */ + u8 weightfactor[MAX_TX_QUEUES]; /**< weight factor for queues */ + u32 minw; /* temporary variable handled by QE */ + u8 res1[0x70-0x64]; +} __attribute__ ((packed)) uec_scheduler_t; + +/* Tx firmware counters +*/ +typedef struct uec_tx_firmware_statistics_pram { + u32 sicoltx; /* single collision */ + u32 mulcoltx; /* multiple collision */ + u32 latecoltxfr; /* late collision */ + u32 frabortduecol; /* frames aborted due to tx collision */ + u32 frlostinmactxer; /* frames lost due to internal MAC error tx */ + u32 carriersenseertx; /* carrier sense error */ + u32 frtxok; /* frames transmitted OK */ + u32 txfrexcessivedefer; + u32 txpkts256; /* total packets(including bad) 256~511 B */ + u32 txpkts512; /* total packets(including bad) 512~1023B */ + u32 txpkts1024; /* total packets(including bad) 1024~1518B */ + u32 txpktsjumbo; /* total packets(including bad) >1024 */ +} __attribute__ ((packed)) uec_tx_firmware_statistics_pram_t; + +/* Tx global parameter table +*/ +typedef struct uec_tx_global_pram { + u16 temoder; + u8 res0[0x38-0x02]; + u32 sqptr; + u32 schedulerbasepointer; + u32 txrmonbaseptr; + u32 tstate; + u8 iphoffset[MAX_IPH_OFFSET_ENTRY]; + u32 vtagtable[0x8]; + u32 tqptr; + u8 res2[0x80-0x74]; +} __attribute__ ((packed)) uec_tx_global_pram_t; + + +/****** Rx data struct collection ******/ +/* Rx thread data, each Rx thread has one this struct. +*/ +typedef struct uec_thread_data_rx { + u8 res0[40]; +} __attribute__ ((packed)) uec_thread_data_rx_t; + +/* Rx thread parameter, each Rx thread has one this struct. +*/ +typedef struct uec_thread_rx_pram { + u8 res0[128]; +} __attribute__ ((packed)) uec_thread_rx_pram_t; + +/* Rx firmware counters +*/ +typedef struct uec_rx_firmware_statistics_pram { + u32 frrxfcser; /* frames with crc error */ + u32 fraligner; /* frames with alignment error */ + u32 inrangelenrxer; /* in range length error */ + u32 outrangelenrxer; /* out of range length error */ + u32 frtoolong; /* frame too long */ + u32 runt; /* runt */ + u32 verylongevent; /* very long event */ + u32 symbolerror; /* symbol error */ + u32 dropbsy; /* drop because of BD not ready */ + u8 res0[0x8]; + u32 mismatchdrop; /* drop because of MAC filtering */ + u32 underpkts; /* total frames less than 64 octets */ + u32 pkts256; /* total frames(including bad)256~511 B */ + u32 pkts512; /* total frames(including bad)512~1023 B */ + u32 pkts1024; /* total frames(including bad)1024~1518 B */ + u32 pktsjumbo; /* total frames(including bad) >1024 B */ + u32 frlossinmacer; + u32 pausefr; /* pause frames */ + u8 res1[0x4]; + u32 removevlan; + u32 replacevlan; + u32 insertvlan; +} __attribute__ ((packed)) uec_rx_firmware_statistics_pram_t; + +/* Rx interrupt coalescing entry, each Rx queue has one this entry. +*/ +typedef struct uec_rx_interrupt_coalescing_entry { + u32 maxvalue; + u32 counter; +} __attribute__ ((packed)) uec_rx_interrupt_coalescing_entry_t; + +typedef struct uec_rx_interrupt_coalescing_table { + uec_rx_interrupt_coalescing_entry_t entry[MAX_RX_QUEUES]; +} __attribute__ ((packed)) uec_rx_interrupt_coalescing_table_t; + +/* RxBD queue entry, each Rx queue has one this entry. +*/ +typedef struct uec_rx_bd_queues_entry { + u32 bdbaseptr; /* BD base pointer */ + u32 bdptr; /* BD pointer */ + u32 externalbdbaseptr; /* external BD base pointer */ + u32 externalbdptr; /* external BD pointer */ +} __attribute__ ((packed)) uec_rx_bd_queues_entry_t; + +/* Rx global paramter table +*/ +typedef struct uec_rx_global_pram { + u32 remoder; /* ethernet mode reg. */ + u32 rqptr; /* base pointer to the Rx Queues */ + u32 res0[0x1]; + u8 res1[0x20-0xC]; + u16 typeorlen; + u8 res2[0x1]; + u8 rxgstpack; /* ack on GRACEFUL STOP RX command */ + u32 rxrmonbaseptr; /* Rx RMON statistics base */ + u8 res3[0x30-0x28]; + u32 intcoalescingptr; /* Interrupt coalescing table pointer */ + u8 res4[0x36-0x34]; + u8 rstate; + u8 res5[0x46-0x37]; + u16 mrblr; /* max receive buffer length reg. */ + u32 rbdqptr; /* RxBD parameter table description */ + u16 mflr; /* max frame length reg. */ + u16 minflr; /* min frame length reg. */ + u16 maxd1; /* max dma1 length reg. */ + u16 maxd2; /* max dma2 length reg. */ + u32 ecamptr; /* external CAM address */ + u32 l2qt; /* VLAN priority mapping table. */ + u32 l3qt[0x8]; /* IP priority mapping table. */ + u16 vlantype; /* vlan type */ + u16 vlantci; /* default vlan tci */ + u8 addressfiltering[64];/* address filtering data structure */ + u32 exfGlobalParam; /* extended filtering global parameters */ + u8 res6[0x100-0xC4]; /* Initialize to zero */ +} __attribute__ ((packed)) uec_rx_global_pram_t; + +#define GRACEFUL_STOP_ACKNOWLEDGE_RX 0x01 + + +/****** UEC common ******/ +/* UCC statistics - hardware counters +*/ +typedef struct uec_hardware_statistics { + u32 tx64; + u32 tx127; + u32 tx255; + u32 rx64; + u32 rx127; + u32 rx255; + u32 txok; + u16 txcf; + u32 tmca; + u32 tbca; + u32 rxfok; + u32 rxbok; + u32 rbyt; + u32 rmca; + u32 rbca; +} __attribute__ ((packed)) uec_hardware_statistics_t; + +/* InitEnet command parameter +*/ +typedef struct uec_init_cmd_pram { + u8 resinit0; + u8 resinit1; + u8 resinit2; + u8 resinit3; + u16 resinit4; + u8 res1[0x1]; + u8 largestexternallookupkeysize; + u32 rgftgfrxglobal; + u32 rxthread[MAX_ENET_INIT_PARAM_ENTRIES_RX]; /* rx threads */ + u8 res2[0x38 - 0x30]; + u32 txglobal; /* tx global */ + u32 txthread[MAX_ENET_INIT_PARAM_ENTRIES_TX]; /* tx threads */ + u8 res3[0x1]; +} __attribute__ ((packed)) uec_init_cmd_pram_t; + +#define ENET_INIT_PARAM_RGF_SHIFT (32 - 4) +#define ENET_INIT_PARAM_TGF_SHIFT (32 - 8) + +#define ENET_INIT_PARAM_RISC_MASK 0x0000003f +#define ENET_INIT_PARAM_PTR_MASK 0x00ffffc0 +#define ENET_INIT_PARAM_SNUM_MASK 0xff000000 +#define ENET_INIT_PARAM_SNUM_SHIFT 24 + +#define ENET_INIT_PARAM_MAGIC_RES_INIT0 0x06 +#define ENET_INIT_PARAM_MAGIC_RES_INIT1 0x30 +#define ENET_INIT_PARAM_MAGIC_RES_INIT2 0xff +#define ENET_INIT_PARAM_MAGIC_RES_INIT3 0x00 +#define ENET_INIT_PARAM_MAGIC_RES_INIT4 0x0400 + +/* structure representing 82xx Address Filtering Enet Address in PRAM +*/ +typedef struct uec_82xx_enet_address { + u8 res1[0x2]; + u16 h; /* address (MSB) */ + u16 m; /* address */ + u16 l; /* address (LSB) */ +} __attribute__ ((packed)) uec_82xx_enet_address_t; + +/* structure representing 82xx Address Filtering PRAM +*/ +typedef struct uec_82xx_address_filtering_pram { + u32 iaddr_h; /* individual address filter, high */ + u32 iaddr_l; /* individual address filter, low */ + u32 gaddr_h; /* group address filter, high */ + u32 gaddr_l; /* group address filter, low */ + uec_82xx_enet_address_t taddr; + uec_82xx_enet_address_t paddr[4]; + u8 res0[0x40-0x38]; +} __attribute__ ((packed)) uec_82xx_address_filtering_pram_t; + +/* Buffer Descriptor +*/ +typedef struct buffer_descriptor { + u16 status; + u16 len; + u32 data; +} __attribute__ ((packed)) qe_bd_t, *p_bd_t; + +#define SIZEOFBD sizeof(qe_bd_t) + +/* Common BD flags +*/ +#define BD_WRAP 0x2000 +#define BD_INT 0x1000 +#define BD_LAST 0x0800 +#define BD_CLEAN 0x3000 + +/* TxBD status flags +*/ +#define TxBD_READY 0x8000 +#define TxBD_PADCRC 0x4000 +#define TxBD_WRAP BD_WRAP +#define TxBD_INT BD_INT +#define TxBD_LAST BD_LAST +#define TxBD_TXCRC 0x0400 +#define TxBD_DEF 0x0200 +#define TxBD_PP 0x0100 +#define TxBD_LC 0x0080 +#define TxBD_RL 0x0040 +#define TxBD_RC 0x003C +#define TxBD_UNDERRUN 0x0002 +#define TxBD_TRUNC 0x0001 + +#define TxBD_ERROR (TxBD_UNDERRUN | TxBD_TRUNC) + +/* RxBD status flags +*/ +#define RxBD_EMPTY 0x8000 +#define RxBD_OWNER 0x4000 +#define RxBD_WRAP BD_WRAP +#define RxBD_INT BD_INT +#define RxBD_LAST BD_LAST +#define RxBD_FIRST 0x0400 +#define RxBD_CMR 0x0200 +#define RxBD_MISS 0x0100 +#define RxBD_BCAST 0x0080 +#define RxBD_MCAST 0x0040 +#define RxBD_LG 0x0020 +#define RxBD_NO 0x0010 +#define RxBD_SHORT 0x0008 +#define RxBD_CRCERR 0x0004 +#define RxBD_OVERRUN 0x0002 +#define RxBD_IPCH 0x0001 + +#define RxBD_ERROR (RxBD_LG | RxBD_NO | RxBD_SHORT | \ + RxBD_CRCERR | RxBD_OVERRUN) + +/* BD access macros +*/ +#define BD_STATUS(_bd) (((p_bd_t)(_bd))->status) +#define BD_STATUS_SET(_bd, _val) (((p_bd_t)(_bd))->status = _val) +#define BD_LENGTH(_bd) (((p_bd_t)(_bd))->len) +#define BD_LENGTH_SET(_bd, _val) (((p_bd_t)(_bd))->len = _val) +#define BD_DATA_CLEAR(_bd) (((p_bd_t)(_bd))->data = 0) +#define BD_IS_DATA(_bd) (((p_bd_t)(_bd))->data) +#define BD_DATA(_bd) ((u8 *)(((p_bd_t)(_bd))->data)) +#define BD_DATA_SET(_bd, _data) (((p_bd_t)(_bd))->data = (u32)(_data)) +#define BD_ADVANCE(_bd,_status,_base) \ + (((_status) & BD_WRAP) ? (_bd) = ((p_bd_t)(_base)) : ++(_bd)) + +/* Rx Prefetched BDs +*/ +typedef struct uec_rx_prefetched_bds { + qe_bd_t bd[MAX_PREFETCHED_BDS]; /* prefetched bd */ +} __attribute__ ((packed)) uec_rx_prefetched_bds_t; + +/* Alignments + */ +#define UEC_RX_GLOBAL_PRAM_ALIGNMENT 64 +#define UEC_TX_GLOBAL_PRAM_ALIGNMENT 64 +#define UEC_THREAD_RX_PRAM_ALIGNMENT 128 +#define UEC_THREAD_TX_PRAM_ALIGNMENT 64 +#define UEC_THREAD_DATA_ALIGNMENT 256 +#define UEC_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT 32 +#define UEC_SCHEDULER_ALIGNMENT 4 +#define UEC_TX_STATISTICS_ALIGNMENT 4 +#define UEC_RX_STATISTICS_ALIGNMENT 4 +#define UEC_RX_INTERRUPT_COALESCING_ALIGNMENT 4 +#define UEC_RX_BD_QUEUES_ALIGNMENT 8 +#define UEC_RX_PREFETCHED_BDS_ALIGNMENT 128 +#define UEC_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT 4 +#define UEC_RX_BD_RING_ALIGNMENT 32 +#define UEC_TX_BD_RING_ALIGNMENT 32 +#define UEC_MRBLR_ALIGNMENT 128 +#define UEC_RX_BD_RING_SIZE_ALIGNMENT 4 +#define UEC_TX_BD_RING_SIZE_MEMORY_ALIGNMENT 32 +#define UEC_RX_DATA_BUF_ALIGNMENT 64 + +#define UEC_VLAN_PRIORITY_MAX 8 +#define UEC_IP_PRIORITY_MAX 64 +#define UEC_TX_VTAG_TABLE_ENTRY_MAX 8 +#define UEC_RX_BD_RING_SIZE_MIN 8 +#define UEC_TX_BD_RING_SIZE_MIN 2 + +/* Ethernet speed +*/ +typedef enum enet_speed { + ENET_SPEED_10BT, /* 10 Base T */ + ENET_SPEED_100BT, /* 100 Base T */ + ENET_SPEED_1000BT /* 1000 Base T */ +} enet_speed_e; + +/* Ethernet Address Type. +*/ +typedef enum enet_addr_type { + ENET_ADDR_TYPE_INDIVIDUAL, + ENET_ADDR_TYPE_GROUP, + ENET_ADDR_TYPE_BROADCAST +} enet_addr_type_e; + +/* TBI / MII Set Register +*/ +typedef enum enet_tbi_mii_reg { + ENET_TBI_MII_CR = 0x00, + ENET_TBI_MII_SR = 0x01, + ENET_TBI_MII_ANA = 0x04, + ENET_TBI_MII_ANLPBPA = 0x05, + ENET_TBI_MII_ANEX = 0x06, + ENET_TBI_MII_ANNPT = 0x07, + ENET_TBI_MII_ANLPANP = 0x08, + ENET_TBI_MII_EXST = 0x0F, + ENET_TBI_MII_JD = 0x10, + ENET_TBI_MII_TBICON = 0x11 +} enet_tbi_mii_reg_e; + +/* UEC number of threads +*/ +typedef enum uec_num_of_threads { + UEC_NUM_OF_THREADS_1 = 0x1, /* 1 */ + UEC_NUM_OF_THREADS_2 = 0x2, /* 2 */ + UEC_NUM_OF_THREADS_4 = 0x0, /* 4 */ + UEC_NUM_OF_THREADS_6 = 0x3, /* 6 */ + UEC_NUM_OF_THREADS_8 = 0x4 /* 8 */ +} uec_num_of_threads_e; + +/* UEC ethernet interface type +*/ +typedef enum enet_interface { + ENET_10_MII, + ENET_10_RMII, + ENET_10_RGMII, + ENET_100_MII, + ENET_100_RMII, + ENET_100_RGMII, + ENET_1000_GMII, + ENET_1000_RGMII, + ENET_1000_TBI, + ENET_1000_RTBI +} enet_interface_e; + +/* UEC initialization info struct +*/ +typedef struct uec_info { + ucc_fast_info_t uf_info; + uec_num_of_threads_e num_threads_tx; + uec_num_of_threads_e num_threads_rx; + qe_risc_allocation_e riscTx; + qe_risc_allocation_e riscRx; + u16 rx_bd_ring_len; + u16 tx_bd_ring_len; + u8 phy_address; + enet_interface_e enet_interface; +} uec_info_t; + +/* UEC driver initialized info +*/ +#define MAX_RXBUF_LEN 1536 +#define MAX_FRAME_LEN 1518 +#define MIN_FRAME_LEN 64 +#define MAX_DMA1_LEN 1520 +#define MAX_DMA2_LEN 1520 + +/* UEC driver private struct +*/ +typedef struct uec_private { + uec_info_t *uec_info; + ucc_fast_private_t *uccf; + struct eth_device *dev; + uec_t *uec_regs; + /* enet init command parameter */ + uec_init_cmd_pram_t *p_init_enet_param; + u32 init_enet_param_offset; + /* Rx and Tx paramter */ + uec_rx_global_pram_t *p_rx_glbl_pram; + u32 rx_glbl_pram_offset; + uec_tx_global_pram_t *p_tx_glbl_pram; + u32 tx_glbl_pram_offset; + uec_send_queue_mem_region_t *p_send_q_mem_reg; + u32 send_q_mem_reg_offset; + uec_thread_data_tx_t *p_thread_data_tx; + u32 thread_dat_tx_offset; + uec_thread_data_rx_t *p_thread_data_rx; + u32 thread_dat_rx_offset; + uec_rx_bd_queues_entry_t *p_rx_bd_qs_tbl; + u32 rx_bd_qs_tbl_offset; + /* BDs specific */ + u8 *p_tx_bd_ring; + u32 tx_bd_ring_offset; + u8 *p_rx_bd_ring; + u32 rx_bd_ring_offset; + u8 *p_rx_buf; + u32 rx_buf_offset; + volatile qe_bd_t *txBd; + volatile qe_bd_t *rxBd; + /* Status */ + int mac_tx_enabled; + int mac_rx_enabled; + int grace_stopped_tx; + int grace_stopped_rx; + int the_first_run; + /* PHY specific */ + struct uec_mii_info *mii_info; + int oldspeed; + int oldduplex; + int oldlink; +} uec_private_t; + +#endif /* __UEC_H__ */ diff --git a/drivers/qe/uec_phy.c b/drivers/qe/uec_phy.c new file mode 100644 index 0000000..fedf636 --- /dev/null +++ b/drivers/qe/uec_phy.c @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2005 Freescale Semiconductor, Inc. + * + * Author: Shlomi Gridish + * + * Description: UCC GETH Driver -- PHY handling + * Driver for UEC on QE + * Based on 8260_io/fcc_enet.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include "common.h" +#include "net.h" +#include "malloc.h" +#include "asm/errno.h" +#include "asm/immap_qe.h" +#include "asm/io.h" +#include "qe.h" +#include "uccf.h" +#include "uec.h" +#include "uec_phy.h" +#include "miiphy.h" + +#if defined(CONFIG_QE) + +#define UEC_VERBOSE_DEBUG +#define ugphy_printk(format, arg...) \ + printf(format "\n", ## arg) + +#define ugphy_dbg(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_err(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_info(format, arg...) \ + ugphy_printk(format , ## arg) +#define ugphy_warn(format, arg...) \ + ugphy_printk(format , ## arg) + +#ifdef UEC_VERBOSE_DEBUG +#define ugphy_vdbg ugphy_dbg +#else +#define ugphy_vdbg(ugeth, fmt, args...) do { } while (0) +#endif /* UEC_VERBOSE_DEBUG */ + +static void config_genmii_advert(struct uec_mii_info *mii_info); +static void genmii_setup_forced(struct uec_mii_info *mii_info); +static void genmii_restart_aneg(struct uec_mii_info *mii_info); +static int gbit_config_aneg(struct uec_mii_info *mii_info); +static int genmii_config_aneg(struct uec_mii_info *mii_info); +static int genmii_update_link(struct uec_mii_info *mii_info); +static int genmii_read_status(struct uec_mii_info *mii_info); +u16 phy_read(struct uec_mii_info *mii_info, u16 regnum); +void phy_write(struct uec_mii_info *mii_info, u16 regnum, u16 val); + +/* Write value to the PHY for this device to the register at regnum, */ +/* waiting until the write is done before it returns. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +void write_phy_reg(struct eth_device *dev, int mii_id, int regnum, int value) +{ + uec_private_t *ugeth = (uec_private_t *)dev->priv; + uec_t *ug_regs; + enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e)regnum; + u32 tmp_reg; + + ug_regs = ugeth->uec_regs; + + /* Stop the MII management read cycle */ + out_be32(&ug_regs->miimcom, 0); + /* Setting up the MII Mangement Address Register */ + tmp_reg = ((u32)mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; + out_be32(&ug_regs->miimadd, tmp_reg); + + /* Setting up the MII Mangement Control Register with the value */ + out_be32(&ug_regs->miimcon, (u32)value); + + /* Wait till MII management write is complete */ + while((in_be32(&ug_regs->miimind)) & MIIMIND_BUSY); + + udelay(100000); +} + +/* Reads from register regnum in the PHY for device dev, */ +/* returning the value. Clears miimcom first. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +int read_phy_reg(struct eth_device *dev, int mii_id, int regnum) +{ + uec_private_t *ugeth = (uec_private_t *)dev->priv; + uec_t *ug_regs; + enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e)regnum; + u32 tmp_reg; + u16 value; + + ug_regs = ugeth->uec_regs; + + /* Setting up the MII Mangement Address Register */ + tmp_reg = ((u32)mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg ; + out_be32(&ug_regs->miimadd, tmp_reg); + + /* Perform an MII management read cycle */ + out_be32(&ug_regs->miimcom, 0); + out_be32(&ug_regs->miimcom, MIIMCOM_READ_CYCLE); + + /* Wait till MII management write is complete */ + while((in_be32(&ug_regs->miimind)) & (MIIMIND_NOT_VALID | MIIMIND_BUSY)); + + udelay(100000); + + /* Read MII management status */ + value = (u16)in_be32(&ug_regs->miimstat); + if(value == 0xffff) + ugphy_warn("read wrong value : mii_id %d,mii_reg %d, base %08x", + mii_id, mii_reg, (u32) &(ug_regs->miimcfg)); + + return (value); +} + +void mii_clear_phy_interrupt(struct uec_mii_info *mii_info) +{ + if(mii_info->phyinfo->ack_interrupt) + mii_info->phyinfo->ack_interrupt(mii_info); +} + +void mii_configure_phy_interrupt(struct uec_mii_info *mii_info, u32 interrupts) +{ + mii_info->interrupts = interrupts; + if(mii_info->phyinfo->config_intr) + mii_info->phyinfo->config_intr(mii_info); +} + +/* Writes MII_ADVERTISE with the appropriate values, after + * sanitizing advertise to make sure only supported features + * are advertised + */ +static void config_genmii_advert(struct uec_mii_info *mii_info) +{ + u32 advertise; + u16 adv; + + /* Only allow advertising what this PHY supports */ + mii_info->advertising &= mii_info->phyinfo->features; + advertise = mii_info->advertising; + + /* Setup standard advertisement */ + adv = phy_read(mii_info, PHY_ANAR); + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + phy_write(mii_info, PHY_ANAR, adv); +} + +static void genmii_setup_forced(struct uec_mii_info *mii_info) +{ + u16 ctrl; + u32 features = mii_info->phyinfo->features; + + ctrl = phy_read(mii_info, PHY_BMCR); + + ctrl &= ~(PHY_BMCR_DPLX|PHY_BMCR_100_MBPS| + PHY_BMCR_1000_MBPS|PHY_BMCR_AUTON); + ctrl |= PHY_BMCR_RESET; + + switch(mii_info->speed) { + case SPEED_1000: + if(features & (SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full)) { + ctrl |= PHY_BMCR_1000_MBPS; + break; + } + mii_info->speed = SPEED_100; + case SPEED_100: + if (features & (SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full)) { + ctrl |= PHY_BMCR_100_MBPS; + break; + } + mii_info->speed = SPEED_10; + case SPEED_10: + if (features & (SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full)) + break; + default: /* Unsupported speed! */ + ugphy_err("%s: Bad speed!", mii_info->dev->name); + break; + } + + phy_write(mii_info, PHY_BMCR, ctrl); +} + +/* Enable and Restart Autonegotiation */ +static void genmii_restart_aneg(struct uec_mii_info *mii_info) +{ + u16 ctl; + + ctl = phy_read(mii_info, PHY_BMCR); + ctl |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); + phy_write(mii_info, PHY_BMCR, ctl); +} + +static int gbit_config_aneg(struct uec_mii_info *mii_info) +{ + u16 adv; + u32 advertise; + + if(mii_info->autoneg) { + /* Configure the ADVERTISE register */ + config_genmii_advert(mii_info); + advertise = mii_info->advertising; + + adv = phy_read(mii_info, MII_1000BASETCONTROL); + adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | + MII_1000BASETCONTROL_HALFDUPLEXCAP); + if (advertise & SUPPORTED_1000baseT_Half) + adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; + if (advertise & SUPPORTED_1000baseT_Full) + adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; + phy_write(mii_info, MII_1000BASETCONTROL, adv); + + /* Start/Restart aneg */ + genmii_restart_aneg(mii_info); + } else + genmii_setup_forced(mii_info); + + return 0; +} + +static int marvell_config_aneg(struct uec_mii_info *mii_info) +{ + /* The Marvell PHY has an errata which requires + * that certain registers get written in order + * to restart autonegotiation */ + phy_write(mii_info, PHY_BMCR, PHY_BMCR_RESET); + + phy_write(mii_info, 0x1d, 0x1f); + phy_write(mii_info, 0x1e, 0x200c); + phy_write(mii_info, 0x1d, 0x5); + phy_write(mii_info, 0x1e, 0); + phy_write(mii_info, 0x1e, 0x100); + + gbit_config_aneg(mii_info); + + return 0; +} + +static int genmii_config_aneg(struct uec_mii_info *mii_info) +{ + if (mii_info->autoneg) { + config_genmii_advert(mii_info); + genmii_restart_aneg(mii_info); + } else + genmii_setup_forced(mii_info); + + return 0; +} + +static int genmii_update_link(struct uec_mii_info *mii_info) +{ + u16 status; + + /* Do a fake read */ + phy_read(mii_info, PHY_BMSR); + + /* Read link and autonegotiation status */ + status = phy_read(mii_info, PHY_BMSR); + if ((status & PHY_BMSR_LS) == 0) + mii_info->link = 0; + else + mii_info->link = 1; + + /* If we are autonegotiating, and not done, + * return an error */ + if (mii_info->autoneg && !(status & PHY_BMSR_AUTN_COMP)) + return -EAGAIN; + + return 0; +} + +static int genmii_read_status(struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there + * was an error */ + err = genmii_update_link(mii_info); + if (err) + return err; + + if (mii_info->autoneg) { + status = phy_read(mii_info, PHY_ANLPAR); + + if (status & (PHY_ANLPAR_10FD | PHY_ANLPAR_TXFD)) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + if (status & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) + mii_info->speed = SPEED_100; + else + mii_info->speed = SPEED_10; + mii_info->pause = 0; + } + /* On non-aneg, we assume what we put in BMCR is the speed, + * though magic-aneg shouldn't prevent this case from occurring + */ + + return 0; +} + +static int marvell_read_status(struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there + * was an error */ + err = genmii_update_link(mii_info); + if (err) + return err; + + /* If the link is up, read the speed and duplex */ + /* If we aren't autonegotiating, assume speeds + * are as set */ + if (mii_info->autoneg && mii_info->link) { + int speed; + status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS); + + /* Get the duplexity */ + if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + + /* Get the speed */ + speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; + switch(speed) { + case MII_M1011_PHY_SPEC_STATUS_1000: + mii_info->speed = SPEED_1000; + break; + case MII_M1011_PHY_SPEC_STATUS_100: + mii_info->speed = SPEED_100; + break; + default: + mii_info->speed = SPEED_10; + break; + } + mii_info->pause = 0; + } + + return 0; +} + +static int marvell_ack_interrupt(struct uec_mii_info *mii_info) +{ + /* Clear the interrupts by reading the reg */ + phy_read(mii_info, MII_M1011_IEVENT); + + return 0; +} + +static int marvell_config_intr(struct uec_mii_info *mii_info) +{ + if(mii_info->interrupts == MII_INTERRUPT_ENABLED) + phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); + else + phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); + + return 0; +} + +static int dm9161_init(struct uec_mii_info *mii_info) +{ + /* Reset the PHY */ + phy_write(mii_info, PHY_BMCR, phy_read(mii_info, PHY_BMCR) | + PHY_BMCR_RESET); + /* PHY and MAC connect*/ + phy_write(mii_info, PHY_BMCR, phy_read(mii_info, PHY_BMCR) & + ~PHY_BMCR_ISO); +#ifdef CONFIG_RMII_MODE + phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_RMII_INIT); +#else + phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT); +#endif + config_genmii_advert(mii_info); + /* Start/restart aneg */ + genmii_config_aneg(mii_info); + /* Delay to wait the aneg compeleted */ + udelay(3000000); + + return 0; +} + +static int dm9161_config_aneg(struct uec_mii_info *mii_info) +{ + return 0; +} + +static int dm9161_read_status(struct uec_mii_info *mii_info) +{ + u16 status; + int err; + + /* Update the link, but return if there was an error*/ + err = genmii_update_link(mii_info); + if (err) + return err; + /* If the link is up, read the speed and duplex + If we aren't autonegotiating assume speeds are as set */ + if (mii_info->autoneg && mii_info->link) { + status = phy_read(mii_info, MII_DM9161_SCSR); + if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) + mii_info->speed = SPEED_100; + else + mii_info->speed = SPEED_10; + + if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) + mii_info->duplex = DUPLEX_FULL; + else + mii_info->duplex = DUPLEX_HALF; + } + + return 0; +} + +static int dm9161_ack_interrupt(struct uec_mii_info *mii_info) +{ + /* Clear the interrupt by reading the reg */ + phy_read(mii_info, MII_DM9161_INTR); + + return 0; +} + +static int dm9161_config_intr(struct uec_mii_info *mii_info) +{ + if (mii_info->interrupts == MII_INTERRUPT_ENABLED) + phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); + else + phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); + + return 0; +} + +static void dm9161_close(struct uec_mii_info *mii_info) +{ +} + +static struct phy_info phy_info_dm9161 = { + .phy_id = 0x0181b880, + .phy_id_mask = 0x0ffffff0, + .name = "Davicom DM9161E", + .init = dm9161_init, + .config_aneg = dm9161_config_aneg, + .read_status = dm9161_read_status, + .close = dm9161_close, +}; + +static struct phy_info phy_info_dm9161a = { + .phy_id = 0x0181b8a0, + .phy_id_mask = 0x0ffffff0, + .name = "Davicom DM9161A", + .features = MII_BASIC_FEATURES, + .init = dm9161_init, + .config_aneg = dm9161_config_aneg, + .read_status = dm9161_read_status, + .ack_interrupt = dm9161_ack_interrupt, + .config_intr = dm9161_config_intr, + .close = dm9161_close, +}; + +static struct phy_info phy_info_marvell = { + .phy_id = 0x01410c00, + .phy_id_mask = 0xffffff00, + .name = "Marvell 88E11x1", + .features = MII_GBIT_FEATURES, + .config_aneg = &marvell_config_aneg, + .read_status = &marvell_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, +}; + +static struct phy_info phy_info_genmii= { + .phy_id = 0x00000000, + .phy_id_mask = 0x00000000, + .name = "Generic MII", + .features = MII_BASIC_FEATURES, + .config_aneg = genmii_config_aneg, + .read_status = genmii_read_status, +}; + +static struct phy_info *phy_info[] = { + &phy_info_dm9161, + &phy_info_dm9161a, + &phy_info_marvell, + &phy_info_genmii, + NULL +}; + +u16 phy_read(struct uec_mii_info *mii_info, u16 regnum) +{ + return mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum); +} + +void phy_write(struct uec_mii_info *mii_info, u16 regnum, u16 val) +{ + mii_info->mdio_write(mii_info->dev, + mii_info->mii_id, + regnum, val); +} + +/* Use the PHY ID registers to determine what type of PHY is attached + * to device dev. return a struct phy_info structure describing that PHY + */ +struct phy_info * get_phy_info(struct uec_mii_info *mii_info) +{ + u16 phy_reg; + u32 phy_ID; + int i; + struct phy_info *theInfo = NULL; + + /* Grab the bits from PHYIR1, and put them in the upper half */ + phy_reg = phy_read(mii_info, PHY_PHYIDR1); + phy_ID = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = phy_read(mii_info, PHY_PHYIDR2); + phy_ID |= (phy_reg & 0xffff); + + /* loop through all the known PHY types, and find one that */ + /* matches the ID we read from the PHY. */ + for (i = 0; phy_info[i]; i++) + if (phy_info[i]->phy_id == + (phy_ID & phy_info[i]->phy_id_mask)) { + theInfo = phy_info[i]; + break; + } + + /* This shouldn't happen, as we have generic PHY support */ + if (theInfo == NULL) { + ugphy_info("UEC: PHY id %x is not supported!", phy_ID); + return NULL; + } else { + ugphy_info("UEC: PHY is %s (%x)", theInfo->name, phy_ID); + } + + return theInfo; +} + +void marvell_phy_interface_mode(struct eth_device *dev, enet_interface_e mode) +{ + uec_private_t *uec = (uec_private_t *)dev->priv; + struct uec_mii_info *mii_info; + + if (!uec->mii_info) { + printf("%s: the PHY not intialized\n", __FUNCTION__); + return; + } + mii_info = uec->mii_info; + + if (mode == ENET_100_RGMII) { + phy_write(mii_info, 0x00, 0x9140); + phy_write(mii_info, 0x1d, 0x001f); + phy_write(mii_info, 0x1e, 0x200c); + phy_write(mii_info, 0x1d, 0x0005); + phy_write(mii_info, 0x1e, 0x0000); + phy_write(mii_info, 0x1e, 0x0100); + phy_write(mii_info, 0x09, 0x0e00); + phy_write(mii_info, 0x04, 0x01e1); + phy_write(mii_info, 0x00, 0x9140); + phy_write(mii_info, 0x00, 0x1000); + udelay(100000); + phy_write(mii_info, 0x00, 0x2900); + phy_write(mii_info, 0x14, 0x0cd2); + phy_write(mii_info, 0x00, 0xa100); + phy_write(mii_info, 0x09, 0x0000); + phy_write(mii_info, 0x1b, 0x800b); + phy_write(mii_info, 0x04, 0x05e1); + phy_write(mii_info, 0x00, 0xa100); + phy_write(mii_info, 0x00, 0x2100); + udelay(1000000); + } else if (mode == ENET_10_RGMII) { + phy_write(mii_info, 0x14, 0x8e40); + phy_write(mii_info, 0x1b, 0x800b); + phy_write(mii_info, 0x14, 0x0c82); + phy_write(mii_info, 0x00, 0x8100); + udelay(1000000); + } +} + +void change_phy_interface_mode(struct eth_device *dev, enet_interface_e mode) +{ +#ifdef CONFIG_PHY_MODE_NEED_CHANGE + marvell_phy_interface_mode(dev, mode); +#endif +} +#endif /* CONFIG_QE */ diff --git a/drivers/qe/uec_phy.h b/drivers/qe/uec_phy.h new file mode 100644 index 0000000..a82f1f5 --- /dev/null +++ b/drivers/qe/uec_phy.h @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2005 Freescale Semiconductor, Inc. + * + * Author: Shlomi Gridish <gridish@freescale.com> + * + * Description: UCC ethernet driver -- PHY handling + * Driver for UEC on QE + * Based on 8260_io/fcc_enet.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef __UEC_PHY_H__ +#define __UEC_PHY_H__ + +#define MII_end ((u32)-2) +#define MII_read ((u32)-1) + +#define MIIMIND_BUSY 0x00000001 +#define MIIMIND_NOTVALID 0x00000004 + +#define UGETH_AN_TIMEOUT 2000 + +/* 1000BT control (Marvell & BCM54xx at least) */ +#define MII_1000BASETCONTROL 0x09 +#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200 +#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100 + +/* Cicada Extended Control Register 1 */ +#define MII_CIS8201_EXT_CON1 0x17 +#define MII_CIS8201_EXTCON1_INIT 0x0000 + +/* Cicada Interrupt Mask Register */ +#define MII_CIS8201_IMASK 0x19 +#define MII_CIS8201_IMASK_IEN 0x8000 +#define MII_CIS8201_IMASK_SPEED 0x4000 +#define MII_CIS8201_IMASK_LINK 0x2000 +#define MII_CIS8201_IMASK_DUPLEX 0x1000 +#define MII_CIS8201_IMASK_MASK 0xf000 + +/* Cicada Interrupt Status Register */ +#define MII_CIS8201_ISTAT 0x1a +#define MII_CIS8201_ISTAT_STATUS 0x8000 +#define MII_CIS8201_ISTAT_SPEED 0x4000 +#define MII_CIS8201_ISTAT_LINK 0x2000 +#define MII_CIS8201_ISTAT_DUPLEX 0x1000 + +/* Cicada Auxiliary Control/Status Register */ +#define MII_CIS8201_AUX_CONSTAT 0x1c +#define MII_CIS8201_AUXCONSTAT_INIT 0x0004 +#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 +#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 +#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 +#define MII_CIS8201_AUXCONSTAT_100 0x0008 + +/* 88E1011 PHY Status Register */ +#define MII_M1011_PHY_SPEC_STATUS 0x11 +#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000 +#define MII_M1011_PHY_SPEC_STATUS_100 0x4000 +#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000 +#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000 +#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800 +#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400 + +#define MII_M1011_IEVENT 0x13 +#define MII_M1011_IEVENT_CLEAR 0x0000 + +#define MII_M1011_IMASK 0x12 +#define MII_M1011_IMASK_INIT 0x6400 +#define MII_M1011_IMASK_CLEAR 0x0000 + +#define MII_DM9161_SCR 0x10 +#define MII_DM9161_SCR_INIT 0x0610 +#define MII_DM9161_SCR_RMII_INIT 0x0710 + +/* DM9161 Specified Configuration and Status Register */ +#define MII_DM9161_SCSR 0x11 +#define MII_DM9161_SCSR_100F 0x8000 +#define MII_DM9161_SCSR_100H 0x4000 +#define MII_DM9161_SCSR_10F 0x2000 +#define MII_DM9161_SCSR_10H 0x1000 + +/* DM9161 Interrupt Register */ +#define MII_DM9161_INTR 0x15 +#define MII_DM9161_INTR_PEND 0x8000 +#define MII_DM9161_INTR_DPLX_MASK 0x0800 +#define MII_DM9161_INTR_SPD_MASK 0x0400 +#define MII_DM9161_INTR_LINK_MASK 0x0200 +#define MII_DM9161_INTR_MASK 0x0100 +#define MII_DM9161_INTR_DPLX_CHANGE 0x0010 +#define MII_DM9161_INTR_SPD_CHANGE 0x0008 +#define MII_DM9161_INTR_LINK_CHANGE 0x0004 +#define MII_DM9161_INTR_INIT 0x0000 +#define MII_DM9161_INTR_STOP \ +(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ + | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) + +/* DM9161 10BT Configuration/Status */ +#define MII_DM9161_10BTCSR 0x12 +#define MII_DM9161_10BTCSR_INIT 0x7800 + +#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ + SUPPORTED_10baseT_Full | \ + SUPPORTED_100baseT_Half | \ + SUPPORTED_100baseT_Full | \ + SUPPORTED_Autoneg | \ + SUPPORTED_TP | \ + SUPPORTED_MII) + +#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ + SUPPORTED_1000baseT_Half | \ + SUPPORTED_1000baseT_Full) + +#define MII_READ_COMMAND 0x00000001 + +#define MII_INTERRUPT_DISABLED 0x0 +#define MII_INTERRUPT_ENABLED 0x1 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +/* Indicates what features are supported by the interface. */ +#define SUPPORTED_10baseT_Half (1 << 0) +#define SUPPORTED_10baseT_Full (1 << 1) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_1000baseT_Half (1 << 4) +#define SUPPORTED_1000baseT_Full (1 << 5) +#define SUPPORTED_Autoneg (1 << 6) +#define SUPPORTED_TP (1 << 7) +#define SUPPORTED_AUI (1 << 8) +#define SUPPORTED_MII (1 << 9) +#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_BNC (1 << 11) +#define SUPPORTED_10000baseT_Full (1 << 12) + +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) +#define ADVERTISED_10000baseT_Full (1 << 12) + +/* Advertisement control register. */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_RESV 0x1c00 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Taken from mii_if_info and sungem_phy.h */ +struct uec_mii_info { + /* Information about the PHY type */ + /* And management functions */ + struct phy_info *phyinfo; + + struct eth_device *dev; + + /* forced speed & duplex (no autoneg) + * partner speed & duplex & pause (autoneg) + */ + int speed; + int duplex; + int pause; + + /* The most recently read link state */ + int link; + + /* Enabled Interrupts */ + u32 interrupts; + + u32 advertising; + int autoneg; + int mii_id; + + /* private data pointer */ + /* For use by PHYs to maintain extra state */ + void *priv; + + /* Provided by ethernet driver */ + int (*mdio_read) (struct eth_device *dev, int mii_id, int reg); + void (*mdio_write) (struct eth_device *dev, int mii_id, int reg, int val); +}; + +/* struct phy_info: a structure which defines attributes for a PHY + * + * id will contain a number which represents the PHY. During + * startup, the driver will poll the PHY to find out what its + * UID--as defined by registers 2 and 3--is. The 32-bit result + * gotten from the PHY will be ANDed with phy_id_mask to + * discard any bits which may change based on revision numbers + * unimportant to functionality + * + * There are 6 commands which take a ugeth_mii_info structure. + * Each PHY must declare config_aneg, and read_status. + */ +struct phy_info { + u32 phy_id; + char *name; + unsigned int phy_id_mask; + u32 features; + + /* Called to initialize the PHY */ + int (*init)(struct uec_mii_info *mii_info); + + /* Called to suspend the PHY for power */ + int (*suspend)(struct uec_mii_info *mii_info); + + /* Reconfigures autonegotiation (or disables it) */ + int (*config_aneg)(struct uec_mii_info *mii_info); + + /* Determines the negotiated speed and duplex */ + int (*read_status)(struct uec_mii_info *mii_info); + + /* Clears any pending interrupts */ + int (*ack_interrupt)(struct uec_mii_info *mii_info); + + /* Enables or disables interrupts */ + int (*config_intr)(struct uec_mii_info *mii_info); + + /* Clears up any memory if needed */ + void (*close)(struct uec_mii_info *mii_info); +}; + +struct phy_info *get_phy_info(struct uec_mii_info *mii_info); +void write_phy_reg(struct eth_device *dev, int mii_id, int regnum, int value); +int read_phy_reg(struct eth_device *dev, int mii_id, int regnum); +void mii_clear_phy_interrupt(struct uec_mii_info *mii_info); +void mii_configure_phy_interrupt(struct uec_mii_info *mii_info, u32 interrupts); +#endif /* __UEC_PHY_H__ */ |