/* * Network device driver for the GMAC ethernet controller on * Apple G4 Powermacs. * * Copyright (C) 2000 Paul Mackerras. */ #include #include #include #include #include #include #include #include #include #include #include #include "gmac.h" #define DEBUG_PHY #define NTX 32 /* must be power of 2 */ #define NRX 32 /* must be power of 2 */ #define RX_BUFLEN (ETH_FRAME_LEN + 8) struct gmac_dma_desc { unsigned int cmd; unsigned int status; unsigned int address; /* phys addr, low 32 bits */ unsigned int hi_addr; }; /* Bits in cmd */ #define RX_OWN 0x80000000 /* 1 = owned by chip */ #define TX_SOP 0x80000000 #define TX_EOP 0x40000000 struct gmac { volatile unsigned int *regs; /* hardware registers, virtual addr */ volatile unsigned int *sysregs; unsigned long desc_page; /* page for DMA descriptors */ volatile struct gmac_dma_desc *rxring; struct sk_buff *rx_buff[NRX]; int next_rx; volatile struct gmac_dma_desc *txring; struct sk_buff *tx_buff[NTX]; int next_tx; int tx_gone; unsigned char tx_full; int phy_addr; int full_duplex; struct net_device_stats stats; }; #define GM_OUT(r, v) out_le32(gm->regs + (r)/4, (v)) #define GM_IN(r) in_le32(gm->regs + (r)/4) #define GM_BIS(r, v) GM_OUT((r), GM_IN(r) | (v)) #define GM_BIC(r, v) GM_OUT((r), GM_IN(r) & ~(v)) #define PHY_B5400 0x6040 #define PHY_B5201 0x6212 static unsigned char dummy_buf[RX_BUFLEN+2]; static struct net_device *gmacs = NULL; /* Prototypes */ static int mii_read(struct gmac *gm, int phy, int r); static int mii_write(struct gmac *gm, int phy, int r, int v); static void powerup_transceiver(struct gmac *gm); static int gmac_reset(struct net_device *dev); static void gmac_mac_init(struct gmac *gm, unsigned char *mac_addr); static void gmac_init_rings(struct gmac *gm); static void gmac_start_dma(struct gmac *gm); static int gmac_open(struct net_device *dev); static int gmac_close(struct net_device *dev); static int gmac_xmit_start(struct sk_buff *skb, struct net_device *dev); static int gmac_tx_cleanup(struct gmac *gm); static void gmac_receive(struct net_device *dev); static void gmac_interrupt(int irq, void *dev_id, struct pt_regs *regs); static struct net_device_stats *gmac_stats(struct net_device *dev); static int gmac_probe(void); /* Stuff for talking to the physical-layer chip */ static int mii_read(struct gmac *gm, int phy, int r) { int timeout; GM_OUT(MIFFRAME, 0x60020000 | (phy << 23) | (r << 18)); for (timeout = 1000; timeout > 0; --timeout) { udelay(20); if (GM_IN(MIFFRAME) & 0x10000) return GM_IN(MIFFRAME) & 0xffff; } return -1; } static int mii_write(struct gmac *gm, int phy, int r, int v) { int timeout; GM_OUT(MIFFRAME, 0x50020000 | (phy << 23) | (r << 18) | (v & 0xffff)); for (timeout = 1000; timeout > 0; --timeout) { udelay(20); if (GM_IN(MIFFRAME) & 0x10000) return 0; } return -1; } static void mii_poll_start(struct gmac *gm) { unsigned int tmp; /* Start the MIF polling on the external transceiver. */ tmp = GM_IN(MIFCONFIG); tmp &= ~(GMAC_MIF_CFGPR_MASK | GMAC_MIF_CFGPD_MASK); tmp |= ((gm->phy_addr & 0x1f) << GMAC_MIF_CFGPD_SHIFT); tmp |= (0x19 << GMAC_MIF_CFGPR_SHIFT); tmp |= GMAC_MIF_CFGPE; GM_OUT(MIFCONFIG, tmp); /* Let the bits set. */ udelay(GMAC_MIF_POLL_DELAY); GM_OUT(MIFINTMASK, 0xffc0); } static void mii_poll_stop(struct gmac *gm) { GM_OUT(MIFINTMASK, 0xffff); GM_BIC(MIFCONFIG, GMAC_MIF_CFGPE); udelay(GMAC_MIF_POLL_DELAY); } static void mii_interrupt(struct gmac *gm) { unsigned long flags; int phy_status; save_flags(flags); cli(); mii_poll_stop(gm); /* May the status change before polling is re-enabled ? */ mii_poll_start(gm); /* We read the Auxilliary Status Summary register */ phy_status = mii_read(gm, gm->phy_addr, 0x19); #ifdef DEBUG_PHY printk("mii_interrupt, phy_status: %x\n", phy_status); #endif /* Auto-neg. complete ? */ if (phy_status & 0x8000) { int full_duplex = 0; switch((phy_status >> 8) & 0x7) { case 2: case 5: full_duplex = 1; break; } if (full_duplex != gm->full_duplex) { GM_BIC(TXMAC_CONFIG, 1); udelay(200); if (full_duplex) { printk("full duplex active\n"); GM_OUT(TXMAC_CONFIG, 6); GM_OUT(XIF_CONFIG, 1); } else { printk("half duplex active\n"); GM_OUT(TXMAC_CONFIG, 0); GM_OUT(XIF_CONFIG, 5); } GM_BIS(TXMAC_CONFIG, 1); gm->full_duplex = full_duplex; } } restore_flags(flags); } static void powerup_transceiver(struct gmac *gm) { int phytype = mii_read(gm, 0, 3); #ifdef DEBUG_PHY int i; #endif switch (phytype) { case PHY_B5400: mii_write(gm, 0, 0, mii_read(gm, 0, 0) & ~0x800); mii_write(gm, 31, 30, mii_read(gm, 31, 30) & ~8); break; case PHY_B5201: mii_write(gm, 0, 30, mii_read(gm, 0, 30) & ~8); break; default: printk(KERN_ERR "GMAC: unknown PHY type %x\n", phytype); } /* Check this */ gm->phy_addr = 0; gm->full_duplex = 0; #ifdef DEBUG_PHY printk("PHY regs:\n"); for (i=0; i<0x20; i++) { printk("%04x ", mii_read(gm, 0, i)); if ((i % 4) == 3) printk("\n"); } #endif } static int gmac_reset(struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; int timeout; /* turn on GB clock */ out_le32(gm->sysregs + 0x20/4, in_le32(gm->sysregs + 0x20/4) | 2); udelay(10); GM_OUT(SW_RESET, 3); for (timeout = 100; timeout > 0; --timeout) { mdelay(10); if ((GM_IN(SW_RESET) & 3) == 0) return 0; } printk(KERN_ERR "GMAC: reset failed!\n"); return -1; } static void gmac_mac_init(struct gmac *gm, unsigned char *mac_addr) { int i; GM_OUT(RANSEED, 937); GM_OUT(DATAPATHMODE, 4); mii_write(gm, 0, 0, 0x1000); GM_OUT(TXDMA_CONFIG, 0xffc00); GM_OUT(RXDMA_CONFIG, 0); GM_OUT(MACPAUSE, 0x1bf0); GM_OUT(IPG0, 0); GM_OUT(IPG1, 8); GM_OUT(IPG2, 4); GM_OUT(MINFRAMESIZE, 64); GM_OUT(MAXFRAMESIZE, 2000); GM_OUT(PASIZE, 7); GM_OUT(JAMSIZE, 4); GM_OUT(ATTEMPT_LIMIT, 16); GM_OUT(SLOTTIME, 64); GM_OUT(MACCNTL_TYPE, 0x8808); GM_OUT(MAC_ADDR_0, (mac_addr[4] << 8) + mac_addr[5]); GM_OUT(MAC_ADDR_1, (mac_addr[2] << 8) + mac_addr[3]); GM_OUT(MAC_ADDR_2, (mac_addr[0] << 8) + mac_addr[1]); GM_OUT(MAC_ADDR_3, 0); GM_OUT(MAC_ADDR_4, 0); GM_OUT(MAC_ADDR_5, 0); GM_OUT(MAC_ADDR_6, 0x0180); GM_OUT(MAC_ADDR_7, 0xc200); GM_OUT(MAC_ADDR_8, 0x0001); GM_OUT(MAC_ADDR_FILTER_0, 0); GM_OUT(MAC_ADDR_FILTER_1, 0); GM_OUT(MAC_ADDR_FILTER_2, 0); GM_OUT(MAC_ADDR_FILTER_MASK21, 0); GM_OUT(MAC_ADDR_FILTER_MASK0, 0); for (i = 0; i < 27; ++i) GM_OUT(MAC_HASHTABLE + i, 0); GM_OUT(MACCNTL_CONFIG, 0); /* default to half duplex */ GM_OUT(TXMAC_CONFIG, 0); GM_OUT(XIF_CONFIG, 5); } static void gmac_init_rings(struct gmac *gm) { int i; struct sk_buff *skb; unsigned char *data; struct gmac_dma_desc *ring; /* init rx ring */ ring = (struct gmac_dma_desc *) gm->rxring; memset(ring, 0, NRX * sizeof(struct gmac_dma_desc)); for (i = 0; i < NRX; ++i, ++ring) { data = dummy_buf; gm->rx_buff[i] = skb = dev_alloc_skb(RX_BUFLEN + 2); if (skb != 0) { /*skb_reserve(skb, 2);*/ data = skb->data; } st_le32(&ring->address, virt_to_bus(data)); st_le32(&ring->cmd, RX_OWN); } /* init tx ring */ ring = (struct gmac_dma_desc *) gm->txring; memset(ring, 0, NRX * sizeof(struct gmac_dma_desc)); /* set pointers in chip */ mb(); GM_OUT(RXDMA_BASE_HIGH, 0); GM_OUT(RXDMA_BASE_LOW, virt_to_bus(gm->rxring)); GM_OUT(TXDMA_BASE_HIGH, 0); GM_OUT(TXDMA_BASE_LOW, virt_to_bus(gm->txring)); } static void gmac_start_dma(struct gmac *gm) { GM_BIS(RXDMA_CONFIG, 1); GM_BIS(RXMAC_CONFIG, 1); GM_OUT(RXDMA_KICK, NRX); GM_BIS(TXDMA_CONFIG, 1); GM_BIS(TXMAC_CONFIG, 1); } static int gmac_open(struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; if (gmac_reset(dev)) return -EIO; MOD_INC_USE_COUNT; powerup_transceiver(gm); gmac_mac_init(gm, dev->dev_addr); gmac_init_rings(gm); gmac_start_dma(gm); mii_interrupt(gm); GM_OUT(INTR_DISABLE, 0xfffdffe8); return 0; } static int gmac_close(struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; int i; mii_poll_stop(gm); GM_BIC(RXDMA_CONFIG, 1); GM_BIC(RXMAC_CONFIG, 1); GM_BIC(TXDMA_CONFIG, 1); GM_BIC(TXMAC_CONFIG, 1); GM_OUT(INTR_DISABLE, ~0U); for (i = 0; i < NRX; ++i) { if (gm->rx_buff[i] != 0) { dev_kfree_skb(gm->rx_buff[i]); gm->rx_buff[i] = 0; } } for (i = 0; i < NTX; ++i) { if (gm->tx_buff[i] != 0) { dev_kfree_skb(gm->tx_buff[i]); gm->tx_buff[i] = 0; } } MOD_DEC_USE_COUNT; return 0; } static int gmac_xmit_start(struct sk_buff *skb, struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; volatile struct gmac_dma_desc *dp; unsigned long flags; int i; save_flags(flags); cli(); i = gm->next_tx; if (gm->tx_buff[i] != 0) { /* buffer is full, can't send this packet at the moment */ dev->tbusy = 1; gm->tx_full = 1; restore_flags(flags); return 1; } gm->next_tx = (i + 1) & (NTX - 1); gm->tx_buff[i] = skb; restore_flags(flags); dp = &gm->txring[i]; dp->status = 0; dp->hi_addr = 0; st_le32(&dp->address, virt_to_bus(skb->data)); mb(); st_le32(&dp->cmd, TX_SOP | TX_EOP | skb->len); mb(); GM_OUT(TXDMA_KICK, gm->next_tx); return 0; } static int gmac_tx_cleanup(struct gmac *gm) { int i = gm->tx_gone; volatile struct gmac_dma_desc *dp; struct sk_buff *skb; int ret = 0; int gone = GM_IN(TXDMA_COMPLETE); while (i != gone) { skb = gm->tx_buff[i]; if (skb == NULL) break; dp = &gm->txring[i]; gm->stats.tx_bytes += skb->len; ++gm->stats.tx_packets; gm->tx_buff[i] = NULL; dev_kfree_skb(skb); if (++i >= NTX) i = 0; } if (i != gm->tx_gone) { ret = gm->tx_full; gm->tx_gone = i; gm->tx_full = 0; } return ret; } static void gmac_receive(struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; int i = gm->next_rx; volatile struct gmac_dma_desc *dp; struct sk_buff *skb; int len; unsigned char *data; for (;;) { dp = &gm->rxring[i]; if (ld_le32(&dp->cmd) & RX_OWN) break; len = (ld_le32(&dp->cmd) >> 16) & 0x7fff; skb = gm->rx_buff[i]; if (skb == 0) { ++gm->stats.rx_dropped; } else if (ld_le32(&dp->status) & 0x40000000) { ++gm->stats.rx_errors; dev_kfree_skb(skb); } else { skb_put(skb, len); skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); gm->stats.rx_bytes += skb->len; ++gm->stats.rx_packets; } data = dummy_buf; gm->rx_buff[i] = skb = dev_alloc_skb(RX_BUFLEN + 2); if (skb != 0) { /*skb_reserve(skb, 2);*/ data = skb->data; } st_le32(&dp->address, virt_to_bus(data)); dp->hi_addr = 0; mb(); st_le32(&dp->cmd, RX_OWN); if (++i >= NRX) i = 0; } gm->next_rx = i; } static void gmac_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct gmac *gm = (struct gmac *) dev->priv; unsigned int status; status = GM_IN(INTR_STATUS); GM_OUT(INTR_ACK, status); if (status & GMAC_IRQ_MIF) { mii_interrupt(gm); } gmac_receive(dev); if (gmac_tx_cleanup(gm)){ dev->tbusy = 0; mark_bh(NET_BH); } } static struct net_device_stats *gmac_stats(struct net_device *dev) { struct gmac *gm = (struct gmac *) dev->priv; return &gm->stats; } static int __init gmac_probe(void) { static int gmacs_found; static struct device_node *next_gmac; struct device_node *gmac; struct gmac *gm; unsigned long descpage; unsigned char *addr; int i; if (gmacs != NULL) return -EBUSY; /* * We could (and maybe should) do this using PCI scanning * for vendor/net_device ID 0x106b/0x21. */ if (!gmacs_found) { next_gmac = find_compatible_devices("network", "gmac"); gmacs_found = 1; } if ((gmac = next_gmac) == 0) return -ENODEV; next_gmac = gmac->next; if (gmac->n_addrs < 1 || gmac->n_intrs < 1) { printk(KERN_ERR "can't use GMAC %s: %d addrs and %d intrs\n", gmac->full_name, gmac->n_addrs, gmac->n_intrs); return -ENODEV; } dev = init_etherdev(0, sizeof(struct gmac)); memset(dev->priv, 0, sizeof(struct gmac)); gm = (struct gmac *) dev->priv; dev->base_addr = gmac->addrs[0].address; gm->regs = (volatile unsigned int *) ioremap(gmac->addrs[0].address, 0x10000); gm->sysregs = (volatile unsigned int *) ioremap(0xf8000000, 0x1000); dev->irq = gmac->intrs[0].line; addr = get_property(gmac, "local-mac-address", NULL); if (addr == NULL) { printk(KERN_ERR "Can't get mac-address for GMAC %s\n", gmac->full_name); return -EAGAIN; } printk(KERN_INFO "%s: GMAC at", dev->name); for (i = 0; i < 6; ++i) { dev->dev_addr[i] = addr[i]; printk("%c%.2x", (i? ':': ' '), addr[i]); } printk("\n"); descpage = get_free_page(GFP_KERNEL); if (descpage == 0) { printk(KERN_ERR "GMAC: can't get a page for descriptors\n"); return -EAGAIN; } gm->desc_page = descpage; gm->rxring = (volatile struct gmac_dma_desc *) descpage; gm->txring = (volatile struct gmac_dma_desc *) (descpage + 0x800); gm->phy_addr = 0; dev->open = gmac_open; dev->stop = gmac_close; dev->hard_start_xmit = gmac_xmit_start; dev->get_stats = gmac_stats; ether_setup(dev); if (request_irq(dev->irq, gmac_interrupt, 0, "GMAC", dev)) { printk(KERN_ERR "GMAC: can't get irq %d\n", dev->irq); return -EAGAIN; } gmacs = dev; return 0; } MODULE_AUTHOR("Paul Mackerras"); MODULE_DESCRIPTION("PowerMac GMAC driver."); static void __exit gmac_cleanup_module(void) { struct gmac *gm; /* XXX should handle more than one */ if (gmacs == NULL) return; gm = (struct gmac *) gmacs->priv; free_irq(gmacs->irq, gmac_interrupt); free_page(gm->descpage); unregister_netdev(gmacs); kfree(gmacs); gmacs = NULL; } module_init(gmac_probe); module_exit(gmac_cleanup_module);