From 23e049442b7b4f87f802470b4fd46a0e50b2fbdd Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 1 Nov 2011 00:53:33 -0400 Subject: drivers/net/ethernet/i825xx/3c505.c: fix build with dynamic debug The `#define filename' screws up the expansion of DEFINE_DYNAMIC_DEBUG_METADATA: drivers/net/ethernet/i825xx/3c505.c: In function 'send_pcb': drivers/net/ethernet/i825xx/3c505.c:390: error: expected identifier before string constant drivers/net/ethernet/i825xx/3c505.c:390: error: expected '}' before '.' token drivers/net/ethernet/i825xx/3c505.c:436: error: expected identifier before string constant drivers/net/ethernet/i825xx/3c505.c:435: error: expected '}' before '.' token drivers/net/ethernet/i825xx/3c505.c: In function 'start_receive': drivers/net/ethernet/i825xx/3c505.c:557: error: expected identifier before string constant drivers/net/ethernet/i825xx/3c505.c:557: error: expected '}' before '.' token drivers/net/ethernet/i825xx/3c505.c: In function 'receive_packet': drivers/net/ethernet/i825xx/3c505.c:629: error: expected identifier before string constant etc So remove that #define and "open-code" it. Cc: Philip Blundell Cc: David Miller Cc: Jason Baron Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/net/ethernet/i825xx/3c505.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/i825xx/3c505.c b/drivers/net/ethernet/i825xx/3c505.c index 40e1a175fce..ba82a266051 100644 --- a/drivers/net/ethernet/i825xx/3c505.c +++ b/drivers/net/ethernet/i825xx/3c505.c @@ -126,15 +126,13 @@ * *********************************************************/ -#define filename __FILE__ - #define timeout_msg "*** timeout at %s:%s (line %d) ***\n" #define TIMEOUT_MSG(lineno) \ - pr_notice(timeout_msg, filename, __func__, (lineno)) + pr_notice(timeout_msg, __FILE__, __func__, (lineno)) #define invalid_pcb_msg "*** invalid pcb length %d at %s:%s (line %d) ***\n" #define INVALID_PCB_MSG(len) \ - pr_notice(invalid_pcb_msg, (len), filename, __func__, __LINE__) + pr_notice(invalid_pcb_msg, (len), __FILE__, __func__, __LINE__) #define search_msg "%s: Looking for 3c505 adapter at address %#x..." -- cgit v1.2.3-18-g5258 From 98f41f694f46085fda475cdee8cc0b6d2c5e6f1f Mon Sep 17 00:00:00 2001 From: Weiping Pan Date: Mon, 31 Oct 2011 17:20:48 +0000 Subject: bonding:update speed/duplex for NETDEV_CHANGE Zheng Liang(lzheng@redhat.com) found a bug that if we config bonding with arp monitor, sometimes bonding driver cannot get the speed and duplex from its slaves, it will assume them to be 100Mb/sec and Full, please see /proc/net/bonding/bond0. But there is no such problem when uses miimon. (Take igb for example) I find that the reason is that after dev_open() in bond_enslave(), bond_update_speed_duplex() will call igb_get_settings() , but in that function, it runs ethtool_cmd_speed_set(ecmd, -1); ecmd->duplex = -1; because igb get an error value of status. So even dev_open() is called, but the device is not really ready to get its settings. Maybe it is safe for us to call igb_get_settings() only after this message shows up, that is "igb: p4p1 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX". So I prefer to update the speed and duplex for a slave when reseices NETDEV_CHANGE/NETDEV_UP event. Changelog V2: 1 remove the "fake 100/Full" logic in bond_update_speed_duplex(), set speed and duplex to -1 when it gets error value of speed and duplex. 2 delete the warning in bond_enslave() if bond_update_speed_duplex() returns error. 3 make bond_info_show_slave() handle bad values of speed and duplex. Signed-off-by: Weiping Pan Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 37 ++++++++++++------------------------- drivers/net/bonding/bond_procfs.c | 12 ++++++++++-- 2 files changed, 22 insertions(+), 27 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c34cc1e7c6f..b2b9109b671 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -550,7 +550,7 @@ down: /* * Get link speed and duplex from the slave's base driver * using ethtool. If for some reason the call fails or the - * values are invalid, fake speed and duplex to 100/Full + * values are invalid, set speed and duplex to -1, * and return error. */ static int bond_update_speed_duplex(struct slave *slave) @@ -560,9 +560,8 @@ static int bond_update_speed_duplex(struct slave *slave) u32 slave_speed; int res; - /* Fake speed and duplex */ - slave->speed = SPEED_100; - slave->duplex = DUPLEX_FULL; + slave->speed = -1; + slave->duplex = -1; res = __ethtool_get_settings(slave_dev, &ecmd); if (res < 0) @@ -1751,16 +1750,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->link = BOND_LINK_DOWN; } - if (bond_update_speed_duplex(new_slave) && - (new_slave->link != BOND_LINK_DOWN)) { - pr_warning("%s: Warning: failed to get speed and duplex from %s, assumed to be 100Mb/sec and Full.\n", - bond_dev->name, new_slave->dev->name); - - if (bond->params.mode == BOND_MODE_8023AD) { - pr_warning("%s: Warning: Operation of 802.3ad mode requires ETHTOOL support in base driver for proper aggregator selection.\n", - bond_dev->name); - } - } + bond_update_speed_duplex(new_slave); if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) { /* if there is a primary slave, remember it */ @@ -3220,6 +3210,7 @@ static int bond_slave_netdev_event(unsigned long event, { struct net_device *bond_dev = slave_dev->master; struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave = NULL; switch (event) { case NETDEV_UNREGISTER: @@ -3230,20 +3221,16 @@ static int bond_slave_netdev_event(unsigned long event, bond_release(bond_dev, slave_dev); } break; + case NETDEV_UP: case NETDEV_CHANGE: - if (bond->params.mode == BOND_MODE_8023AD || bond_is_lb(bond)) { - struct slave *slave; + slave = bond_get_slave_by_dev(bond, slave_dev); + if (slave) { + u32 old_speed = slave->speed; + u8 old_duplex = slave->duplex; - slave = bond_get_slave_by_dev(bond, slave_dev); - if (slave) { - u32 old_speed = slave->speed; - u8 old_duplex = slave->duplex; - - bond_update_speed_duplex(slave); - - if (bond_is_lb(bond)) - break; + bond_update_speed_duplex(slave); + if (bond->params.mode == BOND_MODE_8023AD) { if (old_speed != slave->speed) bond_3ad_adapter_speed_changed(slave); if (old_duplex != slave->duplex) diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 95de93b9038..d2ff52e63cb 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -157,8 +157,16 @@ static void bond_info_show_slave(struct seq_file *seq, seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name); seq_printf(seq, "MII Status: %s\n", (slave->link == BOND_LINK_UP) ? "up" : "down"); - seq_printf(seq, "Speed: %d Mbps\n", slave->speed); - seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half"); + if (slave->speed == -1) + seq_printf(seq, "Speed: %s\n", "Unknown"); + else + seq_printf(seq, "Speed: %d Mbps\n", slave->speed); + + if (slave->duplex == -1) + seq_printf(seq, "Duplex: %s\n", "Unknown"); + else + seq_printf(seq, "Duplex: %s\n", slave->duplex ? "full" : "half"); + seq_printf(seq, "Link Failure Count: %u\n", slave->link_failure_count); -- cgit v1.2.3-18-g5258 From 1b6b7172d0ee37528762bddb83acb56cb38ffac2 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Tue, 25 Oct 2011 12:32:24 +0000 Subject: net/ethernet: sc92031 is not Realtek While the SC92031 could be found on fake "Realtek" NICs, it has no relationship to Realtek, and is actually from Silan. Create a new subdirectory for silan and move sc92031 there. Signed-off-by: Cesar Eduardo Barros Signed-off-by: David S. Miller --- drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/realtek/Kconfig | 12 - drivers/net/ethernet/realtek/Makefile | 1 - drivers/net/ethernet/realtek/sc92031.c | 1609 -------------------------------- drivers/net/ethernet/silan/Kconfig | 33 + drivers/net/ethernet/silan/Makefile | 5 + drivers/net/ethernet/silan/sc92031.c | 1609 ++++++++++++++++++++++++++++++++ 8 files changed, 1649 insertions(+), 1622 deletions(-) delete mode 100644 drivers/net/ethernet/realtek/sc92031.c create mode 100644 drivers/net/ethernet/silan/Kconfig create mode 100644 drivers/net/ethernet/silan/Makefile create mode 100644 drivers/net/ethernet/silan/sc92031.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 6dff5a0e733..597f4d45c63 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -159,6 +159,7 @@ config S6GMAC will be called s6gmac. source "drivers/net/ethernet/seeq/Kconfig" +source "drivers/net/ethernet/silan/Kconfig" source "drivers/net/ethernet/sis/Kconfig" source "drivers/net/ethernet/sfc/Kconfig" source "drivers/net/ethernet/sgi/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index c53ad3afc99..be5dde04026 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SH_ETH) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ +obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ obj-$(CONFIG_NET_VENDOR_SIS) += sis/ obj-$(CONFIG_SFC) += sfc/ obj-$(CONFIG_NET_VENDOR_SGI) += sgi/ diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig index 84083ec6e61..0578859a3c7 100644 --- a/drivers/net/ethernet/realtek/Kconfig +++ b/drivers/net/ethernet/realtek/Kconfig @@ -115,16 +115,4 @@ config R8169 To compile this driver as a module, choose M here: the module will be called r8169. This is recommended. -config SC92031 - tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)" - depends on PCI && EXPERIMENTAL - select CRC32 - ---help--- - This is a driver for the Fast Ethernet PCI network cards based on - the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you - have one of these, say Y here. - - To compile this driver as a module, choose M here: the module - will be called sc92031. This is recommended. - endif # NET_VENDOR_REALTEK diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile index e48cfb6ac42..71b1da30ecb 100644 --- a/drivers/net/ethernet/realtek/Makefile +++ b/drivers/net/ethernet/realtek/Makefile @@ -6,4 +6,3 @@ obj-$(CONFIG_8139CP) += 8139cp.o obj-$(CONFIG_8139TOO) += 8139too.o obj-$(CONFIG_ATP) += atp.o obj-$(CONFIG_R8169) += r8169.o -obj-$(CONFIG_SC92031) += sc92031.o diff --git a/drivers/net/ethernet/realtek/sc92031.c b/drivers/net/ethernet/realtek/sc92031.c deleted file mode 100644 index a284d644053..00000000000 --- a/drivers/net/ethernet/realtek/sc92031.c +++ /dev/null @@ -1,1609 +0,0 @@ -/* Silan SC92031 PCI Fast Ethernet Adapter driver - * - * Based on vendor drivers: - * Silan Fast Ethernet Netcard Driver: - * MODULE_AUTHOR ("gaoyonghong"); - * MODULE_DESCRIPTION ("SILAN Fast Ethernet driver"); - * MODULE_LICENSE("GPL"); - * 8139D Fast Ethernet driver: - * (C) 2002 by gaoyonghong - * MODULE_AUTHOR ("gaoyonghong"); - * MODULE_DESCRIPTION ("Rsltek 8139D PCI Fast Ethernet Adapter driver"); - * MODULE_LICENSE("GPL"); - * Both are almost identical and seem to be based on pci-skeleton.c - * - * Rewritten for 2.6 by Cesar Eduardo Barros - * - * A datasheet for this chip can be found at - * http://www.silan.com.cn/english/product/pdf/SC92031AY.pdf - */ - -/* Note about set_mac_address: I don't know how to change the hardware - * matching, so you need to enable IFF_PROMISC when using it. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define SC92031_NAME "sc92031" - -/* BAR 0 is MMIO, BAR 1 is PIO */ -#ifndef SC92031_USE_BAR -#define SC92031_USE_BAR 0 -#endif - -/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ -static int multicast_filter_limit = 64; -module_param(multicast_filter_limit, int, 0); -MODULE_PARM_DESC(multicast_filter_limit, - "Maximum number of filtered multicast addresses"); - -static int media; -module_param(media, int, 0); -MODULE_PARM_DESC(media, "Media type (0x00 = autodetect," - " 0x01 = 10M half, 0x02 = 10M full," - " 0x04 = 100M half, 0x08 = 100M full)"); - -/* Size of the in-memory receive ring. */ -#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K ,4==128K*/ -#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) - -/* Number of Tx descriptor registers. */ -#define NUM_TX_DESC 4 - -/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - -/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ -#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE -#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) - -/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ -#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4*HZ) - -#define SILAN_STATS_NUM 2 /* number of ETHTOOL_GSTATS */ - -/* media options */ -#define AUTOSELECT 0x00 -#define M10_HALF 0x01 -#define M10_FULL 0x02 -#define M100_HALF 0x04 -#define M100_FULL 0x08 - - /* Symbolic offsets to registers. */ -enum silan_registers { - Config0 = 0x00, // Config0 - Config1 = 0x04, // Config1 - RxBufWPtr = 0x08, // Rx buffer writer poiter - IntrStatus = 0x0C, // Interrupt status - IntrMask = 0x10, // Interrupt mask - RxbufAddr = 0x14, // Rx buffer start address - RxBufRPtr = 0x18, // Rx buffer read pointer - Txstatusall = 0x1C, // Transmit status of all descriptors - TxStatus0 = 0x20, // Transmit status (Four 32bit registers). - TxAddr0 = 0x30, // Tx descriptors (also four 32bit). - RxConfig = 0x40, // Rx configuration - MAC0 = 0x44, // Ethernet hardware address. - MAR0 = 0x4C, // Multicast filter. - RxStatus0 = 0x54, // Rx status - TxConfig = 0x5C, // Tx configuration - PhyCtrl = 0x60, // physical control - FlowCtrlConfig = 0x64, // flow control - Miicmd0 = 0x68, // Mii command0 register - Miicmd1 = 0x6C, // Mii command1 register - Miistatus = 0x70, // Mii status register - Timercnt = 0x74, // Timer counter register - TimerIntr = 0x78, // Timer interrupt register - PMConfig = 0x7C, // Power Manager configuration - CRC0 = 0x80, // Power Manager CRC ( Two 32bit regisers) - Wakeup0 = 0x88, // power Manager wakeup( Eight 64bit regiser) - LSBCRC0 = 0xC8, // power Manager LSBCRC(Two 32bit regiser) - TestD0 = 0xD0, - TestD4 = 0xD4, - TestD8 = 0xD8, -}; - -#define MII_JAB 16 -#define MII_OutputStatus 24 - -#define PHY_16_JAB_ENB 0x1000 -#define PHY_16_PORT_ENB 0x1 - -enum IntrStatusBits { - LinkFail = 0x80000000, - LinkOK = 0x40000000, - TimeOut = 0x20000000, - RxOverflow = 0x0040, - RxOK = 0x0020, - TxOK = 0x0001, - IntrBits = LinkFail|LinkOK|TimeOut|RxOverflow|RxOK|TxOK, -}; - -enum TxStatusBits { - TxCarrierLost = 0x20000000, - TxAborted = 0x10000000, - TxOutOfWindow = 0x08000000, - TxNccShift = 22, - EarlyTxThresShift = 16, - TxStatOK = 0x8000, - TxUnderrun = 0x4000, - TxOwn = 0x2000, -}; - -enum RxStatusBits { - RxStatesOK = 0x80000, - RxBadAlign = 0x40000, - RxHugeFrame = 0x20000, - RxSmallFrame = 0x10000, - RxCRCOK = 0x8000, - RxCrlFrame = 0x4000, - Rx_Broadcast = 0x2000, - Rx_Multicast = 0x1000, - RxAddrMatch = 0x0800, - MiiErr = 0x0400, -}; - -enum RxConfigBits { - RxFullDx = 0x80000000, - RxEnb = 0x40000000, - RxSmall = 0x20000000, - RxHuge = 0x10000000, - RxErr = 0x08000000, - RxAllphys = 0x04000000, - RxMulticast = 0x02000000, - RxBroadcast = 0x01000000, - RxLoopBack = (1 << 23) | (1 << 22), - LowThresholdShift = 12, - HighThresholdShift = 2, -}; - -enum TxConfigBits { - TxFullDx = 0x80000000, - TxEnb = 0x40000000, - TxEnbPad = 0x20000000, - TxEnbHuge = 0x10000000, - TxEnbFCS = 0x08000000, - TxNoBackOff = 0x04000000, - TxEnbPrem = 0x02000000, - TxCareLostCrs = 0x1000000, - TxExdCollNum = 0xf00000, - TxDataRate = 0x80000, -}; - -enum PhyCtrlconfigbits { - PhyCtrlAne = 0x80000000, - PhyCtrlSpd100 = 0x40000000, - PhyCtrlSpd10 = 0x20000000, - PhyCtrlPhyBaseAddr = 0x1f000000, - PhyCtrlDux = 0x800000, - PhyCtrlReset = 0x400000, -}; - -enum FlowCtrlConfigBits { - FlowCtrlFullDX = 0x80000000, - FlowCtrlEnb = 0x40000000, -}; - -enum Config0Bits { - Cfg0_Reset = 0x80000000, - Cfg0_Anaoff = 0x40000000, - Cfg0_LDPS = 0x20000000, -}; - -enum Config1Bits { - Cfg1_EarlyRx = 1 << 31, - Cfg1_EarlyTx = 1 << 30, - - //rx buffer size - Cfg1_Rcv8K = 0x0, - Cfg1_Rcv16K = 0x1, - Cfg1_Rcv32K = 0x3, - Cfg1_Rcv64K = 0x7, - Cfg1_Rcv128K = 0xf, -}; - -enum MiiCmd0Bits { - Mii_Divider = 0x20000000, - Mii_WRITE = 0x400000, - Mii_READ = 0x200000, - Mii_SCAN = 0x100000, - Mii_Tamod = 0x80000, - Mii_Drvmod = 0x40000, - Mii_mdc = 0x20000, - Mii_mdoen = 0x10000, - Mii_mdo = 0x8000, - Mii_mdi = 0x4000, -}; - -enum MiiStatusBits { - Mii_StatusBusy = 0x80000000, -}; - -enum PMConfigBits { - PM_Enable = 1 << 31, - PM_LongWF = 1 << 30, - PM_Magic = 1 << 29, - PM_LANWake = 1 << 28, - PM_LWPTN = (1 << 27 | 1<< 26), - PM_LinkUp = 1 << 25, - PM_WakeUp = 1 << 24, -}; - -/* Locking rules: - * priv->lock protects most of the fields of priv and most of the - * hardware registers. It does not have to protect against softirqs - * between sc92031_disable_interrupts and sc92031_enable_interrupts; - * it also does not need to be used in ->open and ->stop while the - * device interrupts are off. - * Not having to protect against softirqs is very useful due to heavy - * use of mdelay() at _sc92031_reset. - * Functions prefixed with _sc92031_ must be called with the lock held; - * functions prefixed with sc92031_ must be called without the lock held. - * Use mmiowb() before unlocking if the hardware was written to. - */ - -/* Locking rules for the interrupt: - * - the interrupt and the tasklet never run at the same time - * - neither run between sc92031_disable_interrupts and - * sc92031_enable_interrupt - */ - -struct sc92031_priv { - spinlock_t lock; - /* iomap.h cookie */ - void __iomem *port_base; - /* pci device structure */ - struct pci_dev *pdev; - /* tasklet */ - struct tasklet_struct tasklet; - - /* CPU address of rx ring */ - void *rx_ring; - /* PCI address of rx ring */ - dma_addr_t rx_ring_dma_addr; - /* PCI address of rx ring read pointer */ - dma_addr_t rx_ring_tail; - - /* tx ring write index */ - unsigned tx_head; - /* tx ring read index */ - unsigned tx_tail; - /* CPU address of tx bounce buffer */ - void *tx_bufs; - /* PCI address of tx bounce buffer */ - dma_addr_t tx_bufs_dma_addr; - - /* copies of some hardware registers */ - u32 intr_status; - atomic_t intr_mask; - u32 rx_config; - u32 tx_config; - u32 pm_config; - - /* copy of some flags from dev->flags */ - unsigned int mc_flags; - - /* for ETHTOOL_GSTATS */ - u64 tx_timeouts; - u64 rx_loss; - - /* for dev->get_stats */ - long rx_value; -}; - -/* I don't know which registers can be safely read; however, I can guess - * MAC0 is one of them. */ -static inline void _sc92031_dummy_read(void __iomem *port_base) -{ - ioread32(port_base + MAC0); -} - -static u32 _sc92031_mii_wait(void __iomem *port_base) -{ - u32 mii_status; - - do { - udelay(10); - mii_status = ioread32(port_base + Miistatus); - } while (mii_status & Mii_StatusBusy); - - return mii_status; -} - -static u32 _sc92031_mii_cmd(void __iomem *port_base, u32 cmd0, u32 cmd1) -{ - iowrite32(Mii_Divider, port_base + Miicmd0); - - _sc92031_mii_wait(port_base); - - iowrite32(cmd1, port_base + Miicmd1); - iowrite32(Mii_Divider | cmd0, port_base + Miicmd0); - - return _sc92031_mii_wait(port_base); -} - -static void _sc92031_mii_scan(void __iomem *port_base) -{ - _sc92031_mii_cmd(port_base, Mii_SCAN, 0x1 << 6); -} - -static u16 _sc92031_mii_read(void __iomem *port_base, unsigned reg) -{ - return _sc92031_mii_cmd(port_base, Mii_READ, reg << 6) >> 13; -} - -static void _sc92031_mii_write(void __iomem *port_base, unsigned reg, u16 val) -{ - _sc92031_mii_cmd(port_base, Mii_WRITE, (reg << 6) | ((u32)val << 11)); -} - -static void sc92031_disable_interrupts(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - /* tell the tasklet/interrupt not to enable interrupts */ - atomic_set(&priv->intr_mask, 0); - wmb(); - - /* stop interrupts */ - iowrite32(0, port_base + IntrMask); - _sc92031_dummy_read(port_base); - mmiowb(); - - /* wait for any concurrent interrupt/tasklet to finish */ - synchronize_irq(dev->irq); - tasklet_disable(&priv->tasklet); -} - -static void sc92031_enable_interrupts(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - tasklet_enable(&priv->tasklet); - - atomic_set(&priv->intr_mask, IntrBits); - wmb(); - - iowrite32(IntrBits, port_base + IntrMask); - mmiowb(); -} - -static void _sc92031_disable_tx_rx(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - priv->rx_config &= ~RxEnb; - priv->tx_config &= ~TxEnb; - iowrite32(priv->rx_config, port_base + RxConfig); - iowrite32(priv->tx_config, port_base + TxConfig); -} - -static void _sc92031_enable_tx_rx(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - priv->rx_config |= RxEnb; - priv->tx_config |= TxEnb; - iowrite32(priv->rx_config, port_base + RxConfig); - iowrite32(priv->tx_config, port_base + TxConfig); -} - -static void _sc92031_tx_clear(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - - while (priv->tx_head - priv->tx_tail > 0) { - priv->tx_tail++; - dev->stats.tx_dropped++; - } - priv->tx_head = priv->tx_tail = 0; -} - -static void _sc92031_set_mar(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 mar0 = 0, mar1 = 0; - - if ((dev->flags & IFF_PROMISC) || - netdev_mc_count(dev) > multicast_filter_limit || - (dev->flags & IFF_ALLMULTI)) - mar0 = mar1 = 0xffffffff; - else if (dev->flags & IFF_MULTICAST) { - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) { - u32 crc; - unsigned bit = 0; - - crc = ~ether_crc(ETH_ALEN, ha->addr); - crc >>= 24; - - if (crc & 0x01) bit |= 0x02; - if (crc & 0x02) bit |= 0x01; - if (crc & 0x10) bit |= 0x20; - if (crc & 0x20) bit |= 0x10; - if (crc & 0x40) bit |= 0x08; - if (crc & 0x80) bit |= 0x04; - - if (bit > 31) - mar0 |= 0x1 << (bit - 32); - else - mar1 |= 0x1 << bit; - } - } - - iowrite32(mar0, port_base + MAR0); - iowrite32(mar1, port_base + MAR0 + 4); -} - -static void _sc92031_set_rx_config(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - unsigned int old_mc_flags; - u32 rx_config_bits = 0; - - old_mc_flags = priv->mc_flags; - - if (dev->flags & IFF_PROMISC) - rx_config_bits |= RxSmall | RxHuge | RxErr | RxBroadcast - | RxMulticast | RxAllphys; - - if (dev->flags & (IFF_ALLMULTI | IFF_MULTICAST)) - rx_config_bits |= RxMulticast; - - if (dev->flags & IFF_BROADCAST) - rx_config_bits |= RxBroadcast; - - priv->rx_config &= ~(RxSmall | RxHuge | RxErr | RxBroadcast - | RxMulticast | RxAllphys); - priv->rx_config |= rx_config_bits; - - priv->mc_flags = dev->flags & (IFF_PROMISC | IFF_ALLMULTI - | IFF_MULTICAST | IFF_BROADCAST); - - if (netif_carrier_ok(dev) && priv->mc_flags != old_mc_flags) - iowrite32(priv->rx_config, port_base + RxConfig); -} - -static bool _sc92031_check_media(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u16 bmsr; - - bmsr = _sc92031_mii_read(port_base, MII_BMSR); - rmb(); - if (bmsr & BMSR_LSTATUS) { - bool speed_100, duplex_full; - u32 flow_ctrl_config = 0; - u16 output_status = _sc92031_mii_read(port_base, - MII_OutputStatus); - _sc92031_mii_scan(port_base); - - speed_100 = output_status & 0x2; - duplex_full = output_status & 0x4; - - /* Initial Tx/Rx configuration */ - priv->rx_config = (0x40 << LowThresholdShift) | (0x1c0 << HighThresholdShift); - priv->tx_config = 0x48800000; - - /* NOTE: vendor driver had dead code here to enable tx padding */ - - if (!speed_100) - priv->tx_config |= 0x80000; - - // configure rx mode - _sc92031_set_rx_config(dev); - - if (duplex_full) { - priv->rx_config |= RxFullDx; - priv->tx_config |= TxFullDx; - flow_ctrl_config = FlowCtrlFullDX | FlowCtrlEnb; - } else { - priv->rx_config &= ~RxFullDx; - priv->tx_config &= ~TxFullDx; - } - - _sc92031_set_mar(dev); - _sc92031_set_rx_config(dev); - _sc92031_enable_tx_rx(dev); - iowrite32(flow_ctrl_config, port_base + FlowCtrlConfig); - - netif_carrier_on(dev); - - if (printk_ratelimit()) - printk(KERN_INFO "%s: link up, %sMbps, %s-duplex\n", - dev->name, - speed_100 ? "100" : "10", - duplex_full ? "full" : "half"); - return true; - } else { - _sc92031_mii_scan(port_base); - - netif_carrier_off(dev); - - _sc92031_disable_tx_rx(dev); - - if (printk_ratelimit()) - printk(KERN_INFO "%s: link down\n", dev->name); - return false; - } -} - -static void _sc92031_phy_reset(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 phy_ctrl; - - phy_ctrl = ioread32(port_base + PhyCtrl); - phy_ctrl &= ~(PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10); - phy_ctrl |= PhyCtrlAne | PhyCtrlReset; - - switch (media) { - default: - case AUTOSELECT: - phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; - break; - case M10_HALF: - phy_ctrl |= PhyCtrlSpd10; - break; - case M10_FULL: - phy_ctrl |= PhyCtrlDux | PhyCtrlSpd10; - break; - case M100_HALF: - phy_ctrl |= PhyCtrlSpd100; - break; - case M100_FULL: - phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; - break; - } - - iowrite32(phy_ctrl, port_base + PhyCtrl); - mdelay(10); - - phy_ctrl &= ~PhyCtrlReset; - iowrite32(phy_ctrl, port_base + PhyCtrl); - mdelay(1); - - _sc92031_mii_write(port_base, MII_JAB, - PHY_16_JAB_ENB | PHY_16_PORT_ENB); - _sc92031_mii_scan(port_base); - - netif_carrier_off(dev); - netif_stop_queue(dev); -} - -static void _sc92031_reset(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - /* disable PM */ - iowrite32(0, port_base + PMConfig); - - /* soft reset the chip */ - iowrite32(Cfg0_Reset, port_base + Config0); - mdelay(200); - - iowrite32(0, port_base + Config0); - mdelay(10); - - /* disable interrupts */ - iowrite32(0, port_base + IntrMask); - - /* clear multicast address */ - iowrite32(0, port_base + MAR0); - iowrite32(0, port_base + MAR0 + 4); - - /* init rx ring */ - iowrite32(priv->rx_ring_dma_addr, port_base + RxbufAddr); - priv->rx_ring_tail = priv->rx_ring_dma_addr; - - /* init tx ring */ - _sc92031_tx_clear(dev); - - /* clear old register values */ - priv->intr_status = 0; - atomic_set(&priv->intr_mask, 0); - priv->rx_config = 0; - priv->tx_config = 0; - priv->mc_flags = 0; - - /* configure rx buffer size */ - /* NOTE: vendor driver had dead code here to enable early tx/rx */ - iowrite32(Cfg1_Rcv64K, port_base + Config1); - - _sc92031_phy_reset(dev); - _sc92031_check_media(dev); - - /* calculate rx fifo overflow */ - priv->rx_value = 0; - - /* enable PM */ - iowrite32(priv->pm_config, port_base + PMConfig); - - /* clear intr register */ - ioread32(port_base + IntrStatus); -} - -static void _sc92031_tx_tasklet(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - unsigned old_tx_tail; - unsigned entry; - u32 tx_status; - - old_tx_tail = priv->tx_tail; - while (priv->tx_head - priv->tx_tail > 0) { - entry = priv->tx_tail % NUM_TX_DESC; - tx_status = ioread32(port_base + TxStatus0 + entry * 4); - - if (!(tx_status & (TxStatOK | TxUnderrun | TxAborted))) - break; - - priv->tx_tail++; - - if (tx_status & TxStatOK) { - dev->stats.tx_bytes += tx_status & 0x1fff; - dev->stats.tx_packets++; - /* Note: TxCarrierLost is always asserted at 100mbps. */ - dev->stats.collisions += (tx_status >> 22) & 0xf; - } - - if (tx_status & (TxOutOfWindow | TxAborted)) { - dev->stats.tx_errors++; - - if (tx_status & TxAborted) - dev->stats.tx_aborted_errors++; - - if (tx_status & TxCarrierLost) - dev->stats.tx_carrier_errors++; - - if (tx_status & TxOutOfWindow) - dev->stats.tx_window_errors++; - } - - if (tx_status & TxUnderrun) - dev->stats.tx_fifo_errors++; - } - - if (priv->tx_tail != old_tx_tail) - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -static void _sc92031_rx_tasklet_error(struct net_device *dev, - u32 rx_status, unsigned rx_size) -{ - if(rx_size > (MAX_ETH_FRAME_SIZE + 4) || rx_size < 16) { - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - } - - if (!(rx_status & RxStatesOK)) { - dev->stats.rx_errors++; - - if (rx_status & (RxHugeFrame | RxSmallFrame)) - dev->stats.rx_length_errors++; - - if (rx_status & RxBadAlign) - dev->stats.rx_frame_errors++; - - if (!(rx_status & RxCRCOK)) - dev->stats.rx_crc_errors++; - } else { - struct sc92031_priv *priv = netdev_priv(dev); - priv->rx_loss++; - } -} - -static void _sc92031_rx_tasklet(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - dma_addr_t rx_ring_head; - unsigned rx_len; - unsigned rx_ring_offset; - void *rx_ring = priv->rx_ring; - - rx_ring_head = ioread32(port_base + RxBufWPtr); - rmb(); - - /* rx_ring_head is only 17 bits in the RxBufWPtr register. - * we need to change it to 32 bits physical address - */ - rx_ring_head &= (dma_addr_t)(RX_BUF_LEN - 1); - rx_ring_head |= priv->rx_ring_dma_addr & ~(dma_addr_t)(RX_BUF_LEN - 1); - if (rx_ring_head < priv->rx_ring_dma_addr) - rx_ring_head += RX_BUF_LEN; - - if (rx_ring_head >= priv->rx_ring_tail) - rx_len = rx_ring_head - priv->rx_ring_tail; - else - rx_len = RX_BUF_LEN - (priv->rx_ring_tail - rx_ring_head); - - if (!rx_len) - return; - - if (unlikely(rx_len > RX_BUF_LEN)) { - if (printk_ratelimit()) - printk(KERN_ERR "%s: rx packets length > rx buffer\n", - dev->name); - return; - } - - rx_ring_offset = (priv->rx_ring_tail - priv->rx_ring_dma_addr) % RX_BUF_LEN; - - while (rx_len) { - u32 rx_status; - unsigned rx_size, rx_size_align, pkt_size; - struct sk_buff *skb; - - rx_status = le32_to_cpup((__le32 *)(rx_ring + rx_ring_offset)); - rmb(); - - rx_size = rx_status >> 20; - rx_size_align = (rx_size + 3) & ~3; // for 4 bytes aligned - pkt_size = rx_size - 4; // Omit the four octet CRC from the length. - - rx_ring_offset = (rx_ring_offset + 4) % RX_BUF_LEN; - - if (unlikely(rx_status == 0 || - rx_size > (MAX_ETH_FRAME_SIZE + 4) || - rx_size < 16 || - !(rx_status & RxStatesOK))) { - _sc92031_rx_tasklet_error(dev, rx_status, rx_size); - break; - } - - if (unlikely(rx_size_align + 4 > rx_len)) { - if (printk_ratelimit()) - printk(KERN_ERR "%s: rx_len is too small\n", dev->name); - break; - } - - rx_len -= rx_size_align + 4; - - skb = netdev_alloc_skb_ip_align(dev, pkt_size); - if (unlikely(!skb)) { - if (printk_ratelimit()) - printk(KERN_ERR "%s: Couldn't allocate a skb_buff for a packet of size %u\n", - dev->name, pkt_size); - goto next; - } - - if ((rx_ring_offset + pkt_size) > RX_BUF_LEN) { - memcpy(skb_put(skb, RX_BUF_LEN - rx_ring_offset), - rx_ring + rx_ring_offset, RX_BUF_LEN - rx_ring_offset); - memcpy(skb_put(skb, pkt_size - (RX_BUF_LEN - rx_ring_offset)), - rx_ring, pkt_size - (RX_BUF_LEN - rx_ring_offset)); - } else { - memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size); - } - - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - - dev->stats.rx_bytes += pkt_size; - dev->stats.rx_packets++; - - if (rx_status & Rx_Multicast) - dev->stats.multicast++; - - next: - rx_ring_offset = (rx_ring_offset + rx_size_align) % RX_BUF_LEN; - } - mb(); - - priv->rx_ring_tail = rx_ring_head; - iowrite32(priv->rx_ring_tail, port_base + RxBufRPtr); -} - -static void _sc92031_link_tasklet(struct net_device *dev) -{ - if (_sc92031_check_media(dev)) - netif_wake_queue(dev); - else { - netif_stop_queue(dev); - dev->stats.tx_carrier_errors++; - } -} - -static void sc92031_tasklet(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 intr_status, intr_mask; - - intr_status = priv->intr_status; - - spin_lock(&priv->lock); - - if (unlikely(!netif_running(dev))) - goto out; - - if (intr_status & TxOK) - _sc92031_tx_tasklet(dev); - - if (intr_status & RxOK) - _sc92031_rx_tasklet(dev); - - if (intr_status & RxOverflow) - dev->stats.rx_errors++; - - if (intr_status & TimeOut) { - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - } - - if (intr_status & (LinkFail | LinkOK)) - _sc92031_link_tasklet(dev); - -out: - intr_mask = atomic_read(&priv->intr_mask); - rmb(); - - iowrite32(intr_mask, port_base + IntrMask); - mmiowb(); - - spin_unlock(&priv->lock); -} - -static irqreturn_t sc92031_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 intr_status, intr_mask; - - /* mask interrupts before clearing IntrStatus */ - iowrite32(0, port_base + IntrMask); - _sc92031_dummy_read(port_base); - - intr_status = ioread32(port_base + IntrStatus); - if (unlikely(intr_status == 0xffffffff)) - return IRQ_NONE; // hardware has gone missing - - intr_status &= IntrBits; - if (!intr_status) - goto out_none; - - priv->intr_status = intr_status; - tasklet_schedule(&priv->tasklet); - - return IRQ_HANDLED; - -out_none: - intr_mask = atomic_read(&priv->intr_mask); - rmb(); - - iowrite32(intr_mask, port_base + IntrMask); - mmiowb(); - - return IRQ_NONE; -} - -static struct net_device_stats *sc92031_get_stats(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - - // FIXME I do not understand what is this trying to do. - if (netif_running(dev)) { - int temp; - - spin_lock_bh(&priv->lock); - - /* Update the error count. */ - temp = (ioread32(port_base + RxStatus0) >> 16) & 0xffff; - - if (temp == 0xffff) { - priv->rx_value += temp; - dev->stats.rx_fifo_errors = priv->rx_value; - } else - dev->stats.rx_fifo_errors = temp + priv->rx_value; - - spin_unlock_bh(&priv->lock); - } - - return &dev->stats; -} - -static netdev_tx_t sc92031_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - unsigned len; - unsigned entry; - u32 tx_status; - - if (unlikely(skb->len > TX_BUF_SIZE)) { - dev->stats.tx_dropped++; - goto out; - } - - spin_lock(&priv->lock); - - if (unlikely(!netif_carrier_ok(dev))) { - dev->stats.tx_dropped++; - goto out_unlock; - } - - BUG_ON(priv->tx_head - priv->tx_tail >= NUM_TX_DESC); - - entry = priv->tx_head++ % NUM_TX_DESC; - - skb_copy_and_csum_dev(skb, priv->tx_bufs + entry * TX_BUF_SIZE); - - len = skb->len; - if (len < ETH_ZLEN) { - memset(priv->tx_bufs + entry * TX_BUF_SIZE + len, - 0, ETH_ZLEN - len); - len = ETH_ZLEN; - } - - wmb(); - - if (len < 100) - tx_status = len; - else if (len < 300) - tx_status = 0x30000 | len; - else - tx_status = 0x50000 | len; - - iowrite32(priv->tx_bufs_dma_addr + entry * TX_BUF_SIZE, - port_base + TxAddr0 + entry * 4); - iowrite32(tx_status, port_base + TxStatus0 + entry * 4); - mmiowb(); - - if (priv->tx_head - priv->tx_tail >= NUM_TX_DESC) - netif_stop_queue(dev); - -out_unlock: - spin_unlock(&priv->lock); - -out: - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int sc92031_open(struct net_device *dev) -{ - int err; - struct sc92031_priv *priv = netdev_priv(dev); - struct pci_dev *pdev = priv->pdev; - - priv->rx_ring = pci_alloc_consistent(pdev, RX_BUF_LEN, - &priv->rx_ring_dma_addr); - if (unlikely(!priv->rx_ring)) { - err = -ENOMEM; - goto out_alloc_rx_ring; - } - - priv->tx_bufs = pci_alloc_consistent(pdev, TX_BUF_TOT_LEN, - &priv->tx_bufs_dma_addr); - if (unlikely(!priv->tx_bufs)) { - err = -ENOMEM; - goto out_alloc_tx_bufs; - } - priv->tx_head = priv->tx_tail = 0; - - err = request_irq(pdev->irq, sc92031_interrupt, - IRQF_SHARED, dev->name, dev); - if (unlikely(err < 0)) - goto out_request_irq; - - priv->pm_config = 0; - - /* Interrupts already disabled by sc92031_stop or sc92031_probe */ - spin_lock_bh(&priv->lock); - - _sc92031_reset(dev); - mmiowb(); - - spin_unlock_bh(&priv->lock); - sc92031_enable_interrupts(dev); - - if (netif_carrier_ok(dev)) - netif_start_queue(dev); - else - netif_tx_disable(dev); - - return 0; - -out_request_irq: - pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, - priv->tx_bufs_dma_addr); -out_alloc_tx_bufs: - pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, - priv->rx_ring_dma_addr); -out_alloc_rx_ring: - return err; -} - -static int sc92031_stop(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - struct pci_dev *pdev = priv->pdev; - - netif_tx_disable(dev); - - /* Disable interrupts, stop Tx and Rx. */ - sc92031_disable_interrupts(dev); - - spin_lock_bh(&priv->lock); - - _sc92031_disable_tx_rx(dev); - _sc92031_tx_clear(dev); - mmiowb(); - - spin_unlock_bh(&priv->lock); - - free_irq(pdev->irq, dev); - pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, - priv->tx_bufs_dma_addr); - pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, - priv->rx_ring_dma_addr); - - return 0; -} - -static void sc92031_set_multicast_list(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - - spin_lock_bh(&priv->lock); - - _sc92031_set_mar(dev); - _sc92031_set_rx_config(dev); - mmiowb(); - - spin_unlock_bh(&priv->lock); -} - -static void sc92031_tx_timeout(struct net_device *dev) -{ - struct sc92031_priv *priv = netdev_priv(dev); - - /* Disable interrupts by clearing the interrupt mask.*/ - sc92031_disable_interrupts(dev); - - spin_lock(&priv->lock); - - priv->tx_timeouts++; - - _sc92031_reset(dev); - mmiowb(); - - spin_unlock(&priv->lock); - - /* enable interrupts */ - sc92031_enable_interrupts(dev); - - if (netif_carrier_ok(dev)) - netif_wake_queue(dev); -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void sc92031_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - if (sc92031_interrupt(dev->irq, dev) != IRQ_NONE) - sc92031_tasklet((unsigned long)dev); - enable_irq(dev->irq); -} -#endif - -static int sc92031_ethtool_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u8 phy_address; - u32 phy_ctrl; - u16 output_status; - - spin_lock_bh(&priv->lock); - - phy_address = ioread32(port_base + Miicmd1) >> 27; - phy_ctrl = ioread32(port_base + PhyCtrl); - - output_status = _sc92031_mii_read(port_base, MII_OutputStatus); - _sc92031_mii_scan(port_base); - mmiowb(); - - spin_unlock_bh(&priv->lock); - - cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full - | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII; - - cmd->advertising = ADVERTISED_TP | ADVERTISED_MII; - - if ((phy_ctrl & (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) - == (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) - cmd->advertising |= ADVERTISED_Autoneg; - - if ((phy_ctrl & PhyCtrlSpd10) == PhyCtrlSpd10) - cmd->advertising |= ADVERTISED_10baseT_Half; - - if ((phy_ctrl & (PhyCtrlSpd10 | PhyCtrlDux)) - == (PhyCtrlSpd10 | PhyCtrlDux)) - cmd->advertising |= ADVERTISED_10baseT_Full; - - if ((phy_ctrl & PhyCtrlSpd100) == PhyCtrlSpd100) - cmd->advertising |= ADVERTISED_100baseT_Half; - - if ((phy_ctrl & (PhyCtrlSpd100 | PhyCtrlDux)) - == (PhyCtrlSpd100 | PhyCtrlDux)) - cmd->advertising |= ADVERTISED_100baseT_Full; - - if (phy_ctrl & PhyCtrlAne) - cmd->advertising |= ADVERTISED_Autoneg; - - ethtool_cmd_speed_set(cmd, - (output_status & 0x2) ? SPEED_100 : SPEED_10); - cmd->duplex = (output_status & 0x4) ? DUPLEX_FULL : DUPLEX_HALF; - cmd->port = PORT_MII; - cmd->phy_address = phy_address; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = (phy_ctrl & PhyCtrlAne) ? AUTONEG_ENABLE : AUTONEG_DISABLE; - - return 0; -} - -static int sc92031_ethtool_set_settings(struct net_device *dev, - struct ethtool_cmd *cmd) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 speed = ethtool_cmd_speed(cmd); - u32 phy_ctrl; - u32 old_phy_ctrl; - - if (!(speed == SPEED_10 || speed == SPEED_100)) - return -EINVAL; - if (!(cmd->duplex == DUPLEX_HALF || cmd->duplex == DUPLEX_FULL)) - return -EINVAL; - if (!(cmd->port == PORT_MII)) - return -EINVAL; - if (!(cmd->phy_address == 0x1f)) - return -EINVAL; - if (!(cmd->transceiver == XCVR_INTERNAL)) - return -EINVAL; - if (!(cmd->autoneg == AUTONEG_DISABLE || cmd->autoneg == AUTONEG_ENABLE)) - return -EINVAL; - - if (cmd->autoneg == AUTONEG_ENABLE) { - if (!(cmd->advertising & (ADVERTISED_Autoneg - | ADVERTISED_100baseT_Full - | ADVERTISED_100baseT_Half - | ADVERTISED_10baseT_Full - | ADVERTISED_10baseT_Half))) - return -EINVAL; - - phy_ctrl = PhyCtrlAne; - - // FIXME: I'm not sure what the original code was trying to do - if (cmd->advertising & ADVERTISED_Autoneg) - phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; - if (cmd->advertising & ADVERTISED_100baseT_Full) - phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; - if (cmd->advertising & ADVERTISED_100baseT_Half) - phy_ctrl |= PhyCtrlSpd100; - if (cmd->advertising & ADVERTISED_10baseT_Full) - phy_ctrl |= PhyCtrlSpd10 | PhyCtrlDux; - if (cmd->advertising & ADVERTISED_10baseT_Half) - phy_ctrl |= PhyCtrlSpd10; - } else { - // FIXME: Whole branch guessed - phy_ctrl = 0; - - if (speed == SPEED_10) - phy_ctrl |= PhyCtrlSpd10; - else /* cmd->speed == SPEED_100 */ - phy_ctrl |= PhyCtrlSpd100; - - if (cmd->duplex == DUPLEX_FULL) - phy_ctrl |= PhyCtrlDux; - } - - spin_lock_bh(&priv->lock); - - old_phy_ctrl = ioread32(port_base + PhyCtrl); - phy_ctrl |= old_phy_ctrl & ~(PhyCtrlAne | PhyCtrlDux - | PhyCtrlSpd100 | PhyCtrlSpd10); - if (phy_ctrl != old_phy_ctrl) - iowrite32(phy_ctrl, port_base + PhyCtrl); - - spin_unlock_bh(&priv->lock); - - return 0; -} - -static void sc92031_ethtool_get_wol(struct net_device *dev, - struct ethtool_wolinfo *wolinfo) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 pm_config; - - spin_lock_bh(&priv->lock); - pm_config = ioread32(port_base + PMConfig); - spin_unlock_bh(&priv->lock); - - // FIXME: Guessed - wolinfo->supported = WAKE_PHY | WAKE_MAGIC - | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; - wolinfo->wolopts = 0; - - if (pm_config & PM_LinkUp) - wolinfo->wolopts |= WAKE_PHY; - - if (pm_config & PM_Magic) - wolinfo->wolopts |= WAKE_MAGIC; - - if (pm_config & PM_WakeUp) - // FIXME: Guessed - wolinfo->wolopts |= WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; -} - -static int sc92031_ethtool_set_wol(struct net_device *dev, - struct ethtool_wolinfo *wolinfo) -{ - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u32 pm_config; - - spin_lock_bh(&priv->lock); - - pm_config = ioread32(port_base + PMConfig) - & ~(PM_LinkUp | PM_Magic | PM_WakeUp); - - if (wolinfo->wolopts & WAKE_PHY) - pm_config |= PM_LinkUp; - - if (wolinfo->wolopts & WAKE_MAGIC) - pm_config |= PM_Magic; - - // FIXME: Guessed - if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) - pm_config |= PM_WakeUp; - - priv->pm_config = pm_config; - iowrite32(pm_config, port_base + PMConfig); - mmiowb(); - - spin_unlock_bh(&priv->lock); - - return 0; -} - -static int sc92031_ethtool_nway_reset(struct net_device *dev) -{ - int err = 0; - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem *port_base = priv->port_base; - u16 bmcr; - - spin_lock_bh(&priv->lock); - - bmcr = _sc92031_mii_read(port_base, MII_BMCR); - if (!(bmcr & BMCR_ANENABLE)) { - err = -EINVAL; - goto out; - } - - _sc92031_mii_write(port_base, MII_BMCR, bmcr | BMCR_ANRESTART); - -out: - _sc92031_mii_scan(port_base); - mmiowb(); - - spin_unlock_bh(&priv->lock); - - return err; -} - -static const char sc92031_ethtool_stats_strings[SILAN_STATS_NUM][ETH_GSTRING_LEN] = { - "tx_timeout", - "rx_loss", -}; - -static void sc92031_ethtool_get_strings(struct net_device *dev, - u32 stringset, u8 *data) -{ - if (stringset == ETH_SS_STATS) - memcpy(data, sc92031_ethtool_stats_strings, - SILAN_STATS_NUM * ETH_GSTRING_LEN); -} - -static int sc92031_ethtool_get_sset_count(struct net_device *dev, int sset) -{ - switch (sset) { - case ETH_SS_STATS: - return SILAN_STATS_NUM; - default: - return -EOPNOTSUPP; - } -} - -static void sc92031_ethtool_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - struct sc92031_priv *priv = netdev_priv(dev); - - spin_lock_bh(&priv->lock); - data[0] = priv->tx_timeouts; - data[1] = priv->rx_loss; - spin_unlock_bh(&priv->lock); -} - -static const struct ethtool_ops sc92031_ethtool_ops = { - .get_settings = sc92031_ethtool_get_settings, - .set_settings = sc92031_ethtool_set_settings, - .get_wol = sc92031_ethtool_get_wol, - .set_wol = sc92031_ethtool_set_wol, - .nway_reset = sc92031_ethtool_nway_reset, - .get_link = ethtool_op_get_link, - .get_strings = sc92031_ethtool_get_strings, - .get_sset_count = sc92031_ethtool_get_sset_count, - .get_ethtool_stats = sc92031_ethtool_get_ethtool_stats, -}; - - -static const struct net_device_ops sc92031_netdev_ops = { - .ndo_get_stats = sc92031_get_stats, - .ndo_start_xmit = sc92031_start_xmit, - .ndo_open = sc92031_open, - .ndo_stop = sc92031_stop, - .ndo_set_rx_mode = sc92031_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_tx_timeout = sc92031_tx_timeout, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = sc92031_poll_controller, -#endif -}; - -static int __devinit sc92031_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - int err; - void __iomem* port_base; - struct net_device *dev; - struct sc92031_priv *priv; - u32 mac0, mac1; - unsigned long base_addr; - - err = pci_enable_device(pdev); - if (unlikely(err < 0)) - goto out_enable_device; - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (unlikely(err < 0)) - goto out_set_dma_mask; - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (unlikely(err < 0)) - goto out_set_dma_mask; - - err = pci_request_regions(pdev, SC92031_NAME); - if (unlikely(err < 0)) - goto out_request_regions; - - port_base = pci_iomap(pdev, SC92031_USE_BAR, 0); - if (unlikely(!port_base)) { - err = -EIO; - goto out_iomap; - } - - dev = alloc_etherdev(sizeof(struct sc92031_priv)); - if (unlikely(!dev)) { - err = -ENOMEM; - goto out_alloc_etherdev; - } - - pci_set_drvdata(pdev, dev); - SET_NETDEV_DEV(dev, &pdev->dev); - -#if SC92031_USE_BAR == 0 - dev->mem_start = pci_resource_start(pdev, SC92031_USE_BAR); - dev->mem_end = pci_resource_end(pdev, SC92031_USE_BAR); -#elif SC92031_USE_BAR == 1 - dev->base_addr = pci_resource_start(pdev, SC92031_USE_BAR); -#endif - dev->irq = pdev->irq; - - /* faked with skb_copy_and_csum_dev */ - dev->features = NETIF_F_SG | NETIF_F_HIGHDMA | - NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - - dev->netdev_ops = &sc92031_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - dev->ethtool_ops = &sc92031_ethtool_ops; - - priv = netdev_priv(dev); - spin_lock_init(&priv->lock); - priv->port_base = port_base; - priv->pdev = pdev; - tasklet_init(&priv->tasklet, sc92031_tasklet, (unsigned long)dev); - /* Fudge tasklet count so the call to sc92031_enable_interrupts at - * sc92031_open will work correctly */ - tasklet_disable_nosync(&priv->tasklet); - - /* PCI PM Wakeup */ - iowrite32((~PM_LongWF & ~PM_LWPTN) | PM_Enable, port_base + PMConfig); - - mac0 = ioread32(port_base + MAC0); - mac1 = ioread32(port_base + MAC0 + 4); - dev->dev_addr[0] = dev->perm_addr[0] = mac0 >> 24; - dev->dev_addr[1] = dev->perm_addr[1] = mac0 >> 16; - dev->dev_addr[2] = dev->perm_addr[2] = mac0 >> 8; - dev->dev_addr[3] = dev->perm_addr[3] = mac0; - dev->dev_addr[4] = dev->perm_addr[4] = mac1 >> 8; - dev->dev_addr[5] = dev->perm_addr[5] = mac1; - - err = register_netdev(dev); - if (err < 0) - goto out_register_netdev; - -#if SC92031_USE_BAR == 0 - base_addr = dev->mem_start; -#elif SC92031_USE_BAR == 1 - base_addr = dev->base_addr; -#endif - printk(KERN_INFO "%s: SC92031 at 0x%lx, %pM, IRQ %d\n", dev->name, - base_addr, dev->dev_addr, dev->irq); - - return 0; - -out_register_netdev: - free_netdev(dev); -out_alloc_etherdev: - pci_iounmap(pdev, port_base); -out_iomap: - pci_release_regions(pdev); -out_request_regions: -out_set_dma_mask: - pci_disable_device(pdev); -out_enable_device: - return err; -} - -static void __devexit sc92031_remove(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct sc92031_priv *priv = netdev_priv(dev); - void __iomem* port_base = priv->port_base; - - unregister_netdev(dev); - free_netdev(dev); - pci_iounmap(pdev, port_base); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static int sc92031_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct sc92031_priv *priv = netdev_priv(dev); - - pci_save_state(pdev); - - if (!netif_running(dev)) - goto out; - - netif_device_detach(dev); - - /* Disable interrupts, stop Tx and Rx. */ - sc92031_disable_interrupts(dev); - - spin_lock_bh(&priv->lock); - - _sc92031_disable_tx_rx(dev); - _sc92031_tx_clear(dev); - mmiowb(); - - spin_unlock_bh(&priv->lock); - -out: - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; -} - -static int sc92031_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct sc92031_priv *priv = netdev_priv(dev); - - pci_restore_state(pdev); - pci_set_power_state(pdev, PCI_D0); - - if (!netif_running(dev)) - goto out; - - /* Interrupts already disabled by sc92031_suspend */ - spin_lock_bh(&priv->lock); - - _sc92031_reset(dev); - mmiowb(); - - spin_unlock_bh(&priv->lock); - sc92031_enable_interrupts(dev); - - netif_device_attach(dev); - - if (netif_carrier_ok(dev)) - netif_wake_queue(dev); - else - netif_tx_disable(dev); - -out: - return 0; -} - -static DEFINE_PCI_DEVICE_TABLE(sc92031_pci_device_id_table) = { - { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) }, - { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) }, - { PCI_DEVICE(0x1088, 0x2031) }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); - -static struct pci_driver sc92031_pci_driver = { - .name = SC92031_NAME, - .id_table = sc92031_pci_device_id_table, - .probe = sc92031_probe, - .remove = __devexit_p(sc92031_remove), - .suspend = sc92031_suspend, - .resume = sc92031_resume, -}; - -static int __init sc92031_init(void) -{ - return pci_register_driver(&sc92031_pci_driver); -} - -static void __exit sc92031_exit(void) -{ - pci_unregister_driver(&sc92031_pci_driver); -} - -module_init(sc92031_init); -module_exit(sc92031_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Cesar Eduardo Barros "); -MODULE_DESCRIPTION("Silan SC92031 PCI Fast Ethernet Adapter driver"); diff --git a/drivers/net/ethernet/silan/Kconfig b/drivers/net/ethernet/silan/Kconfig new file mode 100644 index 00000000000..ae1ce170864 --- /dev/null +++ b/drivers/net/ethernet/silan/Kconfig @@ -0,0 +1,33 @@ +# +# Silan device configuration +# + +config NET_VENDOR_SILAN + bool "Silan devices" + default y + depends on PCI && EXPERIMENTAL + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Silan devices. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_SILAN + +config SC92031 + tristate "Silan SC92031 PCI Fast Ethernet Adapter driver (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + ---help--- + This is a driver for the Fast Ethernet PCI network cards based on + the Silan SC92031 chip (sometimes also called Rsltek 8139D). If you + have one of these, say Y here. + + To compile this driver as a module, choose M here: the module + will be called sc92031. This is recommended. + +endif # NET_VENDOR_SILAN diff --git a/drivers/net/ethernet/silan/Makefile b/drivers/net/ethernet/silan/Makefile new file mode 100644 index 00000000000..4ad3523dcb9 --- /dev/null +++ b/drivers/net/ethernet/silan/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Silan network device drivers. +# + +obj-$(CONFIG_SC92031) += sc92031.o diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c new file mode 100644 index 00000000000..a284d644053 --- /dev/null +++ b/drivers/net/ethernet/silan/sc92031.c @@ -0,0 +1,1609 @@ +/* Silan SC92031 PCI Fast Ethernet Adapter driver + * + * Based on vendor drivers: + * Silan Fast Ethernet Netcard Driver: + * MODULE_AUTHOR ("gaoyonghong"); + * MODULE_DESCRIPTION ("SILAN Fast Ethernet driver"); + * MODULE_LICENSE("GPL"); + * 8139D Fast Ethernet driver: + * (C) 2002 by gaoyonghong + * MODULE_AUTHOR ("gaoyonghong"); + * MODULE_DESCRIPTION ("Rsltek 8139D PCI Fast Ethernet Adapter driver"); + * MODULE_LICENSE("GPL"); + * Both are almost identical and seem to be based on pci-skeleton.c + * + * Rewritten for 2.6 by Cesar Eduardo Barros + * + * A datasheet for this chip can be found at + * http://www.silan.com.cn/english/product/pdf/SC92031AY.pdf + */ + +/* Note about set_mac_address: I don't know how to change the hardware + * matching, so you need to enable IFF_PROMISC when using it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SC92031_NAME "sc92031" + +/* BAR 0 is MMIO, BAR 1 is PIO */ +#ifndef SC92031_USE_BAR +#define SC92031_USE_BAR 0 +#endif + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ +static int multicast_filter_limit = 64; +module_param(multicast_filter_limit, int, 0); +MODULE_PARM_DESC(multicast_filter_limit, + "Maximum number of filtered multicast addresses"); + +static int media; +module_param(media, int, 0); +MODULE_PARM_DESC(media, "Media type (0x00 = autodetect," + " 0x01 = 10M half, 0x02 = 10M full," + " 0x04 = 100M half, 0x08 = 100M full)"); + +/* Size of the in-memory receive ring. */ +#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K ,4==128K*/ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+14+4).*/ +#define MAX_ETH_FRAME_SIZE 1536 + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) + +#define SILAN_STATS_NUM 2 /* number of ETHTOOL_GSTATS */ + +/* media options */ +#define AUTOSELECT 0x00 +#define M10_HALF 0x01 +#define M10_FULL 0x02 +#define M100_HALF 0x04 +#define M100_FULL 0x08 + + /* Symbolic offsets to registers. */ +enum silan_registers { + Config0 = 0x00, // Config0 + Config1 = 0x04, // Config1 + RxBufWPtr = 0x08, // Rx buffer writer poiter + IntrStatus = 0x0C, // Interrupt status + IntrMask = 0x10, // Interrupt mask + RxbufAddr = 0x14, // Rx buffer start address + RxBufRPtr = 0x18, // Rx buffer read pointer + Txstatusall = 0x1C, // Transmit status of all descriptors + TxStatus0 = 0x20, // Transmit status (Four 32bit registers). + TxAddr0 = 0x30, // Tx descriptors (also four 32bit). + RxConfig = 0x40, // Rx configuration + MAC0 = 0x44, // Ethernet hardware address. + MAR0 = 0x4C, // Multicast filter. + RxStatus0 = 0x54, // Rx status + TxConfig = 0x5C, // Tx configuration + PhyCtrl = 0x60, // physical control + FlowCtrlConfig = 0x64, // flow control + Miicmd0 = 0x68, // Mii command0 register + Miicmd1 = 0x6C, // Mii command1 register + Miistatus = 0x70, // Mii status register + Timercnt = 0x74, // Timer counter register + TimerIntr = 0x78, // Timer interrupt register + PMConfig = 0x7C, // Power Manager configuration + CRC0 = 0x80, // Power Manager CRC ( Two 32bit regisers) + Wakeup0 = 0x88, // power Manager wakeup( Eight 64bit regiser) + LSBCRC0 = 0xC8, // power Manager LSBCRC(Two 32bit regiser) + TestD0 = 0xD0, + TestD4 = 0xD4, + TestD8 = 0xD8, +}; + +#define MII_JAB 16 +#define MII_OutputStatus 24 + +#define PHY_16_JAB_ENB 0x1000 +#define PHY_16_PORT_ENB 0x1 + +enum IntrStatusBits { + LinkFail = 0x80000000, + LinkOK = 0x40000000, + TimeOut = 0x20000000, + RxOverflow = 0x0040, + RxOK = 0x0020, + TxOK = 0x0001, + IntrBits = LinkFail|LinkOK|TimeOut|RxOverflow|RxOK|TxOK, +}; + +enum TxStatusBits { + TxCarrierLost = 0x20000000, + TxAborted = 0x10000000, + TxOutOfWindow = 0x08000000, + TxNccShift = 22, + EarlyTxThresShift = 16, + TxStatOK = 0x8000, + TxUnderrun = 0x4000, + TxOwn = 0x2000, +}; + +enum RxStatusBits { + RxStatesOK = 0x80000, + RxBadAlign = 0x40000, + RxHugeFrame = 0x20000, + RxSmallFrame = 0x10000, + RxCRCOK = 0x8000, + RxCrlFrame = 0x4000, + Rx_Broadcast = 0x2000, + Rx_Multicast = 0x1000, + RxAddrMatch = 0x0800, + MiiErr = 0x0400, +}; + +enum RxConfigBits { + RxFullDx = 0x80000000, + RxEnb = 0x40000000, + RxSmall = 0x20000000, + RxHuge = 0x10000000, + RxErr = 0x08000000, + RxAllphys = 0x04000000, + RxMulticast = 0x02000000, + RxBroadcast = 0x01000000, + RxLoopBack = (1 << 23) | (1 << 22), + LowThresholdShift = 12, + HighThresholdShift = 2, +}; + +enum TxConfigBits { + TxFullDx = 0x80000000, + TxEnb = 0x40000000, + TxEnbPad = 0x20000000, + TxEnbHuge = 0x10000000, + TxEnbFCS = 0x08000000, + TxNoBackOff = 0x04000000, + TxEnbPrem = 0x02000000, + TxCareLostCrs = 0x1000000, + TxExdCollNum = 0xf00000, + TxDataRate = 0x80000, +}; + +enum PhyCtrlconfigbits { + PhyCtrlAne = 0x80000000, + PhyCtrlSpd100 = 0x40000000, + PhyCtrlSpd10 = 0x20000000, + PhyCtrlPhyBaseAddr = 0x1f000000, + PhyCtrlDux = 0x800000, + PhyCtrlReset = 0x400000, +}; + +enum FlowCtrlConfigBits { + FlowCtrlFullDX = 0x80000000, + FlowCtrlEnb = 0x40000000, +}; + +enum Config0Bits { + Cfg0_Reset = 0x80000000, + Cfg0_Anaoff = 0x40000000, + Cfg0_LDPS = 0x20000000, +}; + +enum Config1Bits { + Cfg1_EarlyRx = 1 << 31, + Cfg1_EarlyTx = 1 << 30, + + //rx buffer size + Cfg1_Rcv8K = 0x0, + Cfg1_Rcv16K = 0x1, + Cfg1_Rcv32K = 0x3, + Cfg1_Rcv64K = 0x7, + Cfg1_Rcv128K = 0xf, +}; + +enum MiiCmd0Bits { + Mii_Divider = 0x20000000, + Mii_WRITE = 0x400000, + Mii_READ = 0x200000, + Mii_SCAN = 0x100000, + Mii_Tamod = 0x80000, + Mii_Drvmod = 0x40000, + Mii_mdc = 0x20000, + Mii_mdoen = 0x10000, + Mii_mdo = 0x8000, + Mii_mdi = 0x4000, +}; + +enum MiiStatusBits { + Mii_StatusBusy = 0x80000000, +}; + +enum PMConfigBits { + PM_Enable = 1 << 31, + PM_LongWF = 1 << 30, + PM_Magic = 1 << 29, + PM_LANWake = 1 << 28, + PM_LWPTN = (1 << 27 | 1<< 26), + PM_LinkUp = 1 << 25, + PM_WakeUp = 1 << 24, +}; + +/* Locking rules: + * priv->lock protects most of the fields of priv and most of the + * hardware registers. It does not have to protect against softirqs + * between sc92031_disable_interrupts and sc92031_enable_interrupts; + * it also does not need to be used in ->open and ->stop while the + * device interrupts are off. + * Not having to protect against softirqs is very useful due to heavy + * use of mdelay() at _sc92031_reset. + * Functions prefixed with _sc92031_ must be called with the lock held; + * functions prefixed with sc92031_ must be called without the lock held. + * Use mmiowb() before unlocking if the hardware was written to. + */ + +/* Locking rules for the interrupt: + * - the interrupt and the tasklet never run at the same time + * - neither run between sc92031_disable_interrupts and + * sc92031_enable_interrupt + */ + +struct sc92031_priv { + spinlock_t lock; + /* iomap.h cookie */ + void __iomem *port_base; + /* pci device structure */ + struct pci_dev *pdev; + /* tasklet */ + struct tasklet_struct tasklet; + + /* CPU address of rx ring */ + void *rx_ring; + /* PCI address of rx ring */ + dma_addr_t rx_ring_dma_addr; + /* PCI address of rx ring read pointer */ + dma_addr_t rx_ring_tail; + + /* tx ring write index */ + unsigned tx_head; + /* tx ring read index */ + unsigned tx_tail; + /* CPU address of tx bounce buffer */ + void *tx_bufs; + /* PCI address of tx bounce buffer */ + dma_addr_t tx_bufs_dma_addr; + + /* copies of some hardware registers */ + u32 intr_status; + atomic_t intr_mask; + u32 rx_config; + u32 tx_config; + u32 pm_config; + + /* copy of some flags from dev->flags */ + unsigned int mc_flags; + + /* for ETHTOOL_GSTATS */ + u64 tx_timeouts; + u64 rx_loss; + + /* for dev->get_stats */ + long rx_value; +}; + +/* I don't know which registers can be safely read; however, I can guess + * MAC0 is one of them. */ +static inline void _sc92031_dummy_read(void __iomem *port_base) +{ + ioread32(port_base + MAC0); +} + +static u32 _sc92031_mii_wait(void __iomem *port_base) +{ + u32 mii_status; + + do { + udelay(10); + mii_status = ioread32(port_base + Miistatus); + } while (mii_status & Mii_StatusBusy); + + return mii_status; +} + +static u32 _sc92031_mii_cmd(void __iomem *port_base, u32 cmd0, u32 cmd1) +{ + iowrite32(Mii_Divider, port_base + Miicmd0); + + _sc92031_mii_wait(port_base); + + iowrite32(cmd1, port_base + Miicmd1); + iowrite32(Mii_Divider | cmd0, port_base + Miicmd0); + + return _sc92031_mii_wait(port_base); +} + +static void _sc92031_mii_scan(void __iomem *port_base) +{ + _sc92031_mii_cmd(port_base, Mii_SCAN, 0x1 << 6); +} + +static u16 _sc92031_mii_read(void __iomem *port_base, unsigned reg) +{ + return _sc92031_mii_cmd(port_base, Mii_READ, reg << 6) >> 13; +} + +static void _sc92031_mii_write(void __iomem *port_base, unsigned reg, u16 val) +{ + _sc92031_mii_cmd(port_base, Mii_WRITE, (reg << 6) | ((u32)val << 11)); +} + +static void sc92031_disable_interrupts(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + /* tell the tasklet/interrupt not to enable interrupts */ + atomic_set(&priv->intr_mask, 0); + wmb(); + + /* stop interrupts */ + iowrite32(0, port_base + IntrMask); + _sc92031_dummy_read(port_base); + mmiowb(); + + /* wait for any concurrent interrupt/tasklet to finish */ + synchronize_irq(dev->irq); + tasklet_disable(&priv->tasklet); +} + +static void sc92031_enable_interrupts(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + tasklet_enable(&priv->tasklet); + + atomic_set(&priv->intr_mask, IntrBits); + wmb(); + + iowrite32(IntrBits, port_base + IntrMask); + mmiowb(); +} + +static void _sc92031_disable_tx_rx(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + priv->rx_config &= ~RxEnb; + priv->tx_config &= ~TxEnb; + iowrite32(priv->rx_config, port_base + RxConfig); + iowrite32(priv->tx_config, port_base + TxConfig); +} + +static void _sc92031_enable_tx_rx(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + priv->rx_config |= RxEnb; + priv->tx_config |= TxEnb; + iowrite32(priv->rx_config, port_base + RxConfig); + iowrite32(priv->tx_config, port_base + TxConfig); +} + +static void _sc92031_tx_clear(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + while (priv->tx_head - priv->tx_tail > 0) { + priv->tx_tail++; + dev->stats.tx_dropped++; + } + priv->tx_head = priv->tx_tail = 0; +} + +static void _sc92031_set_mar(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 mar0 = 0, mar1 = 0; + + if ((dev->flags & IFF_PROMISC) || + netdev_mc_count(dev) > multicast_filter_limit || + (dev->flags & IFF_ALLMULTI)) + mar0 = mar1 = 0xffffffff; + else if (dev->flags & IFF_MULTICAST) { + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, dev) { + u32 crc; + unsigned bit = 0; + + crc = ~ether_crc(ETH_ALEN, ha->addr); + crc >>= 24; + + if (crc & 0x01) bit |= 0x02; + if (crc & 0x02) bit |= 0x01; + if (crc & 0x10) bit |= 0x20; + if (crc & 0x20) bit |= 0x10; + if (crc & 0x40) bit |= 0x08; + if (crc & 0x80) bit |= 0x04; + + if (bit > 31) + mar0 |= 0x1 << (bit - 32); + else + mar1 |= 0x1 << bit; + } + } + + iowrite32(mar0, port_base + MAR0); + iowrite32(mar1, port_base + MAR0 + 4); +} + +static void _sc92031_set_rx_config(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + unsigned int old_mc_flags; + u32 rx_config_bits = 0; + + old_mc_flags = priv->mc_flags; + + if (dev->flags & IFF_PROMISC) + rx_config_bits |= RxSmall | RxHuge | RxErr | RxBroadcast + | RxMulticast | RxAllphys; + + if (dev->flags & (IFF_ALLMULTI | IFF_MULTICAST)) + rx_config_bits |= RxMulticast; + + if (dev->flags & IFF_BROADCAST) + rx_config_bits |= RxBroadcast; + + priv->rx_config &= ~(RxSmall | RxHuge | RxErr | RxBroadcast + | RxMulticast | RxAllphys); + priv->rx_config |= rx_config_bits; + + priv->mc_flags = dev->flags & (IFF_PROMISC | IFF_ALLMULTI + | IFF_MULTICAST | IFF_BROADCAST); + + if (netif_carrier_ok(dev) && priv->mc_flags != old_mc_flags) + iowrite32(priv->rx_config, port_base + RxConfig); +} + +static bool _sc92031_check_media(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u16 bmsr; + + bmsr = _sc92031_mii_read(port_base, MII_BMSR); + rmb(); + if (bmsr & BMSR_LSTATUS) { + bool speed_100, duplex_full; + u32 flow_ctrl_config = 0; + u16 output_status = _sc92031_mii_read(port_base, + MII_OutputStatus); + _sc92031_mii_scan(port_base); + + speed_100 = output_status & 0x2; + duplex_full = output_status & 0x4; + + /* Initial Tx/Rx configuration */ + priv->rx_config = (0x40 << LowThresholdShift) | (0x1c0 << HighThresholdShift); + priv->tx_config = 0x48800000; + + /* NOTE: vendor driver had dead code here to enable tx padding */ + + if (!speed_100) + priv->tx_config |= 0x80000; + + // configure rx mode + _sc92031_set_rx_config(dev); + + if (duplex_full) { + priv->rx_config |= RxFullDx; + priv->tx_config |= TxFullDx; + flow_ctrl_config = FlowCtrlFullDX | FlowCtrlEnb; + } else { + priv->rx_config &= ~RxFullDx; + priv->tx_config &= ~TxFullDx; + } + + _sc92031_set_mar(dev); + _sc92031_set_rx_config(dev); + _sc92031_enable_tx_rx(dev); + iowrite32(flow_ctrl_config, port_base + FlowCtrlConfig); + + netif_carrier_on(dev); + + if (printk_ratelimit()) + printk(KERN_INFO "%s: link up, %sMbps, %s-duplex\n", + dev->name, + speed_100 ? "100" : "10", + duplex_full ? "full" : "half"); + return true; + } else { + _sc92031_mii_scan(port_base); + + netif_carrier_off(dev); + + _sc92031_disable_tx_rx(dev); + + if (printk_ratelimit()) + printk(KERN_INFO "%s: link down\n", dev->name); + return false; + } +} + +static void _sc92031_phy_reset(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 phy_ctrl; + + phy_ctrl = ioread32(port_base + PhyCtrl); + phy_ctrl &= ~(PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10); + phy_ctrl |= PhyCtrlAne | PhyCtrlReset; + + switch (media) { + default: + case AUTOSELECT: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; + break; + case M10_HALF: + phy_ctrl |= PhyCtrlSpd10; + break; + case M10_FULL: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd10; + break; + case M100_HALF: + phy_ctrl |= PhyCtrlSpd100; + break; + case M100_FULL: + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; + break; + } + + iowrite32(phy_ctrl, port_base + PhyCtrl); + mdelay(10); + + phy_ctrl &= ~PhyCtrlReset; + iowrite32(phy_ctrl, port_base + PhyCtrl); + mdelay(1); + + _sc92031_mii_write(port_base, MII_JAB, + PHY_16_JAB_ENB | PHY_16_PORT_ENB); + _sc92031_mii_scan(port_base); + + netif_carrier_off(dev); + netif_stop_queue(dev); +} + +static void _sc92031_reset(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + /* disable PM */ + iowrite32(0, port_base + PMConfig); + + /* soft reset the chip */ + iowrite32(Cfg0_Reset, port_base + Config0); + mdelay(200); + + iowrite32(0, port_base + Config0); + mdelay(10); + + /* disable interrupts */ + iowrite32(0, port_base + IntrMask); + + /* clear multicast address */ + iowrite32(0, port_base + MAR0); + iowrite32(0, port_base + MAR0 + 4); + + /* init rx ring */ + iowrite32(priv->rx_ring_dma_addr, port_base + RxbufAddr); + priv->rx_ring_tail = priv->rx_ring_dma_addr; + + /* init tx ring */ + _sc92031_tx_clear(dev); + + /* clear old register values */ + priv->intr_status = 0; + atomic_set(&priv->intr_mask, 0); + priv->rx_config = 0; + priv->tx_config = 0; + priv->mc_flags = 0; + + /* configure rx buffer size */ + /* NOTE: vendor driver had dead code here to enable early tx/rx */ + iowrite32(Cfg1_Rcv64K, port_base + Config1); + + _sc92031_phy_reset(dev); + _sc92031_check_media(dev); + + /* calculate rx fifo overflow */ + priv->rx_value = 0; + + /* enable PM */ + iowrite32(priv->pm_config, port_base + PMConfig); + + /* clear intr register */ + ioread32(port_base + IntrStatus); +} + +static void _sc92031_tx_tasklet(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + unsigned old_tx_tail; + unsigned entry; + u32 tx_status; + + old_tx_tail = priv->tx_tail; + while (priv->tx_head - priv->tx_tail > 0) { + entry = priv->tx_tail % NUM_TX_DESC; + tx_status = ioread32(port_base + TxStatus0 + entry * 4); + + if (!(tx_status & (TxStatOK | TxUnderrun | TxAborted))) + break; + + priv->tx_tail++; + + if (tx_status & TxStatOK) { + dev->stats.tx_bytes += tx_status & 0x1fff; + dev->stats.tx_packets++; + /* Note: TxCarrierLost is always asserted at 100mbps. */ + dev->stats.collisions += (tx_status >> 22) & 0xf; + } + + if (tx_status & (TxOutOfWindow | TxAborted)) { + dev->stats.tx_errors++; + + if (tx_status & TxAborted) + dev->stats.tx_aborted_errors++; + + if (tx_status & TxCarrierLost) + dev->stats.tx_carrier_errors++; + + if (tx_status & TxOutOfWindow) + dev->stats.tx_window_errors++; + } + + if (tx_status & TxUnderrun) + dev->stats.tx_fifo_errors++; + } + + if (priv->tx_tail != old_tx_tail) + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +static void _sc92031_rx_tasklet_error(struct net_device *dev, + u32 rx_status, unsigned rx_size) +{ + if(rx_size > (MAX_ETH_FRAME_SIZE + 4) || rx_size < 16) { + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + } + + if (!(rx_status & RxStatesOK)) { + dev->stats.rx_errors++; + + if (rx_status & (RxHugeFrame | RxSmallFrame)) + dev->stats.rx_length_errors++; + + if (rx_status & RxBadAlign) + dev->stats.rx_frame_errors++; + + if (!(rx_status & RxCRCOK)) + dev->stats.rx_crc_errors++; + } else { + struct sc92031_priv *priv = netdev_priv(dev); + priv->rx_loss++; + } +} + +static void _sc92031_rx_tasklet(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + dma_addr_t rx_ring_head; + unsigned rx_len; + unsigned rx_ring_offset; + void *rx_ring = priv->rx_ring; + + rx_ring_head = ioread32(port_base + RxBufWPtr); + rmb(); + + /* rx_ring_head is only 17 bits in the RxBufWPtr register. + * we need to change it to 32 bits physical address + */ + rx_ring_head &= (dma_addr_t)(RX_BUF_LEN - 1); + rx_ring_head |= priv->rx_ring_dma_addr & ~(dma_addr_t)(RX_BUF_LEN - 1); + if (rx_ring_head < priv->rx_ring_dma_addr) + rx_ring_head += RX_BUF_LEN; + + if (rx_ring_head >= priv->rx_ring_tail) + rx_len = rx_ring_head - priv->rx_ring_tail; + else + rx_len = RX_BUF_LEN - (priv->rx_ring_tail - rx_ring_head); + + if (!rx_len) + return; + + if (unlikely(rx_len > RX_BUF_LEN)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: rx packets length > rx buffer\n", + dev->name); + return; + } + + rx_ring_offset = (priv->rx_ring_tail - priv->rx_ring_dma_addr) % RX_BUF_LEN; + + while (rx_len) { + u32 rx_status; + unsigned rx_size, rx_size_align, pkt_size; + struct sk_buff *skb; + + rx_status = le32_to_cpup((__le32 *)(rx_ring + rx_ring_offset)); + rmb(); + + rx_size = rx_status >> 20; + rx_size_align = (rx_size + 3) & ~3; // for 4 bytes aligned + pkt_size = rx_size - 4; // Omit the four octet CRC from the length. + + rx_ring_offset = (rx_ring_offset + 4) % RX_BUF_LEN; + + if (unlikely(rx_status == 0 || + rx_size > (MAX_ETH_FRAME_SIZE + 4) || + rx_size < 16 || + !(rx_status & RxStatesOK))) { + _sc92031_rx_tasklet_error(dev, rx_status, rx_size); + break; + } + + if (unlikely(rx_size_align + 4 > rx_len)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: rx_len is too small\n", dev->name); + break; + } + + rx_len -= rx_size_align + 4; + + skb = netdev_alloc_skb_ip_align(dev, pkt_size); + if (unlikely(!skb)) { + if (printk_ratelimit()) + printk(KERN_ERR "%s: Couldn't allocate a skb_buff for a packet of size %u\n", + dev->name, pkt_size); + goto next; + } + + if ((rx_ring_offset + pkt_size) > RX_BUF_LEN) { + memcpy(skb_put(skb, RX_BUF_LEN - rx_ring_offset), + rx_ring + rx_ring_offset, RX_BUF_LEN - rx_ring_offset); + memcpy(skb_put(skb, pkt_size - (RX_BUF_LEN - rx_ring_offset)), + rx_ring, pkt_size - (RX_BUF_LEN - rx_ring_offset)); + } else { + memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size); + } + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + dev->stats.rx_bytes += pkt_size; + dev->stats.rx_packets++; + + if (rx_status & Rx_Multicast) + dev->stats.multicast++; + + next: + rx_ring_offset = (rx_ring_offset + rx_size_align) % RX_BUF_LEN; + } + mb(); + + priv->rx_ring_tail = rx_ring_head; + iowrite32(priv->rx_ring_tail, port_base + RxBufRPtr); +} + +static void _sc92031_link_tasklet(struct net_device *dev) +{ + if (_sc92031_check_media(dev)) + netif_wake_queue(dev); + else { + netif_stop_queue(dev); + dev->stats.tx_carrier_errors++; + } +} + +static void sc92031_tasklet(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 intr_status, intr_mask; + + intr_status = priv->intr_status; + + spin_lock(&priv->lock); + + if (unlikely(!netif_running(dev))) + goto out; + + if (intr_status & TxOK) + _sc92031_tx_tasklet(dev); + + if (intr_status & RxOK) + _sc92031_rx_tasklet(dev); + + if (intr_status & RxOverflow) + dev->stats.rx_errors++; + + if (intr_status & TimeOut) { + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + } + + if (intr_status & (LinkFail | LinkOK)) + _sc92031_link_tasklet(dev); + +out: + intr_mask = atomic_read(&priv->intr_mask); + rmb(); + + iowrite32(intr_mask, port_base + IntrMask); + mmiowb(); + + spin_unlock(&priv->lock); +} + +static irqreturn_t sc92031_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 intr_status, intr_mask; + + /* mask interrupts before clearing IntrStatus */ + iowrite32(0, port_base + IntrMask); + _sc92031_dummy_read(port_base); + + intr_status = ioread32(port_base + IntrStatus); + if (unlikely(intr_status == 0xffffffff)) + return IRQ_NONE; // hardware has gone missing + + intr_status &= IntrBits; + if (!intr_status) + goto out_none; + + priv->intr_status = intr_status; + tasklet_schedule(&priv->tasklet); + + return IRQ_HANDLED; + +out_none: + intr_mask = atomic_read(&priv->intr_mask); + rmb(); + + iowrite32(intr_mask, port_base + IntrMask); + mmiowb(); + + return IRQ_NONE; +} + +static struct net_device_stats *sc92031_get_stats(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + + // FIXME I do not understand what is this trying to do. + if (netif_running(dev)) { + int temp; + + spin_lock_bh(&priv->lock); + + /* Update the error count. */ + temp = (ioread32(port_base + RxStatus0) >> 16) & 0xffff; + + if (temp == 0xffff) { + priv->rx_value += temp; + dev->stats.rx_fifo_errors = priv->rx_value; + } else + dev->stats.rx_fifo_errors = temp + priv->rx_value; + + spin_unlock_bh(&priv->lock); + } + + return &dev->stats; +} + +static netdev_tx_t sc92031_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + unsigned len; + unsigned entry; + u32 tx_status; + + if (unlikely(skb->len > TX_BUF_SIZE)) { + dev->stats.tx_dropped++; + goto out; + } + + spin_lock(&priv->lock); + + if (unlikely(!netif_carrier_ok(dev))) { + dev->stats.tx_dropped++; + goto out_unlock; + } + + BUG_ON(priv->tx_head - priv->tx_tail >= NUM_TX_DESC); + + entry = priv->tx_head++ % NUM_TX_DESC; + + skb_copy_and_csum_dev(skb, priv->tx_bufs + entry * TX_BUF_SIZE); + + len = skb->len; + if (len < ETH_ZLEN) { + memset(priv->tx_bufs + entry * TX_BUF_SIZE + len, + 0, ETH_ZLEN - len); + len = ETH_ZLEN; + } + + wmb(); + + if (len < 100) + tx_status = len; + else if (len < 300) + tx_status = 0x30000 | len; + else + tx_status = 0x50000 | len; + + iowrite32(priv->tx_bufs_dma_addr + entry * TX_BUF_SIZE, + port_base + TxAddr0 + entry * 4); + iowrite32(tx_status, port_base + TxStatus0 + entry * 4); + mmiowb(); + + if (priv->tx_head - priv->tx_tail >= NUM_TX_DESC) + netif_stop_queue(dev); + +out_unlock: + spin_unlock(&priv->lock); + +out: + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static int sc92031_open(struct net_device *dev) +{ + int err; + struct sc92031_priv *priv = netdev_priv(dev); + struct pci_dev *pdev = priv->pdev; + + priv->rx_ring = pci_alloc_consistent(pdev, RX_BUF_LEN, + &priv->rx_ring_dma_addr); + if (unlikely(!priv->rx_ring)) { + err = -ENOMEM; + goto out_alloc_rx_ring; + } + + priv->tx_bufs = pci_alloc_consistent(pdev, TX_BUF_TOT_LEN, + &priv->tx_bufs_dma_addr); + if (unlikely(!priv->tx_bufs)) { + err = -ENOMEM; + goto out_alloc_tx_bufs; + } + priv->tx_head = priv->tx_tail = 0; + + err = request_irq(pdev->irq, sc92031_interrupt, + IRQF_SHARED, dev->name, dev); + if (unlikely(err < 0)) + goto out_request_irq; + + priv->pm_config = 0; + + /* Interrupts already disabled by sc92031_stop or sc92031_probe */ + spin_lock_bh(&priv->lock); + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); + sc92031_enable_interrupts(dev); + + if (netif_carrier_ok(dev)) + netif_start_queue(dev); + else + netif_tx_disable(dev); + + return 0; + +out_request_irq: + pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, + priv->tx_bufs_dma_addr); +out_alloc_tx_bufs: + pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, + priv->rx_ring_dma_addr); +out_alloc_rx_ring: + return err; +} + +static int sc92031_stop(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + struct pci_dev *pdev = priv->pdev; + + netif_tx_disable(dev); + + /* Disable interrupts, stop Tx and Rx. */ + sc92031_disable_interrupts(dev); + + spin_lock_bh(&priv->lock); + + _sc92031_disable_tx_rx(dev); + _sc92031_tx_clear(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + free_irq(pdev->irq, dev); + pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, + priv->tx_bufs_dma_addr); + pci_free_consistent(pdev, RX_BUF_LEN, priv->rx_ring, + priv->rx_ring_dma_addr); + + return 0; +} + +static void sc92031_set_multicast_list(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + spin_lock_bh(&priv->lock); + + _sc92031_set_mar(dev); + _sc92031_set_rx_config(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); +} + +static void sc92031_tx_timeout(struct net_device *dev) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + /* Disable interrupts by clearing the interrupt mask.*/ + sc92031_disable_interrupts(dev); + + spin_lock(&priv->lock); + + priv->tx_timeouts++; + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock(&priv->lock); + + /* enable interrupts */ + sc92031_enable_interrupts(dev); + + if (netif_carrier_ok(dev)) + netif_wake_queue(dev); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sc92031_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + if (sc92031_interrupt(dev->irq, dev) != IRQ_NONE) + sc92031_tasklet((unsigned long)dev); + enable_irq(dev->irq); +} +#endif + +static int sc92031_ethtool_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u8 phy_address; + u32 phy_ctrl; + u16 output_status; + + spin_lock_bh(&priv->lock); + + phy_address = ioread32(port_base + Miicmd1) >> 27; + phy_ctrl = ioread32(port_base + PhyCtrl); + + output_status = _sc92031_mii_read(port_base, MII_OutputStatus); + _sc92031_mii_scan(port_base); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII; + + cmd->advertising = ADVERTISED_TP | ADVERTISED_MII; + + if ((phy_ctrl & (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) + == (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) + cmd->advertising |= ADVERTISED_Autoneg; + + if ((phy_ctrl & PhyCtrlSpd10) == PhyCtrlSpd10) + cmd->advertising |= ADVERTISED_10baseT_Half; + + if ((phy_ctrl & (PhyCtrlSpd10 | PhyCtrlDux)) + == (PhyCtrlSpd10 | PhyCtrlDux)) + cmd->advertising |= ADVERTISED_10baseT_Full; + + if ((phy_ctrl & PhyCtrlSpd100) == PhyCtrlSpd100) + cmd->advertising |= ADVERTISED_100baseT_Half; + + if ((phy_ctrl & (PhyCtrlSpd100 | PhyCtrlDux)) + == (PhyCtrlSpd100 | PhyCtrlDux)) + cmd->advertising |= ADVERTISED_100baseT_Full; + + if (phy_ctrl & PhyCtrlAne) + cmd->advertising |= ADVERTISED_Autoneg; + + ethtool_cmd_speed_set(cmd, + (output_status & 0x2) ? SPEED_100 : SPEED_10); + cmd->duplex = (output_status & 0x4) ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_MII; + cmd->phy_address = phy_address; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = (phy_ctrl & PhyCtrlAne) ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int sc92031_ethtool_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 speed = ethtool_cmd_speed(cmd); + u32 phy_ctrl; + u32 old_phy_ctrl; + + if (!(speed == SPEED_10 || speed == SPEED_100)) + return -EINVAL; + if (!(cmd->duplex == DUPLEX_HALF || cmd->duplex == DUPLEX_FULL)) + return -EINVAL; + if (!(cmd->port == PORT_MII)) + return -EINVAL; + if (!(cmd->phy_address == 0x1f)) + return -EINVAL; + if (!(cmd->transceiver == XCVR_INTERNAL)) + return -EINVAL; + if (!(cmd->autoneg == AUTONEG_DISABLE || cmd->autoneg == AUTONEG_ENABLE)) + return -EINVAL; + + if (cmd->autoneg == AUTONEG_ENABLE) { + if (!(cmd->advertising & (ADVERTISED_Autoneg + | ADVERTISED_100baseT_Full + | ADVERTISED_100baseT_Half + | ADVERTISED_10baseT_Full + | ADVERTISED_10baseT_Half))) + return -EINVAL; + + phy_ctrl = PhyCtrlAne; + + // FIXME: I'm not sure what the original code was trying to do + if (cmd->advertising & ADVERTISED_Autoneg) + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; + if (cmd->advertising & ADVERTISED_100baseT_Full) + phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; + if (cmd->advertising & ADVERTISED_100baseT_Half) + phy_ctrl |= PhyCtrlSpd100; + if (cmd->advertising & ADVERTISED_10baseT_Full) + phy_ctrl |= PhyCtrlSpd10 | PhyCtrlDux; + if (cmd->advertising & ADVERTISED_10baseT_Half) + phy_ctrl |= PhyCtrlSpd10; + } else { + // FIXME: Whole branch guessed + phy_ctrl = 0; + + if (speed == SPEED_10) + phy_ctrl |= PhyCtrlSpd10; + else /* cmd->speed == SPEED_100 */ + phy_ctrl |= PhyCtrlSpd100; + + if (cmd->duplex == DUPLEX_FULL) + phy_ctrl |= PhyCtrlDux; + } + + spin_lock_bh(&priv->lock); + + old_phy_ctrl = ioread32(port_base + PhyCtrl); + phy_ctrl |= old_phy_ctrl & ~(PhyCtrlAne | PhyCtrlDux + | PhyCtrlSpd100 | PhyCtrlSpd10); + if (phy_ctrl != old_phy_ctrl) + iowrite32(phy_ctrl, port_base + PhyCtrl); + + spin_unlock_bh(&priv->lock); + + return 0; +} + +static void sc92031_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 pm_config; + + spin_lock_bh(&priv->lock); + pm_config = ioread32(port_base + PMConfig); + spin_unlock_bh(&priv->lock); + + // FIXME: Guessed + wolinfo->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + wolinfo->wolopts = 0; + + if (pm_config & PM_LinkUp) + wolinfo->wolopts |= WAKE_PHY; + + if (pm_config & PM_Magic) + wolinfo->wolopts |= WAKE_MAGIC; + + if (pm_config & PM_WakeUp) + // FIXME: Guessed + wolinfo->wolopts |= WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; +} + +static int sc92031_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u32 pm_config; + + spin_lock_bh(&priv->lock); + + pm_config = ioread32(port_base + PMConfig) + & ~(PM_LinkUp | PM_Magic | PM_WakeUp); + + if (wolinfo->wolopts & WAKE_PHY) + pm_config |= PM_LinkUp; + + if (wolinfo->wolopts & WAKE_MAGIC) + pm_config |= PM_Magic; + + // FIXME: Guessed + if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST)) + pm_config |= PM_WakeUp; + + priv->pm_config = pm_config; + iowrite32(pm_config, port_base + PMConfig); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + return 0; +} + +static int sc92031_ethtool_nway_reset(struct net_device *dev) +{ + int err = 0; + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem *port_base = priv->port_base; + u16 bmcr; + + spin_lock_bh(&priv->lock); + + bmcr = _sc92031_mii_read(port_base, MII_BMCR); + if (!(bmcr & BMCR_ANENABLE)) { + err = -EINVAL; + goto out; + } + + _sc92031_mii_write(port_base, MII_BMCR, bmcr | BMCR_ANRESTART); + +out: + _sc92031_mii_scan(port_base); + mmiowb(); + + spin_unlock_bh(&priv->lock); + + return err; +} + +static const char sc92031_ethtool_stats_strings[SILAN_STATS_NUM][ETH_GSTRING_LEN] = { + "tx_timeout", + "rx_loss", +}; + +static void sc92031_ethtool_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + if (stringset == ETH_SS_STATS) + memcpy(data, sc92031_ethtool_stats_strings, + SILAN_STATS_NUM * ETH_GSTRING_LEN); +} + +static int sc92031_ethtool_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return SILAN_STATS_NUM; + default: + return -EOPNOTSUPP; + } +} + +static void sc92031_ethtool_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct sc92031_priv *priv = netdev_priv(dev); + + spin_lock_bh(&priv->lock); + data[0] = priv->tx_timeouts; + data[1] = priv->rx_loss; + spin_unlock_bh(&priv->lock); +} + +static const struct ethtool_ops sc92031_ethtool_ops = { + .get_settings = sc92031_ethtool_get_settings, + .set_settings = sc92031_ethtool_set_settings, + .get_wol = sc92031_ethtool_get_wol, + .set_wol = sc92031_ethtool_set_wol, + .nway_reset = sc92031_ethtool_nway_reset, + .get_link = ethtool_op_get_link, + .get_strings = sc92031_ethtool_get_strings, + .get_sset_count = sc92031_ethtool_get_sset_count, + .get_ethtool_stats = sc92031_ethtool_get_ethtool_stats, +}; + + +static const struct net_device_ops sc92031_netdev_ops = { + .ndo_get_stats = sc92031_get_stats, + .ndo_start_xmit = sc92031_start_xmit, + .ndo_open = sc92031_open, + .ndo_stop = sc92031_stop, + .ndo_set_rx_mode = sc92031_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_tx_timeout = sc92031_tx_timeout, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = sc92031_poll_controller, +#endif +}; + +static int __devinit sc92031_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err; + void __iomem* port_base; + struct net_device *dev; + struct sc92031_priv *priv; + u32 mac0, mac1; + unsigned long base_addr; + + err = pci_enable_device(pdev); + if (unlikely(err < 0)) + goto out_enable_device; + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (unlikely(err < 0)) + goto out_set_dma_mask; + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (unlikely(err < 0)) + goto out_set_dma_mask; + + err = pci_request_regions(pdev, SC92031_NAME); + if (unlikely(err < 0)) + goto out_request_regions; + + port_base = pci_iomap(pdev, SC92031_USE_BAR, 0); + if (unlikely(!port_base)) { + err = -EIO; + goto out_iomap; + } + + dev = alloc_etherdev(sizeof(struct sc92031_priv)); + if (unlikely(!dev)) { + err = -ENOMEM; + goto out_alloc_etherdev; + } + + pci_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + +#if SC92031_USE_BAR == 0 + dev->mem_start = pci_resource_start(pdev, SC92031_USE_BAR); + dev->mem_end = pci_resource_end(pdev, SC92031_USE_BAR); +#elif SC92031_USE_BAR == 1 + dev->base_addr = pci_resource_start(pdev, SC92031_USE_BAR); +#endif + dev->irq = pdev->irq; + + /* faked with skb_copy_and_csum_dev */ + dev->features = NETIF_F_SG | NETIF_F_HIGHDMA | + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + + dev->netdev_ops = &sc92031_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + dev->ethtool_ops = &sc92031_ethtool_ops; + + priv = netdev_priv(dev); + spin_lock_init(&priv->lock); + priv->port_base = port_base; + priv->pdev = pdev; + tasklet_init(&priv->tasklet, sc92031_tasklet, (unsigned long)dev); + /* Fudge tasklet count so the call to sc92031_enable_interrupts at + * sc92031_open will work correctly */ + tasklet_disable_nosync(&priv->tasklet); + + /* PCI PM Wakeup */ + iowrite32((~PM_LongWF & ~PM_LWPTN) | PM_Enable, port_base + PMConfig); + + mac0 = ioread32(port_base + MAC0); + mac1 = ioread32(port_base + MAC0 + 4); + dev->dev_addr[0] = dev->perm_addr[0] = mac0 >> 24; + dev->dev_addr[1] = dev->perm_addr[1] = mac0 >> 16; + dev->dev_addr[2] = dev->perm_addr[2] = mac0 >> 8; + dev->dev_addr[3] = dev->perm_addr[3] = mac0; + dev->dev_addr[4] = dev->perm_addr[4] = mac1 >> 8; + dev->dev_addr[5] = dev->perm_addr[5] = mac1; + + err = register_netdev(dev); + if (err < 0) + goto out_register_netdev; + +#if SC92031_USE_BAR == 0 + base_addr = dev->mem_start; +#elif SC92031_USE_BAR == 1 + base_addr = dev->base_addr; +#endif + printk(KERN_INFO "%s: SC92031 at 0x%lx, %pM, IRQ %d\n", dev->name, + base_addr, dev->dev_addr, dev->irq); + + return 0; + +out_register_netdev: + free_netdev(dev); +out_alloc_etherdev: + pci_iounmap(pdev, port_base); +out_iomap: + pci_release_regions(pdev); +out_request_regions: +out_set_dma_mask: + pci_disable_device(pdev); +out_enable_device: + return err; +} + +static void __devexit sc92031_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + void __iomem* port_base = priv->port_base; + + unregister_netdev(dev); + free_netdev(dev); + pci_iounmap(pdev, port_base); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int sc92031_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + + pci_save_state(pdev); + + if (!netif_running(dev)) + goto out; + + netif_device_detach(dev); + + /* Disable interrupts, stop Tx and Rx. */ + sc92031_disable_interrupts(dev); + + spin_lock_bh(&priv->lock); + + _sc92031_disable_tx_rx(dev); + _sc92031_tx_clear(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); + +out: + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sc92031_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct sc92031_priv *priv = netdev_priv(dev); + + pci_restore_state(pdev); + pci_set_power_state(pdev, PCI_D0); + + if (!netif_running(dev)) + goto out; + + /* Interrupts already disabled by sc92031_suspend */ + spin_lock_bh(&priv->lock); + + _sc92031_reset(dev); + mmiowb(); + + spin_unlock_bh(&priv->lock); + sc92031_enable_interrupts(dev); + + netif_device_attach(dev); + + if (netif_carrier_ok(dev)) + netif_wake_queue(dev); + else + netif_tx_disable(dev); + +out: + return 0; +} + +static DEFINE_PCI_DEVICE_TABLE(sc92031_pci_device_id_table) = { + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) }, + { PCI_DEVICE(0x1088, 0x2031) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); + +static struct pci_driver sc92031_pci_driver = { + .name = SC92031_NAME, + .id_table = sc92031_pci_device_id_table, + .probe = sc92031_probe, + .remove = __devexit_p(sc92031_remove), + .suspend = sc92031_suspend, + .resume = sc92031_resume, +}; + +static int __init sc92031_init(void) +{ + return pci_register_driver(&sc92031_pci_driver); +} + +static void __exit sc92031_exit(void) +{ + pci_unregister_driver(&sc92031_pci_driver); +} + +module_init(sc92031_init); +module_exit(sc92031_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cesar Eduardo Barros "); +MODULE_DESCRIPTION("Silan SC92031 PCI Fast Ethernet Adapter driver"); -- cgit v1.2.3-18-g5258 From 0a3360e1e18fc6bbe10bebe416db42de5fa02dbd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 29 Oct 2011 08:09:01 +0000 Subject: net/ethernet: Move mac89x0.c from apple to cirrus Macintosh CS89x0 based ethernet cards use a Crystal Semiconductor (Now Cirrus Logic) CS89x0 chip, so the mac89x0 driver should be in drivers/net/ethernet/cirrus instead of drivers/net/ethernet/apple. This also fixes a build problem, as the driver needs a header file from the cirrus directory: drivers/net/ethernet/apple/mac89x0.c:107:20: error: cs89x0.h: No such file or directory Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller --- drivers/net/ethernet/apple/Kconfig | 12 - drivers/net/ethernet/apple/Makefile | 1 - drivers/net/ethernet/apple/mac89x0.c | 634 ---------------------------------- drivers/net/ethernet/cirrus/Kconfig | 14 +- drivers/net/ethernet/cirrus/Makefile | 1 + drivers/net/ethernet/cirrus/mac89x0.c | 634 ++++++++++++++++++++++++++++++++++ 6 files changed, 648 insertions(+), 648 deletions(-) delete mode 100644 drivers/net/ethernet/apple/mac89x0.c create mode 100644 drivers/net/ethernet/cirrus/mac89x0.c (limited to 'drivers/net') diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig index a759d5483ab..1375e2dc946 100644 --- a/drivers/net/ethernet/apple/Kconfig +++ b/drivers/net/ethernet/apple/Kconfig @@ -52,18 +52,6 @@ config BMAC To compile this driver as a module, choose M here: the module will be called bmac. -config MAC89x0 - tristate "Macintosh CS89x0 based ethernet cards" - depends on MAC - ---help--- - Support for CS89x0 chipset based Ethernet cards. If you have a - Nubus or LC-PDS network (Ethernet) card of this type, say Y and - read the Ethernet-HOWTO, available from - . - - To compile this driver as a module, choose M here. This module will - be called mac89x0. - config MACMACE bool "Macintosh (AV) onboard MACE ethernet" depends on MAC diff --git a/drivers/net/ethernet/apple/Makefile b/drivers/net/ethernet/apple/Makefile index 0d3a5919c95..86eaa17af0f 100644 --- a/drivers/net/ethernet/apple/Makefile +++ b/drivers/net/ethernet/apple/Makefile @@ -4,5 +4,4 @@ obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o -obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_MACMACE) += macmace.o diff --git a/drivers/net/ethernet/apple/mac89x0.c b/drivers/net/ethernet/apple/mac89x0.c deleted file mode 100644 index 83781f316d1..00000000000 --- a/drivers/net/ethernet/apple/mac89x0.c +++ /dev/null @@ -1,634 +0,0 @@ -/* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ -/* - Written 1996 by Russell Nelson, with reference to skeleton.c - written 1993-1994 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached at nelson@crynwr.com, Crynwr - Software, 11 Grant St., Potsdam, NY 13676 - - Changelog: - - Mike Cruse : mcruse@cti-ltd.com - : Changes for Linux 2.0 compatibility. - : Added dev_id parameter in net_interrupt(), - : request_irq() and free_irq(). Just NULL for now. - - Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros - : in net_open() and net_close() so kerneld would know - : that the module is in use and wouldn't eject the - : driver prematurely. - - Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c - : as an example. Disabled autoprobing in init_module(), - : not a good thing to do to other devices while Linux - : is running from all accounts. - - Alan Cox : Removed 1.2 support, added 2.1 extra counters. - - David Huggins-Daines - - Split this off into mac89x0.c, and gutted it of all parts which are - not relevant to the existing CS8900 cards on the Macintosh - (i.e. basically the Daynaport CS and LC cards). To be precise: - - * Removed all the media-detection stuff, because these cards are - TP-only. - - * Lobotomized the ISA interrupt bogosity, because these cards use - a hardwired NuBus interrupt and a magic ISAIRQ value in the card. - - * Basically eliminated everything not relevant to getting the - cards minimally functioning on the Macintosh. - - I might add that these cards are badly designed even from the Mac - standpoint, in that Dayna, in their infinite wisdom, used NuBus slot - I/O space and NuBus interrupts for these cards, but neglected to - provide anything even remotely resembling a NuBus ROM. Therefore we - have to probe for them in a brain-damaged ISA-like fashion. - - Arnaldo Carvalho de Melo - 11/01/2001 - check kmalloc and release the allocated memory on failure in - mac89x0_probe and in init_module - use local_irq_{save,restore}(flags) in net_get_stat, not just - local_irq_{dis,en}able() -*/ - -static char *version = -"cs89x0.c:v1.02 11/26/96 Russell Nelson \n"; - -/* ======================= configure the driver here ======================= */ - -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -/* ======================= end of configuration ======================= */ - - -/* Always include 'config.h' first in case the user wants to turn on - or override something. */ -#include - -/* - Sources: - - Crynwr packet driver epktisa. - - Crystal Semiconductor data sheets. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "cs89x0.h" - -static unsigned int net_debug = NET_DEBUG; - -/* Information that need to be kept for each board. */ -struct net_local { - int chip_type; /* one of: CS8900, CS8920, CS8920M */ - char chip_revision; /* revision letter of the chip ('A'...) */ - int send_cmd; /* the propercommand used to send a packet. */ - int rx_mode; - int curr_rx_cfg; - int send_underrun; /* keep track of how many underruns in a row we get */ - struct sk_buff *skb; -}; - -/* Index to functions, as function prototypes. */ - -#if 0 -extern void reset_chip(struct net_device *dev); -#endif -static int net_open(struct net_device *dev); -static int net_send_packet(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t net_interrupt(int irq, void *dev_id); -static void set_multicast_list(struct net_device *dev); -static void net_rx(struct net_device *dev); -static int net_close(struct net_device *dev); -static struct net_device_stats *net_get_stats(struct net_device *dev); -static int set_mac_address(struct net_device *dev, void *addr); - - -/* Example routines you must write ;->. */ -#define tx_done(dev) 1 - -/* For reading/writing registers ISA-style */ -static inline int -readreg_io(struct net_device *dev, int portno) -{ - nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); - return swab16(nubus_readw(dev->base_addr + DATA_PORT)); -} - -static inline void -writereg_io(struct net_device *dev, int portno, int value) -{ - nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); - nubus_writew(swab16(value), dev->base_addr + DATA_PORT); -} - -/* These are for reading/writing registers in shared memory */ -static inline int -readreg(struct net_device *dev, int portno) -{ - return swab16(nubus_readw(dev->mem_start + portno)); -} - -static inline void -writereg(struct net_device *dev, int portno, int value) -{ - nubus_writew(swab16(value), dev->mem_start + portno); -} - -static const struct net_device_ops mac89x0_netdev_ops = { - .ndo_open = net_open, - .ndo_stop = net_close, - .ndo_start_xmit = net_send_packet, - .ndo_get_stats = net_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, -}; - -/* Probe for the CS8900 card in slot E. We won't bother looking - anywhere else until we have a really good reason to do so. */ -struct net_device * __init mac89x0_probe(int unit) -{ - struct net_device *dev; - static int once_is_enough; - struct net_local *lp; - static unsigned version_printed; - int i, slot; - unsigned rev_type = 0; - unsigned long ioaddr; - unsigned short sig; - int err = -ENODEV; - - if (!MACH_IS_MAC) - return ERR_PTR(-ENODEV); - - dev = alloc_etherdev(sizeof(struct net_local)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - - if (once_is_enough) - goto out; - once_is_enough = 1; - - /* We might have to parameterize this later */ - slot = 0xE; - /* Get out now if there's a real NuBus card in slot E */ - if (nubus_find_slot(slot, NULL) != NULL) - goto out; - - /* The pseudo-ISA bits always live at offset 0x300 (gee, - wonder why...) */ - ioaddr = (unsigned long) - nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); - { - unsigned long flags; - int card_present; - - local_irq_save(flags); - card_present = (hwreg_present((void*) ioaddr+4) && - hwreg_present((void*) ioaddr + DATA_PORT)); - local_irq_restore(flags); - - if (!card_present) - goto out; - } - - nubus_writew(0, ioaddr + ADD_PORT); - sig = nubus_readw(ioaddr + DATA_PORT); - if (sig != swab16(CHIP_EISA_ID_SIG)) - goto out; - - /* Initialize the net_device structure. */ - lp = netdev_priv(dev); - - /* Fill in the 'dev' fields. */ - dev->base_addr = ioaddr; - dev->mem_start = (unsigned long) - nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); - dev->mem_end = dev->mem_start + 0x1000; - - /* Turn on shared memory */ - writereg_io(dev, PP_BusCTL, MEMORY_ON); - - /* get the chip type */ - rev_type = readreg(dev, PRODUCT_ID_ADD); - lp->chip_type = rev_type &~ REVISON_BITS; - lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; - - /* Check the chip type and revision in order to set the correct send command - CS8920 revision C and CS8900 revision F can use the faster send. */ - lp->send_cmd = TX_AFTER_381; - if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') - lp->send_cmd = TX_NOW; - if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') - lp->send_cmd = TX_NOW; - - if (net_debug && version_printed++ == 0) - printk(version); - - printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", - dev->name, - lp->chip_type==CS8900?'0':'2', - lp->chip_type==CS8920M?"M":"", - lp->chip_revision, - dev->base_addr); - - /* Try to read the MAC address */ - if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { - printk("\nmac89x0: No EEPROM, giving up now.\n"); - goto out1; - } else { - for (i = 0; i < ETH_ALEN; i += 2) { - /* Big-endian (why??!) */ - unsigned short s = readreg(dev, PP_IA + i); - dev->dev_addr[i] = s >> 8; - dev->dev_addr[i+1] = s & 0xff; - } - } - - dev->irq = SLOT2IRQ(slot); - - /* print the IRQ and ethernet address. */ - - printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr); - - dev->netdev_ops = &mac89x0_netdev_ops; - - err = register_netdev(dev); - if (err) - goto out1; - return NULL; -out1: - nubus_writew(0, dev->base_addr + ADD_PORT); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -#if 0 -/* This is useful for something, but I don't know what yet. */ -void __init reset_chip(struct net_device *dev) -{ - int reset_start_time; - - writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); - - /* wait 30 ms */ - msleep_interruptible(30); - - /* Wait until the chip is reset */ - reset_start_time = jiffies; - while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) - ; -} -#endif - -/* Open/initialize the board. This is called (in the current kernel) - sometime after booting when the 'ifconfig' program is run. - - This routine should set everything up anew at each open, even - registers that "should" only need to be set once at boot, so that - there is non-reboot way to recover if something goes wrong. - */ -static int -net_open(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - int i; - - /* Disable the interrupt for now */ - writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); - - /* Grab the interrupt */ - if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev)) - return -EAGAIN; - - /* Set up the IRQ - Apparently magic */ - if (lp->chip_type == CS8900) - writereg(dev, PP_CS8900_ISAINT, 0); - else - writereg(dev, PP_CS8920_ISAINT, 0); - - /* set the Ethernet address */ - for (i=0; i < ETH_ALEN/2; i++) - writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); - - /* Turn on both receive and transmit operations */ - writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); - - /* Receive only error free packets addressed to this card */ - lp->rx_mode = 0; - writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); - - lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; - - writereg(dev, PP_RxCFG, lp->curr_rx_cfg); - - writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | - TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); - - writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | - TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); - - /* now that we've got our act together, enable everything */ - writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); - netif_start_queue(dev); - return 0; -} - -static int -net_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - unsigned long flags; - - if (net_debug > 3) - printk("%s: sent %d byte packet of type %x\n", - dev->name, skb->len, - (skb->data[ETH_ALEN+ETH_ALEN] << 8) - | skb->data[ETH_ALEN+ETH_ALEN+1]); - - /* keep the upload from being interrupted, since we - ask the chip to start transmitting before the - whole packet has been completely uploaded. */ - local_irq_save(flags); - netif_stop_queue(dev); - - /* initiate a transmit sequence */ - writereg(dev, PP_TxCMD, lp->send_cmd); - writereg(dev, PP_TxLength, skb->len); - - /* Test to see if the chip has allocated memory for the packet */ - if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { - /* Gasp! It hasn't. But that shouldn't happen since - we're waiting for TxOk, so return 1 and requeue this packet. */ - local_irq_restore(flags); - return NETDEV_TX_BUSY; - } - - /* Write the contents of the packet */ - skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame), - skb->len+1); - - local_irq_restore(flags); - dev_kfree_skb (skb); - - return NETDEV_TX_OK; -} - -/* The typical workload of the driver: - Handle the network interface interrupts. */ -static irqreturn_t net_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct net_local *lp; - int ioaddr, status; - - if (dev == NULL) { - printk ("net_interrupt(): irq %d for unknown device.\n", irq); - return IRQ_NONE; - } - - ioaddr = dev->base_addr; - lp = netdev_priv(dev); - - /* we MUST read all the events out of the ISQ, otherwise we'll never - get interrupted again. As a consequence, we can't have any limit - on the number of times we loop in the interrupt handler. The - hardware guarantees that eventually we'll run out of events. Of - course, if you're on a slow machine, and packets are arriving - faster than you can read them off, you're screwed. Hasta la - vista, baby! */ - while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) { - if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); - switch(status & ISQ_EVENT_MASK) { - case ISQ_RECEIVER_EVENT: - /* Got a packet(s). */ - net_rx(dev); - break; - case ISQ_TRANSMITTER_EVENT: - dev->stats.tx_packets++; - netif_wake_queue(dev); - if ((status & TX_OK) == 0) - dev->stats.tx_errors++; - if (status & TX_LOST_CRS) - dev->stats.tx_carrier_errors++; - if (status & TX_SQE_ERROR) - dev->stats.tx_heartbeat_errors++; - if (status & TX_LATE_COL) - dev->stats.tx_window_errors++; - if (status & TX_16_COL) - dev->stats.tx_aborted_errors++; - break; - case ISQ_BUFFER_EVENT: - if (status & READY_FOR_TX) { - /* we tried to transmit a packet earlier, - but inexplicably ran out of buffers. - That shouldn't happen since we only ever - load one packet. Shrug. Do the right - thing anyway. */ - netif_wake_queue(dev); - } - if (status & TX_UNDERRUN) { - if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); - lp->send_underrun++; - if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; - else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; - } - break; - case ISQ_RX_MISS_EVENT: - dev->stats.rx_missed_errors += (status >> 6); - break; - case ISQ_TX_COL_EVENT: - dev->stats.collisions += (status >> 6); - break; - } - } - return IRQ_HANDLED; -} - -/* We have a good packet(s), get it/them out of the buffers. */ -static void -net_rx(struct net_device *dev) -{ - struct sk_buff *skb; - int status, length; - - status = readreg(dev, PP_RxStatus); - if ((status & RX_OK) == 0) { - dev->stats.rx_errors++; - if (status & RX_RUNT) - dev->stats.rx_length_errors++; - if (status & RX_EXTRA_DATA) - dev->stats.rx_length_errors++; - if ((status & RX_CRC_ERROR) && - !(status & (RX_EXTRA_DATA|RX_RUNT))) - /* per str 172 */ - dev->stats.rx_crc_errors++; - if (status & RX_DRIBBLE) - dev->stats.rx_frame_errors++; - return; - } - - length = readreg(dev, PP_RxLength); - /* Malloc up new buffer. */ - skb = alloc_skb(length, GFP_ATOMIC); - if (skb == NULL) { - printk("%s: Memory squeeze, dropping packet.\n", dev->name); - dev->stats.rx_dropped++; - return; - } - skb_put(skb, length); - - skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame), - length); - - if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", - dev->name, length, - (skb->data[ETH_ALEN+ETH_ALEN] << 8) - | skb->data[ETH_ALEN+ETH_ALEN+1]); - - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; -} - -/* The inverse routine to net_open(). */ -static int -net_close(struct net_device *dev) -{ - - writereg(dev, PP_RxCFG, 0); - writereg(dev, PP_TxCFG, 0); - writereg(dev, PP_BufCFG, 0); - writereg(dev, PP_BusCTL, 0); - - netif_stop_queue(dev); - - free_irq(dev->irq, dev); - - /* Update the statistics here. */ - - return 0; - -} - -/* Get the current statistics. This may be called with the card open or - closed. */ -static struct net_device_stats * -net_get_stats(struct net_device *dev) -{ - unsigned long flags; - - local_irq_save(flags); - /* Update the statistics from the device registers. */ - dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); - dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6); - local_irq_restore(flags); - - return &dev->stats; -} - -static void set_multicast_list(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - - if(dev->flags&IFF_PROMISC) - { - lp->rx_mode = RX_ALL_ACCEPT; - } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { - /* The multicast-accept list is initialized to accept-all, and we - rely on higher-level filtering for now. */ - lp->rx_mode = RX_MULTCAST_ACCEPT; - } - else - lp->rx_mode = 0; - - writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); - - /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ - writereg(dev, PP_RxCFG, lp->curr_rx_cfg | - (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); -} - - -static int set_mac_address(struct net_device *dev, void *addr) -{ - int i; - printk("%s: Setting MAC address to ", dev->name); - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); - printk(".\n"); - /* set the Ethernet address */ - for (i=0; i < ETH_ALEN/2; i++) - writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); - - return 0; -} - -#ifdef MODULE - -static struct net_device *dev_cs89x0; -static int debug; - -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)"); -MODULE_LICENSE("GPL"); - -int __init -init_module(void) -{ - net_debug = debug; - dev_cs89x0 = mac89x0_probe(-1); - if (IS_ERR(dev_cs89x0)) { - printk(KERN_WARNING "mac89x0.c: No card found\n"); - return PTR_ERR(dev_cs89x0); - } - return 0; -} - -void -cleanup_module(void) -{ - unregister_netdev(dev_cs89x0); - nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT); - free_netdev(dev_cs89x0); -} -#endif /* MODULE */ diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig index 6cbb81ccc02..1f8648f099c 100644 --- a/drivers/net/ethernet/cirrus/Kconfig +++ b/drivers/net/ethernet/cirrus/Kconfig @@ -6,7 +6,7 @@ config NET_VENDOR_CIRRUS bool "Cirrus devices" default y depends on ISA || EISA || MACH_IXDP2351 || ARCH_IXDP2X01 \ - || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) + || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) || MAC ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -47,4 +47,16 @@ config EP93XX_ETH This is a driver for the ethernet hardware included in EP93xx CPUs. Say Y if you are building a kernel for EP93xx based devices. +config MAC89x0 + tristate "Macintosh CS89x0 based ethernet cards" + depends on MAC + ---help--- + Support for CS89x0 chipset based Ethernet cards. If you have a + Nubus or LC-PDS network (Ethernet) card of this type, say Y and + read the Ethernet-HOWTO, available from + . + + To compile this driver as a module, choose M here. This module will + be called mac89x0. + endif # NET_VENDOR_CIRRUS diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile index 14bd77e0cb5..ca245e2b5d9 100644 --- a/drivers/net/ethernet/cirrus/Makefile +++ b/drivers/net/ethernet/cirrus/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_CS89x0) += cs89x0.o obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o +obj-$(CONFIG_MAC89x0) += mac89x0.o diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c new file mode 100644 index 00000000000..83781f316d1 --- /dev/null +++ b/drivers/net/ethernet/cirrus/mac89x0.c @@ -0,0 +1,634 @@ +/* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ +/* + Written 1996 by Russell Nelson, with reference to skeleton.c + written 1993-1994 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached at nelson@crynwr.com, Crynwr + Software, 11 Grant St., Potsdam, NY 13676 + + Changelog: + + Mike Cruse : mcruse@cti-ltd.com + : Changes for Linux 2.0 compatibility. + : Added dev_id parameter in net_interrupt(), + : request_irq() and free_irq(). Just NULL for now. + + Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros + : in net_open() and net_close() so kerneld would know + : that the module is in use and wouldn't eject the + : driver prematurely. + + Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c + : as an example. Disabled autoprobing in init_module(), + : not a good thing to do to other devices while Linux + : is running from all accounts. + + Alan Cox : Removed 1.2 support, added 2.1 extra counters. + + David Huggins-Daines + + Split this off into mac89x0.c, and gutted it of all parts which are + not relevant to the existing CS8900 cards on the Macintosh + (i.e. basically the Daynaport CS and LC cards). To be precise: + + * Removed all the media-detection stuff, because these cards are + TP-only. + + * Lobotomized the ISA interrupt bogosity, because these cards use + a hardwired NuBus interrupt and a magic ISAIRQ value in the card. + + * Basically eliminated everything not relevant to getting the + cards minimally functioning on the Macintosh. + + I might add that these cards are badly designed even from the Mac + standpoint, in that Dayna, in their infinite wisdom, used NuBus slot + I/O space and NuBus interrupts for these cards, but neglected to + provide anything even remotely resembling a NuBus ROM. Therefore we + have to probe for them in a brain-damaged ISA-like fashion. + + Arnaldo Carvalho de Melo - 11/01/2001 + check kmalloc and release the allocated memory on failure in + mac89x0_probe and in init_module + use local_irq_{save,restore}(flags) in net_get_stat, not just + local_irq_{dis,en}able() +*/ + +static char *version = +"cs89x0.c:v1.02 11/26/96 Russell Nelson \n"; + +/* ======================= configure the driver here ======================= */ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* ======================= end of configuration ======================= */ + + +/* Always include 'config.h' first in case the user wants to turn on + or override something. */ +#include + +/* + Sources: + + Crynwr packet driver epktisa. + + Crystal Semiconductor data sheets. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cs89x0.h" + +static unsigned int net_debug = NET_DEBUG; + +/* Information that need to be kept for each board. */ +struct net_local { + int chip_type; /* one of: CS8900, CS8920, CS8920M */ + char chip_revision; /* revision letter of the chip ('A'...) */ + int send_cmd; /* the propercommand used to send a packet. */ + int rx_mode; + int curr_rx_cfg; + int send_underrun; /* keep track of how many underruns in a row we get */ + struct sk_buff *skb; +}; + +/* Index to functions, as function prototypes. */ + +#if 0 +extern void reset_chip(struct net_device *dev); +#endif +static int net_open(struct net_device *dev); +static int net_send_packet(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t net_interrupt(int irq, void *dev_id); +static void set_multicast_list(struct net_device *dev); +static void net_rx(struct net_device *dev); +static int net_close(struct net_device *dev); +static struct net_device_stats *net_get_stats(struct net_device *dev); +static int set_mac_address(struct net_device *dev, void *addr); + + +/* Example routines you must write ;->. */ +#define tx_done(dev) 1 + +/* For reading/writing registers ISA-style */ +static inline int +readreg_io(struct net_device *dev, int portno) +{ + nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); + return swab16(nubus_readw(dev->base_addr + DATA_PORT)); +} + +static inline void +writereg_io(struct net_device *dev, int portno, int value) +{ + nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); + nubus_writew(swab16(value), dev->base_addr + DATA_PORT); +} + +/* These are for reading/writing registers in shared memory */ +static inline int +readreg(struct net_device *dev, int portno) +{ + return swab16(nubus_readw(dev->mem_start + portno)); +} + +static inline void +writereg(struct net_device *dev, int portno, int value) +{ + nubus_writew(swab16(value), dev->mem_start + portno); +} + +static const struct net_device_ops mac89x0_netdev_ops = { + .ndo_open = net_open, + .ndo_stop = net_close, + .ndo_start_xmit = net_send_packet, + .ndo_get_stats = net_get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +/* Probe for the CS8900 card in slot E. We won't bother looking + anywhere else until we have a really good reason to do so. */ +struct net_device * __init mac89x0_probe(int unit) +{ + struct net_device *dev; + static int once_is_enough; + struct net_local *lp; + static unsigned version_printed; + int i, slot; + unsigned rev_type = 0; + unsigned long ioaddr; + unsigned short sig; + int err = -ENODEV; + + if (!MACH_IS_MAC) + return ERR_PTR(-ENODEV); + + dev = alloc_etherdev(sizeof(struct net_local)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + } + + if (once_is_enough) + goto out; + once_is_enough = 1; + + /* We might have to parameterize this later */ + slot = 0xE; + /* Get out now if there's a real NuBus card in slot E */ + if (nubus_find_slot(slot, NULL) != NULL) + goto out; + + /* The pseudo-ISA bits always live at offset 0x300 (gee, + wonder why...) */ + ioaddr = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); + { + unsigned long flags; + int card_present; + + local_irq_save(flags); + card_present = (hwreg_present((void*) ioaddr+4) && + hwreg_present((void*) ioaddr + DATA_PORT)); + local_irq_restore(flags); + + if (!card_present) + goto out; + } + + nubus_writew(0, ioaddr + ADD_PORT); + sig = nubus_readw(ioaddr + DATA_PORT); + if (sig != swab16(CHIP_EISA_ID_SIG)) + goto out; + + /* Initialize the net_device structure. */ + lp = netdev_priv(dev); + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + dev->mem_start = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); + dev->mem_end = dev->mem_start + 0x1000; + + /* Turn on shared memory */ + writereg_io(dev, PP_BusCTL, MEMORY_ON); + + /* get the chip type */ + rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; + lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + /* Check the chip type and revision in order to set the correct send command + CS8920 revision C and CS8900 revision F can use the faster send. */ + lp->send_cmd = TX_AFTER_381; + if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') + lp->send_cmd = TX_NOW; + if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') + lp->send_cmd = TX_NOW; + + if (net_debug && version_printed++ == 0) + printk(version); + + printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", + dev->name, + lp->chip_type==CS8900?'0':'2', + lp->chip_type==CS8920M?"M":"", + lp->chip_revision, + dev->base_addr); + + /* Try to read the MAC address */ + if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { + printk("\nmac89x0: No EEPROM, giving up now.\n"); + goto out1; + } else { + for (i = 0; i < ETH_ALEN; i += 2) { + /* Big-endian (why??!) */ + unsigned short s = readreg(dev, PP_IA + i); + dev->dev_addr[i] = s >> 8; + dev->dev_addr[i+1] = s & 0xff; + } + } + + dev->irq = SLOT2IRQ(slot); + + /* print the IRQ and ethernet address. */ + + printk(" IRQ %d ADDR %pM\n", dev->irq, dev->dev_addr); + + dev->netdev_ops = &mac89x0_netdev_ops; + + err = register_netdev(dev); + if (err) + goto out1; + return NULL; +out1: + nubus_writew(0, dev->base_addr + ADD_PORT); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +#if 0 +/* This is useful for something, but I don't know what yet. */ +void __init reset_chip(struct net_device *dev) +{ + int reset_start_time; + + writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); + + /* wait 30 ms */ + msleep_interruptible(30); + + /* Wait until the chip is reset */ + reset_start_time = jiffies; + while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) + ; +} +#endif + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ +static int +net_open(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + int i; + + /* Disable the interrupt for now */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); + + /* Grab the interrupt */ + if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev)) + return -EAGAIN; + + /* Set up the IRQ - Apparently magic */ + if (lp->chip_type == CS8900) + writereg(dev, PP_CS8900_ISAINT, 0); + else + writereg(dev, PP_CS8920_ISAINT, 0); + + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + /* Turn on both receive and transmit operations */ + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); + + /* Receive only error free packets addressed to this card */ + lp->rx_mode = 0; + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); + + lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; + + writereg(dev, PP_RxCFG, lp->curr_rx_cfg); + + writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | + TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); + + writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | + TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); + + /* now that we've got our act together, enable everything */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); + netif_start_queue(dev); + return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned long flags; + + if (net_debug > 3) + printk("%s: sent %d byte packet of type %x\n", + dev->name, skb->len, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + /* keep the upload from being interrupted, since we + ask the chip to start transmitting before the + whole packet has been completely uploaded. */ + local_irq_save(flags); + netif_stop_queue(dev); + + /* initiate a transmit sequence */ + writereg(dev, PP_TxCMD, lp->send_cmd); + writereg(dev, PP_TxLength, skb->len); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* Gasp! It hasn't. But that shouldn't happen since + we're waiting for TxOk, so return 1 and requeue this packet. */ + local_irq_restore(flags); + return NETDEV_TX_BUSY; + } + + /* Write the contents of the packet */ + skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame), + skb->len+1); + + local_irq_restore(flags); + dev_kfree_skb (skb); + + return NETDEV_TX_OK; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static irqreturn_t net_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct net_local *lp; + int ioaddr, status; + + if (dev == NULL) { + printk ("net_interrupt(): irq %d for unknown device.\n", irq); + return IRQ_NONE; + } + + ioaddr = dev->base_addr; + lp = netdev_priv(dev); + + /* we MUST read all the events out of the ISQ, otherwise we'll never + get interrupted again. As a consequence, we can't have any limit + on the number of times we loop in the interrupt handler. The + hardware guarantees that eventually we'll run out of events. Of + course, if you're on a slow machine, and packets are arriving + faster than you can read them off, you're screwed. Hasta la + vista, baby! */ + while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) { + if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); + switch(status & ISQ_EVENT_MASK) { + case ISQ_RECEIVER_EVENT: + /* Got a packet(s). */ + net_rx(dev); + break; + case ISQ_TRANSMITTER_EVENT: + dev->stats.tx_packets++; + netif_wake_queue(dev); + if ((status & TX_OK) == 0) + dev->stats.tx_errors++; + if (status & TX_LOST_CRS) + dev->stats.tx_carrier_errors++; + if (status & TX_SQE_ERROR) + dev->stats.tx_heartbeat_errors++; + if (status & TX_LATE_COL) + dev->stats.tx_window_errors++; + if (status & TX_16_COL) + dev->stats.tx_aborted_errors++; + break; + case ISQ_BUFFER_EVENT: + if (status & READY_FOR_TX) { + /* we tried to transmit a packet earlier, + but inexplicably ran out of buffers. + That shouldn't happen since we only ever + load one packet. Shrug. Do the right + thing anyway. */ + netif_wake_queue(dev); + } + if (status & TX_UNDERRUN) { + if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); + lp->send_underrun++; + if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; + else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; + } + break; + case ISQ_RX_MISS_EVENT: + dev->stats.rx_missed_errors += (status >> 6); + break; + case ISQ_TX_COL_EVENT: + dev->stats.collisions += (status >> 6); + break; + } + } + return IRQ_HANDLED; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct net_device *dev) +{ + struct sk_buff *skb; + int status, length; + + status = readreg(dev, PP_RxStatus); + if ((status & RX_OK) == 0) { + dev->stats.rx_errors++; + if (status & RX_RUNT) + dev->stats.rx_length_errors++; + if (status & RX_EXTRA_DATA) + dev->stats.rx_length_errors++; + if ((status & RX_CRC_ERROR) && + !(status & (RX_EXTRA_DATA|RX_RUNT))) + /* per str 172 */ + dev->stats.rx_crc_errors++; + if (status & RX_DRIBBLE) + dev->stats.rx_frame_errors++; + return; + } + + length = readreg(dev, PP_RxLength); + /* Malloc up new buffer. */ + skb = alloc_skb(length, GFP_ATOMIC); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + dev->stats.rx_dropped++; + return; + } + skb_put(skb, length); + + skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame), + length); + + if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += length; +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct net_device *dev) +{ + + writereg(dev, PP_RxCFG, 0); + writereg(dev, PP_TxCFG, 0); + writereg(dev, PP_BufCFG, 0); + writereg(dev, PP_BusCTL, 0); + + netif_stop_queue(dev); + + free_irq(dev->irq, dev); + + /* Update the statistics here. */ + + return 0; + +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +net_get_stats(struct net_device *dev) +{ + unsigned long flags; + + local_irq_save(flags); + /* Update the statistics from the device registers. */ + dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); + dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6); + local_irq_restore(flags); + + return &dev->stats; +} + +static void set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + + if(dev->flags&IFF_PROMISC) + { + lp->rx_mode = RX_ALL_ACCEPT; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + /* The multicast-accept list is initialized to accept-all, and we + rely on higher-level filtering for now. */ + lp->rx_mode = RX_MULTCAST_ACCEPT; + } + else + lp->rx_mode = 0; + + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); + + /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ + writereg(dev, PP_RxCFG, lp->curr_rx_cfg | + (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); +} + + +static int set_mac_address(struct net_device *dev, void *addr) +{ + int i; + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + return 0; +} + +#ifdef MODULE + +static struct net_device *dev_cs89x0; +static int debug; + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)"); +MODULE_LICENSE("GPL"); + +int __init +init_module(void) +{ + net_debug = debug; + dev_cs89x0 = mac89x0_probe(-1); + if (IS_ERR(dev_cs89x0)) { + printk(KERN_WARNING "mac89x0.c: No card found\n"); + return PTR_ERR(dev_cs89x0); + } + return 0; +} + +void +cleanup_module(void) +{ + unregister_netdev(dev_cs89x0); + nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT); + free_netdev(dev_cs89x0); +} +#endif /* MODULE */ -- cgit v1.2.3-18-g5258 From 2edcd4ca43df3c1d1d392753531cc73a53e709ba Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 2 Nov 2011 01:49:44 -0400 Subject: net: fix typo in drivers/net/ethernet/xilinx/ll_temac_main.c Commit 9e903e085262 ("net: add skb frag size accessors") used frag_size instead of skb_frag_size in this file. Fixes this build error: drivers/net/ethernet/xilinx/ll_temac_main.c: In function 'temac_start_xmit': drivers/net/ethernet/xilinx/ll_temac_main.c:717:3: error: implicit declaration of function 'frag_size' [-Werror=implicit-function-declaration] Signed-off-by: Stephen Rothwell Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/ll_temac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 4d1658e78de..caf3659e173 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -716,8 +716,8 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; cur_p->phys = dma_map_single(ndev->dev.parent, skb_frag_address(frag), - frag_size(frag), DMA_TO_DEVICE); - cur_p->len = frag_size(frag); + skb_frag_size(frag), DMA_TO_DEVICE); + cur_p->len = skb_frag_size(frag); cur_p->app0 = 0; frag++; } -- cgit v1.2.3-18-g5258 From 34c9ef8bc66e21bdecb215b2fb7d93092468d27d Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Fri, 21 Oct 2011 04:33:47 +0000 Subject: e1000e: demote a debugging WARN to a debug log message This debugging message was recently added but it does not need to be as alarming as a WARN. Signed-off-by: Bruce Allan Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 6a17c62cb86..e2a80a283fd 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -866,8 +866,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) if (test_and_set_bit(__E1000_ACCESS_SHARED_RESOURCE, &hw->adapter->state)) { - WARN(1, "e1000e: %s: contention for Phy access\n", - hw->adapter->netdev->name); + e_dbg("contention for Phy access\n"); return -E1000_ERR_PHY; } -- cgit v1.2.3-18-g5258 From 243559f436f26b571ea3a4e70ff082892dc58f16 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Sat, 22 Oct 2011 05:18:10 +0000 Subject: e100: make sure vlan support isn't advertised on old adapters e100 parts don't support vlan offload but they generally do allow use of vlans in higher software layers via the 8021q module. That said, there are a couple of really old revisions of e100 hardware that don't even allow the longer frame sizes required for vlan use with standard MTU. Use the VLAN_CHALLENGED flag to prevent vlan binding to these devices. Reported-by: Michael Tokarev Signed-off-by: Jesse Brandeburg CC: Michael Tokarev CC: David Lamparter Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e100.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index ae17cd1a907..5a2fdf7a00c 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -2810,6 +2810,10 @@ static int __devinit e100_probe(struct pci_dev *pdev, e100_get_defaults(nic); + /* D100 MAC doesn't allow rx of vlan packets with normal MTU */ + if (nic->mac < mac_82558_D101_A4) + netdev->features |= NETIF_F_VLAN_CHALLENGED; + /* locks must be initialized before calling hw_reset */ spin_lock_init(&nic->cb_lock); spin_lock_init(&nic->cmd_lock); -- cgit v1.2.3-18-g5258 From d5a0e3640c05b7d07c548f9f8f986dbb87cfad98 Mon Sep 17 00:00:00 2001 From: "Kantecki, Tomasz" Date: Mon, 17 Oct 2011 22:06:59 +0000 Subject: igb: Fix for I347AT4 PHY cable length unit detection The PHY cable length unit detection was not using the correct the correct PHY data variable for I347AT4. Signed-off-by: Tomasz Kantecki Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 7edf31efe75..b17d7c20f81 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -1687,7 +1687,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) if (ret_val) goto out; - is_cm = !(phy_data & I347AT4_PCDC_CABLE_LENGTH_UNIT); + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); /* Populate the phy structure with cable length in meters */ phy->min_cable_length = phy_data / (is_cm ? 100 : 1); -- cgit v1.2.3-18-g5258 From 232ef6bc451de2bc17c22fd116838cd89b94e1c1 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 19 Oct 2011 07:41:58 +0000 Subject: ixgbe: Fix link issues caused by a reset while interface is down Interface fails to obtain link on 82599 SFP in the following scenario: 1. Set advertised speed to GB: ethtool -s eth0 advertise 0x20 2. Bring interface down ip link set eth0 down 3. Issue any command that leads to a reset: ethtool -t eth0 4. Bring link back up: ip link set eth0 up Following patch makes sure that the driver flaps the Tx laser every time ixgbe_start_hw() is called, and not only when the speed is set. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 09b8e88b299..b7abf43a877 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6125,7 +6125,6 @@ static void ixgbe_sfp_link_config_subtask(struct ixgbe_adapter *adapter) autoneg = hw->phy.autoneg_advertised; if ((!autoneg) && (hw->mac.ops.get_link_capabilities)) hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiation); - hw->mac.autotry_restart = false; if (hw->mac.ops.setup_link) hw->mac.ops.setup_link(hw, autoneg, negotiation, true); -- cgit v1.2.3-18-g5258 From 93d3ce8fafb888702311fc8c5917faa4c25b8266 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 19 Oct 2011 07:59:55 +0000 Subject: ixgbe: fix disabling of Tx laser at probe register_netdev() calls ndo_set_features() which may result in HW reset which in turn will bring the laser back up. This patch moves ixgbe_laser_tx_disable() below register_netdev() in ixgbe_probe() to make sure laser is shut off on load. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index b7abf43a877..2e9fd9dccf0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7588,13 +7588,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, goto err_eeprom; } - /* power down the optics for multispeed fiber and 82599 SFP+ fiber */ - if (hw->mac.ops.disable_tx_laser && - ((hw->phy.multispeed_fiber) || - ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) && - (hw->mac.type == ixgbe_mac_82599EB)))) - hw->mac.ops.disable_tx_laser(hw); - setup_timer(&adapter->service_timer, &ixgbe_service_timer, (unsigned long) adapter); @@ -7692,6 +7685,13 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, if (err) goto err_register; + /* power down the optics for multispeed fiber and 82599 SFP+ fiber */ + if (hw->mac.ops.disable_tx_laser && + ((hw->phy.multispeed_fiber) || + ((hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) && + (hw->mac.type == ixgbe_mac_82599EB)))) + hw->mac.ops.disable_tx_laser(hw); + /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); -- cgit v1.2.3-18-g5258 From b120818e652965669d3f1abaeaa5c3ccdfb28126 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Sat, 15 Oct 2011 05:00:10 +0000 Subject: ixgbe: fix smatch splat due to missing NULL check ixgbe_ieee_ets and ixgbe_ieee_pfc are intialized at the same time. Do a check for both before configuring IEEE802.1Qaz. Also max_frame was causing a sparse warning resolved here as well. Reported-by: Dan Carpenter Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 33 ++++++++++----------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 2e9fd9dccf0..8ef92d1a6aa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3345,34 +3345,25 @@ static void ixgbe_configure_dcb(struct ixgbe_adapter *adapter) hw->mac.ops.set_vfta(&adapter->hw, 0, 0, true); - /* reconfigure the hardware */ - if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE) { #ifdef IXGBE_FCOE - if (adapter->netdev->features & NETIF_F_FCOE_MTU) - max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE); + if (adapter->netdev->features & NETIF_F_FCOE_MTU) + max_frame = max(max_frame, IXGBE_FCOE_JUMBO_FRAME_SIZE); #endif + + /* reconfigure the hardware */ + if (adapter->dcbx_cap & DCB_CAP_DCBX_VER_CEE) { ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame, DCB_TX_CONFIG); ixgbe_dcb_calculate_tc_credits(hw, &adapter->dcb_cfg, max_frame, DCB_RX_CONFIG); ixgbe_dcb_hw_config(hw, &adapter->dcb_cfg); - } else { - struct net_device *dev = adapter->netdev; - - if (adapter->ixgbe_ieee_ets) { - struct ieee_ets *ets = adapter->ixgbe_ieee_ets; - int max_frame = dev->mtu + ETH_HLEN + ETH_FCS_LEN; - - ixgbe_dcb_hw_ets(&adapter->hw, ets, max_frame); - } - - if (adapter->ixgbe_ieee_pfc) { - struct ieee_pfc *pfc = adapter->ixgbe_ieee_pfc; - u8 *prio_tc = adapter->ixgbe_ieee_ets->prio_tc; - - ixgbe_dcb_hw_pfc_config(&adapter->hw, pfc->pfc_en, - prio_tc); - } + } else if (adapter->ixgbe_ieee_ets && adapter->ixgbe_ieee_pfc) { + ixgbe_dcb_hw_ets(&adapter->hw, + adapter->ixgbe_ieee_ets, + max_frame); + ixgbe_dcb_hw_pfc_config(&adapter->hw, + adapter->ixgbe_ieee_pfc->pfc_en, + adapter->ixgbe_ieee_ets->prio_tc); } /* Enable RSS Hash per TC */ -- cgit v1.2.3-18-g5258 From 9487dc844054e1fc691fb82f4e19da337e2ca35e Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Fri, 21 Oct 2011 07:55:15 +0000 Subject: ixgbe: Fix compiler warnings Wrap SR-IOV specific functions in CONFIG_PCI_IOV to avoid compiler warnings. Signed-off-by: Greg Rose Tested-by: Sibai Li Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index 5a7e1eb3359..4a5d8897faa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -42,10 +42,12 @@ int ixgbe_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); int ixgbe_ndo_get_vf_config(struct net_device *netdev, int vf, struct ifla_vf_info *ivi); void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter); +#ifdef CONFIG_PCI_IOV void ixgbe_disable_sriov(struct ixgbe_adapter *adapter); void ixgbe_enable_sriov(struct ixgbe_adapter *adapter, const struct ixgbe_info *ii); int ixgbe_check_vf_assignment(struct ixgbe_adapter *adapter); +#endif #endif /* _IXGBE_SRIOV_H_ */ -- cgit v1.2.3-18-g5258 From 331bcf45feb76d507a769d9d3b26ff5626804117 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Sat, 22 Oct 2011 05:21:32 +0000 Subject: ixgbe: fix reading of the buffer returned by the firmware This patch fixes some issues found in the buffer read portion of ixgbe_host_interface_command() - use `bi` as the buffer index counter instead of `i` - add conversion to native cpu byte ordering on register read - fix conversion from bytes to dword - use dword_len instead of buf_len when reading the register Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 834f044be4c..f1365fef4ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -3344,7 +3344,7 @@ static u8 ixgbe_calculate_checksum(u8 *buffer, u32 length) static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer, u32 length) { - u32 hicr, i; + u32 hicr, i, bi; u32 hdr_size = sizeof(struct ixgbe_hic_hdr); u8 buf_len, dword_len; @@ -3398,9 +3398,9 @@ static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer, dword_len = hdr_size >> 2; /* first pull in the header so we know the buffer length */ - for (i = 0; i < dword_len; i++) { - buffer[i] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, i); - le32_to_cpus(&buffer[i]); + for (bi = 0; bi < dword_len; bi++) { + buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi); + le32_to_cpus(&buffer[bi]); } /* If there is any thing in data position pull it in */ @@ -3414,12 +3414,14 @@ static s32 ixgbe_host_interface_command(struct ixgbe_hw *hw, u32 *buffer, goto out; } - /* Calculate length in DWORDs, add one for odd lengths */ - dword_len = (buf_len + 1) >> 2; + /* Calculate length in DWORDs, add 3 for odd lengths */ + dword_len = (buf_len + 3) >> 2; - /* Pull in the rest of the buffer (i is where we left off)*/ - for (; i < buf_len; i++) - buffer[i] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, i); + /* Pull in the rest of the buffer (bi is where we left off)*/ + for (; bi <= dword_len; bi++) { + buffer[bi] = IXGBE_READ_REG_ARRAY(hw, IXGBE_FLEX_MNG, bi); + le32_to_cpus(&buffer[bi]); + } out: return ret_val; -- cgit v1.2.3-18-g5258 From 8599e251b3dd18c7bcb342d5b4acecc420f43606 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Wed, 19 Oct 2011 08:48:49 +0000 Subject: ixgbe: DCB, return max for IEEE traffic classes Returning the max traffic classes on get requests simplifies user space configurations because applications will know explicitly how many traffic classes can be used. Typical switch implementations use 2 or 3 traffic classes so this not seen often today. And user space can learn the number of traffic classes by return codes but this allows user space to configure ixgbe correctly at the start. Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index 3631d639d86..33b93ffb87c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -561,11 +561,12 @@ static int ixgbe_dcbnl_ieee_getets(struct net_device *dev, struct ixgbe_adapter *adapter = netdev_priv(dev); struct ieee_ets *my_ets = adapter->ixgbe_ieee_ets; + ets->ets_cap = adapter->dcb_cfg.num_tcs.pg_tcs; + /* No IEEE PFC settings available */ if (!my_ets) - return -EINVAL; + return 0; - ets->ets_cap = adapter->dcb_cfg.num_tcs.pg_tcs; ets->cbs = my_ets->cbs; memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)); memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)); @@ -621,11 +622,12 @@ static int ixgbe_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *my_pfc = adapter->ixgbe_ieee_pfc; int i; + pfc->pfc_cap = adapter->dcb_cfg.num_tcs.pfc_tcs; + /* No IEEE PFC settings available */ if (!my_pfc) - return -EINVAL; + return 0; - pfc->pfc_cap = adapter->dcb_cfg.num_tcs.pfc_tcs; pfc->pfc_en = my_pfc->pfc_en; pfc->mbc = my_pfc->mbc; pfc->delay = my_pfc->delay; -- cgit v1.2.3-18-g5258 From c1a7e1ebc17a9243d99ba0432d1138d74114dea7 Mon Sep 17 00:00:00 2001 From: Greg Rose Date: Thu, 20 Oct 2011 04:14:49 +0000 Subject: ixgbevf: Update release version Signed-off-by: Greg Rose Tested-by: Sibai Li Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 5e92cc2079b..4c8e19951d5 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -54,7 +54,7 @@ char ixgbevf_driver_name[] = "ixgbevf"; static const char ixgbevf_driver_string[] = "Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver"; -#define DRV_VERSION "2.1.0-k" +#define DRV_VERSION "2.2.0-k" const char ixgbevf_driver_version[] = DRV_VERSION; static char ixgbevf_copyright[] = "Copyright (c) 2009 - 2010 Intel Corporation."; -- cgit v1.2.3-18-g5258 From 016f97b11b3c7fe834260150d0f9cb36d06b2eb8 Mon Sep 17 00:00:00 2001 From: Padmanabh Ratnakar Date: Thu, 3 Nov 2011 01:49:13 +0000 Subject: be2net: Fix endian issue in RX filter command Use cpu_to_le32() for mcast_num field in RX filter command as this field is of type u32. Signed-off-by: Padmanabh Ratnakar Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 824b8e6021f..bd8332cbf61 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1540,7 +1540,7 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) req->if_flags_mask = req->if_flags = cpu_to_le32(BE_IF_FLAGS_MULTICAST); - req->mcast_num = cpu_to_le16(netdev_mc_count(adapter->netdev)); + req->mcast_num = cpu_to_le32(netdev_mc_count(adapter->netdev)); netdev_for_each_mc_addr(ha, adapter->netdev) memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN); } -- cgit v1.2.3-18-g5258 From 1610c79f1e9545d0a64dc6bb4f9affdfcf1d5726 Mon Sep 17 00:00:00 2001 From: Padmanabh Ratnakar Date: Thu, 3 Nov 2011 01:49:27 +0000 Subject: be2net: Fix disabling multicast promiscous mode If user tries to disable multicast promiscous mode, the adapter remains in this mode as resetting the multicast promiscous mode was missing in RX filter command. Fixed this. Signed-off-by: Padmanabh Ratnakar Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index bd8332cbf61..03fe7cde2e6 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1540,6 +1540,13 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) req->if_flags_mask = req->if_flags = cpu_to_le32(BE_IF_FLAGS_MULTICAST); + + /* Reset mcast promisc mode if already set by setting mask + * and not setting flags field + */ + req->if_flags_mask |= + cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS); + req->mcast_num = cpu_to_le32(netdev_mc_count(adapter->netdev)); netdev_for_each_mc_addr(ha, adapter->netdev) memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN); -- cgit v1.2.3-18-g5258 From 9372cacb300df3ee0a8be8a25bea15d16a95c583 Mon Sep 17 00:00:00 2001 From: Padmanabh Ratnakar Date: Thu, 3 Nov 2011 01:49:55 +0000 Subject: be2net: Prevent CQ full condition for Lancer Indicate to HW that the CQ is cleaned up before posting new RX buffers. This prevents the HW to go into CQ full error condition. Signed-off-by: Padmanabh Ratnakar Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 21804972fa2..30606f5ec0d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1905,6 +1905,8 @@ loop_continue: be_rx_stats_update(rxo, rxcp); } + be_cq_notify(adapter, rx_cq->id, false, work_done); + /* Refill the queue */ if (work_done && atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM) be_post_rx_frags(rxo, GFP_ATOMIC); @@ -1912,10 +1914,8 @@ loop_continue: /* All consumed */ if (work_done < budget) { napi_complete(napi); - be_cq_notify(adapter, rx_cq->id, true, work_done); - } else { - /* More to be consumed; continue with interrupts disabled */ - be_cq_notify(adapter, rx_cq->id, false, work_done); + /* Arm CQ */ + be_cq_notify(adapter, rx_cq->id, true, 0); } return work_done; } -- cgit v1.2.3-18-g5258 From e1cfb67acd5e890bbad695000d2c997bfb7f1756 Mon Sep 17 00:00:00 2001 From: Padmanabh Ratnakar Date: Thu, 3 Nov 2011 01:50:08 +0000 Subject: be2net: Add detect UE feature for Lancer Add code to detect UE in case of Lancer. Signed-off-by: Padmanabh Ratnakar Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 3 +- drivers/net/ethernet/emulex/benet/be_hw.h | 2 + drivers/net/ethernet/emulex/benet/be_main.c | 58 +++++++++++++++++++---------- 3 files changed, 42 insertions(+), 21 deletions(-) (limited to 'drivers/net') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 03fe7cde2e6..2c7b36673df 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -318,8 +318,7 @@ static int be_mbox_db_ready_wait(struct be_adapter *adapter, void __iomem *db) if (msecs > 4000) { dev_err(&adapter->pdev->dev, "mbox poll timed out\n"); - if (!lancer_chip(adapter)) - be_detect_dump_ue(adapter); + be_detect_dump_ue(adapter); return -1; } diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index fbc8a915519..f2c89e3ccab 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -48,6 +48,8 @@ /* Lancer SLIPORT_CONTROL SLIPORT_STATUS registers */ #define SLIPORT_STATUS_OFFSET 0x404 #define SLIPORT_CONTROL_OFFSET 0x408 +#define SLIPORT_ERROR1_OFFSET 0x40C +#define SLIPORT_ERROR2_OFFSET 0x410 #define SLIPORT_STATUS_ERR_MASK 0x80000000 #define SLIPORT_STATUS_RN_MASK 0x01000000 diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 30606f5ec0d..e0aed188c57 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1977,42 +1977,62 @@ static int be_poll_tx_mcc(struct napi_struct *napi, int budget) void be_detect_dump_ue(struct be_adapter *adapter) { - u32 ue_status_lo, ue_status_hi, ue_status_lo_mask, ue_status_hi_mask; + u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0; + u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0; u32 i; - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_LOW, &ue_status_lo); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_HIGH, &ue_status_hi); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_LOW_MASK, &ue_status_lo_mask); - pci_read_config_dword(adapter->pdev, - PCICFG_UE_STATUS_HI_MASK, &ue_status_hi_mask); + if (lancer_chip(adapter)) { + sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); + if (sliport_status & SLIPORT_STATUS_ERR_MASK) { + sliport_err1 = ioread32(adapter->db + + SLIPORT_ERROR1_OFFSET); + sliport_err2 = ioread32(adapter->db + + SLIPORT_ERROR2_OFFSET); + } + } else { + pci_read_config_dword(adapter->pdev, + PCICFG_UE_STATUS_LOW, &ue_lo); + pci_read_config_dword(adapter->pdev, + PCICFG_UE_STATUS_HIGH, &ue_hi); + pci_read_config_dword(adapter->pdev, + PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask); + pci_read_config_dword(adapter->pdev, + PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask); - ue_status_lo = (ue_status_lo & (~ue_status_lo_mask)); - ue_status_hi = (ue_status_hi & (~ue_status_hi_mask)); + ue_lo = (ue_lo & (~ue_lo_mask)); + ue_hi = (ue_hi & (~ue_hi_mask)); + } - if (ue_status_lo || ue_status_hi) { + if (ue_lo || ue_hi || + sliport_status & SLIPORT_STATUS_ERR_MASK) { adapter->ue_detected = true; adapter->eeh_err = true; dev_err(&adapter->pdev->dev, "UE Detected!!\n"); } - if (ue_status_lo) { - for (i = 0; ue_status_lo; ue_status_lo >>= 1, i++) { - if (ue_status_lo & 1) + if (ue_lo) { + for (i = 0; ue_lo; ue_lo >>= 1, i++) { + if (ue_lo & 1) dev_err(&adapter->pdev->dev, "UE: %s bit set\n", ue_status_low_desc[i]); } } - if (ue_status_hi) { - for (i = 0; ue_status_hi; ue_status_hi >>= 1, i++) { - if (ue_status_hi & 1) + if (ue_hi) { + for (i = 0; ue_hi; ue_hi >>= 1, i++) { + if (ue_hi & 1) dev_err(&adapter->pdev->dev, "UE: %s bit set\n", ue_status_hi_desc[i]); } } + if (sliport_status & SLIPORT_STATUS_ERR_MASK) { + dev_err(&adapter->pdev->dev, + "sliport status 0x%x\n", sliport_status); + dev_err(&adapter->pdev->dev, + "sliport error1 0x%x\n", sliport_err1); + dev_err(&adapter->pdev->dev, + "sliport error2 0x%x\n", sliport_err2); + } } static void be_worker(struct work_struct *work) @@ -2022,7 +2042,7 @@ static void be_worker(struct work_struct *work) struct be_rx_obj *rxo; int i; - if (!adapter->ue_detected && !lancer_chip(adapter)) + if (!adapter->ue_detected) be_detect_dump_ue(adapter); /* when interrupts are not yet enabled, just reap any pending -- cgit v1.2.3-18-g5258