/* * This is purely informative. The HW can poll the RW (and RX) ring * buffers for available descriptors but we don't need to trigger a * timer for that in qemu. */ static void ftgmac100_rxpoll(Ftgmac100State *s) { /* Polling times : * * Speed TIME_SEL=0 TIME_SEL=1 * * 10 51.2 ms 819.2 ms * 100 5.12 ms 81.92 ms * 1000 1.024 ms 16.384 ms */ static const int div[] = { 20, 200, 1000 }; uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr); uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0; uint32_t period __attribute__ ((unused)); if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) { cnt <<= 4; } if (s->maccr & FTGMAC100_MACCR_GIGA_MODE) { speed = 2; } period = cnt / div[speed]; DEBUG("polling in %d ms\n", period); }
iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR); } static void ftgmac100_init_hw(struct ftgmac100 *priv) { /* setup ring buffer base registers */ ftgmac100_set_rx_ring_base(priv, priv->descs_dma_addr + offsetof(struct ftgmac100_descs, rxdes)); ftgmac100_set_normal_prio_tx_ring_base(priv, priv->descs_dma_addr + offsetof(struct ftgmac100_descs, txdes)); ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE); iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC); ftgmac100_set_mac(priv, priv->netdev->dev_addr); } #define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \ FTGMAC100_MACCR_RXDMA_EN | \ FTGMAC100_MACCR_TXMAC_EN | \ FTGMAC100_MACCR_RXMAC_EN | \ FTGMAC100_MACCR_FULLDUP | \ FTGMAC100_MACCR_CRC_APD | \ FTGMAC100_MACCR_RX_RUNT | \ FTGMAC100_MACCR_RX_BROADPKT) static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed) {
static int ftgmac100_init(struct eth_device *dev, bd_t *bd) { struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; struct ftgmac100_data *priv = dev->priv; struct ftgmac100_txdes *txdes; struct ftgmac100_rxdes *rxdes; unsigned int maccr; void *buf; int i; debug("%s()\n", __func__); if (!priv->txdes) { txdes = dma_alloc_coherent( sizeof(*txdes) * PKTBUFSTX, &priv->txdes_dma); if (!txdes) panic("ftgmac100: out of memory\n"); memset(txdes, 0, sizeof(*txdes) * PKTBUFSTX); priv->txdes = txdes; } txdes = priv->txdes; if (!priv->rxdes) { rxdes = dma_alloc_coherent( sizeof(*rxdes) * PKTBUFSRX, &priv->rxdes_dma); if (!rxdes) panic("ftgmac100: out of memory\n"); memset(rxdes, 0, sizeof(*rxdes) * PKTBUFSRX); priv->rxdes = rxdes; } rxdes = priv->rxdes; /* set the ethernet address */ ftgmac100_set_mac_from_env(dev); /* disable all interrupts */ writel(0, &ftgmac100->ier); /* initialize descriptors */ priv->tx_index = 0; priv->rx_index = 0; txdes[PKTBUFSTX - 1].txdes0 = FTGMAC100_TXDES0_EDOTR; rxdes[PKTBUFSRX - 1].rxdes0 = FTGMAC100_RXDES0_EDORR; for (i = 0; i < PKTBUFSTX; i++) { /* TXBUF_BADR */ if (!txdes[i].txdes2) { buf = memalign(ARCH_DMA_MINALIGN, CFG_XBUF_SIZE); if (!buf) panic("ftgmac100: out of memory\n"); txdes[i].txdes3 = virt_to_phys(buf); txdes[i].txdes2 = (uint)buf; } txdes[i].txdes1 = 0; } for (i = 0; i < PKTBUFSRX; i++) { /* RXBUF_BADR */ if (!rxdes[i].rxdes2) { buf = net_rx_packets[i]; rxdes[i].rxdes3 = virt_to_phys(buf); rxdes[i].rxdes2 = (uint)buf; } rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY; } /* transmit ring */ writel(priv->txdes_dma, &ftgmac100->txr_badr); /* receive ring */ writel(priv->rxdes_dma, &ftgmac100->rxr_badr); /* poll receive descriptor automatically */ writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc); /* config receive buffer size register */ writel(FTGMAC100_RBSR_SIZE(RBSR_DEFAULT_VALUE), &ftgmac100->rbsr); /* enable transmitter, receiver */ maccr = FTGMAC100_MACCR_TXMAC_EN | FTGMAC100_MACCR_RXMAC_EN | FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_CRC_APD | FTGMAC100_MACCR_FULLDUP | FTGMAC100_MACCR_RX_RUNT | FTGMAC100_MACCR_RX_BROADPKT; writel(maccr, &ftgmac100->maccr); if (!ftgmac100_phy_init(dev)) { if (!ftgmac100_update_link_speed(dev)) return -1; } return 0; }
static void ftgmac100_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { Ftgmac100State *s = FTGMAC100(opaque); DEBUG("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr); switch (addr & 0xff) { case FTGMAC100_ISR: /* Interrupt status */ s->isr &= ~value; break; case FTGMAC100_IER: /* Interrupt control */ s->ier = value; break; case FTGMAC100_MAC_MADR: /* MAC */ s->conf.macaddr.a[0] = value >> 8; s->conf.macaddr.a[1] = value; break; case FTGMAC100_MAC_LADR: s->conf.macaddr.a[2] = value >> 24; s->conf.macaddr.a[3] = value >> 16; s->conf.macaddr.a[4] = value >> 8; s->conf.macaddr.a[5] = value; break; case FTGMAC100_RXR_BADR: /* Ring buffer address */ s->rx_ring = value; s->rx_descriptor = s->rx_ring; break; case FTGMAC100_RBSR: /* DMA buffer size */ s->rbsr = value; break; case FTGMAC100_NPTXR_BADR: /* Transmit buffer address */ s->tx_ring = value; s->tx_descriptor = s->tx_ring; break; case FTGMAC100_NPTXPD: /* Trigger transmit */ if (s->maccr & FTGMAC100_MACCR_TXDMA_EN) { ftgmac100_do_tx(s); } break; case FTGMAC100_RXPD: /* Receive Poll Demand Register */ if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) && !s->rx_enabled) { /* * TODO: This is required for u-boot but clearly, this is * broken. */ ftgmac100_enable_rx(s); } break; case FTGMAC100_APTC: /* Automatic polling */ s->aptcr = value; if (FTGMAC100_APTC_RXPOLL_CNT(s->aptcr)) { ftgmac100_rxpoll(s); } if (FTGMAC100_APTC_TXPOLL_CNT(s->aptcr)) { qemu_log_mask(LOG_UNIMP, "%s: no transmit polling\n", __func__); } break; case FTGMAC100_MACCR: /* MAC Device control */ s->maccr = value; if (value & FTGMAC100_MACCR_SW_RST) { ftgmac100_reset(DEVICE(s)); } if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) && !s->rx_enabled) { ftgmac100_enable_rx(s); } if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) == 0) { s->rx_enabled = 0; } break; case FTGMAC100_PHYCR: /* PHY Device control */ s->phycr = value; if (value & FTGMAC100_PHYCR_MIIWR) { do_phy_write(s, extract32(value, 21, 5), s->phydata & 0xffff); s->phycr &= ~FTGMAC100_PHYCR_MIIWR; } else { s->phydata = do_phy_read(s, extract32(value, 21, 5)) << 16; s->phycr &= ~FTGMAC100_PHYCR_MIIRD; } break; case FTGMAC100_PHYDATA: s->phydata = value; break; default: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" HWADDR_PRIx "\n", TYPE_FTGMAC100, __func__, addr); break; } ftgmac100_update_irq(s); }
static int ftgmac100_start(struct udevice *dev) { struct eth_pdata *plat = dev_get_platdata(dev); struct ftgmac100_data *priv = dev_get_priv(dev); struct ftgmac100 *ftgmac100 = priv->iobase; struct phy_device *phydev = priv->phydev; unsigned int maccr; ulong start, end; int ret; int i; debug("%s()\n", __func__); ftgmac100_reset(priv); /* set the ethernet address */ ftgmac100_set_mac(priv, plat->enetaddr); /* disable all interrupts */ writel(0, &ftgmac100->ier); /* initialize descriptors */ priv->tx_index = 0; priv->rx_index = 0; for (i = 0; i < PKTBUFSTX; i++) { priv->txdes[i].txdes3 = 0; priv->txdes[i].txdes0 = 0; } priv->txdes[PKTBUFSTX - 1].txdes0 = priv->txdes0_edotr_mask; start = (ulong)&priv->txdes[0]; end = start + roundup(sizeof(priv->txdes), ARCH_DMA_MINALIGN); flush_dcache_range(start, end); for (i = 0; i < PKTBUFSRX; i++) { priv->rxdes[i].rxdes3 = (unsigned int)net_rx_packets[i]; priv->rxdes[i].rxdes0 = 0; } priv->rxdes[PKTBUFSRX - 1].rxdes0 = priv->rxdes0_edorr_mask; start = (ulong)&priv->rxdes[0]; end = start + roundup(sizeof(priv->rxdes), ARCH_DMA_MINALIGN); flush_dcache_range(start, end); /* transmit ring */ writel((u32)priv->txdes, &ftgmac100->txr_badr); /* receive ring */ writel((u32)priv->rxdes, &ftgmac100->rxr_badr); /* poll receive descriptor automatically */ writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc); /* config receive buffer size register */ writel(FTGMAC100_RBSR_SIZE(FTGMAC100_RBSR_DEFAULT), &ftgmac100->rbsr); /* enable transmitter, receiver */ maccr = FTGMAC100_MACCR_TXMAC_EN | FTGMAC100_MACCR_RXMAC_EN | FTGMAC100_MACCR_TXDMA_EN | FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_CRC_APD | FTGMAC100_MACCR_FULLDUP | FTGMAC100_MACCR_RX_RUNT | FTGMAC100_MACCR_RX_BROADPKT; writel(maccr, &ftgmac100->maccr); ret = phy_startup(phydev); if (ret) { dev_err(phydev->dev, "Could not start PHY\n"); return ret; } ret = ftgmac100_phy_adjust_link(priv); if (ret) { dev_err(phydev->dev, "Could not adjust link\n"); return ret; } printf("%s: link up, %d Mbps %s-duplex mac:%pM\n", phydev->dev->name, phydev->speed, phydev->duplex ? "full" : "half", plat->enetaddr); return 0; }