/* * (C) Copyright 2006 * Mindspeed Technologies * * 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 #if defined(CONFIG_CMD_NET) && defined(CONFIG_COMCERTO_EMAC) #if !defined(CONFIG_EMAC0) && !defined(CONFIG_EMAC1) #error must define one of CONFIG_EMAC0 or CONFIG_EMAC1 #endif #include #include #include #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) #include #endif #define MAX_DEVNAME_SIZE 8 struct bdesc { volatile u8 *bptr; volatile u32 bcontrol; u32 dummy1; u32 dummy2; }; struct fdesc { /* Hardware mapped fields */ void *next; /* 4 bytes */ volatile u32 system; /* 4 bytes */ volatile u32 fstatus; /* 4 bytes */ volatile u32 fcontrol; /* 4 bytes */ struct bdesc bdesc; /* 16 bytes */ }; struct emac_dev { char devname[MAX_DEVNAME_SIZE]; u32 baseaddr; u32 idma_baseaddr; s8 miifdl; s8 mii100l; unsigned int md_clk; u8 phy_addr; u8 mii_mode; u8 phyctrldis; u8 mii_link_pol; }; static struct emac_dev emac0 = { .devname = "emac0", .baseaddr = EMAC0_BASEADDR, .idma_baseaddr = IDMA_EMAC0_BASEADDR, #ifdef CONFIG_EMAC0_MIIFDL .miifdl = CONFIG_EMAC0_MIIFDL, #else .miifdl = -1, #endif #ifdef CONFIG_EMAC0_MII100L .mii100l = CONFIG_EMAC0_MII100L, #else .mii100l = -1, #endif #ifdef CONFIG_EMAC0_MII_MODE .mii_mode = CONFIG_EMAC0_MII_MODE, #else .mii_mode = EMAC_RMII_SMII_MODE, #endif #ifdef CONFIG_EMAC0_PHYCTRLDIS .phyctrldis = CONFIG_EMAC0_PHYCTRLDIS, #else .phyctrldis = 0x1, #endif #if CONFIG_EMAC0_MII_LINK_POL .mii_link_pol = CONFIG_EMAC0_MII_LINK_POL, #else .mii_link_pol = 0x0, #endif #ifdef CONFIG_EMAC0_MDCLOCK .md_clk = CONFIG_EMAC0_MDCLOCK, #else .md_clk = 2500000, #endif #ifdef CONFIG_EMAC0_PHY_ADDR .phy_addr = CONFIG_EMAC0_PHY_ADDR, #else .phy_addr = 0x00, #endif }; #ifdef CONFIG_COMCERTO_800 static struct emac_dev emac1 = { .devname = "emac1", .baseaddr = EMAC1_BASEADDR, .idma_baseaddr = IDMA_EMAC1_BASEADDR, #ifdef CONFIG_EMAC1_MIIFDL .miifdl = CONFIG_EMAC1_MIIFDL, #else .miifdl = -1, #endif #ifdef CONFIG_EMAC1_MII100L .mii100l = CONFIG_EMAC1_MII100L, #else .mii100l = -1, #endif #ifdef CONFIG_EMAC1_MII_MODE .mii_mode = CONFIG_EMAC1_MII_MODE, #else .mii_mode = EMAC_RMII_SMII_MODE, #endif #ifdef CONFIG_EMAC1_PHYCTRLDIS .phyctrldis = CONFIG_EMAC1_PHYCTRLDIS, #else .phyctrldis = 0x1, #endif #if CONFIG_EMAC1_MII_LINK_POL .mii_link_pol = CONFIG_EMAC1_MII_LINK_POL, #else .mii_link_pol = 0x0, #endif #ifdef CONFIG_EMAC1_MDCLOCK .md_clk = CONFIG_EMAC1_MDCLOCK, #else .md_clk = 2500000, #endif #ifdef CONFIG_EMAC1_PHY_ADDR .phy_addr = CONFIG_EMAC1_PHY_ADDR, #else .phy_addr = 0x00, #endif }; #endif /* CONFIG_COMCERTO_800 */ #ifdef CONFIG_EMAC0 static struct emac_dev *emac = &emac0; #else static struct emac_dev *emac = &emac1; #endif #define NUM_RX_DESC 16 #define MAX_RX_BUFF_SIZE 2048 static u32 rx_next; static u8 rx_ring_data_buff[NUM_RX_DESC * MAX_RX_BUFF_SIZE]; static struct fdesc buff_aligned[NUM_RX_DESC] __attribute((aligned(16))); static struct fdesc tx_fdesc __attribute((aligned(16))); static struct fdesc *rx_ring; #define EMAC_disable_rx(emac)\ *(volatile u32 *)((emac)->baseaddr + EMAC_RXCTRL) &= ~RX_EN #define EMAC_disable_tx(emac)\ *(volatile u32 *)((emac)->baseaddr + EMAC_TXCTRL) &= ~TX_EN #define EMAC_enable_rx(emac)\ *(volatile u32 *)((emac)->baseaddr + EMAC_RXCTRL) |= RX_EN #define EMAC_enable_tx(emac)\ *(volatile u32 *)((emac)->baseaddr + EMAC_TXCTRL) |= TX_EN static int get_mdcckse(int md_clk) { unsigned int div = CFG_HZ_CLOCK / md_clk; if (div > 60) return 0x3; else if (div > 28) return 0x2; else if (div > 16) return 0x1; else return 0x0; } #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) static int comcerto_miiphy_write(char *devname, unsigned char addr, unsigned char reg, unsigned short value) { struct emac_dev *emac; if ((addr > 31) || (reg > 31)) return 1; if (!strcmp(devname, emac0.devname)) emac = &emac0; #ifdef CONFIG_COMCERTO_800 else if (!strcmp(devname, emac1.devname)) emac = &emac1; #endif else return -1; if (*(volatile u32 *)(emac->baseaddr + EMAC_MDCA) & EMAC_MDIO_BUSY) return 1; /* start a write */ *(volatile u32 *)(emac->baseaddr + EMAC_MDDATA) = value; *(volatile u32 *)(emac->baseaddr + EMAC_MDCA) = (reg | (addr << 5) | EMAC_MDIO_WRITE | EMAC_MDIO_BUSY); while (*(volatile u32 *)(emac->baseaddr + EMAC_MDCA) & EMAC_MDIO_BUSY) ; return 0; } static int comcerto_miiphy_read(char *devname, unsigned char addr, unsigned char reg, unsigned short *value) { struct emac_dev *emac; if ((addr > 31) || (reg > 31)) return 1; if (!strcmp(devname, emac0.devname)) emac = &emac0; #ifdef CONFIG_COMCERTO_800 else if (!strcmp(devname, emac1.devname)) emac = &emac1; #endif else return -1; if (*(volatile u32 *)(emac->baseaddr + EMAC_MDCA) & EMAC_MDIO_BUSY) return 1; *(volatile u32 *)(emac->baseaddr + EMAC_MDCA) = (reg | (addr << 5) | EMAC_MDIO_BUSY); while (*(volatile u32 *)(emac->baseaddr + EMAC_MDCA) & EMAC_MDIO_BUSY) ; *value = *(volatile u32 *)(emac->baseaddr + EMAC_MDDATA); return 0; } static u32 get_ocr_mii_cfg(struct emac_dev *emac, u32 ocr) { unsigned short tmp; miiphy_read(emac->devname, emac->phy_addr, PHY_BMSR, &tmp); #ifdef CFG_FAULT_ECHO_LINK_DOWN if (miiphy_link(emac->devname, emac->phy_addr)) ocr &= ~EMAC_OCR_MII100L; else ocr |= EMAC_OCR_MII100L; #else ocr &= ~EMAC_OCR_MII100L; #endif /* CFG_FAULT_ECHO_LINK_DOWN */ if (emac->miifdl < 0) { /* get link duplex from phy */ if (miiphy_duplex(emac->devname, emac->phy_addr) == HALF) ocr |= EMAC_OCR_MIIFDL; else ocr &= ~EMAC_OCR_MIIFDL; } else { /* force link duplex */ if (emac->miifdl) ocr |= EMAC_OCR_MIIFDL; else ocr &= ~EMAC_OCR_MIIFDL; } if (emac->mii100l < 0) { /* get link speed from phy */ if (miiphy_speed(emac->devname, emac->phy_addr) == _10BASET) ocr |= EMAC_OCR_MII100L; else ocr &= ~EMAC_OCR_MII100L; } else { /* force link speed */ if (emac->mii100l) ocr |= EMAC_OCR_MII100L; else ocr &= ~EMAC_OCR_MII100L; } return ocr; } #else static u32 get_ocr_mii_cfg(struct emac_dev *emac, u32 ocr) { ocr &= ~EMAC_OCR_MII100L; if (emac->miifdl < 0) { ocr &= ~EMAC_OCR_MIIFDL; } else { /* force link duplex */ if (emac->miifdl) ocr |= EMAC_OCR_MIIFDL; else ocr &= ~EMAC_OCR_MIIFDL; } if (emac->mii100l < 0) { ocr &= ~EMAC_OCR_MII100L; } else { /* force link speed */ if (emac->mii100l) ocr |= EMAC_OCR_MII100L; else ocr &= ~EMAC_OCR_MII100L; } return ocr; } #endif /* defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */ #ifdef CONFIG_NET_MULTI #else int comcerto_miiphy_initialize(bd_t *bis) { #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) u32 ocr; #ifdef CONFIG_EMAC0 ocr = *(volatile u32 *)(emac0.baseaddr + EMAC_OCR); ocr &= ~(0x3 << 2); *(volatile u32 *)(emac0.baseaddr + EMAC_OCR) = ocr | (get_mdcckse(emac0.md_clk) << 2); miiphy_register(emac0.devname, comcerto_miiphy_read, comcerto_miiphy_write); #endif #ifdef CONFIG_EMAC1 ocr = *(volatile u32 *)(emac1.baseaddr + EMAC_OCR); ocr &= ~(0x3 << 2); *(volatile u32 *)(emac1.baseaddr + EMAC_OCR) = ocr | (get_mdcckse(emac1.md_clk) << 2); miiphy_register(emac1.devname, comcerto_miiphy_read, comcerto_miiphy_write); #endif #endif /* defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */ return 0; } #endif /* CONFIG_NET_MULTI */ static int emac_arc_add(struct emac_dev *emac, u8 *mac_addr) { u32 temp; u8 offset = 0; /* we are in boot loader, even though this EMAC handles more than 20 entries, we only gonna use one (Entry 0) */ temp = *mac_addr++; temp <<= 8; temp |= *mac_addr++; temp <<= 8; temp |= *mac_addr++; temp <<= 8; temp |= *mac_addr++; *(volatile u32 *)(emac->baseaddr + EMAC_ARC_TABLE + offset) = temp; offset += 4; temp = *mac_addr++; temp <<= 8; temp |= *mac_addr++; temp <<= 16; *(volatile u32 *)(emac->baseaddr + EMAC_ARC_TABLE + offset) = ((*(volatile u32 *)(emac->baseaddr + EMAC_ARC_TABLE + offset)) & 0x0000FFFF) | temp; *(volatile u32 *)(emac->baseaddr + EMAC_ARCENA) |= 1; *(volatile u32 *)(emac->baseaddr + EMAC_ARCCTRL) |= ARC_COMPENA; return 1; } static void emac_rx_ring_init(void) { u32 i; u8 *pU8; pU8 = rx_ring_data_buff; rx_ring = buff_aligned; for (i = 0; i < NUM_RX_DESC - 1; i++) { rx_ring[i].next = &rx_ring[i + 1]; rx_ring[i].system = i; rx_ring[i].fstatus = 0; rx_ring[i].fcontrol = IDMA_FCONTROL_FREADY; rx_ring[i].bdesc.bptr = pU8; rx_ring[i].bdesc.bcontrol = 0; pU8 += MAX_RX_BUFF_SIZE; } rx_ring[i].next = &rx_ring[0]; rx_ring[i].system = i; rx_ring[i].fstatus = 0; rx_ring[i].fcontrol = 0; rx_ring[i].bdesc.bptr = pU8; rx_ring[i].bdesc.bcontrol = 0; rx_next = 0; } static void emac_ip_init(struct emac_dev *emac) { u32 i; /* EMAC_MACCTRL register : set FullDup (as MII_Conn is high), Conn as Force MII, and Soft reset */ *(volatile u32 *)(emac->baseaddr + EMAC_MACCTRL) = MAC_FULLDUP | MAC_MIIRATE | MAC_RESET; /* wait reset done */ while (*(volatile u32 *)(emac->baseaddr + EMAC_MACCTRL) & MAC_RESET) ; /* EMAC_ARC_TABLE init */ for (i = 0; i < (6 * MAX_ARC_ENTRIES); i += 4) *(volatile u32 *)((emac->baseaddr + EMAC_ARC_TABLE) + i) = 0; /* EMAC_ARCENA : set to 0 as no ARC entry yet */ *(volatile u32 *)(emac->baseaddr + EMAC_ARCENA) = 0; /* EMAC_ARCCTRL : accept BroadCast only (Local MAC address no yet defined) */ *(volatile u32 *)(emac->baseaddr + EMAC_ARCCTRL) = ARC_BROADACC; /* EMAC_TXCTRL : set Tx_en */ *(volatile u32 *)(emac->baseaddr + EMAC_TXCTRL) = TX_EN; /* EMAC_RXCTRL : set Rx_en, Strip CRC, ShortEn, */ *(volatile u32 *)(emac->baseaddr + EMAC_RXCTRL) = RX_EN | RX_STRIPCRC | RX_SHORTEN; } static void emac_ipif_init(struct emac_dev *emac) { u32 ocr, icr; #ifdef CONFIG_COMCERTO_530 ocr = 0x00200200 | (get_mdcckse(emac->md_clk) << 2); /* force 100Mbit, full duplex, link up */ icr = 0x00000000; ocr = get_ocr_mii_cfg(emac, ocr); #else ocr = 0x00200200 | (get_mdcckse(emac->md_clk) << 2); /* force 100Mbit, full duplex, link up */ icr = 0x00008000; /* use software settings */ switch (emac->mii_mode) { case EMAC_MII_MODE: ocr |= EMAC_OCR_SEL_MII_MODE; break; case EMAC_FORCE_RMII_MODE: ocr &= ~EMAC_OCR_SEL_MII_MODE; ocr |= EMAC_OCR_SW_SMII_OVRD; ocr &= ~EMAC_OCR_SW_SMII_MODE; break; case EMAC_FORCE_SMII_MODE: ocr &= ~EMAC_OCR_SEL_MII_MODE; ocr |= EMAC_OCR_SW_SMII_OVRD; ocr |= EMAC_OCR_SW_SMII_MODE; break; case EMAC_RMII_SMII_MODE: default: ocr &= ~EMAC_OCR_SEL_MII_MODE; ocr &= ~EMAC_OCR_SW_SMII_OVRD; break; } switch (emac->mii_mode) { case EMAC_MII_MODE: ocr = get_ocr_mii_cfg(emac, ocr); break; case EMAC_FORCE_RMII_MODE: case EMAC_FORCE_SMII_MODE: case EMAC_RMII_SMII_MODE: default: if (emac->phyctrldis) { icr |= EMAC_ICR_PHYCTRLDIS; ocr = get_ocr_mii_cfg(emac, ocr); } else { icr &= ~EMAC_ICR_PHYCTRLDIS; icr &= ~(EMAC_ICR_MII100L_POL | EMAC_ICR_MIIFDL_POL | EMAC_ICR_MIILINK_POL); icr |= (emac->mii_link_pol & 0x7) << 8; } break; } #endif /* Init EMAC Operation Controlling Register */ *(volatile u32 *)(emac->baseaddr + EMAC_OCR) = ocr; *(volatile u32 *)(emac->baseaddr + EMAC_ICR) = icr; /* init DMA TX/RX Data FIFO Config Registers */ *(volatile u32 *)(emac->baseaddr + EMAC_HOST_FIFO_CONTROL) = EMAC_HOST_HBTXRQ_EN | EMAC_HOST_RXFF_EN | EMAC_HOST_HBTXRQ_EN | EMAC_HOST_TXFF_EN; /* TX threshold Host =EMAC */ *(volatile u32 *)(emac->baseaddr + EMAC_HOST_FIFO_TXHIGH) = 0x000F3; *(volatile u32 *)(emac->baseaddr + EMAC_HOST_FIFO_TXLOW) = 0x00F0; /* RX threshold */ *(volatile u32 *)(emac->baseaddr + EMAC_HOST_FIFO_RXHIGH) = 0x0013; *(volatile u32 *)(emac->baseaddr + EMAC_HOST_FIFO_RXLOW) = 0x0012; /* arm side */ *(volatile u32 *)(emac->baseaddr + EMAC_ARM_FIFO_TXHIGH) = 0x00C0; *(volatile u32 *)(emac->baseaddr + EMAC_ARM_FIFO_TXLOW) = 0x0020; /* RX threshold */ *(volatile u32 *)(emac->baseaddr + EMAC_ARM_FIFO_RXHIGH) = 0x00E0; *(volatile u32 *)(emac->baseaddr + EMAC_ARM_FIFO_RXLOW) = 0x0040; *(volatile u32 *)(emac->baseaddr + EMAC_ARM_FIFO_CONTROL) = EMAC_ARM_RXDREQWE | EMAC_ARM_TXDREQRE; *(volatile u32 *)ASA_TERMINAL_COUNT_CFG |= ASA_TC_REQIDMAEN; } static void emac_dma_init(struct emac_dev *emac) { /* Init IDMA */ /* Memory to EMAC (Tx) */ *(volatile u32 *)(emac->idma_baseaddr + MMEM_BURST) = (IDMA_BURST_MASK & 0xFF) | (IDMA_PRTY_MASK & 0x0200); /* burst size 255, priority 1 */ *(volatile u32 *)(emac->idma_baseaddr + MMEM_START) = 0; /* EMAC to Memory (Rx) */ *(volatile u32 *)(emac->idma_baseaddr + EMMM_BURST) = (IDMA_BURST_MASK & 0xFF) | (IDMA_PRTY_MASK & 0x0100); /* burst size 255, priority 1 */ *(volatile u32 *)(emac->idma_baseaddr + EMMM_START) = 0; } static int emac_init(struct emac_dev *emac, u8 *mac_addr) { *(volatile u32 *)(emac->idma_baseaddr + EMMM_SOFT_RESET) = 0x1; *(volatile u32 *)(emac->idma_baseaddr + MMEM_SOFT_RESET) = 0x1; *(volatile u32 *)(emac->baseaddr + EMAC_OCR) |= EMAC_OCR_IP_HRESET; udelay(100); *(volatile u32 *)(emac->baseaddr + EMAC_OCR) &= ~EMAC_OCR_IP_HRESET; emac_ipif_init(emac); emac_ip_init(emac); emac_dma_init(emac); /* Add Mac Address in the ARC table */ emac_arc_add(emac, mac_addr); /* Init Ehternet buffers */ emac_rx_ring_init(); /* Start Rx EDMA */ *(volatile u32 *)(emac->idma_baseaddr + EMMM_HEAD) = (u32) &rx_ring[rx_next]; *(volatile u32 *)(emac->idma_baseaddr + EMMM_START) |= IDMA_START; return 0; } static int emac_send(struct emac_dev *emac, volatile void *packet, int length) { int i; memset(&tx_fdesc, 0, sizeof(struct fdesc)); /* build the tx frame descriptor here */ tx_fdesc.fcontrol = IDMA_FCONTROL_FREADY | IDMA_FCONTROL_FLAST; tx_fdesc.fstatus = 0; tx_fdesc.bdesc.bptr = packet; tx_fdesc.bdesc.bcontrol = length | IDMA_BCONTROL_BLAST; /* Check if DMA Stopped */ if (!(*(volatile u32 *)(emac->idma_baseaddr + MMEM_START) & IDMA_START)) { *(volatile u32 *)(emac->idma_baseaddr + MMEM_HEAD) = (u32) &tx_fdesc; *(volatile u32 *)(emac->idma_baseaddr + MMEM_START) |= IDMA_START; } else { printf("Emac: tx EDMA busy!\n"); return (-1); } i = 0; while ((tx_fdesc.fstatus & IDMA_FSTATUS_FRAME_DONE_MASK) == 0) { udelay(100); i++; if (i == 50000) { printf("Emac: tx timed out! %x\n", tx_fdesc.fstatus); return (-1); } } if (*(volatile u32 *)(emac->idma_baseaddr + MMEM_START) & IDMA_START) { printf("Emac: tx did not stop after sending a packet!\n"); } return (length); } static int emac_rx(struct emac_dev *emac) { int rx_prev; int length; int total_length = 0; /* loop thru rx FDescs */ while (1) { if ((rx_ring[rx_next].fstatus & IDMA_FSTATUS_FRAME_DONE_MASK) == 0) break; /* mark rx_next not usable */ rx_ring[rx_next].fcontrol = 0; if (rx_ring[rx_next].fstatus & (1 << 10)) { length = rx_ring[rx_next].bdesc.bcontrol & IDMA_BCONTROL_BLEN_MASK; if (length > MAX_RX_BUFF_SIZE) { printf("Emac: frame too big (%d bytes)!\n", length); length = MAX_RX_BUFF_SIZE; } /* Pass the packet up to the protocol layers. */ NetReceive(rx_ring[rx_next].bdesc.bptr, length); total_length += length; } /* rx_prev can be used now */ if (rx_next == 0) rx_prev = NUM_RX_DESC - 1; else rx_prev = rx_next - 1; rx_ring[rx_prev].fstatus = 0; rx_ring[rx_prev].fcontrol = IDMA_FCONTROL_FREADY; rx_next++; if (rx_next == NUM_RX_DESC) rx_next = 0; } /* Check if DMA Stopped, if RX is stopped, restart */ if (!(*(volatile u32 *)(emac->idma_baseaddr + EMMM_START) & IDMA_START)) { *(volatile u32 *)(emac->idma_baseaddr + EMMM_HEAD) = (u32) &rx_ring[rx_next]; *(volatile u32 *)(emac->idma_baseaddr + EMMM_START) |= IDMA_START; } return (total_length); } static void emac_halt(struct emac_dev *emac) { /* stop EMAC */ EMAC_disable_rx(emac); EMAC_disable_tx(emac); *(volatile u32 *)(emac->idma_baseaddr + EMMM_SOFT_RESET) = 0x1; *(volatile u32 *)(emac->idma_baseaddr + MMEM_SOFT_RESET) = 0x1; } #ifdef CONFIG_NET_MULTI #else int eth_send(volatile void *packet, int length) { if (length <= 0) { printf("Emac: bad packet size: %d\n", length); return (-1); } return emac_send(emac, packet, length); } int eth_init(bd_t * bd) { return emac_init(emac, bd->bi_enetaddr); } int eth_rx(void) { return emac_rx(emac); } void eth_halt(void) { emac_halt(emac); } #endif /* CONFIG_NET_MULTI */ #endif /* CFG_CMD_NET && defined(CONFIG_COMCERTO_EMAC) */