/* * Copyright 2014 Broadcom Corporation. * * SPDX-License-Identifier: GPL-2.0+ */ #ifdef BCM_GMAC_DEBUG #ifndef DEBUG #define DEBUG #endif #endif #include #include #include #include #include #include #include "bcm-sf2-eth.h" #include "bcm-sf2-eth-gmac.h" #define SPINWAIT(exp, us) { \ uint countdown = (us) + 9; \ while ((exp) && (countdown >= 10)) {\ udelay(10); \ countdown -= 10; \ } \ } static int gmac_disable_dma(struct eth_dma *dma, int dir); static int gmac_enable_dma(struct eth_dma *dma, int dir); /* DMA Descriptor */ typedef struct { /* misc control bits */ uint32_t ctrl1; /* buffer count and address extension */ uint32_t ctrl2; /* memory address of the date buffer, bits 31:0 */ uint32_t addrlow; /* memory address of the date buffer, bits 63:32 */ uint32_t addrhigh; } dma64dd_t; uint32_t g_dmactrlflags; static uint32_t dma_ctrlflags(uint32_t mask, uint32_t flags) { debug("%s enter\n", __func__); g_dmactrlflags &= ~mask; g_dmactrlflags |= flags; /* If trying to enable parity, check if parity is actually supported */ if (g_dmactrlflags & DMA_CTRL_PEN) { uint32_t control; control = readl(GMAC0_DMA_TX_CTRL_ADDR); writel(control | D64_XC_PD, GMAC0_DMA_TX_CTRL_ADDR); if (readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_PD) { /* * We *can* disable it, therefore it is supported; * restore control register */ writel(control, GMAC0_DMA_TX_CTRL_ADDR); } else { /* Not supported, don't allow it to be enabled */ g_dmactrlflags &= ~DMA_CTRL_PEN; } } return g_dmactrlflags; } static inline void reg32_clear_bits(uint32_t reg, uint32_t value) { uint32_t v = readl(reg); v &= ~(value); writel(v, reg); } static inline void reg32_set_bits(uint32_t reg, uint32_t value) { uint32_t v = readl(reg); v |= value; writel(v, reg); } #ifdef BCM_GMAC_DEBUG static void dma_tx_dump(struct eth_dma *dma) { dma64dd_t *descp = NULL; uint8_t *bufp; int i; printf("TX DMA Register:\n"); printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n", readl(GMAC0_DMA_TX_CTRL_ADDR), readl(GMAC0_DMA_TX_PTR_ADDR), readl(GMAC0_DMA_TX_ADDR_LOW_ADDR), readl(GMAC0_DMA_TX_ADDR_HIGH_ADDR), readl(GMAC0_DMA_TX_STATUS0_ADDR), readl(GMAC0_DMA_TX_STATUS1_ADDR)); printf("TX Descriptors:\n"); for (i = 0; i < TX_BUF_NUM; i++) { descp = (dma64dd_t *)(dma->tx_desc_aligned) + i; printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n", descp->ctrl1, descp->ctrl2, descp->addrhigh, descp->addrlow); } printf("TX Buffers:\n"); /* Initialize TX DMA descriptor table */ for (i = 0; i < TX_BUF_NUM; i++) { bufp = (uint8_t *)(dma->tx_buf + i * TX_BUF_SIZE); printf("buf%d:0x%x; ", i, (uint32_t)bufp); } printf("\n"); } static void dma_rx_dump(struct eth_dma *dma) { dma64dd_t *descp = NULL; uint8_t *bufp; int i; printf("RX DMA Register:\n"); printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n", readl(GMAC0_DMA_RX_CTRL_ADDR), readl(GMAC0_DMA_RX_PTR_ADDR), readl(GMAC0_DMA_RX_ADDR_LOW_ADDR), readl(GMAC0_DMA_RX_ADDR_HIGH_ADDR), readl(GMAC0_DMA_RX_STATUS0_ADDR), readl(GMAC0_DMA_RX_STATUS1_ADDR)); printf("RX Descriptors:\n"); for (i = 0; i < RX_BUF_NUM; i++) { descp = (dma64dd_t *)(dma->rx_desc_aligned) + i; printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n", descp->ctrl1, descp->ctrl2, descp->addrhigh, descp->addrlow); } printf("RX Buffers:\n"); for (i = 0; i < RX_BUF_NUM; i++) { bufp = dma->rx_buf + i * RX_BUF_SIZE; printf("buf%d:0x%x; ", i, (uint32_t)bufp); } printf("\n"); } #endif static int dma_tx_init(struct eth_dma *dma) { dma64dd_t *descp = NULL; uint8_t *bufp; int i; uint32_t ctrl; debug("%s enter\n", __func__); /* clear descriptor memory */ memset((void *)(dma->tx_desc_aligned), 0, TX_BUF_NUM * sizeof(dma64dd_t)); memset(dma->tx_buf, 0, TX_BUF_NUM * TX_BUF_SIZE); /* Initialize TX DMA descriptor table */ for (i = 0; i < TX_BUF_NUM; i++) { descp = (dma64dd_t *)(dma->tx_desc_aligned) + i; bufp = dma->tx_buf + i * TX_BUF_SIZE; /* clear buffer memory */ memset((void *)bufp, 0, TX_BUF_SIZE); ctrl = 0; /* if last descr set endOfTable */ if (i == (TX_BUF_NUM-1)) ctrl = D64_CTRL1_EOT; descp->ctrl1 = ctrl; descp->ctrl2 = 0; descp->addrlow = (uint32_t)bufp; descp->addrhigh = 0; } /* flush descriptor and buffer */ descp = dma->tx_desc_aligned; bufp = dma->tx_buf; flush_dcache_range((unsigned long)descp, (unsigned long)(descp + sizeof(dma64dd_t) * TX_BUF_NUM)); flush_dcache_range((unsigned long)(bufp), (unsigned long)(bufp + TX_BUF_SIZE * TX_BUF_NUM)); /* initialize the DMA channel */ writel((uint32_t)(dma->tx_desc_aligned), GMAC0_DMA_TX_ADDR_LOW_ADDR); writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR); /* now update the dma last descriptor */ writel(((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK, GMAC0_DMA_TX_PTR_ADDR); return 0; } static int dma_rx_init(struct eth_dma *dma) { uint32_t last_desc; dma64dd_t *descp = NULL; uint8_t *bufp; uint32_t ctrl; int i; debug("%s enter\n", __func__); /* clear descriptor memory */ memset((void *)(dma->rx_desc_aligned), 0, RX_BUF_NUM * sizeof(dma64dd_t)); /* clear buffer memory */ memset(dma->rx_buf, 0, RX_BUF_NUM * RX_BUF_SIZE); /* Initialize RX DMA descriptor table */ for (i = 0; i < RX_BUF_NUM; i++) { descp = (dma64dd_t *)(dma->rx_desc_aligned) + i; bufp = dma->rx_buf + i * RX_BUF_SIZE; ctrl = 0; /* if last descr set endOfTable */ if (i == (RX_BUF_NUM - 1)) ctrl = D64_CTRL1_EOT; descp->ctrl1 = ctrl; descp->ctrl2 = RX_BUF_SIZE; descp->addrlow = (uint32_t)bufp; descp->addrhigh = 0; last_desc = ((uint32_t)(descp) & D64_XP_LD_MASK) + sizeof(dma64dd_t); } descp = dma->rx_desc_aligned; bufp = dma->rx_buf; /* flush descriptor and buffer */ flush_dcache_range((unsigned long)descp, (unsigned long)(descp + sizeof(dma64dd_t) * RX_BUF_NUM)); flush_dcache_range((unsigned long)(bufp), (unsigned long)(bufp + RX_BUF_SIZE * RX_BUF_NUM)); /* initailize the DMA channel */ writel((uint32_t)descp, GMAC0_DMA_RX_ADDR_LOW_ADDR); writel(0, GMAC0_DMA_RX_ADDR_HIGH_ADDR); /* now update the dma last descriptor */ writel(last_desc, GMAC0_DMA_RX_PTR_ADDR); return 0; } static int dma_init(struct eth_dma *dma) { debug(" %s enter\n", __func__); /* * Default flags: For backwards compatibility both * Rx Overflow Continue and Parity are DISABLED. */ dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN, 0); debug("rx burst len 0x%x\n", (readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK) >> D64_RC_BL_SHIFT); debug("tx burst len 0x%x\n", (readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_BL_MASK) >> D64_XC_BL_SHIFT); dma_tx_init(dma); dma_rx_init(dma); /* From end of chip_init() */ /* enable the overflow continue feature and disable parity */ dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN /* mask */, DMA_CTRL_ROC /* value */); return 0; } static int dma_deinit(struct eth_dma *dma) { debug(" %s enter\n", __func__); gmac_disable_dma(dma, MAC_DMA_RX); gmac_disable_dma(dma, MAC_DMA_TX); free(dma->tx_buf); dma->tx_buf = NULL; free(dma->tx_desc); dma->tx_desc = NULL; dma->tx_desc_aligned = NULL; free(dma->rx_buf); dma->rx_buf = NULL; free(dma->rx_desc); dma->rx_desc = NULL; dma->rx_desc_aligned = NULL; return 0; } int gmac_tx_packet(struct eth_dma *dma, void *packet, int length) { uint8_t *bufp = dma->tx_buf + dma->cur_tx_index * TX_BUF_SIZE; /* kick off the dma */ size_t len = length; int txout = dma->cur_tx_index; uint32_t flags; dma64dd_t *descp = NULL; uint32_t ctrl; uint32_t last_desc = (((uint32_t)dma->tx_desc_aligned) + sizeof(dma64dd_t)) & D64_XP_LD_MASK; size_t buflen; debug("%s enter\n", __func__); /* load the buffer */ memcpy(bufp, packet, len); /* Add 4 bytes for Ethernet FCS/CRC */ buflen = len + 4; ctrl = (buflen & D64_CTRL2_BC_MASK); /* the transmit will only be one frame or set SOF, EOF */ /* also set int on completion */ flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF; /* txout points to the descriptor to uset */ /* if last descriptor then set EOT */ if (txout == (TX_BUF_NUM - 1)) { flags |= D64_CTRL1_EOT; last_desc = ((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK; } /* write the descriptor */ descp = ((dma64dd_t *)(dma->tx_desc_aligned)) + txout; descp->addrlow = (uint32_t)bufp; descp->addrhigh = 0; descp->ctrl1 = flags; descp->ctrl2 = ctrl; /* flush descriptor and buffer */ flush_dcache_range((unsigned long)descp, (unsigned long)(descp + sizeof(dma64dd_t))); flush_dcache_range((unsigned long)bufp, (unsigned long)(bufp + TX_BUF_SIZE)); /* now update the dma last descriptor */ writel(last_desc, GMAC0_DMA_TX_PTR_ADDR); /* tx dma should be enabled so packet should go out */ /* update txout */ dma->cur_tx_index = (txout + 1) & (TX_BUF_NUM - 1); return 0; } bool gmac_check_tx_done(struct eth_dma *dma) { /* wait for tx to complete */ uint32_t intstatus; bool xfrdone = false; debug("%s enter\n", __func__); intstatus = readl(GMAC0_INT_STATUS_ADDR); debug("int(0x%x)\n", intstatus); if (intstatus & (I_XI0 | I_XI1 | I_XI2 | I_XI3)) { xfrdone = true; /* clear the int bits */ intstatus &= ~(I_XI0 | I_XI1 | I_XI2 | I_XI3); writel(intstatus, GMAC0_INT_STATUS_ADDR); } else { debug("Tx int(0x%x)\n", intstatus); } return xfrdone; } int gmac_check_rx_done(struct eth_dma *dma, uint8_t *buf) { void *bufp, *datap; size_t rcvlen = 0, buflen = 0; uint32_t stat0 = 0, stat1 = 0; uint32_t control, offset; uint8_t statbuf[HWRXOFF*2]; int index, curr, active; dma64dd_t *descp = NULL; /* udelay(50); */ /* * this api will check if a packet has been received. * If so it will return the address of the buffer and current * descriptor index will be incremented to the * next descriptor. Once done with the frame the buffer should be * added back onto the descriptor and the lastdscr should be updated * to this descriptor. */ index = dma->cur_rx_index; offset = (uint32_t)(dma->rx_desc_aligned); stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR) & D64_RS0_CD_MASK; stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR) & D64_RS0_CD_MASK; curr = ((stat0 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t); active = ((stat1 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t); /* check if any frame */ if (index == curr) return -1; debug("received packet\n"); debug("expect(0x%x) curr(0x%x) active(0x%x)\n", index, curr, active); /* remove warning */ if (index == active) ; /* get the packet pointer that corresponds to the rx descriptor */ bufp = dma->rx_buf + index * RX_BUF_SIZE; descp = (dma64dd_t *)(dma->rx_desc_aligned) + index; /* flush descriptor and buffer */ flush_dcache_range((unsigned long)descp, (unsigned long)(descp + sizeof(dma64dd_t))); flush_dcache_range((unsigned long)bufp, (unsigned long)(bufp + RX_BUF_SIZE)); buflen = (descp->ctrl2 & D64_CTRL2_BC_MASK); stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR); stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR); debug("bufp(0x%x) index(0x%x) buflen(0x%x) stat0(0x%x) stat1(0x%x)\n", (uint32_t)bufp, index, buflen, stat0, stat1); dma->cur_rx_index = (index + 1) & (RX_BUF_NUM - 1); /* get buffer offset */ control = readl(GMAC0_DMA_RX_CTRL_ADDR); offset = (control & D64_RC_RO_MASK) >> D64_RC_RO_SHIFT; rcvlen = *(uint16_t *)bufp; debug("Received %d bytes\n", rcvlen); /* copy status into temp buf then copy data from rx buffer */ memcpy(statbuf, bufp, offset); datap = (void *)((uint32_t)bufp + offset); memcpy(buf, datap, rcvlen); /* update descriptor that is being added back on ring */ descp->ctrl2 = RX_BUF_SIZE; descp->addrlow = (uint32_t)bufp; descp->addrhigh = 0; /* flush descriptor */ flush_dcache_range((unsigned long)descp, (unsigned long)(descp + sizeof(dma64dd_t))); /* set the lastdscr for the rx ring */ writel(((uint32_t)descp) & D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR); return (int)rcvlen; } static int gmac_disable_dma(struct eth_dma *dma, int dir) { int status; debug("%s enter\n", __func__); if (dir == MAC_DMA_TX) { /* address PR8249/PR7577 issue */ /* suspend tx DMA first */ writel(D64_XC_SE, GMAC0_DMA_TX_CTRL_ADDR); SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) & D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED) && (status != D64_XS0_XS_IDLE) && (status != D64_XS0_XS_STOPPED), 10000); /* * PR2414 WAR: DMA engines are not disabled until * transfer finishes */ writel(0, GMAC0_DMA_TX_CTRL_ADDR); SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) & D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED), 10000); /* wait for the last transaction to complete */ udelay(2); status = (status == D64_XS0_XS_DISABLED); } else { /* * PR2414 WAR: DMA engines are not disabled until * transfer finishes */ writel(0, GMAC0_DMA_RX_CTRL_ADDR); SPINWAIT(((status = (readl(GMAC0_DMA_RX_STATUS0_ADDR) & D64_RS0_RS_MASK)) != D64_RS0_RS_DISABLED), 10000); status = (status == D64_RS0_RS_DISABLED); } return status; } static int gmac_enable_dma(struct eth_dma *dma, int dir) { uint32_t control; debug("%s enter\n", __func__); if (dir == MAC_DMA_TX) { dma->cur_tx_index = 0; /* * These bits 20:18 (burstLen) of control register can be * written but will take effect only if these bits are * valid. So this will not affect previous versions * of the DMA. They will continue to have those bits set to 0. */ control = readl(GMAC0_DMA_TX_CTRL_ADDR); control |= D64_XC_XE; if ((g_dmactrlflags & DMA_CTRL_PEN) == 0) control |= D64_XC_PD; writel(control, GMAC0_DMA_TX_CTRL_ADDR); /* initailize the DMA channel */ writel((uint32_t)(dma->tx_desc_aligned), GMAC0_DMA_TX_ADDR_LOW_ADDR); writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR); } else { dma->cur_rx_index = 0; control = (readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_AE) | D64_RC_RE; if ((g_dmactrlflags & DMA_CTRL_PEN) == 0) control |= D64_RC_PD; if (g_dmactrlflags & DMA_CTRL_ROC) control |= D64_RC_OC; /* * These bits 20:18 (burstLen) of control register can be * written but will take effect only if these bits are * valid. So this will not affect previous versions * of the DMA. They will continue to have those bits set to 0. */ control &= ~D64_RC_BL_MASK; /* Keep default Rx burstlen */ control |= readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK; control |= HWRXOFF << D64_RC_RO_SHIFT; writel(control, GMAC0_DMA_RX_CTRL_ADDR); /* * the rx descriptor ring should have * the addresses set properly; * set the lastdscr for the rx ring */ writel(((uint32_t)(dma->rx_desc_aligned) + (RX_BUF_NUM - 1) * RX_BUF_SIZE) & D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR); } return 0; } bool gmac_mii_busywait(unsigned int timeout) { uint32_t tmp = 0; while (timeout > 10) { tmp = readl(GMAC_MII_CTRL_ADDR); if (tmp & (1 << GMAC_MII_BUSY_SHIFT)) { udelay(10); timeout -= 10; } else { break; } } return tmp & (1 << GMAC_MII_BUSY_SHIFT); } int gmac_miiphy_read(const char *devname, unsigned char phyaddr, unsigned char reg, unsigned short *value) { uint32_t tmp = 0; (void)devname; /* Busy wait timeout is 1ms */ if (gmac_mii_busywait(1000)) { error("%s: Prepare MII read: MII/MDIO busy\n", __func__); return -1; } /* Read operation */ tmp = GMAC_MII_DATA_READ_CMD; tmp |= (phyaddr << GMAC_MII_PHY_ADDR_SHIFT) | (reg << GMAC_MII_PHY_REG_SHIFT); debug("MII read cmd 0x%x, phy 0x%x, reg 0x%x\n", tmp, phyaddr, reg); writel(tmp, GMAC_MII_DATA_ADDR); if (gmac_mii_busywait(1000)) { error("%s: MII read failure: MII/MDIO busy\n", __func__); return -1; } *value = readl(GMAC_MII_DATA_ADDR) & 0xffff; debug("MII read data 0x%x\n", *value); return 0; } int gmac_miiphy_write(const char *devname, unsigned char phyaddr, unsigned char reg, unsigned short value) { uint32_t tmp = 0; (void)devname; /* Busy wait timeout is 1ms */ if (gmac_mii_busywait(1000)) { error("%s: Prepare MII write: MII/MDIO busy\n", __func__); return -1; } /* Write operation */ tmp = GMAC_MII_DATA_WRITE_CMD | (value & 0xffff); tmp |= ((phyaddr << GMAC_MII_PHY_ADDR_SHIFT) | (reg << GMAC_MII_PHY_REG_SHIFT)); debug("MII write cmd 0x%x, phy 0x%x, reg 0x%x, data 0x%x\n", tmp, phyaddr, reg, value); writel(tmp, GMAC_MII_DATA_ADDR); if (gmac_mii_busywait(1000)) { error("%s: MII write failure: MII/MDIO busy\n", __func__); return -1; } return 0; } void gmac_init_reset(void) { debug("%s enter\n", __func__); /* set command config reg CC_SR */ reg32_set_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR); udelay(GMAC_RESET_DELAY); } void gmac_clear_reset(void) { debug("%s enter\n", __func__); /* clear command config reg CC_SR */ reg32_clear_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR); udelay(GMAC_RESET_DELAY); } static void gmac_enable_local(bool en) { uint32_t cmdcfg; debug("%s enter\n", __func__); /* read command config reg */ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR); /* put mac in reset */ gmac_init_reset(); cmdcfg |= CC_SR; /* first deassert rx_ena and tx_ena while in reset */ cmdcfg &= ~(CC_RE | CC_TE); /* write command config reg */ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR); /* bring mac out of reset */ gmac_clear_reset(); /* if not enable exit now */ if (!en) return; /* enable the mac transmit and receive paths now */ udelay(2); cmdcfg &= ~CC_SR; cmdcfg |= (CC_RE | CC_TE); /* assert rx_ena and tx_ena when out of reset to enable the mac */ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR); return; } int gmac_enable(void) { gmac_enable_local(1); /* clear interrupts */ writel(I_INTMASK, GMAC0_INT_STATUS_ADDR); return 0; } int gmac_disable(void) { gmac_enable_local(0); return 0; } int gmac_set_speed(int speed, int duplex) { uint32_t cmdcfg; uint32_t hd_ena; uint32_t speed_cfg; hd_ena = duplex ? 0 : CC_HD; if (speed == 1000) { speed_cfg = 2; } else if (speed == 100) { speed_cfg = 1; } else if (speed == 10) { speed_cfg = 0; } else { error("%s: Invalid GMAC speed(%d)!\n", __func__, speed); return -1; } cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR); cmdcfg &= ~(CC_ES_MASK | CC_HD); cmdcfg |= ((speed_cfg << CC_ES_SHIFT) | hd_ena); printf("Change GMAC speed to %dMB\n", speed); debug("GMAC speed cfg 0x%x\n", cmdcfg); writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR); return 0; } int gmac_set_mac_addr(unsigned char *mac) { /* set our local address */ debug("GMAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); writel(htonl(*(uint32_t *)mac), UNIMAC0_MAC_MSB_ADDR); writew(htons(*(uint32_t *)&mac[4]), UNIMAC0_MAC_LSB_ADDR); return 0; } int gmac_mac_init(struct eth_device *dev) { struct eth_info *eth = (struct eth_info *)(dev->priv); struct eth_dma *dma = &(eth->dma); uint32_t tmp; uint32_t cmdcfg; int chipid; debug("%s enter\n", __func__); /* Always use GMAC0 */ printf("Using GMAC%d\n", 0); /* Reset AMAC0 core */ writel(0, AMAC0_IDM_RESET_ADDR); tmp = readl(AMAC0_IO_CTRL_DIRECT_ADDR); /* Set clock */ tmp &= ~(1 << AMAC0_IO_CTRL_CLK_250_SEL_SHIFT); tmp |= (1 << AMAC0_IO_CTRL_GMII_MODE_SHIFT); /* Set Tx clock */ tmp &= ~(1 << AMAC0_IO_CTRL_DEST_SYNC_MODE_EN_SHIFT); writel(tmp, AMAC0_IO_CTRL_DIRECT_ADDR); /* reset gmac */ /* * As AMAC is just reset, NO need? * set eth_data into loopback mode to ensure no rx traffic * gmac_loopback(eth_data, TRUE); * ET_TRACE(("%s gmac loopback\n", __func__)); * udelay(1); */ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR); cmdcfg &= ~(CC_TE | CC_RE | CC_RPI | CC_TAI | CC_HD | CC_ML | CC_CFE | CC_RL | CC_RED | CC_PE | CC_TPI | CC_PAD_EN | CC_PF); cmdcfg |= (CC_PROM | CC_NLC | CC_CFE); /* put mac in reset */ gmac_init_reset(); writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR); gmac_clear_reset(); /* enable clear MIB on read */ reg32_set_bits(GMAC0_DEV_CTRL_ADDR, DC_MROR); /* PHY: set smi_master to drive mdc_clk */ reg32_set_bits(GMAC0_PHY_CTRL_ADDR, PC_MTE); /* clear persistent sw intstatus */ writel(0, GMAC0_INT_STATUS_ADDR); if (dma_init(dma) < 0) { error("%s: GMAC dma_init failed\n", __func__); goto err_exit; } chipid = CHIPID; printf("%s: Chip ID: 0x%x\n", __func__, chipid); /* set switch bypass mode */ tmp = readl(SWITCH_GLOBAL_CONFIG_ADDR); tmp |= (1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT); /* Switch mode */ /* tmp &= ~(1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT); */ writel(tmp, SWITCH_GLOBAL_CONFIG_ADDR); tmp = readl(CRMU_CHIP_IO_PAD_CONTROL_ADDR); tmp &= ~(1 << CDRU_IOMUX_FORCE_PAD_IN_SHIFT); writel(tmp, CRMU_CHIP_IO_PAD_CONTROL_ADDR); /* Set MDIO to internal GPHY */ tmp = readl(GMAC_MII_CTRL_ADDR); /* Select internal MDC/MDIO bus*/ tmp &= ~(1 << GMAC_MII_CTRL_BYP_SHIFT); /* select MDC/MDIO connecting to on-chip internal PHYs */ tmp &= ~(1 << GMAC_MII_CTRL_EXT_SHIFT); /* * give bit[6:0](MDCDIV) with required divisor to set * the MDC clock frequency, 66MHZ/0x1A=2.5MHZ */ tmp |= 0x1A; writel(tmp, GMAC_MII_CTRL_ADDR); if (gmac_mii_busywait(1000)) { error("%s: Configure MDIO: MII/MDIO busy\n", __func__); goto err_exit; } /* Configure GMAC0 */ /* enable one rx interrupt per received frame */ writel(1 << GMAC0_IRL_FRAMECOUNT_SHIFT, GMAC0_INTR_RECV_LAZY_ADDR); /* read command config reg */ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR); /* enable 802.3x tx flow control (honor received PAUSE frames) */ cmdcfg &= ~CC_RPI; /* enable promiscuous mode */ cmdcfg |= CC_PROM; /* Disable loopback mode */ cmdcfg &= ~CC_ML; /* set the speed */ cmdcfg &= ~(CC_ES_MASK | CC_HD); /* Set to 1Gbps and full duplex by default */ cmdcfg |= (2 << CC_ES_SHIFT); /* put mac in reset */ gmac_init_reset(); /* write register */ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR); /* bring mac out of reset */ gmac_clear_reset(); /* set max frame lengths; account for possible vlan tag */ writel(PKTSIZE + 32, UNIMAC0_FRM_LENGTH_ADDR); return 0; err_exit: dma_deinit(dma); return -1; } int gmac_add(struct eth_device *dev) { struct eth_info *eth = (struct eth_info *)(dev->priv); struct eth_dma *dma = &(eth->dma); void *tmp; /* * Desc has to be 16-byte aligned ? * If it is 8-byte aligned by malloc, fail Tx */ tmp = malloc(sizeof(dma64dd_t) * TX_BUF_NUM + 8); if (tmp == NULL) { printf("%s: Failed to allocate TX desc Buffer\n", __func__); return -1; } dma->tx_desc = (void *)tmp; dma->tx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf)); debug("TX Descriptor Buffer: %p; length: 0x%x\n", dma->tx_desc_aligned, sizeof(dma64dd_t) * TX_BUF_NUM); tmp = malloc(TX_BUF_SIZE * TX_BUF_NUM); if (tmp == NULL) { printf("%s: Failed to allocate TX Data Buffer\n", __func__); free(dma->tx_desc); return -1; } dma->tx_buf = (uint8_t *)tmp; debug("TX Data Buffer: %p; length: 0x%x\n", dma->tx_buf, TX_BUF_SIZE * TX_BUF_NUM); /* Desc has to be 16-byte aligned ? */ tmp = malloc(sizeof(dma64dd_t) * RX_BUF_NUM + 8); if (tmp == NULL) { printf("%s: Failed to allocate RX Descriptor\n", __func__); free(dma->tx_desc); free(dma->tx_buf); return -1; } dma->rx_desc = tmp; dma->rx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf)); debug("RX Descriptor Buffer: %p, length: 0x%x\n", dma->rx_desc_aligned, sizeof(dma64dd_t) * RX_BUF_NUM); tmp = malloc(RX_BUF_SIZE * RX_BUF_NUM); if (tmp == NULL) { printf("%s: Failed to allocate RX Data Buffer\n", __func__); free(dma->tx_desc); free(dma->tx_buf); free(dma->rx_desc); return -1; } dma->rx_buf = tmp; debug("RX Data Buffer: %p; length: 0x%x\n", dma->rx_buf, RX_BUF_SIZE * RX_BUF_NUM); g_dmactrlflags = 0; eth->phy_interface = PHY_INTERFACE_MODE_GMII; dma->tx_packet = gmac_tx_packet; dma->check_tx_done = gmac_check_tx_done; dma->check_rx_done = gmac_check_rx_done; dma->enable_dma = gmac_enable_dma; dma->disable_dma = gmac_disable_dma; eth->miiphy_read = gmac_miiphy_read; eth->miiphy_write = gmac_miiphy_write; eth->mac_init = gmac_mac_init; eth->disable_mac = gmac_disable; eth->enable_mac = gmac_enable; eth->set_mac_addr = gmac_set_mac_addr; eth->set_mac_speed = gmac_set_speed; return 0; }