162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver. 362306a36Sopenharmony_ci * Once again I am out to prove that every ethernet 462306a36Sopenharmony_ci * controller out there can be most efficiently programmed 562306a36Sopenharmony_ci * if you make it look like a LANCE. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 1996, 1999, 2003, 2006, 2008 David S. Miller (davem@davemloft.net) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/fcntl.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/ioport.h> 1762306a36Sopenharmony_ci#include <linux/in.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/crc32.h> 2362306a36Sopenharmony_ci#include <linux/netdevice.h> 2462306a36Sopenharmony_ci#include <linux/etherdevice.h> 2562306a36Sopenharmony_ci#include <linux/skbuff.h> 2662306a36Sopenharmony_ci#include <linux/ethtool.h> 2762306a36Sopenharmony_ci#include <linux/bitops.h> 2862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/pgtable.h> 3162306a36Sopenharmony_ci#include <linux/platform_device.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <asm/io.h> 3462306a36Sopenharmony_ci#include <asm/dma.h> 3562306a36Sopenharmony_ci#include <asm/byteorder.h> 3662306a36Sopenharmony_ci#include <asm/idprom.h> 3762306a36Sopenharmony_ci#include <asm/openprom.h> 3862306a36Sopenharmony_ci#include <asm/oplib.h> 3962306a36Sopenharmony_ci#include <asm/auxio.h> 4062306a36Sopenharmony_ci#include <asm/irq.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "sunqe.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define DRV_NAME "sunqe" 4562306a36Sopenharmony_ci#define DRV_VERSION "4.1" 4662306a36Sopenharmony_ci#define DRV_RELDATE "August 27, 2008" 4762306a36Sopenharmony_ci#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic char version[] = 5062306a36Sopenharmony_ci DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n"; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 5362306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR); 5462306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun QuadEthernet 10baseT SBUS card driver"); 5562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct sunqec *root_qec_dev; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void qe_set_multicast(struct net_device *dev); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define QEC_RESET_TRIES 200 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline int qec_global_reset(void __iomem *gregs) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int tries = QEC_RESET_TRIES; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci sbus_writel(GLOB_CTRL_RESET, gregs + GLOB_CTRL); 6862306a36Sopenharmony_ci while (--tries) { 6962306a36Sopenharmony_ci u32 tmp = sbus_readl(gregs + GLOB_CTRL); 7062306a36Sopenharmony_ci if (tmp & GLOB_CTRL_RESET) { 7162306a36Sopenharmony_ci udelay(20); 7262306a36Sopenharmony_ci continue; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (tries) 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci printk(KERN_ERR "QuadEther: AIEEE cannot reset the QEC!\n"); 7962306a36Sopenharmony_ci return -1; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define MACE_RESET_RETRIES 200 8362306a36Sopenharmony_ci#define QE_RESET_RETRIES 200 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic inline int qe_stop(struct sunqe *qep) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci void __iomem *cregs = qep->qcregs; 8862306a36Sopenharmony_ci void __iomem *mregs = qep->mregs; 8962306a36Sopenharmony_ci int tries; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Reset the MACE, then the QEC channel. */ 9262306a36Sopenharmony_ci sbus_writeb(MREGS_BCONFIG_RESET, mregs + MREGS_BCONFIG); 9362306a36Sopenharmony_ci tries = MACE_RESET_RETRIES; 9462306a36Sopenharmony_ci while (--tries) { 9562306a36Sopenharmony_ci u8 tmp = sbus_readb(mregs + MREGS_BCONFIG); 9662306a36Sopenharmony_ci if (tmp & MREGS_BCONFIG_RESET) { 9762306a36Sopenharmony_ci udelay(20); 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci if (!tries) { 10362306a36Sopenharmony_ci printk(KERN_ERR "QuadEther: AIEEE cannot reset the MACE!\n"); 10462306a36Sopenharmony_ci return -1; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci sbus_writel(CREG_CTRL_RESET, cregs + CREG_CTRL); 10862306a36Sopenharmony_ci tries = QE_RESET_RETRIES; 10962306a36Sopenharmony_ci while (--tries) { 11062306a36Sopenharmony_ci u32 tmp = sbus_readl(cregs + CREG_CTRL); 11162306a36Sopenharmony_ci if (tmp & CREG_CTRL_RESET) { 11262306a36Sopenharmony_ci udelay(20); 11362306a36Sopenharmony_ci continue; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci if (!tries) { 11862306a36Sopenharmony_ci printk(KERN_ERR "QuadEther: Cannot reset QE channel!\n"); 11962306a36Sopenharmony_ci return -1; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void qe_init_rings(struct sunqe *qep) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct qe_init_block *qb = qep->qe_block; 12762306a36Sopenharmony_ci struct sunqe_buffers *qbufs = qep->buffers; 12862306a36Sopenharmony_ci __u32 qbufs_dvma = (__u32)qep->buffers_dvma; 12962306a36Sopenharmony_ci int i; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci qep->rx_new = qep->rx_old = qep->tx_new = qep->tx_old = 0; 13262306a36Sopenharmony_ci memset(qb, 0, sizeof(struct qe_init_block)); 13362306a36Sopenharmony_ci memset(qbufs, 0, sizeof(struct sunqe_buffers)); 13462306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 13562306a36Sopenharmony_ci qb->qe_rxd[i].rx_addr = qbufs_dvma + qebuf_offset(rx_buf, i); 13662306a36Sopenharmony_ci qb->qe_rxd[i].rx_flags = 13762306a36Sopenharmony_ci (RXD_OWN | ((RXD_PKT_SZ) & RXD_LENGTH)); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int qe_init(struct sunqe *qep, int from_irq) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct sunqec *qecp = qep->parent; 14462306a36Sopenharmony_ci void __iomem *cregs = qep->qcregs; 14562306a36Sopenharmony_ci void __iomem *mregs = qep->mregs; 14662306a36Sopenharmony_ci void __iomem *gregs = qecp->gregs; 14762306a36Sopenharmony_ci const unsigned char *e = &qep->dev->dev_addr[0]; 14862306a36Sopenharmony_ci __u32 qblk_dvma = (__u32)qep->qblock_dvma; 14962306a36Sopenharmony_ci u32 tmp; 15062306a36Sopenharmony_ci int i; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Shut it up. */ 15362306a36Sopenharmony_ci if (qe_stop(qep)) 15462306a36Sopenharmony_ci return -EAGAIN; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Setup initial rx/tx init block pointers. */ 15762306a36Sopenharmony_ci sbus_writel(qblk_dvma + qib_offset(qe_rxd, 0), cregs + CREG_RXDS); 15862306a36Sopenharmony_ci sbus_writel(qblk_dvma + qib_offset(qe_txd, 0), cregs + CREG_TXDS); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Enable/mask the various irq's. */ 16162306a36Sopenharmony_ci sbus_writel(0, cregs + CREG_RIMASK); 16262306a36Sopenharmony_ci sbus_writel(1, cregs + CREG_TIMASK); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci sbus_writel(0, cregs + CREG_QMASK); 16562306a36Sopenharmony_ci sbus_writel(CREG_MMASK_RXCOLL, cregs + CREG_MMASK); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Setup the FIFO pointers into QEC local memory. */ 16862306a36Sopenharmony_ci tmp = qep->channel * sbus_readl(gregs + GLOB_MSIZE); 16962306a36Sopenharmony_ci sbus_writel(tmp, cregs + CREG_RXRBUFPTR); 17062306a36Sopenharmony_ci sbus_writel(tmp, cregs + CREG_RXWBUFPTR); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci tmp = sbus_readl(cregs + CREG_RXRBUFPTR) + 17362306a36Sopenharmony_ci sbus_readl(gregs + GLOB_RSIZE); 17462306a36Sopenharmony_ci sbus_writel(tmp, cregs + CREG_TXRBUFPTR); 17562306a36Sopenharmony_ci sbus_writel(tmp, cregs + CREG_TXWBUFPTR); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Clear the channel collision counter. */ 17862306a36Sopenharmony_ci sbus_writel(0, cregs + CREG_CCNT); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* For 10baseT, inter frame space nor throttle seems to be necessary. */ 18162306a36Sopenharmony_ci sbus_writel(0, cregs + CREG_PIPG); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Now dork with the AMD MACE. */ 18462306a36Sopenharmony_ci sbus_writeb(MREGS_PHYCONFIG_AUTO, mregs + MREGS_PHYCONFIG); 18562306a36Sopenharmony_ci sbus_writeb(MREGS_TXFCNTL_AUTOPAD, mregs + MREGS_TXFCNTL); 18662306a36Sopenharmony_ci sbus_writeb(0, mregs + MREGS_RXFCNTL); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* The QEC dma's the rx'd packets from local memory out to main memory, 18962306a36Sopenharmony_ci * and therefore it interrupts when the packet reception is "complete". 19062306a36Sopenharmony_ci * So don't listen for the MACE talking about it. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci sbus_writeb(MREGS_IMASK_COLL | MREGS_IMASK_RXIRQ, mregs + MREGS_IMASK); 19362306a36Sopenharmony_ci sbus_writeb(MREGS_BCONFIG_BSWAP | MREGS_BCONFIG_64TS, mregs + MREGS_BCONFIG); 19462306a36Sopenharmony_ci sbus_writeb((MREGS_FCONFIG_TXF16 | MREGS_FCONFIG_RXF32 | 19562306a36Sopenharmony_ci MREGS_FCONFIG_RFWU | MREGS_FCONFIG_TFWU), 19662306a36Sopenharmony_ci mregs + MREGS_FCONFIG); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Only usable interface on QuadEther is twisted pair. */ 19962306a36Sopenharmony_ci sbus_writeb(MREGS_PLSCONFIG_TP, mregs + MREGS_PLSCONFIG); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Tell MACE we are changing the ether address. */ 20262306a36Sopenharmony_ci sbus_writeb(MREGS_IACONFIG_ACHNGE | MREGS_IACONFIG_PARESET, 20362306a36Sopenharmony_ci mregs + MREGS_IACONFIG); 20462306a36Sopenharmony_ci while ((sbus_readb(mregs + MREGS_IACONFIG) & MREGS_IACONFIG_ACHNGE) != 0) 20562306a36Sopenharmony_ci barrier(); 20662306a36Sopenharmony_ci sbus_writeb(e[0], mregs + MREGS_ETHADDR); 20762306a36Sopenharmony_ci sbus_writeb(e[1], mregs + MREGS_ETHADDR); 20862306a36Sopenharmony_ci sbus_writeb(e[2], mregs + MREGS_ETHADDR); 20962306a36Sopenharmony_ci sbus_writeb(e[3], mregs + MREGS_ETHADDR); 21062306a36Sopenharmony_ci sbus_writeb(e[4], mregs + MREGS_ETHADDR); 21162306a36Sopenharmony_ci sbus_writeb(e[5], mregs + MREGS_ETHADDR); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Clear out the address filter. */ 21462306a36Sopenharmony_ci sbus_writeb(MREGS_IACONFIG_ACHNGE | MREGS_IACONFIG_LARESET, 21562306a36Sopenharmony_ci mregs + MREGS_IACONFIG); 21662306a36Sopenharmony_ci while ((sbus_readb(mregs + MREGS_IACONFIG) & MREGS_IACONFIG_ACHNGE) != 0) 21762306a36Sopenharmony_ci barrier(); 21862306a36Sopenharmony_ci for (i = 0; i < 8; i++) 21962306a36Sopenharmony_ci sbus_writeb(0, mregs + MREGS_FILTER); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Address changes are now complete. */ 22262306a36Sopenharmony_ci sbus_writeb(0, mregs + MREGS_IACONFIG); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci qe_init_rings(qep); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Wait a little bit for the link to come up... */ 22762306a36Sopenharmony_ci mdelay(5); 22862306a36Sopenharmony_ci if (!(sbus_readb(mregs + MREGS_PHYCONFIG) & MREGS_PHYCONFIG_LTESTDIS)) { 22962306a36Sopenharmony_ci int tries = 50; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci while (--tries) { 23262306a36Sopenharmony_ci u8 tmp; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mdelay(5); 23562306a36Sopenharmony_ci barrier(); 23662306a36Sopenharmony_ci tmp = sbus_readb(mregs + MREGS_PHYCONFIG); 23762306a36Sopenharmony_ci if ((tmp & MREGS_PHYCONFIG_LSTAT) != 0) 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (tries == 0) 24162306a36Sopenharmony_ci printk(KERN_NOTICE "%s: Warning, link state is down.\n", qep->dev->name); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Missed packet counter is cleared on a read. */ 24562306a36Sopenharmony_ci sbus_readb(mregs + MREGS_MPCNT); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Reload multicast information, this will enable the receiver 24862306a36Sopenharmony_ci * and transmitter. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ci qe_set_multicast(qep->dev); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* QEC should now start to show interrupts. */ 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* Grrr, certain error conditions completely lock up the AMD MACE, 25762306a36Sopenharmony_ci * so when we get these we _must_ reset the chip. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic int qe_is_bolixed(struct sunqe *qep, u32 qe_status) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct net_device *dev = qep->dev; 26262306a36Sopenharmony_ci int mace_hwbug_workaround = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (qe_status & CREG_STAT_EDEFER) { 26562306a36Sopenharmony_ci printk(KERN_ERR "%s: Excessive transmit defers.\n", dev->name); 26662306a36Sopenharmony_ci dev->stats.tx_errors++; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (qe_status & CREG_STAT_CLOSS) { 27062306a36Sopenharmony_ci printk(KERN_ERR "%s: Carrier lost, link down?\n", dev->name); 27162306a36Sopenharmony_ci dev->stats.tx_errors++; 27262306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (qe_status & CREG_STAT_ERETRIES) { 27662306a36Sopenharmony_ci printk(KERN_ERR "%s: Excessive transmit retries (more than 16).\n", dev->name); 27762306a36Sopenharmony_ci dev->stats.tx_errors++; 27862306a36Sopenharmony_ci mace_hwbug_workaround = 1; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (qe_status & CREG_STAT_LCOLL) { 28262306a36Sopenharmony_ci printk(KERN_ERR "%s: Late transmit collision.\n", dev->name); 28362306a36Sopenharmony_ci dev->stats.tx_errors++; 28462306a36Sopenharmony_ci dev->stats.collisions++; 28562306a36Sopenharmony_ci mace_hwbug_workaround = 1; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (qe_status & CREG_STAT_FUFLOW) { 28962306a36Sopenharmony_ci printk(KERN_ERR "%s: Transmit fifo underflow, driver bug.\n", dev->name); 29062306a36Sopenharmony_ci dev->stats.tx_errors++; 29162306a36Sopenharmony_ci mace_hwbug_workaround = 1; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (qe_status & CREG_STAT_JERROR) { 29562306a36Sopenharmony_ci printk(KERN_ERR "%s: Jabber error.\n", dev->name); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (qe_status & CREG_STAT_BERROR) { 29962306a36Sopenharmony_ci printk(KERN_ERR "%s: Babble error.\n", dev->name); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (qe_status & CREG_STAT_CCOFLOW) { 30362306a36Sopenharmony_ci dev->stats.tx_errors += 256; 30462306a36Sopenharmony_ci dev->stats.collisions += 256; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (qe_status & CREG_STAT_TXDERROR) { 30862306a36Sopenharmony_ci printk(KERN_ERR "%s: Transmit descriptor is bogus, driver bug.\n", dev->name); 30962306a36Sopenharmony_ci dev->stats.tx_errors++; 31062306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 31162306a36Sopenharmony_ci mace_hwbug_workaround = 1; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (qe_status & CREG_STAT_TXLERR) { 31562306a36Sopenharmony_ci printk(KERN_ERR "%s: Transmit late error.\n", dev->name); 31662306a36Sopenharmony_ci dev->stats.tx_errors++; 31762306a36Sopenharmony_ci mace_hwbug_workaround = 1; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (qe_status & CREG_STAT_TXPERR) { 32162306a36Sopenharmony_ci printk(KERN_ERR "%s: Transmit DMA parity error.\n", dev->name); 32262306a36Sopenharmony_ci dev->stats.tx_errors++; 32362306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 32462306a36Sopenharmony_ci mace_hwbug_workaround = 1; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (qe_status & CREG_STAT_TXSERR) { 32862306a36Sopenharmony_ci printk(KERN_ERR "%s: Transmit DMA sbus error ack.\n", dev->name); 32962306a36Sopenharmony_ci dev->stats.tx_errors++; 33062306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 33162306a36Sopenharmony_ci mace_hwbug_workaround = 1; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (qe_status & CREG_STAT_RCCOFLOW) { 33562306a36Sopenharmony_ci dev->stats.rx_errors += 256; 33662306a36Sopenharmony_ci dev->stats.collisions += 256; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (qe_status & CREG_STAT_RUOFLOW) { 34062306a36Sopenharmony_ci dev->stats.rx_errors += 256; 34162306a36Sopenharmony_ci dev->stats.rx_over_errors += 256; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (qe_status & CREG_STAT_MCOFLOW) { 34562306a36Sopenharmony_ci dev->stats.rx_errors += 256; 34662306a36Sopenharmony_ci dev->stats.rx_missed_errors += 256; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXFOFLOW) { 35062306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive fifo overflow.\n", dev->name); 35162306a36Sopenharmony_ci dev->stats.rx_errors++; 35262306a36Sopenharmony_ci dev->stats.rx_over_errors++; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (qe_status & CREG_STAT_RLCOLL) { 35662306a36Sopenharmony_ci printk(KERN_ERR "%s: Late receive collision.\n", dev->name); 35762306a36Sopenharmony_ci dev->stats.rx_errors++; 35862306a36Sopenharmony_ci dev->stats.collisions++; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (qe_status & CREG_STAT_FCOFLOW) { 36262306a36Sopenharmony_ci dev->stats.rx_errors += 256; 36362306a36Sopenharmony_ci dev->stats.rx_frame_errors += 256; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (qe_status & CREG_STAT_CECOFLOW) { 36762306a36Sopenharmony_ci dev->stats.rx_errors += 256; 36862306a36Sopenharmony_ci dev->stats.rx_crc_errors += 256; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXDROP) { 37262306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive packet dropped.\n", dev->name); 37362306a36Sopenharmony_ci dev->stats.rx_errors++; 37462306a36Sopenharmony_ci dev->stats.rx_dropped++; 37562306a36Sopenharmony_ci dev->stats.rx_missed_errors++; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXSMALL) { 37962306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive buffer too small, driver bug.\n", dev->name); 38062306a36Sopenharmony_ci dev->stats.rx_errors++; 38162306a36Sopenharmony_ci dev->stats.rx_length_errors++; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXLERR) { 38562306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive late error.\n", dev->name); 38662306a36Sopenharmony_ci dev->stats.rx_errors++; 38762306a36Sopenharmony_ci mace_hwbug_workaround = 1; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXPERR) { 39162306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive DMA parity error.\n", dev->name); 39262306a36Sopenharmony_ci dev->stats.rx_errors++; 39362306a36Sopenharmony_ci dev->stats.rx_missed_errors++; 39462306a36Sopenharmony_ci mace_hwbug_workaround = 1; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXSERR) { 39862306a36Sopenharmony_ci printk(KERN_ERR "%s: Receive DMA sbus error ack.\n", dev->name); 39962306a36Sopenharmony_ci dev->stats.rx_errors++; 40062306a36Sopenharmony_ci dev->stats.rx_missed_errors++; 40162306a36Sopenharmony_ci mace_hwbug_workaround = 1; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (mace_hwbug_workaround) 40562306a36Sopenharmony_ci qe_init(qep, 1); 40662306a36Sopenharmony_ci return mace_hwbug_workaround; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* Per-QE receive interrupt service routine. Just like on the happy meal 41062306a36Sopenharmony_ci * we receive directly into skb's with a small packet copy water mark. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_cistatic void qe_rx(struct sunqe *qep) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct qe_rxd *rxbase = &qep->qe_block->qe_rxd[0]; 41562306a36Sopenharmony_ci struct net_device *dev = qep->dev; 41662306a36Sopenharmony_ci struct qe_rxd *this; 41762306a36Sopenharmony_ci struct sunqe_buffers *qbufs = qep->buffers; 41862306a36Sopenharmony_ci __u32 qbufs_dvma = (__u32)qep->buffers_dvma; 41962306a36Sopenharmony_ci int elem = qep->rx_new; 42062306a36Sopenharmony_ci u32 flags; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci this = &rxbase[elem]; 42362306a36Sopenharmony_ci while (!((flags = this->rx_flags) & RXD_OWN)) { 42462306a36Sopenharmony_ci struct sk_buff *skb; 42562306a36Sopenharmony_ci unsigned char *this_qbuf = 42662306a36Sopenharmony_ci &qbufs->rx_buf[elem & (RX_RING_SIZE - 1)][0]; 42762306a36Sopenharmony_ci __u32 this_qbuf_dvma = qbufs_dvma + 42862306a36Sopenharmony_ci qebuf_offset(rx_buf, (elem & (RX_RING_SIZE - 1))); 42962306a36Sopenharmony_ci struct qe_rxd *end_rxd = 43062306a36Sopenharmony_ci &rxbase[(elem+RX_RING_SIZE)&(RX_RING_MAXSIZE-1)]; 43162306a36Sopenharmony_ci int len = (flags & RXD_LENGTH) - 4; /* QE adds ether FCS size to len */ 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Check for errors. */ 43462306a36Sopenharmony_ci if (len < ETH_ZLEN) { 43562306a36Sopenharmony_ci dev->stats.rx_errors++; 43662306a36Sopenharmony_ci dev->stats.rx_length_errors++; 43762306a36Sopenharmony_ci dev->stats.rx_dropped++; 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len + 2); 44062306a36Sopenharmony_ci if (skb == NULL) { 44162306a36Sopenharmony_ci dev->stats.rx_dropped++; 44262306a36Sopenharmony_ci } else { 44362306a36Sopenharmony_ci skb_reserve(skb, 2); 44462306a36Sopenharmony_ci skb_put(skb, len); 44562306a36Sopenharmony_ci skb_copy_to_linear_data(skb, this_qbuf, 44662306a36Sopenharmony_ci len); 44762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, qep->dev); 44862306a36Sopenharmony_ci netif_rx(skb); 44962306a36Sopenharmony_ci dev->stats.rx_packets++; 45062306a36Sopenharmony_ci dev->stats.rx_bytes += len; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci end_rxd->rx_addr = this_qbuf_dvma; 45462306a36Sopenharmony_ci end_rxd->rx_flags = (RXD_OWN | ((RXD_PKT_SZ) & RXD_LENGTH)); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci elem = NEXT_RX(elem); 45762306a36Sopenharmony_ci this = &rxbase[elem]; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci qep->rx_new = elem; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void qe_tx_reclaim(struct sunqe *qep); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/* Interrupts for all QE's get filtered out via the QEC master controller, 46562306a36Sopenharmony_ci * so we just run through each qe and check to see who is signaling 46662306a36Sopenharmony_ci * and thus needs to be serviced. 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_cistatic irqreturn_t qec_interrupt(int irq, void *dev_id) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct sunqec *qecp = dev_id; 47162306a36Sopenharmony_ci u32 qec_status; 47262306a36Sopenharmony_ci int channel = 0; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Latch the status now. */ 47562306a36Sopenharmony_ci qec_status = sbus_readl(qecp->gregs + GLOB_STAT); 47662306a36Sopenharmony_ci while (channel < 4) { 47762306a36Sopenharmony_ci if (qec_status & 0xf) { 47862306a36Sopenharmony_ci struct sunqe *qep = qecp->qes[channel]; 47962306a36Sopenharmony_ci u32 qe_status; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci qe_status = sbus_readl(qep->qcregs + CREG_STAT); 48262306a36Sopenharmony_ci if (qe_status & CREG_STAT_ERRORS) { 48362306a36Sopenharmony_ci if (qe_is_bolixed(qep, qe_status)) 48462306a36Sopenharmony_ci goto next; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci if (qe_status & CREG_STAT_RXIRQ) 48762306a36Sopenharmony_ci qe_rx(qep); 48862306a36Sopenharmony_ci if (netif_queue_stopped(qep->dev) && 48962306a36Sopenharmony_ci (qe_status & CREG_STAT_TXIRQ)) { 49062306a36Sopenharmony_ci spin_lock(&qep->lock); 49162306a36Sopenharmony_ci qe_tx_reclaim(qep); 49262306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(qep) > 0) { 49362306a36Sopenharmony_ci /* Wake net queue and return to 49462306a36Sopenharmony_ci * lazy tx reclaim. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci netif_wake_queue(qep->dev); 49762306a36Sopenharmony_ci sbus_writel(1, qep->qcregs + CREG_TIMASK); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci spin_unlock(&qep->lock); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci next: 50262306a36Sopenharmony_ci ; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci qec_status >>= 4; 50562306a36Sopenharmony_ci channel++; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci return IRQ_HANDLED; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int qe_open(struct net_device *dev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci qep->mconfig = (MREGS_MCONFIG_TXENAB | 51662306a36Sopenharmony_ci MREGS_MCONFIG_RXENAB | 51762306a36Sopenharmony_ci MREGS_MCONFIG_MBAENAB); 51862306a36Sopenharmony_ci return qe_init(qep, 0); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int qe_close(struct net_device *dev) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci qe_stop(qep); 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* Reclaim TX'd frames from the ring. This must always run under 53062306a36Sopenharmony_ci * the IRQ protected qep->lock. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_cistatic void qe_tx_reclaim(struct sunqe *qep) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct qe_txd *txbase = &qep->qe_block->qe_txd[0]; 53562306a36Sopenharmony_ci int elem = qep->tx_old; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci while (elem != qep->tx_new) { 53862306a36Sopenharmony_ci u32 flags = txbase[elem].tx_flags; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (flags & TXD_OWN) 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci elem = NEXT_TX(elem); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci qep->tx_old = elem; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void qe_tx_timeout(struct net_device *dev, unsigned int txqueue) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 55062306a36Sopenharmony_ci int tx_full; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci spin_lock_irq(&qep->lock); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Try to reclaim, if that frees up some tx 55562306a36Sopenharmony_ci * entries, we're fine. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci qe_tx_reclaim(qep); 55862306a36Sopenharmony_ci tx_full = TX_BUFFS_AVAIL(qep) <= 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci spin_unlock_irq(&qep->lock); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (! tx_full) 56362306a36Sopenharmony_ci goto out; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name); 56662306a36Sopenharmony_ci qe_init(qep, 1); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciout: 56962306a36Sopenharmony_ci netif_wake_queue(dev); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* Get a packet queued to go onto the wire. */ 57362306a36Sopenharmony_cistatic netdev_tx_t qe_start_xmit(struct sk_buff *skb, struct net_device *dev) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 57662306a36Sopenharmony_ci struct sunqe_buffers *qbufs = qep->buffers; 57762306a36Sopenharmony_ci __u32 txbuf_dvma, qbufs_dvma = (__u32)qep->buffers_dvma; 57862306a36Sopenharmony_ci unsigned char *txbuf; 57962306a36Sopenharmony_ci int len, entry; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock_irq(&qep->lock); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci qe_tx_reclaim(qep); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci len = skb->len; 58662306a36Sopenharmony_ci entry = qep->tx_new; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci txbuf = &qbufs->tx_buf[entry & (TX_RING_SIZE - 1)][0]; 58962306a36Sopenharmony_ci txbuf_dvma = qbufs_dvma + 59062306a36Sopenharmony_ci qebuf_offset(tx_buf, (entry & (TX_RING_SIZE - 1))); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Avoid a race... */ 59362306a36Sopenharmony_ci qep->qe_block->qe_txd[entry].tx_flags = TXD_UPDATE; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci skb_copy_from_linear_data(skb, txbuf, len); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci qep->qe_block->qe_txd[entry].tx_addr = txbuf_dvma; 59862306a36Sopenharmony_ci qep->qe_block->qe_txd[entry].tx_flags = 59962306a36Sopenharmony_ci (TXD_OWN | TXD_SOP | TXD_EOP | (len & TXD_LENGTH)); 60062306a36Sopenharmony_ci qep->tx_new = NEXT_TX(entry); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Get it going. */ 60362306a36Sopenharmony_ci sbus_writel(CREG_CTRL_TWAKEUP, qep->qcregs + CREG_CTRL); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dev->stats.tx_packets++; 60662306a36Sopenharmony_ci dev->stats.tx_bytes += len; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(qep) <= 0) { 60962306a36Sopenharmony_ci /* Halt the net queue and enable tx interrupts. 61062306a36Sopenharmony_ci * When the tx queue empties the tx irq handler 61162306a36Sopenharmony_ci * will wake up the queue and return us back to 61262306a36Sopenharmony_ci * the lazy tx reclaim scheme. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci netif_stop_queue(dev); 61562306a36Sopenharmony_ci sbus_writel(0, qep->qcregs + CREG_TIMASK); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci spin_unlock_irq(&qep->lock); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci dev_kfree_skb(skb); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return NETDEV_TX_OK; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void qe_set_multicast(struct net_device *dev) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 62762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 62862306a36Sopenharmony_ci u8 new_mconfig = qep->mconfig; 62962306a36Sopenharmony_ci int i; 63062306a36Sopenharmony_ci u32 crc; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Lock out others. */ 63362306a36Sopenharmony_ci netif_stop_queue(dev); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 64)) { 63662306a36Sopenharmony_ci sbus_writeb(MREGS_IACONFIG_ACHNGE | MREGS_IACONFIG_LARESET, 63762306a36Sopenharmony_ci qep->mregs + MREGS_IACONFIG); 63862306a36Sopenharmony_ci while ((sbus_readb(qep->mregs + MREGS_IACONFIG) & MREGS_IACONFIG_ACHNGE) != 0) 63962306a36Sopenharmony_ci barrier(); 64062306a36Sopenharmony_ci for (i = 0; i < 8; i++) 64162306a36Sopenharmony_ci sbus_writeb(0xff, qep->mregs + MREGS_FILTER); 64262306a36Sopenharmony_ci sbus_writeb(0, qep->mregs + MREGS_IACONFIG); 64362306a36Sopenharmony_ci } else if (dev->flags & IFF_PROMISC) { 64462306a36Sopenharmony_ci new_mconfig |= MREGS_MCONFIG_PROMISC; 64562306a36Sopenharmony_ci } else { 64662306a36Sopenharmony_ci u16 hash_table[4]; 64762306a36Sopenharmony_ci u8 *hbytes = (unsigned char *) &hash_table[0]; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci memset(hash_table, 0, sizeof(hash_table)); 65062306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 65162306a36Sopenharmony_ci crc = ether_crc_le(6, ha->addr); 65262306a36Sopenharmony_ci crc >>= 26; 65362306a36Sopenharmony_ci hash_table[crc >> 4] |= 1 << (crc & 0xf); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci /* Program the qe with the new filter value. */ 65662306a36Sopenharmony_ci sbus_writeb(MREGS_IACONFIG_ACHNGE | MREGS_IACONFIG_LARESET, 65762306a36Sopenharmony_ci qep->mregs + MREGS_IACONFIG); 65862306a36Sopenharmony_ci while ((sbus_readb(qep->mregs + MREGS_IACONFIG) & MREGS_IACONFIG_ACHNGE) != 0) 65962306a36Sopenharmony_ci barrier(); 66062306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 66162306a36Sopenharmony_ci u8 tmp = *hbytes++; 66262306a36Sopenharmony_ci sbus_writeb(tmp, qep->mregs + MREGS_FILTER); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci sbus_writeb(0, qep->mregs + MREGS_IACONFIG); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* Any change of the logical address filter, the physical address, 66862306a36Sopenharmony_ci * or enabling/disabling promiscuous mode causes the MACE to disable 66962306a36Sopenharmony_ci * the receiver. So we must re-enable them here or else the MACE 67062306a36Sopenharmony_ci * refuses to listen to anything on the network. Sheesh, took 67162306a36Sopenharmony_ci * me a day or two to find this bug. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci qep->mconfig = new_mconfig; 67462306a36Sopenharmony_ci sbus_writeb(qep->mconfig, qep->mregs + MREGS_MCONFIG); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Let us get going again. */ 67762306a36Sopenharmony_ci netif_wake_queue(dev); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/* Ethtool support... */ 68162306a36Sopenharmony_cistatic void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci const struct linux_prom_registers *regs; 68462306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 68562306a36Sopenharmony_ci struct platform_device *op; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci strscpy(info->driver, "sunqe", sizeof(info->driver)); 68862306a36Sopenharmony_ci strscpy(info->version, "3.0", sizeof(info->version)); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci op = qep->op; 69162306a36Sopenharmony_ci regs = of_get_property(op->dev.of_node, "reg", NULL); 69262306a36Sopenharmony_ci if (regs) 69362306a36Sopenharmony_ci snprintf(info->bus_info, sizeof(info->bus_info), "SBUS:%d", 69462306a36Sopenharmony_ci regs->which_io); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic u32 qe_get_link(struct net_device *dev) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct sunqe *qep = netdev_priv(dev); 70162306a36Sopenharmony_ci void __iomem *mregs = qep->mregs; 70262306a36Sopenharmony_ci u8 phyconfig; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_lock_irq(&qep->lock); 70562306a36Sopenharmony_ci phyconfig = sbus_readb(mregs + MREGS_PHYCONFIG); 70662306a36Sopenharmony_ci spin_unlock_irq(&qep->lock); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return phyconfig & MREGS_PHYCONFIG_LSTAT; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic const struct ethtool_ops qe_ethtool_ops = { 71262306a36Sopenharmony_ci .get_drvinfo = qe_get_drvinfo, 71362306a36Sopenharmony_ci .get_link = qe_get_link, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/* This is only called once at boot time for each card probed. */ 71762306a36Sopenharmony_cistatic void qec_init_once(struct sunqec *qecp, struct platform_device *op) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci u8 bsizes = qecp->qec_bursts; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (sbus_can_burst64() && (bsizes & DMA_BURST64)) { 72262306a36Sopenharmony_ci sbus_writel(GLOB_CTRL_B64, qecp->gregs + GLOB_CTRL); 72362306a36Sopenharmony_ci } else if (bsizes & DMA_BURST32) { 72462306a36Sopenharmony_ci sbus_writel(GLOB_CTRL_B32, qecp->gregs + GLOB_CTRL); 72562306a36Sopenharmony_ci } else { 72662306a36Sopenharmony_ci sbus_writel(GLOB_CTRL_B16, qecp->gregs + GLOB_CTRL); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Packetsize only used in 100baseT BigMAC configurations, 73062306a36Sopenharmony_ci * set it to zero just to be on the safe side. 73162306a36Sopenharmony_ci */ 73262306a36Sopenharmony_ci sbus_writel(GLOB_PSIZE_2048, qecp->gregs + GLOB_PSIZE); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Set the local memsize register, divided up to one piece per QE channel. */ 73562306a36Sopenharmony_ci sbus_writel((resource_size(&op->resource[1]) >> 2), 73662306a36Sopenharmony_ci qecp->gregs + GLOB_MSIZE); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Divide up the local QEC memory amongst the 4 QE receiver and 73962306a36Sopenharmony_ci * transmitter FIFOs. Basically it is (total / 2 / num_channels). 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1, 74262306a36Sopenharmony_ci qecp->gregs + GLOB_TSIZE); 74362306a36Sopenharmony_ci sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1, 74462306a36Sopenharmony_ci qecp->gregs + GLOB_RSIZE); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic u8 qec_get_burst(struct device_node *dp) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci u8 bsizes, bsizes_more; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Find and set the burst sizes for the QEC, since it 75262306a36Sopenharmony_ci * does the actual dma for all 4 channels. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci bsizes = of_getintprop_default(dp, "burst-sizes", 0xff); 75562306a36Sopenharmony_ci bsizes &= 0xff; 75662306a36Sopenharmony_ci bsizes_more = of_getintprop_default(dp->parent, "burst-sizes", 0xff); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (bsizes_more != 0xff) 75962306a36Sopenharmony_ci bsizes &= bsizes_more; 76062306a36Sopenharmony_ci if (bsizes == 0xff || (bsizes & DMA_BURST16) == 0 || 76162306a36Sopenharmony_ci (bsizes & DMA_BURST32)==0) 76262306a36Sopenharmony_ci bsizes = (DMA_BURST32 - 1); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return bsizes; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic struct sunqec *get_qec(struct platform_device *child) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct platform_device *op = to_platform_device(child->dev.parent); 77062306a36Sopenharmony_ci struct sunqec *qecp; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci qecp = platform_get_drvdata(op); 77362306a36Sopenharmony_ci if (!qecp) { 77462306a36Sopenharmony_ci qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL); 77562306a36Sopenharmony_ci if (qecp) { 77662306a36Sopenharmony_ci u32 ctrl; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci qecp->op = op; 77962306a36Sopenharmony_ci qecp->gregs = of_ioremap(&op->resource[0], 0, 78062306a36Sopenharmony_ci GLOB_REG_SIZE, 78162306a36Sopenharmony_ci "QEC Global Registers"); 78262306a36Sopenharmony_ci if (!qecp->gregs) 78362306a36Sopenharmony_ci goto fail; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Make sure the QEC is in MACE mode. */ 78662306a36Sopenharmony_ci ctrl = sbus_readl(qecp->gregs + GLOB_CTRL); 78762306a36Sopenharmony_ci ctrl &= 0xf0000000; 78862306a36Sopenharmony_ci if (ctrl != GLOB_CTRL_MMODE) { 78962306a36Sopenharmony_ci printk(KERN_ERR "qec: Not in MACE mode!\n"); 79062306a36Sopenharmony_ci goto fail; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (qec_global_reset(qecp->gregs)) 79462306a36Sopenharmony_ci goto fail; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci qecp->qec_bursts = qec_get_burst(op->dev.of_node); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci qec_init_once(qecp, op); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (request_irq(op->archdata.irqs[0], qec_interrupt, 80162306a36Sopenharmony_ci IRQF_SHARED, "qec", (void *) qecp)) { 80262306a36Sopenharmony_ci printk(KERN_ERR "qec: Can't register irq.\n"); 80362306a36Sopenharmony_ci goto fail; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci platform_set_drvdata(op, qecp); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci qecp->next_module = root_qec_dev; 80962306a36Sopenharmony_ci root_qec_dev = qecp; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return qecp; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cifail: 81662306a36Sopenharmony_ci if (qecp->gregs) 81762306a36Sopenharmony_ci of_iounmap(&op->resource[0], qecp->gregs, GLOB_REG_SIZE); 81862306a36Sopenharmony_ci kfree(qecp); 81962306a36Sopenharmony_ci return NULL; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic const struct net_device_ops qec_ops = { 82362306a36Sopenharmony_ci .ndo_open = qe_open, 82462306a36Sopenharmony_ci .ndo_stop = qe_close, 82562306a36Sopenharmony_ci .ndo_start_xmit = qe_start_xmit, 82662306a36Sopenharmony_ci .ndo_set_rx_mode = qe_set_multicast, 82762306a36Sopenharmony_ci .ndo_tx_timeout = qe_tx_timeout, 82862306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 82962306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic int qec_ether_init(struct platform_device *op) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci static unsigned version_printed; 83562306a36Sopenharmony_ci struct net_device *dev; 83662306a36Sopenharmony_ci struct sunqec *qecp; 83762306a36Sopenharmony_ci struct sunqe *qe; 83862306a36Sopenharmony_ci int i, res; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (version_printed++ == 0) 84162306a36Sopenharmony_ci printk(KERN_INFO "%s", version); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct sunqe)); 84462306a36Sopenharmony_ci if (!dev) 84562306a36Sopenharmony_ci return -ENOMEM; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci eth_hw_addr_set(dev, idprom->id_ethaddr); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci qe = netdev_priv(dev); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci res = -ENODEV; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci i = of_getintprop_default(op->dev.of_node, "channel#", -1); 85462306a36Sopenharmony_ci if (i == -1) 85562306a36Sopenharmony_ci goto fail; 85662306a36Sopenharmony_ci qe->channel = i; 85762306a36Sopenharmony_ci spin_lock_init(&qe->lock); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci qecp = get_qec(op); 86062306a36Sopenharmony_ci if (!qecp) 86162306a36Sopenharmony_ci goto fail; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci qecp->qes[qe->channel] = qe; 86462306a36Sopenharmony_ci qe->dev = dev; 86562306a36Sopenharmony_ci qe->parent = qecp; 86662306a36Sopenharmony_ci qe->op = op; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci res = -ENOMEM; 86962306a36Sopenharmony_ci qe->qcregs = of_ioremap(&op->resource[0], 0, 87062306a36Sopenharmony_ci CREG_REG_SIZE, "QEC Channel Registers"); 87162306a36Sopenharmony_ci if (!qe->qcregs) { 87262306a36Sopenharmony_ci printk(KERN_ERR "qe: Cannot map channel registers.\n"); 87362306a36Sopenharmony_ci goto fail; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci qe->mregs = of_ioremap(&op->resource[1], 0, 87762306a36Sopenharmony_ci MREGS_REG_SIZE, "QE MACE Registers"); 87862306a36Sopenharmony_ci if (!qe->mregs) { 87962306a36Sopenharmony_ci printk(KERN_ERR "qe: Cannot map MACE registers.\n"); 88062306a36Sopenharmony_ci goto fail; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci qe->qe_block = dma_alloc_coherent(&op->dev, PAGE_SIZE, 88462306a36Sopenharmony_ci &qe->qblock_dvma, GFP_ATOMIC); 88562306a36Sopenharmony_ci qe->buffers = dma_alloc_coherent(&op->dev, sizeof(struct sunqe_buffers), 88662306a36Sopenharmony_ci &qe->buffers_dvma, GFP_ATOMIC); 88762306a36Sopenharmony_ci if (qe->qe_block == NULL || qe->qblock_dvma == 0 || 88862306a36Sopenharmony_ci qe->buffers == NULL || qe->buffers_dvma == 0) 88962306a36Sopenharmony_ci goto fail; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Stop this QE. */ 89262306a36Sopenharmony_ci qe_stop(qe); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &op->dev); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci dev->watchdog_timeo = 5*HZ; 89762306a36Sopenharmony_ci dev->irq = op->archdata.irqs[0]; 89862306a36Sopenharmony_ci dev->dma = 0; 89962306a36Sopenharmony_ci dev->ethtool_ops = &qe_ethtool_ops; 90062306a36Sopenharmony_ci dev->netdev_ops = &qec_ops; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci res = register_netdev(dev); 90362306a36Sopenharmony_ci if (res) 90462306a36Sopenharmony_ci goto fail; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci platform_set_drvdata(op, qe); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci printk(KERN_INFO "%s: qe channel[%d] %pM\n", dev->name, qe->channel, 90962306a36Sopenharmony_ci dev->dev_addr); 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cifail: 91362306a36Sopenharmony_ci if (qe->qcregs) 91462306a36Sopenharmony_ci of_iounmap(&op->resource[0], qe->qcregs, CREG_REG_SIZE); 91562306a36Sopenharmony_ci if (qe->mregs) 91662306a36Sopenharmony_ci of_iounmap(&op->resource[1], qe->mregs, MREGS_REG_SIZE); 91762306a36Sopenharmony_ci if (qe->qe_block) 91862306a36Sopenharmony_ci dma_free_coherent(&op->dev, PAGE_SIZE, 91962306a36Sopenharmony_ci qe->qe_block, qe->qblock_dvma); 92062306a36Sopenharmony_ci if (qe->buffers) 92162306a36Sopenharmony_ci dma_free_coherent(&op->dev, 92262306a36Sopenharmony_ci sizeof(struct sunqe_buffers), 92362306a36Sopenharmony_ci qe->buffers, 92462306a36Sopenharmony_ci qe->buffers_dvma); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci free_netdev(dev); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return res; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic int qec_sbus_probe(struct platform_device *op) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci return qec_ether_init(op); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic int qec_sbus_remove(struct platform_device *op) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct sunqe *qp = platform_get_drvdata(op); 93962306a36Sopenharmony_ci struct net_device *net_dev = qp->dev; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci unregister_netdev(net_dev); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci of_iounmap(&op->resource[0], qp->qcregs, CREG_REG_SIZE); 94462306a36Sopenharmony_ci of_iounmap(&op->resource[1], qp->mregs, MREGS_REG_SIZE); 94562306a36Sopenharmony_ci dma_free_coherent(&op->dev, PAGE_SIZE, 94662306a36Sopenharmony_ci qp->qe_block, qp->qblock_dvma); 94762306a36Sopenharmony_ci dma_free_coherent(&op->dev, sizeof(struct sunqe_buffers), 94862306a36Sopenharmony_ci qp->buffers, qp->buffers_dvma); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci free_netdev(net_dev); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic const struct of_device_id qec_sbus_match[] = { 95662306a36Sopenharmony_ci { 95762306a36Sopenharmony_ci .name = "qe", 95862306a36Sopenharmony_ci }, 95962306a36Sopenharmony_ci {}, 96062306a36Sopenharmony_ci}; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qec_sbus_match); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic struct platform_driver qec_sbus_driver = { 96562306a36Sopenharmony_ci .driver = { 96662306a36Sopenharmony_ci .name = "qec", 96762306a36Sopenharmony_ci .of_match_table = qec_sbus_match, 96862306a36Sopenharmony_ci }, 96962306a36Sopenharmony_ci .probe = qec_sbus_probe, 97062306a36Sopenharmony_ci .remove = qec_sbus_remove, 97162306a36Sopenharmony_ci}; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic int __init qec_init(void) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci return platform_driver_register(&qec_sbus_driver); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void __exit qec_exit(void) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci platform_driver_unregister(&qec_sbus_driver); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci while (root_qec_dev) { 98362306a36Sopenharmony_ci struct sunqec *next = root_qec_dev->next_module; 98462306a36Sopenharmony_ci struct platform_device *op = root_qec_dev->op; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci free_irq(op->archdata.irqs[0], (void *) root_qec_dev); 98762306a36Sopenharmony_ci of_iounmap(&op->resource[0], root_qec_dev->gregs, 98862306a36Sopenharmony_ci GLOB_REG_SIZE); 98962306a36Sopenharmony_ci kfree(root_qec_dev); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci root_qec_dev = next; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cimodule_init(qec_init); 99662306a36Sopenharmony_cimodule_exit(qec_exit); 997