* [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
@ 2008-08-17 4:59 David H. Lynch Jr.
2008-08-18 12:30 ` Ben Hutchings
2008-08-18 17:01 ` [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 EthernetNIC Stephen Neuendorffer
0 siblings, 2 replies; 8+ messages in thread
From: David H. Lynch Jr. @ 2008-08-17 4:59 UTC (permalink / raw)
To: linuxppc-embedded, netdev
Please bear with me. This is my first patch submission.
Grant Likely of Secret Labs has kindly done a prelimary view.
Hopefully I have correct the issues he raised.
Ethernet driver for Xilinx LL TEMAC
Original Author Yoshio Kashiwagi
Updated and Maintained by David Lynch
Signed-off-by: David H. Lynch Jr <dhlii@dlasys.net>
---
drivers/net/Kconfig | 5
drivers/net/Makefile | 1
drivers/net/xps_lltemac.c | 1283
++++++++++++++++++++++++++++++++++++++++
include/linux/xilinx_devices.h | 2
4 files changed, 1290 insertions(+), 1 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index fd0dd80..71a3eee 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2332,6 +2332,11 @@ config MV643XX_ETH
Some boards that use the Discovery chipset are the Momenco
Ocelot C and Jaguar ATX and Pegasos II.
+config XPS_LLTEMAC
+ tristate "Xilinx LLTEMAC 10/100/1000 Ethernet MAC driver"
+ help
+ This driver supports the Xilinx 10/100/1000 LLTEMAC found in Virtex
4 FPGAs
+
config QLA3XXX
tristate "QLogic QLA3XXX Network Driver Support"
depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1f09934..9196bab 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_PICO_TEMAC) += pico_temac.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+obj-$(CONFIG_XPS_LLTEMAC) += xps_lltemac.o
obj-$(CONFIG_QLA3XXX) += qla3xxx.o
obj-$(CONFIG_PPP) += ppp_generic.o
diff --git a/drivers/net/xps_lltemac.c b/drivers/net/xps_lltemac.c
new file mode 100644
index 0000000..1f2c158
--- /dev/null
+++ b/drivers/net/xps_lltemac.c
@@ -0,0 +1,1283 @@
+/*======================================================================
+
+ Driver for Xilinx temac ethernet NIC's
+
+ Author: Yoshio Kashiwagi
+ Copyright (c) 2008 Nissin Systems Co.,Ltd.
+
+ Revisons: David H. Lynch Jr. <dhlii@dlasys.net>
+ Copyright (C) 2005-2008 DLA Systems
+
+======================================================================*/
+
+#define DRV_NAME "xilinx_lltemac"
+#define DRV_AUTHOR "Yoshio Kashiwagi"
+#define DRV_EMAIL ""
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <linux/mii.h>
+#include <linux/in.h>
+#include <linux/pci.h>
+
+#include <linux/ip.h>
+#include <linux/tcp.h> /* just needed for sizeof(tcphdr) */
+#include <linux/udp.h> /* needed for sizeof(udphdr) */
+#include <asm/delay.h>
+#include <asm/io.h>
+
+/* register access modes */
+typedef enum { REG_DCR = 1, REG_IND, REG_DIR} REG_MODE;
+
+#define MII_ANI 0x10
+#define PHY_NUM 0
+#define PHY_TIMEOUT 10000
+
+#define MII_SSR 0x11
+#define MII_SSR_LINK (1 << 10)
+#define MII_SSR_SPDMASK 0xC000
+#define MII_SSR_SPD1000 (1 << 15)
+#define MII_SSR_SPD100 (1 << 14)
+#define MII_SSR_SPD10 0
+#define MII_SSR_FD (1 << 13)
+
+#define MII_ISR 0x13
+
+/* packet size info */
+#define XTE_MTU 1500 /* max MTU size of
Ethernet frame */
+#define XTE_HDR_SIZE 14 /* size of Ethernet
header */
+#define XTE_TRL_SIZE 4 /* size of Ethernet
trailer (FCS) */
+#define XTE_MAX_FRAME_SIZE (XTE_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE)
+#define XTE_JUMBO_MTU 9000
+#define XTE_MAX_JUMBO_FRAME_SIZE (XTE_JUMBO_MTU + XTE_HDR_SIZE +
XTE_TRL_SIZE)
+
+/** Configuration options
+ *
+ * Device configuration options. See the temac_setoptions(),
+ * XTemac_ClearOptions() and XTemac_GetOptions() for information on how
to use
+ * options.
+ *
+ * The default state of the options are noted and are what the device
and driver
+ * will be set to after calling XTemac_Reset() or XTemac_Initialize().
+ *
+ */
+
+#define XTE_OPTION_PROMISC (1 << 0) /**< Accept
all incoming packets. This option defaults to disabled (cleared) */
+#define XTE_OPTION_JUMBO (1 << 1) /**< Jumbo
frame support for Tx & Rx. This option defaults to disabled (cleared) */
+#define XTE_OPTION_VLAN (1 << 2) /**< VLAN
Rx & Tx frame support. This option defaults to disabled (cleared) */
+#define XTE_OPTION_FLOW_CONTROL (1 << 4) /**< Enable
recognition of flow control frames on Rx This option defaults to enabled
(set) */
+#define XTE_OPTION_FCS_STRIP (1 << 5) /**< Strip
FCS and PAD from incoming frames. Note: PAD from VLAN frames is not
stripped. This option defaults to disabled (set) */
+#define XTE_OPTION_FCS_INSERT (1 << 6) /**<
Generate FCS field and add PAD automatically for outgoing frames. This
option defaults to enabled (set) */
+#define XTE_OPTION_LENTYPE_ERR (1 << 7) /**< Enable
Length/Type error checking for incoming frames. When this option is
+
set, the MAC will filter frames that have a mismatched type/length field
+
and if XTE_OPTION_REPORT_RXERR is set, the user is notified when these
+
types of frames are encountered. When this option is cleared, the MAC will
+
allow these types of frames to be received.
+
This option defaults to enabled (set) */
+#define XTE_OPTION_TXEN (1 << 11) /**< Enable
the transmitter. This option defaults to enabled (set) */
+#define XTE_OPTION_RXEN (1 << 12) /**< Enable
the receiver This option defaults to enabled (set) */
+#define XTE_OPTION_DEFAULTS \
+ (XTE_OPTION_TXEN | \
+ XTE_OPTION_FLOW_CONTROL | \
+ XTE_OPTION_RXEN) /**< Default options set when
device is initialized or reset */
+
+/* XPS_LL_TEMAC SDMA registers definition */
+
+#define TX_NXTDESC_PTR 0x00 /* r */
+#define TX_CURBUF_ADDR 0x04 /* r */
+#define TX_CURBUF_LENGTH 0x08 /* r */
+#define TX_CURDESC_PTR 0x0c /* rw */
+#define TX_TAILDESC_PTR 0x10 /* rw */
+#define TX_CHNL_CTRL 0x14 /* rw */
+/* 0:7 24:31 IRQTimeout */
+/* 8:15 16:23 IRQCount */
+/* 16:20 11:15 Reserved */
+/* 21 10 0 */
+/* 22 9 UseIntOnEnd */
+/* 23 8 LdIRQCnt */
+/* 24 7 IRQEn */
+/* 25:28 3:6 Reserved */
+/* 29 2 IrqErrEn */
+/* 30 1 IrqDlyEn */
+/* 31 0 IrqCoalEn */
+#define CHNL_CTRL_IRQ_IOE (1 << 9)
+#define CHNL_CTRL_IRQ_EN (1 << 7)
+#define CHNL_CTRL_IRQ_ERR_EN (1 << 2)
+#define CHNL_CTRL_IRQ_DLY_EN (1 << 1)
+#define CHNL_CTRL_IRQ_COAL_EN (1 << 0)
+#define TX_IRQ_REG 0x18 /* rw */
+/* 0:7 24:31 DltTmrValue */
+/* 8:15 16:23 ClscCntrValue */
+/* 16:17 14:15 Reserved */
+/* 18:21 10:13 ClscCnt */
+/* 22:23 8:9 DlyCnt */
+/* 24:28 3::7 Reserved */
+/* 29 2 ErrIrq */
+/* 30 1 DlyIrq */
+/* 31 0 CoalIrq */
+#define TX_CHNL_STS 0x1c /* r */
+/* 0:9 22:31 Reserved */
+/* 10 21 TailPErr */
+/* 11 20 CmpErr */
+/* 12 19 AddrErr */
+/* 13 18 NxtPErr */
+/* 14 17 CurPErr */
+/* 15 16 BsyWr */
+/* 16:23 8:15 Reserved */
+/* 24 7 Error */
+/* 25 6 IOE */
+/* 26 5 SOE */
+/* 27 4 Cmplt */
+/* 28 3 SOP */
+/* 29 2 EOP */
+/* 30 1 EngBusy */
+/* 31 0 Reserved */
+
+#define RX_NXTDESC_PTR 0x20 /* r */
+#define RX_CURBUF_ADDR 0x24 /* r */
+#define RX_CURBUF_LENGTH 0x28 /* r */
+#define RX_CURDESC_PTR 0x2c /* rw */
+#define RX_TAILDESC_PTR 0x30 /* rw */
+#define RX_CHNL_CTRL 0x34 /* rw */
+/* 0:7 24:31 IRQTimeout */
+/* 8:15 16:23 IRQCount */
+/* 16:20 11:15 Reserved */
+/* 21 10 0 */
+/* 22 9 UseIntOnEnd */
+/* 23 8 LdIRQCnt */
+/* 24 7 IRQEn */
+/* 25:28 3:6 Reserved */
+/* 29 2 IrqErrEn */
+/* 30 1 IrqDlyEn */
+/* 31 0 IrqCoalEn */
+#define RX_IRQ_REG 0x38 /* rw */
+#define IRQ_COAL (1 << 0)
+#define IRQ_DLY (1 << 1)
+#define IRQ_ERR (1 << 2)
+#define IRQ_DMAERR (1 << 7) /* this is not documented
??? */
+/* 0:7 24:31 DltTmrValue */
+/* 8:15 16:23 ClscCntrValue */
+/* 16:17 14:15 Reserved */
+/* 18:21 10:13 ClscCnt */
+/* 22:23 8:9 DlyCnt */
+/* 24:28 3::7 Reserved */
+#define RX_CHNL_STS 0x3c /* r */
+#define CHNL_STS_ENGBUSY (1 << 1)
+#define CHNL_STS_EOP (1 << 2)
+#define CHNL_STS_SOP (1 << 3)
+#define CHNL_STS_CMPLT (1 << 4)
+#define CHNL_STS_SOE (1 << 5)
+#define CHNL_STS_IOE (1 << 6)
+#define CHNL_STS_ERR (1 << 7)
+
+#define CHNL_STS_BSYWR (1 << 16)
+#define CHNL_STS_CURPERR (1 << 17)
+#define CHNL_STS_NXTPERR (1 << 18)
+#define CHNL_STS_ADDRERR (1 << 19)
+#define CHNL_STS_CMPERR (1 << 20)
+#define CHNL_STS_TAILERR (1 << 21)
+/* 0:9 22:31 Reserved */
+/* 10 21 TailPErr */
+/* 11 20 CmpErr */
+/* 12 19 AddrErr */
+/* 13 18 NxtPErr */
+/* 14 17 CurPErr */
+/* 15 16 BsyWr */
+/* 16:23 8:15 Reserved */
+/* 24 7 Error */
+/* 25 6 IOE */
+/* 26 5 SOE */
+/* 27 4 Cmplt */
+/* 28 3 SOP */
+/* 29 2 EOP */
+/* 30 1 EngBusy */
+/* 31 0 Reserved */
+
+#define DMA_CONTROL_REG 0x40 /* rw */
+#define DMA_CONTROL_RST (1 << 0)
+
+/* XPS_LL_TEMAC direct registers definition */
+
+#define XTE_RAF0_OFFSET 0x00
+#define RAF0_RST (1 << 0)
+#define RAF0_MCSTREJ (1 << 1)
+#define RAF0_BCSTREJ (1 << 2)
+#define XTE_TPF0_OFFSET 0x04
+#define XTE_IFGP0_OFFSET 0x08
+#define XTE_ISR0_OFFSET 0x0c
+#define ISR0_HARDACSCMPLT (1 << 0)
+#define ISR0_AUTONEG (1 << 1)
+#define ISR0_RXCMPLT (1 << 2)
+#define ISR0_RXREJ (1 << 3)
+#define ISR0_RXFIFOOVR (1 << 4)
+#define ISR0_TXCMPLT (1 << 5)
+#define ISR0_RXDCMLCK (1 << 6)
+
+#define XTE_IPR0_OFFSET 0x10
+#define XTE_IER0_OFFSET 0x14
+
+#define XTE_MSW0_OFFSET 0x20
+#define XTE_LSW0_OFFSET 0x24
+#define XTE_CTL0_OFFSET 0x28
+#define XTE_RDY0_OFFSET 0x2c
+
+#define XTE_RSE_MIIM_RR_MASK 0x0002
+#define XTE_RSE_MIIM_WR_MASK 0x0004
+#define XTE_RSE_CFG_RR_MASK 0x0020
+#define XTE_RSE_CFG_WR_MASK 0x0040
+
+/* XPS_LL_TEMAC indirect registers offset definition */
+
+#define XTE_RXC0_OFFSET 0x00000200 /**< Rx
configuration word 0 */
+#define XTE_RXC1_OFFSET 0x00000240 /**< Rx
configuration word 1 */
+#define XTE_RXC1_RXRST_MASK (1 << 31) /**<
Receiver reset */
+#define XTE_RXC1_RXJMBO_MASK (1 << 30) /**<
Jumbo frame enable */
+#define XTE_RXC1_RXFCS_MASK (1 << 29) /**<
FCS not stripped */
+#define XTE_RXC1_RXEN_MASK (1 << 28) /**<
Receiver enable */
+#define XTE_RXC1_RXVLAN_MASK (1 << 27) /**<
VLAN enable */
+#define XTE_RXC1_RXHD_MASK (1 << 26) /**<
Half duplex */
+#define XTE_RXC1_RXLT_MASK (1 << 25) /**<
Length/type check disable */
+
+#define XTE_TXC_OFFSET 0x00000280 /**< Tx
configuration */
+#define XTE_TXC_TXRST_MASK (1 << 31) /**<
Transmitter reset */
+#define XTE_TXC_TXJMBO_MASK (1 << 30) /**<
Jumbo frame enable */
+#define XTE_TXC_TXFCS_MASK (1 << 29) /**<
Generate FCS */
+#define XTE_TXC_TXEN_MASK (1 << 28) /**<
Transmitter enable */
+#define XTE_TXC_TXVLAN_MASK (1 << 27) /**<
VLAN enable */
+#define XTE_TXC_TXHD_MASK (1 << 26) /**<
Half duplex */
+#define XTE_FCC_OFFSET 0x000002C0 /**<
Flow control configuration */
+#define XTE_FCC_RXFLO_MASK (1 << 29) /**< Rx
flow control enable */
+#define XTE_FCC_TXFLO_MASK (1 << 30) /**< Tx
flow control enable */
+
+#define XTE_EMCFG_OFFSET 0x00000300 /**<
EMAC configuration */
+#define XTE_EMCFG_LINKSPD_MASK 0xC0000000 /**<
Link speed */
+#define XTE_EMCFG_HOSTEN_MASK (1 << 26) /**<
Host interface enable */
+#define XTE_EMCFG_LINKSPD_10 0x00000000 /**<
XTE_EMCFG_LINKSPD_MASK for 10 Mbit */
+#define XTE_EMCFG_LINKSPD_100 (1 << 30) /**<
XTE_EMCFG_LINKSPD_MASK for 100 Mbit */
+#define XTE_EMCFG_LINKSPD_1000 (1 << 31) /**<
XTE_EMCFG_LINKSPD_MASK for 1000 Mbit */
+
+#define XTE_GMIC_OFFSET 0x00000320 /**<
RGMII/SGMII configuration */
+#define XTE_MC_OFFSET 0x00000340 /**<
Management configuration */
+#define XTE_MC_MDIO_MASK (1 << 6) /**<
MII management enable */
+#define XTE_MDIO_CLOCK_DIV_100MHz 0x28 /* 100
MHz host clock */
+#define XTE_MDIO_DIV_DFT 29 /*
Default MDIO clock divisor */
+#define XTE_UAW0_OFFSET 0x00000380 /**<
Unicast address word 0 */
+#define XTE_UAW1_OFFSET 0x00000384 /**<
Unicast address word 1 */
+
+#define XTE_MAW0_OFFSET 0x00000388 /**<
Multicast address word 0 */
+#define XTE_MAW1_OFFSET 0x0000038C /**<
Multicast address word 1 */
+#define XTE_AFM_OFFSET 0x00000390 /**<
Promisciuous mode */
+#define XTE_AFM_EPPRM_MASK (1 << 31) /**<
Promiscuous mode enable */
+
+#define XTE_TIS_OFFSET 0x000003A0 /*
Interrupt Request status */
+#define TIS_FRIS (1 << 0)
+#define TIS_MRIS (1 << 1)
+#define TIS_MWIS (1 << 2)
+#define TIS_ARIS (1 << 3)
+#define TIS_AWIS (1 << 4)
+#define TIS_CRIS (1 << 5)
+#define TIS_CWIS (1 << 6)
+#define XTE_TIE_OFFSET 0x000003A4 /*
Interrupt Request enable */
+
+ /** MII Mamagement Control register (MGTCR) */
+#define XTE_MGTDR_OFFSET 0x000003B0 /**<
MII data */
+#define XTE_MIIMAI_OFFSET 0x000003B4 /**<
MII control */
+
+#define CNTLREG_WRITE_ENABLE_MASK 0x8000
+#define CNTLREG_EMAC1SEL_MASK 0x0400
+#define CNTLREG_ADDRESSCODE_MASK 0x03ff
+
+/* CDMAC descriptor status bit definitions */
+
+#define STS_CTRL_APP0_ERR (1 << 31)
+#define STS_CTRL_APP0_IRQONEND (1 << 30)
+#define STS_CTRL_APP0_STOPONEND (1 << 29) /* undoccumented */
+#define STS_CTRL_APP0_CMPLT (1 << 28)
+#define STS_CTRL_APP0_SOP (1 << 27)
+#define STS_CTRL_APP0_EOP (1 << 26)
+#define STS_CTRL_APP0_ENGBUSY (1 << 25)
+#define STS_CTRL_APP0_ENGRST (1 << 24) /* undocumented */
+
+#define TX_CONTROL_CALC_CSUM_MASK 1
+
+#define ALIGNMENT 32
+#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
+
+#define MULTICAST_CAM_TABLE_NUM 4
+
+#define TX_BD_NUM 64
+#define RX_BD_NUM 128
+
+#define XILINX_GSRD3_NAPI
+
+
+/* TX/RX CURDESC_PTR points to first descriptor */
+/* TX/RX TAILDESC_PTR points to last descriptor in linked list */
+
+struct cdmac_bd {
+ struct cdmac_bd *next;
+ unsigned char *phys;
+ u32 len;
+ u32 app0;
+ u32 app1; /* TX start << 16 | insert */
+ u32 app2; /* TX csum */
+ u32 app3; /* unused ? */
+ u32 app4; /* skb for TX length for RX */
+} ;
+/* APP0 bits */
+/* 0 Error */
+/* 1 IrqOnEnd generate an interrupt at completion of DMA op */
+/* 2 reserved */
+/* 3 completed Current descriptor completed */
+/* 4 SOP TX - marks first desc/ RX marks first desct */
+/* 5 EOP TX marks last desc/RX marks last desc */
+/* 6 EngBusy DMA is processing */
+/* 7 reserved */
+/* 8:31 application specific */
+
+struct temac_local {
+ struct net_device_stats stats; /* Statistics for
this device */
+ struct net_device *dev;
+ unsigned long sdma_base_addr;
+ int tx_irq;
+ int rx_irq;
+ unsigned int mii:1; /* mii port
available */
+
+ int emac_num;
+ u16 phy_addr;
+ int LinkSpeed; /* Speed of link
10/100/1000 */
+ u32 options; /* Current options word */
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ spinlock_t tx_lock;
+ struct cdmac_bd *tx_bd_v;
+ struct cdmac_bd *tx_bd_p;
+ struct cdmac_bd *rx_bd_v;
+ struct cdmac_bd *rx_bd_p;
+ volatile int tx_bd_ci;
+ volatile int tx_bd_next;
+ volatile int tx_bd_tail;
+ int rx_bd_ci;
+ struct sk_buff **rx_skb;
+};
+
+static u32
+_ior(u32 offset)
+{
+ u32 value;
+ value = (*(volatile u32 *)(offset));
+ __asm__ __volatile__("eieio");
+ return value;
+}
+
+static void
+_iow(u32 offset, u32 value)
+{
+ (*(volatile u32 *)(offset) = value);
+ __asm__ __volatile__("eieio");
+}
+
+static u32
+tior(struct net_device *ndev, int offset)
+{
+ return _ior(ndev->base_addr + offset);
+}
+
+static void
+tiow(struct net_device *ndev, int offset, u32 value)
+{
+ _iow(ndev->base_addr + offset, value);
+}
+
+static u32
+tio_setclr(struct net_device *ndev, u32 reg_num, u32 val, int flg)
+{
+ u32 Reg = tior(ndev, reg_num) & ~val;
+ if (flg)
+ Reg |= val;
+ tiow(ndev, reg_num, Reg);
+ return 0;
+}
+
+static u32
+sd_ior(struct net_device *ndev, int offset)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ return _ior(lp->sdma_base_addr + offset);
+}
+
+static void
+sd_iow(struct net_device *ndev, int offset, u32 value)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ _iow(lp->sdma_base_addr + offset, value);
+
+}
+
+/***************************************************************************
+ * Reads an MII register from the MII PHY attached to the Xilinx Temac.
+ *
+ * Parameters:
+ * dev - the temac device.
+ * phy_addr - the address of the PHY [0..31]
+ * reg_num - the number of the register to read. 0-6 are defined by
+ * the MII spec, but most PHYs have more.
+ * reg_value - this is set to the specified register's value
+ *
+ * Returns:
+ * Success or Failure
+ */
+static unsigned int
+mdio_read(struct net_device *ndev, int phy_id, int reg_num)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT;
+ u32 rv = 0;
+ unsigned long flags;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+
+ tiow(ndev, XTE_LSW0_OFFSET, ((phy_id << 5) | (reg_num)));
+ tiow(ndev, XTE_CTL0_OFFSET, XTE_MIIMAI_OFFSET | (lp->emac_num
<< 10));
+ while(!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_MIIM_RR_MASK) &&
timeout--);
+ rv = tior(ndev, XTE_LSW0_OFFSET);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ return rv;
+}
+
+/***************************************************************************
+ * Writes an MII register from the MII PHY attached to the Xilinx Temac.
+ *
+ * Parameters:
+ * dev - the temac device.
+ * phy_id - the address of the PHY [0..31]
+ * reg_num - the number of the register to read. 0-6 are defined by
+ * the MII spec, but most PHYs have more.
+ * reg_value - the value to set
+ *
+ * Returns:
+ * Success or Failure
+ */
+static void
+mdio_write(struct net_device *ndev, int phy_id, int reg_num, int reg_val)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT, status;
+ unsigned long flags;
+
+ if (lp->mii) {
+ spin_lock_irqsave(&lp->lock, flags);
+
+ tiow(ndev, XTE_LSW0_OFFSET, reg_val);
+ tiow(ndev, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK |
XTE_MGTDR_OFFSET);
+ tiow(ndev, XTE_LSW0_OFFSET, ((phy_id << 5) | (reg_num)));
+ tiow(ndev, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK |
XTE_MIIMAI_OFFSET | (lp->emac_num << 10));
+ while(!(status = tior(ndev, XTE_RDY0_OFFSET) &
XTE_RSE_MIIM_WR_MASK) && timeout--);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+}
+
+static u32
+emac_cfg_read(struct net_device *ndev, u16 reg_num)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = 10000;
+
+ if (lp->mii) {
+ tiow(ndev, XTE_CTL0_OFFSET, (lp->emac_num << 10) | reg_num);
+ while(!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_CFG_RR_MASK) &&
timeout--);
+ return (u32) tior(ndev, XTE_LSW0_OFFSET);
+ }
+
+ return 0;
+}
+
+static void
+emac_cfg_write(struct net_device *ndev, u32 reg_num, u32 val)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = 10000;
+
+ if (lp->mii) {
+ tiow(ndev, XTE_LSW0_OFFSET, val);
+ tiow(ndev, XTE_CTL0_OFFSET, (CNTLREG_WRITE_ENABLE_MASK |
(lp->emac_num << 10) | reg_num));
+ while(!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_CFG_WR_MASK) &&
timeout--);
+ }
+}
+
+static u32
+emac_cfg_setclr(struct net_device *ndev, u32 reg_num, u32 val, int flg)
+{
+ u32 Reg = emac_cfg_read(ndev, reg_num) & ~val;
+ if (flg)
+ Reg |= val;
+ emac_cfg_write(ndev, reg_num, Reg);
+ return 0;
+}
+
+/*
+Changes the mac address if the controller is not running.
+
+static int (*set_mac_address)(struct net_device *dev, void *addr);
+Function that can be implemented if the interface supports the ability
to change its
+hardware address. Many interfaces don't support this ability at all.
Others use the
+default eth_mac_addr implementation (from drivers/net/net_init.c).
eth_mac_addr
+only copies the new address into dev->dev_addr, and it does so only if
the interface
+is not running. Drivers that use eth_mac_addr should set the hardware MAC
+address from dev->dev_addr in their open method.
+
+*/
+static int
+temac_set_mac_address(struct net_device *ndev, void *address)
+{
+ if (address)
+ memcpy(ndev->dev_addr, address, ETH_ALEN);
+
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ random_ether_addr(ndev->dev_addr);
+ }
+ /* set up unicast MAC address filter set its mac address */
+ emac_cfg_write(ndev, XTE_UAW0_OFFSET,
+ ((ndev->dev_addr[0]) |
+ (ndev->dev_addr[1] << 8) |
+ (ndev->dev_addr[2] << 16) |
+ (ndev->dev_addr[3] << 24)));
+ /* There are reserved bits in EUAW1 so don't affect them Set MAC
bits [47:32] in EUAW1 */
+ emac_cfg_write(ndev, XTE_UAW1_OFFSET,
+ (ndev->dev_addr[4] & 0x000000ff) |
+ (ndev->dev_addr[5] << 8));
+
+ return 0;
+}
+
+/*
+OPTIONAL
+static void (*set_multicast_list)(struct net_device *dev);
+Method called when the multicast list for the device changes and when the
+flags change. See the section Multicast for further details and a sample
+implementation.
+*/
+static void
+temac_set_multicast_list(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 multi_addr_msw, multi_addr_lsw;
+ int i;
+
+ spin_lock(&lp->lock);
+
+ if(ndev->flags & IFF_PROMISC) {
+ printk(KERN_NOTICE "%s: Promiscuos mode enabled.\n", ndev->name);
+ emac_cfg_write(ndev, XTE_AFM_OFFSET, 0x80000000);
+ } else {
+ struct dev_mc_list *mclist;
+ for(i = 0, mclist = ndev->mc_list; mclist && i <
ndev->mc_count; i++, mclist = mclist->next) {
+
+ if(i >= MULTICAST_CAM_TABLE_NUM) break;
+ multi_addr_msw = ((mclist->dmi_addr[3] << 24) |
(mclist->dmi_addr[2] << 16) | (mclist->dmi_addr[1] << 8) |
mclist->dmi_addr[0]);
+ emac_cfg_write(ndev, XTE_MAW0_OFFSET, multi_addr_msw);
+ multi_addr_lsw = ((mclist->dmi_addr[5] << 8) |
mclist->dmi_addr[4]);
+ multi_addr_lsw |= (i << 16);
+ emac_cfg_write(ndev, XTE_MAW1_OFFSET, multi_addr_lsw);
+ }
+ }
+ spin_unlock(&lp->lock);
+}
+
+static void
+temac_phy_init(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ unsigned int ret, Reg;
+ int ii;
+
+ /* Set default MDIO divisor */
+ /* Set up MII management registers to write to PHY */
+ emac_cfg_write(ndev, XTE_MC_OFFSET, XTE_MC_MDIO_MASK |
XTE_MDIO_DIV_DFT);
+
+ /*
+ Set A-N Advertisement Regs for Full Duplex modes ONLY
+ address 4 = Autonegotiate Advertise Register
+ Disable 1000 Mbps for negotiation if not built for GEth
+ */
+ mdio_write(ndev, PHY_NUM, MII_ADVERTISE, mdio_read(ndev, PHY_NUM,
MII_ADVERTISE) | ADVERTISE_10FULL | ADVERTISE_100FULL | ADVERTISE_CSMA);
+ mdio_write(ndev, PHY_NUM, MII_CTRL1000, ADVERTISE_1000FULL);
+
+ /*
+ Soft reset the PHY
+ address 0 = Basic Mode Control Register
+ */
+ mdio_write(ndev, PHY_NUM, MII_BMCR, mdio_read(ndev, PHY_NUM,
MII_BMCR) | BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Wait for a PHY Link (auto-negotiation to complete)... */
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ ii = 64;
+ while (((ret & BMSR_LSTATUS) != BMSR_LSTATUS) && ii--) {
+ mdelay(500);
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ }
+ ret = mdio_read(ndev, PHY_NUM, MII_SSR);
+
+ Reg = emac_cfg_read(ndev, XTE_EMCFG_OFFSET) & ~XTE_EMCFG_LINKSPD_MASK;
+ if (ret & MII_SSR_LINK) {
+ switch (ret & MII_SSR_SPDMASK) {
+ case MII_SSR_SPD1000: /* 1000Base-T */
+ lp->LinkSpeed = 1000;
+ emac_cfg_write(ndev, XTE_EMCFG_OFFSET, Reg | (u32)
XTE_EMCFG_LINKSPD_1000);
+ break;
+ case MII_SSR_SPD100: /* 100Base-T */
+ lp->LinkSpeed = 100;
+ emac_cfg_write(ndev, XTE_EMCFG_OFFSET, Reg |
XTE_EMCFG_LINKSPD_100);
+ break;
+ case MII_SSR_SPD10: /* 10Base-T */
+ lp->LinkSpeed = 10;
+ break;
+ };
+ if ((ret & MII_SSR_FD) == 0x0) {
+ /* set up Tx/Rx config reg for half duplex */
+ ret = emac_cfg_read(ndev, XTE_TXC_OFFSET);
+ emac_cfg_write(ndev, XTE_TXC_OFFSET, ret | XTE_TXC_TXHD_MASK);
+ ret = emac_cfg_read(ndev, XTE_RXC1_OFFSET);
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, ret |
XTE_RXC1_RXHD_MASK);
+ }
+ }
+}
+
+/*
-----------------------------------------------------------------------------
+-----------------------------------------------------------------------------
*/
+static int
+temac_bd_init(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ unsigned long align;
+ int ii;
+
+ lp->rx_skb = kzalloc(sizeof(struct sk_buff)*RX_BD_NUM, GFP_KERNEL);
+ /* allocate the tx and rx ring buffer descriptors. */
+ /* returns a virtual addres and a physical address. */
+ lp->tx_bd_v = dma_alloc_coherent(NULL, sizeof(struct cdmac_bd) *
TX_BD_NUM, (dma_addr_t *)&lp->tx_bd_p, GFP_KERNEL);
+ lp->rx_bd_v = dma_alloc_coherent(NULL, sizeof(struct cdmac_bd) *
RX_BD_NUM, (dma_addr_t *)&lp->rx_bd_p, GFP_KERNEL);
+
+ for(ii = 0;ii < TX_BD_NUM;ii++) {
+ memset((char *)&lp->tx_bd_v[ii], 0, sizeof(struct cdmac_bd));
+ if(ii == (TX_BD_NUM - 1)) {
+ lp->tx_bd_v[ii].next = &lp->tx_bd_p[0];
+ } else {
+ lp->tx_bd_v[ii].next = &lp->tx_bd_p[ii + 1];
+ }
+ }
+ for(ii = 0;ii < RX_BD_NUM;ii++) {
+ memset((char *)&lp->rx_bd_v[ii], 0, sizeof(struct cdmac_bd));
+ if(ii == (RX_BD_NUM - 1)) {
+ lp->rx_bd_v[ii].next = &lp->rx_bd_p[0];
+ } else {
+ lp->rx_bd_v[ii].next = &lp->rx_bd_p[ii + 1];
+ }
+ skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE + ALIGNMENT, GFP_ATOMIC);
+ if(skb == 0) {
+ printk("alloc_skb error %d\n", ii);
+ return -1;
+ }
+ lp->rx_skb[ii] = skb;
+/* this is how to get skb's aligned !!! */
+ align = BUFFER_ALIGN(skb->data);
+ if(align)
+ skb_reserve(skb, align);
+ /* returns physical address of skb->data */
+ lp->rx_bd_v[ii].phys = (unsigned char *)pci_map_single(NULL,
skb->data, XTE_MAX_JUMBO_FRAME_SIZE, PCI_DMA_FROMDEVICE);
+ lp->rx_bd_v[ii].len = XTE_MAX_JUMBO_FRAME_SIZE;
+ lp->rx_bd_v[ii].app0 = STS_CTRL_APP0_IRQONEND;
+ }
+
+ sd_iow(ndev, TX_CHNL_CTRL, 0x10220400 | CHNL_CTRL_IRQ_EN |
CHNL_CTRL_IRQ_DLY_EN | CHNL_CTRL_IRQ_COAL_EN);
+ /* sd_iow(ndev, TX_CHNL_CTRL, 0x10220483); */
+ /*sd_iow(ndev, TX_CHNL_CTRL, 0x00100483); */
+ sd_iow(ndev, RX_CHNL_CTRL, 0xff010000 | CHNL_CTRL_IRQ_EN |
CHNL_CTRL_IRQ_DLY_EN | CHNL_CTRL_IRQ_COAL_EN | CHNL_CTRL_IRQ_IOE);
+ /* sd_iow(ndev, RX_CHNL_CTRL, 0xff010283); */
+
+ sd_iow(ndev, RX_CURDESC_PTR, (uintptr_t)&lp->rx_bd_p[0]);
+ sd_iow(ndev, RX_TAILDESC_PTR, (uintptr_t)&lp->rx_bd_p[RX_BD_NUM - 1]);
+
+ return 0;
+}
+
+struct temac_option {
+ int flg;
+ u32 opt;
+ u32 reg;
+ u32 m_or;
+ u32 m_and;
+
+};
+
+struct temac_option temac_options[] = {
+ { 0, XTE_OPTION_JUMBO, XTE_TXC_OFFSET,
XTE_TXC_TXJMBO_MASK, 0}, /* Turn on jumbo packet support for both Rx
and Tx */
+ { 0, XTE_OPTION_JUMBO, XTE_RXC1_OFFSET,
XTE_RXC1_RXJMBO_MASK, 0},
+ { 0, XTE_OPTION_VLAN, XTE_TXC_OFFSET,
XTE_TXC_TXVLAN_MASK, 0}, /* Turn on VLAN packet support for both Rx
and Tx */
+ { 0, XTE_OPTION_VLAN, XTE_RXC1_OFFSET,
XTE_RXC1_RXVLAN_MASK, 0},
+ { 0, XTE_OPTION_FCS_STRIP, XTE_RXC1_OFFSET,
XTE_RXC1_RXFCS_MASK, 0}, /* Turn on FCS stripping on receive packets */
+ { 0, XTE_OPTION_FCS_INSERT, XTE_TXC_OFFSET,
XTE_TXC_TXFCS_MASK, 0}, /* Turn on FCS insertion on transmit
packets */
+ { 0, XTE_OPTION_LENTYPE_ERR, XTE_RXC1_OFFSET,
XTE_RXC1_RXLT_MASK, 0}, /* Turn on length/type field checking on
receive packets */
+ { 0, XTE_OPTION_FLOW_CONTROL, XTE_FCC_OFFSET,
XTE_FCC_RXFLO_MASK, 0}, /* Turn on flow control */
+ { 0, XTE_OPTION_FLOW_CONTROL, XTE_FCC_OFFSET,
XTE_FCC_TXFLO_MASK, 0}, /* Turn on flow control */
+ { 0, XTE_OPTION_PROMISC, XTE_AFM_OFFSET,
XTE_AFM_EPPRM_MASK, 0}, /* Turn on promiscuous frame filtering
(all frames are received ) */
+ { 0, XTE_OPTION_TXEN, XTE_TXC_OFFSET,
XTE_TXC_TXEN_MASK, 0}, /* Enable transmitter if not already
enabled */
+ { 0, XTE_OPTION_RXEN, XTE_RXC1_OFFSET,
XTE_RXC1_RXEN_MASK, 0}, /* Enable receiver? */
+ { 0, 0, 0, 0, 0}
+};
+
+/*****************************************************************************/
+/**
+ * Set options for the driver/device. The driver should be stopped with
+ * XTemac_Stop() before changing options.
+ *
+ * @param InstancePtr is a pointer to the instance to be worked on.
+ * @param Options are the options to set. Multiple options can be set
by OR'ing
+ * XTE_*_OPTIONS constants together. Options not specified are not
+ * affected.
+ *
+ * @return
+ * - 0 if the options were set successfully
+ * - XST_DEVICE_IS_STARTED if the device has not yet been stopped
+ * - XST_NO_FEATURE if setting an option requires HW support not present
+ *
+ * @note
+ * See xtemac.h for a description of the available options.
+ *
+
******************************************************************************/
+static u32
+temac_setoptions(struct net_device *ndev, u32 Options) {
+ struct temac_local *lp = netdev_priv(ndev);
+ struct temac_option *tp = &temac_options[0];
+
+ while (tp->opt) {
+ if (tp->flg)
+ tio_setclr(ndev, tp->reg, tp->m_or, (Options & tp->opt));
+ else
+ emac_cfg_setclr(ndev, tp->reg, tp->m_or, (Options & tp->opt));
+ tp++;
+ }
+ lp->options |= Options;
+
+ return (0);
+}
+
+/*
+Initilize temac board
+ */
+static void
+temac_device_reset(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+
+ /* Perform a software reset */
+
+ /* 0x300 host enable bit ? */
+ /* reset PHY through control register ?:1 */
+
+ /* Reset the device */
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK);
+ while (emac_cfg_read(ndev, XTE_RXC1_OFFSET) & XTE_RXC1_RXRST_MASK)
; /* Wait for the receiver to finish reset */
+
+ emac_cfg_write(ndev, XTE_TXC_OFFSET, XTE_TXC_TXRST_MASK);
+ while (emac_cfg_read(ndev, XTE_TXC_OFFSET) & XTE_TXC_TXRST_MASK) ;
/* Wait for the transmitter to finish reset */
+ /* Disable the receiver */
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, emac_cfg_read(ndev,
XTE_RXC1_OFFSET) & ~XTE_RXC1_RXEN_MASK);
+
+ tiow(ndev, XTE_RAF0_OFFSET, 1);
/* reset */
+ while(tior(ndev, XTE_RAF0_OFFSET) & 1);
/* wait for reset */
+ /* ISR0/IER0/IPR0 bits */
+ /* b1 autoneg
complete */
+ /* b2 receive
complete */
+ /* b5 transmit
complete */
+ /* b0 = interrupts from TIS/TIE registers */
+
+
+ sd_iow(ndev, DMA_CONTROL_REG, DMA_CONTROL_RST); /*
Reset ? */
+ while(sd_ior(ndev, DMA_CONTROL_REG) & DMA_CONTROL_RST);
+
+ printk(KERN_INFO "%s: Xilinx Embedded Tri-Mode Ethernet MAC %s
%s\n", ndev->name, __DATE__, __TIME__);
+ // printk(KERN_INFO "temac %08x[%08x] sdma %08x[%08x]\n", (u32)
ndev->base_addr, LLTEMAC_BASEADDR, lp->sdma_base_addr, SDMACTRL_BASEADDR);
+ printk(KERN_INFO "temac %08x sdma %08x\n", (u32) ndev->base_addr,
lp->sdma_base_addr);
+
+ temac_bd_init(ndev);
+ /* emac_cfg_write(ndev, XTE_AFM_OFFSET, 0x00000000); */
+ emac_cfg_write(ndev, XTE_RXC0_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_TXC_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK);
+
+ /* Sync default options with HW but leave receiver and transmitter
disabled. */
+ temac_setoptions(ndev, lp-> options & ~(XTE_OPTION_TXEN |
XTE_OPTION_RXEN));
+ temac_phy_init(ndev);
+ temac_set_mac_address(ndev, 0);
+ /* Set address filter table */
+ temac_set_multicast_list(ndev);
+ if (temac_setoptions(ndev, lp->options))
+ dev_err(ndev, "Error setting TEMAC options\n");
+
+ /* Init Driver variable */
+ ndev->trans_start = 0;
+ spin_lock_init(&lp->lock);
+ spin_lock_init(&lp->tx_lock);
+ spin_lock_init(&lp->rx_lock);
+}
+
+static void
+temac_hard_start_xmit_done(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct cdmac_bd *cur_p;
+ unsigned int stat = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->tx_lock, flags);
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
+ stat = cur_p->app0;
+
+ while(stat & STS_CTRL_APP0_CMPLT) {
+ pci_unmap_single(NULL, (unsigned long)cur_p->phys, cur_p->len,
PCI_DMA_TODEVICE);
+ if (cur_p->app4)
+ dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
+ cur_p->app0 = 0;
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += cur_p->len;
+
+ lp->tx_bd_ci++;
+ if (lp->tx_bd_ci >= TX_BD_NUM) lp->tx_bd_ci = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
+ stat = cur_p->app0;
+ }
+
+ spin_unlock_irqrestore(&lp->tx_lock, flags);
+
+ if(netif_queue_stopped(ndev)) {
+ netif_wake_queue(ndev);
+ }
+}
+
+static int
+temac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct cdmac_bd *cur_p, *start_p, *tail_p;
+ int i;
+ unsigned long num_frag;
+ skb_frag_t *frag;
+
+ spin_lock(&lp->tx_lock);
+
+ num_frag = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[0];
+ start_p = &lp->tx_bd_p[lp->tx_bd_tail];
+ cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
+
+ if(cur_p->app0 & STS_CTRL_APP0_CMPLT) {
+ if(!netif_queue_stopped(ndev)) {
+ netif_stop_queue(ndev);
+ spin_unlock(&lp->tx_lock);
+ return NETDEV_TX_BUSY;
+ }
+ return NETDEV_TX_BUSY;
+ }
+
+ cur_p->app0 = 0;
+ if(skb->ip_summed == CHECKSUM_PARTIAL) {
+ const struct iphdr *ip = ip_hdr(skb);
+ int length=0, start, insert=0, headlen;
+
+ switch(ip->protocol) {
+ case IPPROTO_TCP:
+ start = sizeof(struct iphdr) + ETH_HLEN;
+ insert = sizeof(struct iphdr) + ETH_HLEN + 16;
+ length = ip->tot_len - sizeof(struct iphdr);
+ headlen = ETH_HLEN + sizeof(struct iphdr) +
sizeof(struct tcphdr);
+ break;
+ case IPPROTO_UDP:
+ start = sizeof(struct iphdr) + ETH_HLEN;
+ insert = sizeof(struct iphdr) + ETH_HLEN + 6;
+ length = ip->tot_len - sizeof(struct iphdr);
+ headlen = ETH_HLEN + sizeof(struct iphdr) +
sizeof(struct udphdr);
+ break;
+ default:
+ break;
+ }
+ cur_p->app1 = ((start << 16) | insert);
+ cur_p->app2 = csum_tcpudp_magic(ip->saddr, ip->daddr, length,
ip->protocol, 0);
+ skb->data[insert] = 0;
+ skb->data[insert + 1] = 0;
+ }
+ cur_p->app0 |= STS_CTRL_APP0_SOP;
+ cur_p->len = skb_headlen(skb);
+ cur_p->phys = (unsigned char *)pci_map_single(NULL, skb->data,
skb->len, PCI_DMA_TODEVICE);
+ cur_p->app4 = (unsigned long)skb;
+
+ for(i = 0;i < num_frag;i++) {
+ lp->tx_bd_tail++;
+ if (lp->tx_bd_tail >= TX_BD_NUM) lp->tx_bd_tail = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
+ cur_p->phys = (unsigned char *)pci_map_single(NULL, (void
*)page_address(frag->page) + frag->page_offset, frag-> size,
PCI_DMA_TODEVICE);
+ cur_p->len = frag->size;
+ cur_p->app0 = 0;
+ frag++;
+ }
+ cur_p->app0 |= STS_CTRL_APP0_EOP;
+
+ tail_p = &lp->tx_bd_p[lp->tx_bd_tail];
+ lp->tx_bd_tail++;
+ if (lp->tx_bd_tail >= TX_BD_NUM) lp->tx_bd_tail = 0;
+
+ if(!(sd_ior(ndev, TX_CHNL_STS) & CHNL_STS_ENGBUSY)) { /*
EngBusy ? */
+ sd_iow(ndev, TX_CURDESC_PTR, (uintptr_t)start_p);
+ sd_iow(ndev, TX_TAILDESC_PTR, (uintptr_t)tail_p); /* DMA
start */
+ }
+
+ spin_unlock(&lp->tx_lock);
+
+ return 0;
+}
+
+/*
+Stop the interface.
+Stops the interface. The interface is stopped when it is brought down.
+This function should reverse operations performed at open time.
+*/
+static int
+temac_stop(struct net_device *ndev)
+{
+ return 0;
+}
+
+static void
+ll_temac_recv(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct sk_buff *skb, *new_skb;
+ unsigned int bdstat;
+ unsigned long align;
+ struct cdmac_bd *cur_p, *tail_p;
+ int length;
+ unsigned long skb_vaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->rx_lock, flags);
+
+ tail_p = &lp->rx_bd_p[lp->rx_bd_ci];
+ cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
+
+ bdstat = cur_p->app0;
+ while((bdstat & STS_CTRL_APP0_CMPLT)) {
+
+ skb = lp->rx_skb[lp->rx_bd_ci];
+ length = cur_p->app4;
+
+ skb_vaddr = virt_to_bus(skb->data);
+ pci_unmap_single(NULL, skb_vaddr, length, PCI_DMA_FROMDEVICE);
+
+ skb_put(skb, length);
+ skb->dev = ndev;
+ skb->protocol = eth_type_trans(skb, ndev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(skb);
+
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += length;
+
+ new_skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE + ALIGNMENT,
GFP_ATOMIC);
+ if(new_skb == 0) {
+ printk("no memory for new sk_buff\n");
+ spin_unlock_irqrestore(&lp->rx_lock, flags);
+ return;
+ }
+
+ align = BUFFER_ALIGN(new_skb->data);
+ if(align) skb_reserve(new_skb, align);
+
+ cur_p->app0 = STS_CTRL_APP0_IRQONEND;
+ cur_p->phys = (unsigned char *)
+ pci_map_single(NULL, new_skb->data,
+ XTE_MAX_JUMBO_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ cur_p->len = XTE_MAX_JUMBO_FRAME_SIZE;
+ lp->rx_skb[lp->rx_bd_ci] = new_skb;
+
+ lp->rx_bd_ci++;
+ if(lp->rx_bd_ci >= RX_BD_NUM) lp->rx_bd_ci = 0;
+
+ cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
+ bdstat = cur_p->app0;
+ }
+ sd_iow(ndev, RX_TAILDESC_PTR, (uintptr_t)tail_p);
+
+ spin_unlock_irqrestore(&lp->rx_lock, flags);
+}
+
+static irqreturn_t
+ll_temac_tx_interrupt(int irq, void *dev_id)
+{
+ unsigned int status;
+ struct net_device *ndev = (struct net_device *)dev_id;
+
+ status = sd_ior(ndev, TX_IRQ_REG);
+ sd_iow(ndev, TX_IRQ_REG, status);
+
+ if(status & (IRQ_COAL | IRQ_DLY))
+ temac_hard_start_xmit_done(ndev);
+ if(status & 0x080)
+ dev_err(ndev, "DMA error 0x%x\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+ll_temac_rx_interrupt(int irq, void * dev_id)
+{
+ unsigned int status;
+ struct net_device *ndev = (struct net_device *)dev_id;
+
+ status = sd_ior(ndev, RX_IRQ_REG);
+ sd_iow(ndev, RX_IRQ_REG, status);
+
+ if(status & (IRQ_COAL | IRQ_DLY)) ll_temac_recv(ndev);
+
+ return IRQ_HANDLED;
+}
+
+/*
+Whenever an application needs to get statistics for the interface, this
method is
+called. This happens, for example, when ifconfig or netstat -i is run.
A sample
+implementation for snull is introduced in the section Statistical
Information.
+ */
+static struct net_device_stats *
+temac_get_stats(struct net_device *ndev)
+{
+ return netdev_priv(ndev);
+}
+
+static int
+temac_open(struct net_device *ndev)
+{
+
+ return 0;
+}
+
+/*
+OPTIONAL
+void (*poll_controller)(struct net_device *dev);
+Function that asks the driver to check for events on the interface in
situations
+where interrupts are disabled. It is used for specific in-kernel
networking tasks,
+such as remote consoles and kernel debugging over the network.
+
+*/
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void
+temac_poll_controller(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+
+ disable_irq(lp->tx_irq);
+ disable_irq(lp->rx_irq);
+
+ ll_temac_rx_interrupt(lp->tx_irq, ndev, 0);
+ ll_temac_tx_interrupt(lp->rx_irq, ndev, 0);
+
+ enable_irq(lp->tx_irq);
+ enable_irq(lp->rx_irq);
+}
+#endif
+/*
+OPTIONAL
+
+int (*change_mtu)(struct net_device *dev, int new_mtu);
+Function that takes action if there is a change in the maximum transfer
unit (MTU)
+for the interface. If the driver needs to do anything particular when
the MTU is
+changed by the user, it should declare its own function; otherwise, the
default does
+the right thing. snull has a template for the function if you are
interested.
+*/
+static int
+temac_change_mtu(struct net_device *ndev, int newmtu)
+{
+ dev_info(ndev, "new MTU %d\n", newmtu);
+ ndev->mtu = newmtu; /* change mtu in net_device structure */
+
+ return 0;
+}
+
+
+static int __init
+temac_device_map(struct platform_device *pdev, struct net_device *ndev,
int num, unsigned long *addr)
+{
+ struct resource *res;
+ int erC = 0;
+
+ if((res = platform_get_resource(pdev, IORESOURCE_MEM, num)) == 0) {
+ erC = -ENODEV;
+ goto fail;
+ }
+ if (request_mem_region(res->start, res->end - res->start, pdev->name)
== 0) {
+ printk(KERN_ERR "%s: failed to request registers\n", pdev->name);
+ erC = -ENXIO;
+ goto fail_reserve;
+ }
+
+ *addr = (unsigned long) ioremap_nocache(res->start, res->end -
res->start);
+ if (*addr == 0) {
+ printk(KERN_ERR "%s: failed to remap registers\n", pdev->name);
+ erC = -ENXIO;
+ goto fail_remap;
+ }
+ // dev_info(pdev, "num %d start %p, end %p, addr %p\n", num,
res->start, res->end, *addr);
+ return 0;
+fail_remap:
+ release_region(*addr, res->end-res->start);
+fail_reserve:
+fail:
+ return erC;
+}
+/*
+Search TEMAC board, allocate space and register it
+ */
+static int __init
+temac_device_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev = alloc_etherdev(sizeof(struct temac_local));
+ // struct eth_plat_info *plat = pdev->dev.platform_data;
+ struct temac_local *lp;
+ u8 addr[] = { 0x0, 0x50, 0xc2, 0x44, 0x2f, 0xff };
+ int erC = 0;
+
+ /* Init network device */
+ if (!ndev) {
+ dev_err(pdev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ /* setup board info structure */
+ /* Clear memory */
+ lp = netdev_priv(ndev);
+ memset(lp, 0, sizeof(struct temac_local));
+ if ((erC = temac_device_map(pdev, ndev, 0, &ndev->base_addr))) {
+ dev_err("pdev, could not allocate temac regs.\n");
+ return erC;
+ }
+ if ((erC = temac_device_map(pdev, ndev, 1, &lp->sdma_base_addr))) {
+ dev_err(pdev, "could not allocate sdma regs.\n");
+ return erC;
+ }
+ if ((lp->rx_irq = platform_get_irq(pdev, 0)) < 0) {
+ dev_err(pdev, "could not allocate rx irq.\n");
+ return -ENOMEM;
+ }
+ // dev_info(pdev, "rx_irq %d \n", lp->rx_irq);
+ if ((lp->tx_irq = platform_get_irq(pdev, 1)) < 0) {
+ dev_err(pdev, "could not allocate tx irq.\n");
+ return -ENOMEM;
+ }
+ // dev_info(pdev, "tx_irq %d \n", lp->tx_irq);
+
+ dev_info(pdev, "%s", DRV_NAME ".c:v " __DATE__ " " DRV_AUTHOR " "
DRV_EMAIL "\n");
+
+ lp->dev = ndev;
+ lp->emac_num = 0;
+ lp->options = XTE_OPTION_DEFAULTS;
+ lp->LinkSpeed = 1000; /* Tell driver that the PHY is
10/100/1000 capable */
+ lp->mii = 1; /* really important can't
read/write anyting until set */
+
+ // memcpy(ndev->dev_addr, plat->hwaddr, ETH_ALEN);
+ temac_set_mac_address(ndev, addr);
+ /* from this point we assume that we have found a TEMAC */
+ /* driver system function */
+ ether_setup(ndev);
+ /* The TEMAC-specific entries in the device structure. */
+ ndev->open = &temac_open;
+ ndev->stop = &temac_stop;
+ ndev->get_stats = &temac_get_stats;
+ ndev->set_mac_address = &temac_set_mac_address;
+ ndev->hard_start_xmit = &temac_hard_start_xmit;
+ ndev->set_multicast_list = &temac_set_multicast_list;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ ndev->poll_controller = &temac_poll_controller;
+#endif
+ ndev->change_mtu = &temac_change_mtu;
+ ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
+
+ ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST;
+
+ erC = request_irq(lp->tx_irq, ll_temac_tx_interrupt, 0, ndev->name,
ndev);
+ erC = request_irq(lp->rx_irq, ll_temac_rx_interrupt, 0, ndev->name,
ndev);
+ temac_device_reset(ndev);
+
+ if ((erC = register_netdev(ndev)))
+ goto nodev;
+ return 0;
+
+ release_region(ndev->base_addr, 0x18);
+nodev:
+ free_netdev(ndev);
+ ndev = NULL;
+ return erC;
+}
+
+static int __devexit temac_device_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ // struct temac_local *lp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ // free_irq(dev->irq, dev);
+ // dma_free_coherent(NULL, sizeof(struct recv_desc_bufs), lp->dlist,
(dma_addr_t)lp->dlist_phys);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return 0;
+}
+static struct platform_driver temac_device_driver = {
+ .probe = temac_device_probe,
+ .remove = __devexit_p(temac_device_remove),
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+temac_init_module(void)
+{
+ printk(KERN_INFO "temac_init_module()");
+ return platform_driver_register(&temac_device_driver);
+}
+
+static void __exit
+temac_cleanup_module(void)
+{
+ platform_driver_unregister(&temac_device_driver);
+}
+
+module_init(temac_init_module);
+module_exit(temac_cleanup_module);
+
+MODULE_DESCRIPTION("Xilinx Tri-Mode Eth MAC driver");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+// MODULE_ALIAS("platform:" DRV_NAME);
+
+
diff --git a/include/linux/xilinx_devices.h b/include/linux/xilinx_devices.h
index 41ad421..79ca491 100755
--- a/include/linux/xilinx_devices.h
+++ b/include/linux/xilinx_devices.h
@@ -94,7 +94,7 @@ struct xtemac_platform_data {
#define XTEMAC_DMA_SGDMA 3 /* scatter gather DMA */
#endif
-#if defined(CONFIG_XILINX_LLTEMAC)
+#if defined(CONFIG_XILINX_LLTEMAC) || defined(CONFIG_XPS_LLTEMAC)
/* LLTEMAC platform data */
struct xlltemac_platform_data {
u8 tx_csum;
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
2008-08-17 4:59 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
@ 2008-08-18 12:30 ` Ben Hutchings
2008-08-18 14:36 ` Ben Hutchings
2008-08-18 17:01 ` [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 EthernetNIC Stephen Neuendorffer
1 sibling, 1 reply; 8+ messages in thread
From: Ben Hutchings @ 2008-08-18 12:30 UTC (permalink / raw)
To: David H. Lynch Jr.; +Cc: netdev, linuxppc-embedded
David H. Lynch Jr. wrote:
[...]
> drivers/net/Kconfig | 5
> drivers/net/Makefile | 1
> drivers/net/xps_lltemac.c | 1283
> ++++++++++++++++++++++++++++++++++++++++
You need to disable line-wrapping for posted patches. If your mailer
always wraps the body of a message, send the patch as an attachment.
Some of the code appears to be indented wrongly but that may also have
been broken by your mailer. Use scripts/checkpatch.pl to find the
formatting errors.
[...]
> diff --git a/drivers/net/xps_lltemac.c b/drivers/net/xps_lltemac.c
> new file mode 100644
> index 0000000..1f2c158
> --- /dev/null
> +++ b/drivers/net/xps_lltemac.c
> @@ -0,0 +1,1283 @@
> +/*======================================================================
> +
> + Driver for Xilinx temac ethernet NIC's
> +
> + Author: Yoshio Kashiwagi
> + Copyright (c) 2008 Nissin Systems Co.,Ltd.
> +
> + Revisons: David H. Lynch Jr. <dhlii@dlasys.net>
> + Copyright (C) 2005-2008 DLA Systems
> +
> +======================================================================*/
> +
> +#define DRV_NAME "xilinx_lltemac"
Should be "xps_lltemac" to match the module name.
[...]
> +#include <asm/io.h>
Should be <linux/io.h>; <asm/io.h> doesn't define the same things on every
architecture.
> +/* register access modes */
> +typedef enum { REG_DCR = 1, REG_IND, REG_DIR} REG_MODE;
typedef'd names are deprecated, and enum/struct/union names should be
lower-case.
[...]
> +/* packet size info */
> +#define XTE_MTU 1500 /* max MTU size of
> Ethernet frame */
> +#define XTE_HDR_SIZE 14 /* size of Ethernet
> header */
> +#define XTE_TRL_SIZE 4 /* size of Ethernet
> trailer (FCS) */
> +#define XTE_MAX_FRAME_SIZE (XTE_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE)
What about VLAN tags?
[...]
> This option defaults to enabled (set) */
> +#define XTE_OPTION_TXEN (1 << 11) /**< Enable
> the transmitter. This option defaults to enabled (set) */
> +#define XTE_OPTION_RXEN (1 << 12) /**< Enable
> the receiver This option defaults to enabled (set) */
> +#define XTE_OPTION_DEFAULTS \
> + (XTE_OPTION_TXEN | \
> + XTE_OPTION_FLOW_CONTROL | \
> + XTE_OPTION_RXEN) /**< Default options set when
> device is initialized or reset */
"/**<" looks like the start of a doxygen comment. You should use kernel-
doc format for structured comments.
[...]
> +#define ALIGNMENT 32
That name is a bit generic and might result in a clash later. How about
using XTE_ALIGNMENT instead?
[...]
> +static u32
> +_ior(u32 offset)
> +{
> + u32 value;
> + value = (*(volatile u32 *)(offset));
> + __asm__ __volatile__("eieio");
> + return value;
> +}
> +
> +static void
> +_iow(u32 offset, u32 value)
> +{
> + (*(volatile u32 *)(offset) = value);
> + __asm__ __volatile__("eieio");
> +}
Why aren't you using the generic I/O functions?
If this driver is PowerPC specific, you need to declare that dependency
in Kconfig.
[...]
> +/***************************************************************************
> + * Reads an MII register from the MII PHY attached to the Xilinx Temac.
> + *
> + * Parameters:
> + * dev - the temac device.
> + * phy_addr - the address of the PHY [0..31]
> + * reg_num - the number of the register to read. 0-6 are defined by
> + * the MII spec, but most PHYs have more.
> + * reg_value - this is set to the specified register's value
> + *
> + * Returns:
> + * Success or Failure
> + */
Again, use kernel-doc for structured comments.
The "Returns" description is wrong.
> +static unsigned int
> +mdio_read(struct net_device *ndev, int phy_id, int reg_num)
> +{
> + struct temac_local *lp = netdev_priv(ndev);
> + u32 timeout = PHY_TIMEOUT;
> + u32 rv = 0;
> + unsigned long flags;
> +
> + if (lp->mii) {
> + spin_lock_irqsave(&lp->lock, flags);
> +
> + tiow(ndev, XTE_LSW0_OFFSET, ((phy_id << 5) | (reg_num)));
You need to range-check reg_num before this point.
> + tiow(ndev, XTE_CTL0_OFFSET, XTE_MIIMAI_OFFSET | (lp->emac_num
> << 10));
> + while(!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_MIIM_RR_MASK) &&
> timeout--);
You must not use a simple counter for timing loops. Use udelay() to wait
between polling attempts. Also, never put a semi-colon on the same line
as the while-statement.
> + rv = tior(ndev, XTE_LSW0_OFFSET);
> +
> + spin_unlock_irqrestore(&lp->lock, flags);
> + }
> + return rv;
> +}
The same problems apply to mdio_write(), emac_cfg_read() and
emac_cfg_write().
> +static u32
> +emac_cfg_setclr(struct net_device *ndev, u32 reg_num, u32 val, int flg)
> +{
> + u32 Reg = emac_cfg_read(ndev, reg_num) & ~val;
> + if (flg)
> + Reg |= val;
> + emac_cfg_write(ndev, reg_num, Reg);
> + return 0;
> +}
> +
> +/*
> +Changes the mac address if the controller is not running.
> +
> +static int (*set_mac_address)(struct net_device *dev, void *addr);
> +Function that can be implemented if the interface supports the ability
> to change its
> +hardware address. Many interfaces don't support this ability at all.
> Others use the
> +default eth_mac_addr implementation (from drivers/net/net_init.c).
> eth_mac_addr
> +only copies the new address into dev->dev_addr, and it does so only if
> the interface
> +is not running. Drivers that use eth_mac_addr should set the hardware MAC
> +address from dev->dev_addr in their open method.
> +
> +*/
This comment and several others following it appear to be copied from some
tutorial documentation and are not very useful for this specific
implementation. Please remove them.
[...]
> +static void
> +temac_set_multicast_list(struct net_device *ndev)
> +{
> + struct temac_local *lp = netdev_priv(ndev);
> + u32 multi_addr_msw, multi_addr_lsw;
> + int i;
> +
> + spin_lock(&lp->lock);
> +
> + if(ndev->flags & IFF_PROMISC) {
> + printk(KERN_NOTICE "%s: Promiscuos mode enabled.\n", ndev->name);
Typo: the word is "promiscuous".
> + emac_cfg_write(ndev, XTE_AFM_OFFSET, 0x80000000);
> + } else {
> + struct dev_mc_list *mclist;
> + for(i = 0, mclist = ndev->mc_list; mclist && i <
> ndev->mc_count; i++, mclist = mclist->next) {
> +
> + if(i >= MULTICAST_CAM_TABLE_NUM) break;
So you just ignore the remaining addresses?!
Surely you should add "|| ndev->mc_count > MULTICAST_CAM_TABLE_NUM" to the
condition for setting the MAC to be promiscuous?
> + multi_addr_msw = ((mclist->dmi_addr[3] << 24) |
> (mclist->dmi_addr[2] << 16) | (mclist->dmi_addr[1] << 8) |
> mclist->dmi_addr[0]);
> + emac_cfg_write(ndev, XTE_MAW0_OFFSET, multi_addr_msw);
> + multi_addr_lsw = ((mclist->dmi_addr[5] << 8) |
> mclist->dmi_addr[4]);
> + multi_addr_lsw |= (i << 16);
> + emac_cfg_write(ndev, XTE_MAW1_OFFSET, multi_addr_lsw);
> + }
> + }
> + spin_unlock(&lp->lock);
> +}
> +
> +static void
> +temac_phy_init(struct net_device *ndev)
> +{
> + struct temac_local *lp = netdev_priv(ndev);
> + unsigned int ret, Reg;
> + int ii;
> +
> + /* Set default MDIO divisor */
> + /* Set up MII management registers to write to PHY */
> + emac_cfg_write(ndev, XTE_MC_OFFSET, XTE_MC_MDIO_MASK |
> XTE_MDIO_DIV_DFT);
> +
> + /*
> + Set A-N Advertisement Regs for Full Duplex modes ONLY
> + address 4 = Autonegotiate Advertise Register
> + Disable 1000 Mbps for negotiation if not built for GEth
There's no conditional here so this last line doesn't make sense.
> + */
> + mdio_write(ndev, PHY_NUM, MII_ADVERTISE, mdio_read(ndev, PHY_NUM,
> MII_ADVERTISE) | ADVERTISE_10FULL | ADVERTISE_100FULL | ADVERTISE_CSMA);
> + mdio_write(ndev, PHY_NUM, MII_CTRL1000, ADVERTISE_1000FULL);
> +
> + /*
> + Soft reset the PHY
> + address 0 = Basic Mode Control Register
You're using MII_BMCR so this line of the comment is unneeded.
> + */
> + mdio_write(ndev, PHY_NUM, MII_BMCR, mdio_read(ndev, PHY_NUM,
> MII_BMCR) | BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
> +
> + /* Wait for a PHY Link (auto-negotiation to complete)... */
Why? You should handle AN using a delayed work item or PHY interrupts.
[...]
> +/* this is how to get skb's aligned !!! */
We know.
> + align = BUFFER_ALIGN(skb->data);
> + if(align)
> + skb_reserve(skb, align);
There's no harm in passing 0 to skb_reserve; remove the test and combine
the remaining two lines.
[...]
> + sd_iow(ndev, TX_CHNL_CTRL, 0x10220400 | CHNL_CTRL_IRQ_EN |
> CHNL_CTRL_IRQ_DLY_EN | CHNL_CTRL_IRQ_COAL_EN);
> + /* sd_iow(ndev, TX_CHNL_CTRL, 0x10220483); */
> + /*sd_iow(ndev, TX_CHNL_CTRL, 0x00100483); */
> + sd_iow(ndev, RX_CHNL_CTRL, 0xff010000 | CHNL_CTRL_IRQ_EN |
> CHNL_CTRL_IRQ_DLY_EN | CHNL_CTRL_IRQ_COAL_EN | CHNL_CTRL_IRQ_IOE);
> + /* sd_iow(ndev, RX_CHNL_CTRL, 0xff010283); */
Don't comment-out code. If it's wrong, delete it.
[...]
> +/*****************************************************************************/
> +/**
> + * Set options for the driver/device. The driver should be stopped with
> + * XTemac_Stop() before changing options.
> + *
> + * @param InstancePtr is a pointer to the instance to be worked on.
> + * @param Options are the options to set. Multiple options can be set
> by OR'ing
> + * XTE_*_OPTIONS constants together. Options not specified are not
> + * affected.
> + *
> + * @return
> + * - 0 if the options were set successfully
> + * - XST_DEVICE_IS_STARTED if the device has not yet been stopped
> + * - XST_NO_FEATURE if setting an option requires HW support not present
> + *
> + * @note
> + * See xtemac.h for a description of the available options.
> +
> ******************************************************************************/
Kill this comment; it's in the wrong format and full of misinformation.
> +static void
> +temac_hard_start_xmit_done(struct net_device *ndev)
> +{
[...]
> + if(netif_queue_stopped(ndev)) {
> + netif_wake_queue(ndev);
Remove the test; netif_wake_queue() does that itself.
> + }
> +}
> +
> +static int
> +temac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct temac_local *lp = netdev_priv(ndev);
> + struct cdmac_bd *cur_p, *start_p, *tail_p;
> + int i;
> + unsigned long num_frag;
> + skb_frag_t *frag;
> +
> + spin_lock(&lp->tx_lock);
The kernel has its own TX lock so you shouldn't need this. Use
netif_tx_lock() to synchronise TX reconfiguration with this.
> + num_frag = skb_shinfo(skb)->nr_frags;
> + frag = &skb_shinfo(skb)->frags[0];
> + start_p = &lp->tx_bd_p[lp->tx_bd_tail];
> + cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
> +
> + if(cur_p->app0 & STS_CTRL_APP0_CMPLT) {
> + if(!netif_queue_stopped(ndev)) {
> + netif_stop_queue(ndev);
> + spin_unlock(&lp->tx_lock);
> + return NETDEV_TX_BUSY;
> + }
> + return NETDEV_TX_BUSY;
Here tx_lock is left locked if you stop the queue. What are you trying
to do here?
[...]
> +/*
> +Stop the interface.
> +Stops the interface. The interface is stopped when it is brought down.
> +This function should reverse operations performed at open time.
> +*/
> +static int
> +temac_stop(struct net_device *ndev)
> +{
> + return 0;
You are missing some code here...
[...]
> +static struct net_device_stats *
> +temac_get_stats(struct net_device *ndev)
> +{
> + return netdev_priv(ndev);
Not even the right type. Do you read your compiler warnings?
> +}
> +
> +static int
> +temac_open(struct net_device *ndev)
> +{
> +
> + return 0;
You have got to be kidding.
[...]
> +static int
> +temac_change_mtu(struct net_device *ndev, int newmtu)
> +{
> + dev_info(ndev, "new MTU %d\n", newmtu);
> + ndev->mtu = newmtu; /* change mtu in net_device structure */
Needs range-checking. Or you can just use the default implementation.
> + return 0;
> +}
[...]
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
2008-08-18 12:30 ` Ben Hutchings
@ 2008-08-18 14:36 ` Ben Hutchings
0 siblings, 0 replies; 8+ messages in thread
From: Ben Hutchings @ 2008-08-18 14:36 UTC (permalink / raw)
To: David H. Lynch Jr.; +Cc: netdev, linuxppc-embedded
I wrote:
> > +static struct net_device_stats *
> > +temac_get_stats(struct net_device *ndev)
> > +{
> > + return netdev_priv(ndev);
>
> Not even the right type. Do you read your compiler warnings?
Sorry, I now see that this is correct, though it's very fragile - it will
silently break if struct temac_local is reordered.
struct net_device includes a stats buffer which you should be able to use
instead of adding your own in struct temac_local.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 EthernetNIC
2008-08-17 4:59 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
2008-08-18 12:30 ` Ben Hutchings
@ 2008-08-18 17:01 ` Stephen Neuendorffer
1 sibling, 0 replies; 8+ messages in thread
From: Stephen Neuendorffer @ 2008-08-18 17:01 UTC (permalink / raw)
To: David H. Lynch Jr., linuxppc-embedded, netdev
> +static struct platform_driver temac_device_driver =3D {
> + .probe =3D temac_device_probe,
> + .remove =3D __devexit_p(temac_device_remove),
> + .suspend =3D NULL,
> + .resume =3D NULL,
> + .driver =3D {
> + .name =3D DRV_NAME,
> + .owner =3D THIS_MODULE,
> + },
> +};
I understand you're probably still using arch/ppc, but you'll need to
implement the of bindings as well. This can be done relatively easily
in parallel with a platform_device binding. (and you can probably get
alot of the code out of the xilinx LLTEMAC driver.
> diff --git a/include/linux/xilinx_devices.h
b/include/linux/xilinx_devices.h
> index 41ad421..79ca491 100755
> --- a/include/linux/xilinx_devices.h
> +++ b/include/linux/xilinx_devices.h
> @@ -94,7 +94,7 @@ struct xtemac_platform_data {
> #define XTEMAC_DMA_SGDMA 3 /* scatter gather DMA */
> #endif
> =
> -#if defined(CONFIG_XILINX_LLTEMAC)
> +#if defined(CONFIG_XILINX_LLTEMAC) || defined(CONFIG_XPS_LLTEMAC)
> /* LLTEMAC platform data */
> struct xlltemac_platform_data {
> u8 tx_csum;
You probably want to generate patches against mainline in the future...
Steve
This email and any attachments are intended for the sole use of the named r=
ecipient(s) and contain(s) confidential information that may be proprietary=
, privileged or copyrighted under applicable law. If you are not the intend=
ed recipient, do not read, copy, or forward this email message or any attac=
hments. Delete this email message and any attachments immediately.
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
@ 2008-08-19 9:34 David H. Lynch Jr.
2008-08-22 16:10 ` Sergey Temerkhanov
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: David H. Lynch Jr. @ 2008-08-19 9:34 UTC (permalink / raw)
To: linuxppc-embedded, netdev
[-- Attachment #1: Type: text/plain, Size: 8 bytes --]
Pass II
[-- Attachment #2: lltemac --]
[-- Type: text/plain, Size: 45237 bytes --]
Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
Original Author Yoshio Kashiwagi
Updated and Maintained by David Lynch
Signed-off-by: David H. Lynch Jr. <dhlii@dlasys.net>
---
drivers/net/Kconfig | 5
drivers/net/Makefile | 1
drivers/net/xps_lltemac.c | 1562 ++++++++++++++++++++++++++++++++++++++++
include/linux/xilinx_devices.h | 2
4 files changed, 1569 insertions(+), 1 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 033e13f..71b4c17 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2332,6 +2332,11 @@ config MV643XX_ETH
Some boards that use the Discovery chipset are the Momenco
Ocelot C and Jaguar ATX and Pegasos II.
+config XPS_LLTEMAC
+ tristate "Xilinx LLTEMAC 10/100/1000 Ethernet MAC driver"
+ help
+ This driver supports the Xilinx 10/100/1000 LLTEMAC found in Virtex 4 FPGAs
+
config QLA3XXX
tristate "QLogic QLA3XXX Network Driver Support"
depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1f09934..9196bab 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_PICO_TEMAC) += pico_temac.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
+obj-$(CONFIG_XPS_LLTEMAC) += xps_lltemac.o
obj-$(CONFIG_QLA3XXX) += qla3xxx.o
obj-$(CONFIG_PPP) += ppp_generic.o
diff --git a/drivers/net/xps_lltemac.c b/drivers/net/xps_lltemac.c
new file mode 100644
index 0000000..8af4e7a
--- /dev/null
+++ b/drivers/net/xps_lltemac.c
@@ -0,0 +1,1562 @@
+/*======================================================================
+
+ Driver for Xilinx temac ethernet NIC's
+
+ Author: Yoshio Kashiwagi
+ Copyright (c) 2008 Nissin Systems Co.,Ltd.
+
+ Revisons: David H. Lynch Jr. <dhlii@dlasys.net>
+ Copyright (C) 2005-2008 DLA Systems
+
+======================================================================*/
+/* DRV_NAME is for compatibility with existing xilinx ll temac driver
+ * also with of/dtc object names */
+#define DRV_NAME "xilinx_lltemac"
+#define DRV_AUTHOR "Yoshio Kashiwagi"
+#define DRV_EMAIL ""
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <linux/mii.h>
+#include <linux/in.h>
+#include <linux/pci.h>
+
+#include <linux/ip.h>
+#include <linux/tcp.h> /* just needed for sizeof(tcphdr) */
+#include <linux/udp.h> /* needed for sizeof(udphdr) */
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#define MII_ANI 0x10
+#define PHY_NUM 0
+#define PHY_TIMEOUT 10000
+
+#define MII_SSR 0x11
+#define MII_SSR_LINK (1 << 10)
+#define MII_SSR_SPDMASK 0xC000
+#define MII_SSR_SPD1000 (1 << 15)
+#define MII_SSR_SPD100 (1 << 14)
+#define MII_SSR_SPD10 0
+#define MII_SSR_FD (1 << 13)
+
+#define MII_ISR 0x13
+#define MII_REG_MAX 0xff
+#define TEMAC_REG_MAX 0x3ff
+#define PHY_ADDR_INVALID 0xff
+
+/* packet size info */
+#define XTE_MTU 1500 /* max MTU size of Ethernet frame */
+#define XTE_HDR_SIZE 14 /* size of Ethernet header */
+#define XTE_TRL_SIZE 4 /* size of Ethernet trailer (FCS) */
+#define XTE_MAX_FRAME_SIZE (XTE_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE)
+#define XTE_JUMBO_MTU 9000
+#define XTE_MAX_JUMBO_FRAME_SIZE (XTE_JUMBO_MTU + XTE_HDR_SIZE + XTE_TRL_SIZE)
+
+/* Configuration options */
+
+/* Accept all incoming packets.
+ * This option defaults to disabled (cleared) */
+#define XTE_OPTION_PROMISC (1 << 0)
+/* Jumbo frame support for Tx & Rx.
+ * This option defaults to disabled (cleared) */
+#define XTE_OPTION_JUMBO (1 << 1)
+/* VLAN Rx & Tx frame support.
+ * This option defaults to disabled (cleared) */
+#define XTE_OPTION_VLAN (1 << 2)
+/* Enable recognition of flow control frames on Rx
+ * This option defaults to enabled (set) */
+#define XTE_OPTION_FLOW_CONTROL (1 << 4)
+/* Strip FCS and PAD from incoming frames.
+ * Note: PAD from VLAN frames is not stripped.
+ * This option defaults to disabled (set) */
+#define XTE_OPTION_FCS_STRIP (1 << 5)
+/* Generate FCS field and add PAD automatically for outgoing frames.
+ * This option defaults to enabled (set) */
+#define XTE_OPTION_FCS_INSERT (1 << 6)
+/* Enable Length/Type error checking for incoming frames. When this option is
+set, the MAC will filter frames that have a mismatched type/length field
+and if XTE_OPTION_REPORT_RXERR is set, the user is notified when these
+types of frames are encountered. When this option is cleared, the MAC will
+allow these types of frames to be received.
+This option defaults to enabled (set) */
+#define XTE_OPTION_LENTYPE_ERR (1 << 7)
+/* Enable the transmitter.
+ * This option defaults to enabled (set) */
+#define XTE_OPTION_TXEN (1 << 11)
+/* Enable the receiver
+* This option defaults to enabled (set) */
+#define XTE_OPTION_RXEN (1 << 12)
+
+/* Default options set when device is initialized or reset */
+#define XTE_OPTION_DEFAULTS \
+ (XTE_OPTION_TXEN | \
+ XTE_OPTION_FLOW_CONTROL | \
+ XTE_OPTION_RXEN)
+
+/* XPS_LL_TEMAC SDMA registers definition */
+
+#define TX_NXTDESC_PTR 0x00 /* r */
+#define TX_CURBUF_ADDR 0x04 /* r */
+#define TX_CURBUF_LENGTH 0x08 /* r */
+#define TX_CURDESC_PTR 0x0c /* rw */
+#define TX_TAILDESC_PTR 0x10 /* rw */
+#define TX_CHNL_CTRL 0x14 /* rw */
+/*
+ 0:7 24:31 IRQTimeout
+ 8:15 16:23 IRQCount
+ 16:20 11:15 Reserved
+ 21 10 0
+ 22 9 UseIntOnEnd
+ 23 8 LdIRQCnt
+ 24 7 IRQEn
+ 25:28 3:6 Reserved
+ 29 2 IrqErrEn
+ 30 1 IrqDlyEn
+ 31 0 IrqCoalEn
+*/
+#define CHNL_CTRL_IRQ_IOE (1 << 9)
+#define CHNL_CTRL_IRQ_EN (1 << 7)
+#define CHNL_CTRL_IRQ_ERR_EN (1 << 2)
+#define CHNL_CTRL_IRQ_DLY_EN (1 << 1)
+#define CHNL_CTRL_IRQ_COAL_EN (1 << 0)
+#define TX_IRQ_REG 0x18 /* rw */
+/*
+ 0:7 24:31 DltTmrValue
+ 8:15 16:23 ClscCntrValue
+ 16:17 14:15 Reserved
+ 18:21 10:13 ClscCnt
+ 22:23 8:9 DlyCnt
+ 24:28 3::7 Reserved
+ 29 2 ErrIrq
+ 30 1 DlyIrq
+ 31 0 CoalIrq
+ */
+#define TX_CHNL_STS 0x1c /* r */
+/*
+ 0:9 22:31 Reserved
+ 10 21 TailPErr
+ 11 20 CmpErr
+ 12 19 AddrErr
+ 13 18 NxtPErr
+ 14 17 CurPErr
+ 15 16 BsyWr
+ 16:23 8:15 Reserved
+ 24 7 Error
+ 25 6 IOE
+ 26 5 SOE
+ 27 4 Cmplt
+ 28 3 SOP
+ 29 2 EOP
+ 30 1 EngBusy
+ 31 0 Reserved
+*/
+
+#define RX_NXTDESC_PTR 0x20 /* r */
+#define RX_CURBUF_ADDR 0x24 /* r */
+#define RX_CURBUF_LENGTH 0x28 /* r */
+#define RX_CURDESC_PTR 0x2c /* rw */
+#define RX_TAILDESC_PTR 0x30 /* rw */
+#define RX_CHNL_CTRL 0x34 /* rw */
+/*
+ 0:7 24:31 IRQTimeout
+ 8:15 16:23 IRQCount
+ 16:20 11:15 Reserved
+ 21 10 0
+ 22 9 UseIntOnEnd
+ 23 8 LdIRQCnt
+ 24 7 IRQEn
+ 25:28 3:6 Reserved
+ 29 2 IrqErrEn
+ 30 1 IrqDlyEn
+ 31 0 IrqCoalEn
+ */
+#define RX_IRQ_REG 0x38 /* rw */
+#define IRQ_COAL (1 << 0)
+#define IRQ_DLY (1 << 1)
+#define IRQ_ERR (1 << 2)
+#define IRQ_DMAERR (1 << 7) /* this is not documented ??? */
+/*
+ 0:7 24:31 DltTmrValue
+ 8:15 16:23 ClscCntrValue
+ 16:17 14:15 Reserved
+ 18:21 10:13 ClscCnt
+ 22:23 8:9 DlyCnt
+ 24:28 3::7 Reserved
+*/
+#define RX_CHNL_STS 0x3c /* r */
+#define CHNL_STS_ENGBUSY (1 << 1)
+#define CHNL_STS_EOP (1 << 2)
+#define CHNL_STS_SOP (1 << 3)
+#define CHNL_STS_CMPLT (1 << 4)
+#define CHNL_STS_SOE (1 << 5)
+#define CHNL_STS_IOE (1 << 6)
+#define CHNL_STS_ERR (1 << 7)
+
+#define CHNL_STS_BSYWR (1 << 16)
+#define CHNL_STS_CURPERR (1 << 17)
+#define CHNL_STS_NXTPERR (1 << 18)
+#define CHNL_STS_ADDRERR (1 << 19)
+#define CHNL_STS_CMPERR (1 << 20)
+#define CHNL_STS_TAILERR (1 << 21)
+/*
+ 0:9 22:31 Reserved
+ 10 21 TailPErr
+ 11 20 CmpErr
+ 12 19 AddrErr
+ 13 18 NxtPErr
+ 14 17 CurPErr
+ 15 16 BsyWr
+ 16:23 8:15 Reserved
+ 24 7 Error
+ 25 6 IOE
+ 26 5 SOE
+ 27 4 Cmplt
+ 28 3 SOP
+ 29 2 EOP
+ 30 1 EngBusy
+ 31 0 Reserved
+*/
+
+#define DMA_CONTROL_REG 0x40 /* rw */
+#define DMA_CONTROL_RST (1 << 0)
+
+/* XPS_LL_TEMAC direct registers definition */
+
+#define XTE_RAF0_OFFSET 0x00
+#define RAF0_RST (1 << 0)
+#define RAF0_MCSTREJ (1 << 1)
+#define RAF0_BCSTREJ (1 << 2)
+#define XTE_TPF0_OFFSET 0x04
+#define XTE_IFGP0_OFFSET 0x08
+#define XTE_ISR0_OFFSET 0x0c
+#define ISR0_HARDACSCMPLT (1 << 0)
+#define ISR0_AUTONEG (1 << 1)
+#define ISR0_RXCMPLT (1 << 2)
+#define ISR0_RXREJ (1 << 3)
+#define ISR0_RXFIFOOVR (1 << 4)
+#define ISR0_TXCMPLT (1 << 5)
+#define ISR0_RXDCMLCK (1 << 6)
+
+#define XTE_IPR0_OFFSET 0x10
+#define XTE_IER0_OFFSET 0x14
+
+#define XTE_MSW0_OFFSET 0x20
+#define XTE_LSW0_OFFSET 0x24
+#define XTE_CTL0_OFFSET 0x28
+#define XTE_RDY0_OFFSET 0x2c
+
+#define XTE_RSE_MIIM_RR_MASK 0x0002
+#define XTE_RSE_MIIM_WR_MASK 0x0004
+#define XTE_RSE_CFG_RR_MASK 0x0020
+#define XTE_RSE_CFG_WR_MASK 0x0040
+
+/* XPS_LL_TEMAC indirect registers offset definition */
+
+/* Rx configuration word 0 */
+#define XTE_RXC0_OFFSET 0x00000200
+/* Rx configuration word 1 */
+#define XTE_RXC1_OFFSET 0x00000240
+/* Receiver reset */
+#define XTE_RXC1_RXRST_MASK (1 << 31)
+/* Jumbo frame enable */
+#define XTE_RXC1_RXJMBO_MASK (1 << 30)
+/* FCS not stripped */
+#define XTE_RXC1_RXFCS_MASK (1 << 29)
+/* Receiver enable */
+#define XTE_RXC1_RXEN_MASK (1 << 28)
+/* VLAN enable */
+#define XTE_RXC1_RXVLAN_MASK (1 << 27)
+/* Half duplex */
+#define XTE_RXC1_RXHD_MASK (1 << 26)
+/* Length/type check disable */
+#define XTE_RXC1_RXLT_MASK (1 << 25)
+
+/* Tx configuration */
+#define XTE_TXC_OFFSET 0x00000280
+/* Transmitter reset */
+#define XTE_TXC_TXRST_MASK (1 << 31)
+/* Jumbo frame enable */
+#define XTE_TXC_TXJMBO_MASK (1 << 30)
+/* Generate FCS */
+#define XTE_TXC_TXFCS_MASK (1 << 29)
+/* Transmitter enable */
+#define XTE_TXC_TXEN_MASK (1 << 28)
+/* VLAN enable */
+#define XTE_TXC_TXVLAN_MASK (1 << 27)
+/* Half duplex */
+#define XTE_TXC_TXHD_MASK (1 << 26)
+/* Flow control configuration */
+#define XTE_FCC_OFFSET 0x000002C0
+/* Rx flow control enable */
+#define XTE_FCC_RXFLO_MASK (1 << 29)
+/* Tx flow control enable */
+#define XTE_FCC_TXFLO_MASK (1 << 30)
+
+/* EMAC configuration */
+#define XTE_EMCFG_OFFSET 0x00000300
+/* Link speed */
+#define XTE_EMCFG_LINKSPD_MASK 0xC0000000
+/* Host interface enable */
+#define XTE_EMCFG_HOSTEN_MASK (1 << 26)
+/* XTE_EMCFG_LINKSPD_MASK for 10 Mbit */
+#define XTE_EMCFG_LINKSPD_10 0x00000000
+/* XTE_EMCFG_LINKSPD_MASK for 100 Mbit */
+#define XTE_EMCFG_LINKSPD_100 (1 << 30)
+/* XTE_EMCFG_LINKSPD_MASK for 1000 Mbit */
+#define XTE_EMCFG_LINKSPD_1000 (1 << 31)
+
+/* RGMII/SGMII configuration */
+#define XTE_GMIC_OFFSET 0x00000320
+/* Management configuration */
+#define XTE_MC_OFFSET 0x00000340
+/* MII management enable */
+#define XTE_MC_MDIO_MASK (1 << 6)
+/* 100 MHz host clock */
+#define XTE_MDIO_CLOCK_DIV_100MHz 0x28
+/* Default MDIO clock divisor */
+#define XTE_MDIO_DIV_DFT 29
+/* Unicast address word 0 */
+#define XTE_UAW0_OFFSET 0x00000380
+/* Unicast address word 1 */
+#define XTE_UAW1_OFFSET 0x00000384
+
+/* Multicast address word 0 */
+#define XTE_MAW0_OFFSET 0x00000388
+/* Multicast address word 1 */
+#define XTE_MAW1_OFFSET 0x0000038C
+/* Promisciuous mode */
+#define XTE_AFM_OFFSET 0x00000390
+/* Promiscuous mode enable */
+#define XTE_AFM_EPPRM_MASK (1 << 31)
+
+/* Interrupt Request status */
+#define XTE_TIS_OFFSET 0x000003A0
+#define TIS_FRIS (1 << 0)
+#define TIS_MRIS (1 << 1)
+#define TIS_MWIS (1 << 2)
+#define TIS_ARIS (1 << 3)
+#define TIS_AWIS (1 << 4)
+#define TIS_CRIS (1 << 5)
+#define TIS_CWIS (1 << 6)
+/* Interrupt Request enable */
+#define XTE_TIE_OFFSET 0x000003A4
+
+/** MII Mamagement Control register (MGTCR) */
+
+/* MII data */
+#define XTE_MGTDR_OFFSET 0x000003B0
+/* MII control */
+#define XTE_MIIMAI_OFFSET 0x000003B4
+
+#define CNTLREG_WRITE_ENABLE_MASK 0x8000
+#define CNTLREG_EMAC1SEL_MASK 0x0400
+#define CNTLREG_ADDRESSCODE_MASK 0x03ff
+
+/* CDMAC descriptor status bit definitions */
+
+#define STS_CTRL_APP0_ERR (1 << 31)
+#define STS_CTRL_APP0_IRQONEND (1 << 30)
+/* undoccumented */
+#define STS_CTRL_APP0_STOPONEND (1 << 29)
+#define STS_CTRL_APP0_CMPLT (1 << 28)
+#define STS_CTRL_APP0_SOP (1 << 27)
+#define STS_CTRL_APP0_EOP (1 << 26)
+#define STS_CTRL_APP0_ENGBUSY (1 << 25)
+/* undocumented */
+#define STS_CTRL_APP0_ENGRST (1 << 24)
+
+#define TX_CONTROL_CALC_CSUM_MASK 1
+
+#define XTE_ALIGN 32
+#define BUFFER_ALIGN(adr) ((XTE_ALIGN - ((u32) adr)) % XTE_ALIGN)
+
+#define MULTICAST_CAM_TABLE_NUM 4
+
+#define TX_BD_NUM 64
+#define RX_BD_NUM 128
+
+#define XILINX_GSRD3_NAPI
+
+
+/* TX/RX CURDESC_PTR points to first descriptor */
+/* TX/RX TAILDESC_PTR points to last descriptor in linked list */
+
+struct cdmac_bd {
+ struct cdmac_bd *next;
+ unsigned char *phys;
+ u32 len;
+ u32 app0;
+ u32 app1; /* TX start << 16 | insert */
+ u32 app2; /* TX csum */
+ u32 app3; /* unused ? */
+ u32 app4; /* skb for TX length for RX */
+} ;
+/* APP0 bits
+ 0 Error
+ 1 IrqOnEnd generate an interrupt at completion of DMA op
+ 2 reserved
+ 3 completed Current descriptor completed
+ 4 SOP TX - marks first desc/ RX marks first desct
+ 5 EOP TX marks last desc/RX marks last desc
+ 6 EngBusy DMA is processing
+ 7 reserved
+ 8:31 application specific
+ */
+
+struct temac_region {
+ void __iomem *addr;
+ void __iomem *base;
+ unsigned long len;
+};
+
+struct temac_local {
+ /* Statistics for this device */
+ struct net_device_stats stats;
+ struct net_device *dev;
+ struct temac_region regs;
+ struct temac_region sdma;
+ int tx_irq;
+ int rx_irq;
+
+ int emac_num;
+ u16 phy_addr;
+ /* Speed of link 10/100/1000 */
+ int LinkSpeed;
+ /* Current options word */
+ u32 options;
+ spinlock_t lock;
+ spinlock_t rx_lock;
+ struct cdmac_bd *tx_bd_v;
+ struct cdmac_bd *tx_bd_p;
+ struct cdmac_bd *rx_bd_v;
+ struct cdmac_bd *rx_bd_p;
+ int tx_bd_ci;
+ int tx_bd_next;
+ int tx_bd_tail;
+ int rx_bd_ci;
+ struct sk_buff **rx_skb;
+};
+
+
+static u32
+tior(struct net_device *ndev, int offset)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+
+ return in_be32((u32 *)(lp->regs.addr + offset));
+}
+
+static void
+tiow(struct net_device *ndev, int offset, u32 value)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ out_be32((u32 *) (lp->regs.addr + offset), value);
+}
+
+static u32
+tio_setclr(struct net_device *ndev, u32 reg_num, u32 val, int flg)
+{
+ u32 Reg = tior(ndev, reg_num) & ~val;
+ if (flg)
+ Reg |= val;
+ tiow(ndev, reg_num, Reg);
+ return 0;
+}
+
+static u32
+sd_ior(struct net_device *ndev, int offset)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ return in_be32((u32 *)(lp->sdma.addr + offset));
+}
+
+static void
+sd_iow(struct net_device *ndev, int offset, u32 value)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ out_be32((u32 *) (lp->sdma.addr + offset), value);
+}
+
+static unsigned int
+mdio_read(struct net_device *ndev, int phy_id, int reg_num)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT;
+ u32 rv = 0;
+ unsigned long flags;
+
+ if ((reg_num > MII_REG_MAX) ||
+ (phy_id == PHY_ADDR_INVALID)) {
+ dev_err(&ndev->dev,
+ "mdio_read(%x, %x) invalid reg_num or invalid phy_id\n",
+ reg_num, phy_id);
+ return -1;
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ tiow(ndev, XTE_LSW0_OFFSET,
+ ((phy_id << 5) | (reg_num)));
+ tiow(ndev, XTE_CTL0_OFFSET,
+ XTE_MIIMAI_OFFSET | (lp->emac_num << 10));
+ while (!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_MIIM_RR_MASK)) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev, "read_MII busy timeout!!\n");
+ return -1;
+ }
+ }
+ rv = tior(ndev, XTE_LSW0_OFFSET);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return rv;
+}
+
+static void
+mdio_write(struct net_device *ndev, int phy_id, int reg_num, int reg_val)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT, status;
+ unsigned long flags;
+
+ if ((reg_num > MII_REG_MAX) ||
+ (phy_id == PHY_ADDR_INVALID)) {
+ dev_err(&ndev->dev,
+ "mdio_write(%x, %x)invalid reg_num or invalid phy_id\n",
+ reg_num, phy_id);
+ return -1;
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ tiow(ndev, XTE_LSW0_OFFSET,
+ reg_val);
+ tiow(ndev, XTE_CTL0_OFFSET,
+ CNTLREG_WRITE_ENABLE_MASK | XTE_MGTDR_OFFSET);
+ tiow(ndev, XTE_LSW0_OFFSET,
+ ((phy_id << 5) | (reg_num)));
+ tiow(ndev, XTE_CTL0_OFFSET,
+ CNTLREG_WRITE_ENABLE_MASK
+ | XTE_MIIMAI_OFFSET
+ | (lp->emac_num << 10));
+ while (!(status = tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_MIIM_WR_MASK)) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev, "write_MII busy timeout!!\n");
+ return ;
+ }
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static u32
+emac_cfg_read(struct net_device *ndev, u16 reg_num)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT;
+
+ if (reg_num > TEMAC_REG_MAX) {
+ dev_err(&ndev->dev,
+ "emac_cfg_read(%x) invalid reg_num\n",
+ reg_num);
+ return -1;
+ }
+
+ tiow(ndev, XTE_CTL0_OFFSET, (lp->emac_num << 10) | reg_num);
+ while (!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_CFG_RR_MASK)) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev, "read temac busy timeout!!\n");
+ return -1;
+ }
+ }
+ return (u32) tior(ndev, XTE_LSW0_OFFSET);
+
+}
+
+static void
+emac_cfg_write(struct net_device *ndev, u32 reg_num, u32 val)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = PHY_TIMEOUT;
+
+ if (reg_num > TEMAC_REG_MAX) {
+ dev_err(&ndev->dev,
+ "emac_cfg_write(%x) invalid reg_num\n",
+ reg_num);
+ return -1;
+ }
+
+ tiow(ndev, XTE_LSW0_OFFSET, val);
+ tiow(ndev,
+ XTE_CTL0_OFFSET,
+ (CNTLREG_WRITE_ENABLE_MASK
+ | (lp->emac_num << 10)
+ | reg_num));
+ while (!(tior(ndev, XTE_RDY0_OFFSET) & XTE_RSE_CFG_WR_MASK)) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev, "write temac busy timeout!!\n");
+ return ;
+ }
+ }
+}
+
+static u32
+emac_cfg_setclr(struct net_device *ndev, u32 reg_num, u32 val, int flg)
+{
+ u32 Reg;
+ if (reg_num > TEMAC_REG_MAX) {
+ dev_err(&ndev->dev,
+ "emac_cfg_setclr(%x) invalid reg_num\n",
+ reg_num);
+ return -1;
+ }
+
+ Reg = emac_cfg_read(ndev, reg_num) & ~val;
+ if (flg)
+ Reg |= val;
+ emac_cfg_write(ndev, reg_num, Reg);
+ return 0;
+}
+
+static int
+temac_set_mac_address(struct net_device *ndev, void *address)
+{
+ if (address)
+ memcpy(ndev->dev_addr, address, ETH_ALEN);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ random_ether_addr(ndev->dev_addr);
+
+ /* set up unicast MAC address filter set its mac address */
+ emac_cfg_write(ndev, XTE_UAW0_OFFSET,
+ ((ndev->dev_addr[0]) |
+ (ndev->dev_addr[1] << 8) |
+ (ndev->dev_addr[2] << 16) |
+ (ndev->dev_addr[3] << 24)));
+ /* There are reserved bits in EUAW1
+ * so don't affect them Set MAC bits [47:32] in EUAW1 */
+ emac_cfg_write(ndev, XTE_UAW1_OFFSET,
+ (ndev->dev_addr[4] & 0x000000ff) |
+ (ndev->dev_addr[5] << 8));
+
+ return 0;
+}
+
+static void
+temac_set_multicast_list(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 multi_addr_msw, multi_addr_lsw;
+ int i;
+
+ spin_lock(&lp->lock);
+
+ if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC)
+ || ndev->mc_count > MULTICAST_CAM_TABLE_NUM) {
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. If it was a promisc request the
+ * flag is already set. If not we assert it.
+ */
+ ndev->flags |= IFF_PROMISC;
+ dev_info(&ndev->dev, "%s: Promiscuous mode enabled.\n",
+ ndev->name);
+ emac_cfg_write(ndev, XTE_AFM_OFFSET, XTE_AFM_EPPRM_MASK);
+ } else if (ndev->mc_count) {
+ struct dev_mc_list *mclist;
+ for (i = 0, mclist = ndev->mc_list;
+ mclist && i < ndev->mc_count;
+ i++, mclist = mclist->next) {
+
+ if (i >= MULTICAST_CAM_TABLE_NUM)
+ break;
+ multi_addr_msw = ((mclist->dmi_addr[3] << 24)
+ | (mclist->dmi_addr[2] << 16)
+ | (mclist->dmi_addr[1] << 8)
+ | mclist->dmi_addr[0]);
+ emac_cfg_write(ndev, XTE_MAW0_OFFSET, multi_addr_msw);
+ multi_addr_lsw = ((mclist->dmi_addr[5] << 8)
+ | mclist->dmi_addr[4]);
+ multi_addr_lsw |= (i << 16);
+ emac_cfg_write(ndev, XTE_MAW1_OFFSET, multi_addr_lsw);
+ }
+ } else {
+ dev_info(&ndev->dev, "%s: Promiscuous mode disabled.\n",
+ ndev->name);
+ emac_cfg_write(ndev,
+ XTE_AFM_OFFSET,
+ emac_cfg_read(ndev, XTE_AFM_OFFSET)
+ & ~XTE_AFM_EPPRM_MASK);
+ emac_cfg_write(ndev, XTE_MAW0_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_MAW1_OFFSET, 0);
+ }
+ spin_unlock(&lp->lock);
+}
+
+static void
+temac_phy_init(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ unsigned int ret, Reg;
+ int ii;
+
+ /* Set default MDIO divisor */
+ /* Set up MII management registers to write to PHY */
+ emac_cfg_write(ndev,
+ XTE_MC_OFFSET,
+ XTE_MC_MDIO_MASK | XTE_MDIO_DIV_DFT);
+
+ /*
+ Set A-N Advertisement Regs for Full Duplex modes ONLY
+ */
+ mdio_write(ndev,
+ PHY_NUM,
+ MII_ADVERTISE,
+ mdio_read(ndev,
+ PHY_NUM,
+ MII_ADVERTISE)
+ | ADVERTISE_10FULL
+ | ADVERTISE_100FULL
+ | ADVERTISE_CSMA);
+ mdio_write(ndev, PHY_NUM, MII_CTRL1000, ADVERTISE_1000FULL);
+
+ /*
+ Soft reset the PHY
+ */
+ mdio_write(ndev,
+ PHY_NUM,
+ MII_BMCR,
+ mdio_read(ndev, PHY_NUM, MII_BMCR)
+ | BMCR_RESET
+ | BMCR_ANENABLE
+ | BMCR_ANRESTART);
+
+ /* Wait for a PHY Link (auto-negotiation to complete)... */
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ ii = 64;
+ while (((ret & BMSR_LSTATUS) != BMSR_LSTATUS) && ii--) {
+ mdelay(500);
+ ret = mdio_read(ndev, PHY_NUM, MII_BMSR);
+ }
+ ret = mdio_read(ndev, PHY_NUM, MII_SSR);
+
+ Reg = emac_cfg_read(ndev, XTE_EMCFG_OFFSET) & ~XTE_EMCFG_LINKSPD_MASK;
+ if (ret & MII_SSR_LINK) {
+ switch (ret & MII_SSR_SPDMASK) {
+ case MII_SSR_SPD1000: /* 1000Base-T */
+ lp->LinkSpeed = 1000;
+ emac_cfg_write(ndev,
+ XTE_EMCFG_OFFSET,
+ Reg | (u32) XTE_EMCFG_LINKSPD_1000);
+ break;
+ case MII_SSR_SPD100: /* 100Base-T */
+ lp->LinkSpeed = 100;
+ emac_cfg_write(ndev,
+ XTE_EMCFG_OFFSET,
+ Reg | XTE_EMCFG_LINKSPD_100);
+ break;
+ case MII_SSR_SPD10: /* 10Base-T */
+ lp->LinkSpeed = 10;
+ break;
+ };
+ if ((ret & MII_SSR_FD) == 0x0) {
+ /* set up Tx/Rx config reg for half duplex */
+ ret = emac_cfg_read(ndev, XTE_TXC_OFFSET);
+ emac_cfg_write(ndev,
+ XTE_TXC_OFFSET,
+ ret | XTE_TXC_TXHD_MASK);
+ ret = emac_cfg_read(ndev, XTE_RXC1_OFFSET);
+ emac_cfg_write(ndev,
+ XTE_RXC1_OFFSET,
+ ret | XTE_RXC1_RXHD_MASK);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+----------------------------------------------------------------------------- */
+static int
+temac_bd_init(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct sk_buff *skb;
+ int ii;
+
+ lp->rx_skb = kzalloc(sizeof(struct sk_buff)*RX_BD_NUM, GFP_KERNEL);
+ /* allocate the tx and rx ring buffer descriptors. */
+ /* returns a virtual addres and a physical address. */
+ lp->tx_bd_v = dma_alloc_coherent(NULL,
+ sizeof(struct cdmac_bd) * TX_BD_NUM,
+ (dma_addr_t *)&lp->tx_bd_p,
+ GFP_KERNEL);
+ lp->rx_bd_v = dma_alloc_coherent(NULL,
+ sizeof(struct cdmac_bd) * RX_BD_NUM,
+ (dma_addr_t *)&lp->rx_bd_p,
+ GFP_KERNEL);
+
+ for (ii = 0; ii < TX_BD_NUM; ii++) {
+ memset((char *)&lp->tx_bd_v[ii], 0, sizeof(struct cdmac_bd));
+ if (ii == (TX_BD_NUM - 1))
+ lp->tx_bd_v[ii].next = &lp->tx_bd_p[0];
+ else
+ lp->tx_bd_v[ii].next = &lp->tx_bd_p[ii + 1];
+
+ }
+ for (ii = 0; ii < RX_BD_NUM; ii++) {
+ memset((char *)&lp->rx_bd_v[ii], 0, sizeof(struct cdmac_bd));
+ if (ii == (RX_BD_NUM - 1))
+ lp->rx_bd_v[ii].next = &lp->rx_bd_p[0];
+ else
+ lp->rx_bd_v[ii].next = &lp->rx_bd_p[ii + 1];
+
+ skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE
+ + XTE_ALIGN, GFP_ATOMIC);
+ if (skb == 0) {
+ dev_err(&ndev->dev, "alloc_skb error %d\n", ii);
+ return -1;
+ }
+ lp->rx_skb[ii] = skb;
+ skb_reserve(skb, BUFFER_ALIGN(skb->data));
+ /* returns physical address of skb->data */
+ lp->rx_bd_v[ii].phys = (unsigned char *)pci_map_single(NULL,
+ skb->data,
+ XTE_MAX_JUMBO_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ lp->rx_bd_v[ii].len = XTE_MAX_JUMBO_FRAME_SIZE;
+ lp->rx_bd_v[ii].app0 = STS_CTRL_APP0_IRQONEND;
+ }
+
+ sd_iow(ndev,
+ TX_CHNL_CTRL,
+ 0x10220400
+ | CHNL_CTRL_IRQ_EN
+ | CHNL_CTRL_IRQ_DLY_EN
+ | CHNL_CTRL_IRQ_COAL_EN);
+ /* 0x10220483 */
+ /* 0x00100483 */
+ sd_iow(ndev,
+ RX_CHNL_CTRL,
+ 0xff010000
+ | CHNL_CTRL_IRQ_EN
+ | CHNL_CTRL_IRQ_DLY_EN
+ | CHNL_CTRL_IRQ_COAL_EN
+ | CHNL_CTRL_IRQ_IOE);
+ /* 0xff010283 */
+
+ sd_iow(ndev, RX_CURDESC_PTR, (uintptr_t)&lp->rx_bd_p[0]);
+ sd_iow(ndev, RX_TAILDESC_PTR, (uintptr_t)&lp->rx_bd_p[RX_BD_NUM - 1]);
+
+ return 0;
+}
+
+struct temac_option {
+ int flg;
+ u32 opt;
+ u32 reg;
+ u32 m_or;
+ u32 m_and;
+
+};
+
+struct temac_option temac_options[] = {
+ /* Turn on jumbo packet support for both Rx and Tx */
+ { 0,
+ XTE_OPTION_JUMBO,
+ XTE_TXC_OFFSET,
+ XTE_TXC_TXJMBO_MASK,
+ 0
+ },
+ { 0,
+ XTE_OPTION_JUMBO,
+ XTE_RXC1_OFFSET,
+ XTE_RXC1_RXJMBO_MASK,
+ 0
+ },
+ /* Turn on VLAN packet support for both Rx and Tx */
+ { 0,
+ XTE_OPTION_VLAN,
+ XTE_TXC_OFFSET,
+ XTE_TXC_TXVLAN_MASK,
+ 0
+ },
+ { 0,
+ XTE_OPTION_VLAN,
+ XTE_RXC1_OFFSET,
+ XTE_RXC1_RXVLAN_MASK,
+ 0
+ },
+ /* Turn on FCS stripping on receive packets */
+ { 0,
+ XTE_OPTION_FCS_STRIP,
+ XTE_RXC1_OFFSET,
+ XTE_RXC1_RXFCS_MASK,
+ 0
+ },
+ /* Turn on FCS insertion on transmit packets */
+ { 0,
+ XTE_OPTION_FCS_INSERT,
+ XTE_TXC_OFFSET,
+ XTE_TXC_TXFCS_MASK,
+ 0
+ },
+ /* Turn on length/type field checking on receive packets */
+ { 0,
+ XTE_OPTION_LENTYPE_ERR,
+ XTE_RXC1_OFFSET,
+ XTE_RXC1_RXLT_MASK,
+ 0
+ },
+ /* Turn on flow control */
+ { 0,
+ XTE_OPTION_FLOW_CONTROL,
+ XTE_FCC_OFFSET,
+ XTE_FCC_RXFLO_MASK,
+ 0
+ },
+ /* Turn on flow control */
+ { 0,
+ XTE_OPTION_FLOW_CONTROL,
+ XTE_FCC_OFFSET,
+ XTE_FCC_TXFLO_MASK,
+ 0
+ },
+ /* Turn on promiscuous frame filtering (all frames are received ) */
+ { 0,
+ XTE_OPTION_PROMISC,
+ XTE_AFM_OFFSET,
+ XTE_AFM_EPPRM_MASK,
+ 0
+ },
+ /* Enable transmitter if not already enabled */
+ { 0,
+ XTE_OPTION_TXEN,
+ XTE_TXC_OFFSET,
+ XTE_TXC_TXEN_MASK,
+ 0
+ },
+ /* Enable receiver? */
+ { 0,
+ XTE_OPTION_RXEN,
+ XTE_RXC1_OFFSET,
+ XTE_RXC1_RXEN_MASK,
+ 0
+ },
+ { 0,
+ 0,
+ 0,
+ 0,
+ 0
+ }
+};
+
+static u32
+temac_setoptions(struct net_device *ndev, u32 Options) {
+ struct temac_local *lp = netdev_priv(ndev);
+ struct temac_option *tp = &temac_options[0];
+
+ while (tp->opt) {
+ if (tp->flg)
+ tio_setclr(ndev,
+ tp->reg,
+ tp->m_or,
+ (Options & tp->opt));
+ else
+ emac_cfg_setclr(ndev,
+ tp->reg,
+ tp->m_or,
+ (Options & tp->opt));
+ tp++;
+ }
+ lp->options |= Options;
+
+ return (0);
+}
+
+/* Initilize temac */
+static void
+temac_device_reset(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ u32 timeout = 1000;
+
+ /* Perform a software reset */
+
+ /* 0x300 host enable bit ? */
+ /* reset PHY through control register ?:1 */
+
+ /* Reset the device */
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK);
+ /* Wait for the receiver to finish reset */
+ while (emac_cfg_read(ndev, XTE_RXC1_OFFSET) & XTE_RXC1_RXRST_MASK) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev,
+ "temac_device_reset RX reset timeout!!\n");
+ break;
+ }
+ }
+
+ emac_cfg_write(ndev, XTE_TXC_OFFSET, XTE_TXC_TXRST_MASK);
+ /* Wait for the transmitter to finish reset */
+ timeout = 1000;
+ while (emac_cfg_read(ndev, XTE_TXC_OFFSET) & XTE_TXC_TXRST_MASK) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev,
+ "temac_device_reset TX reset timeout!!\n");
+ break;
+ }
+ }
+
+ /* Disable the receiver */
+ emac_cfg_write(ndev,
+ XTE_RXC1_OFFSET,
+ emac_cfg_read(ndev, XTE_RXC1_OFFSET)
+ & ~XTE_RXC1_RXEN_MASK);
+
+ /* reset */
+ tiow(ndev, XTE_RAF0_OFFSET, 1);
+ /* wait for reset */
+ timeout = 1000;
+ while (tior(ndev, XTE_RAF0_OFFSET) & 1) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev,
+ "temac_device_reset RAF reset timeout!!\n");
+ break;
+ }
+ }
+
+ /* ISR0/IER0/IPR0 bits */
+ /* b1 autoneg complete */
+ /* b2 receive complete */
+ /* b5 transmit complete */
+ /* b0 = interrupts from TIS/TIE registers */
+
+
+ /* Reset */
+ sd_iow(ndev, DMA_CONTROL_REG, DMA_CONTROL_RST);
+ timeout = 1000;
+ while (sd_ior(ndev, DMA_CONTROL_REG) & DMA_CONTROL_RST) {
+ udelay(1);
+ if (--timeout == 0) {
+ dev_err(&ndev->dev,
+ "temac_device_reset DMA reset timeout!!\n");
+ break;
+ }
+ }
+
+ dev_info(&ndev->dev,
+ "%s: Xilinx Embedded Tri-Mode Ethernet MAC %s %s\n",
+ ndev->name,
+ __DATE__,
+ __TIME__);
+ dev_info(&ndev->dev, "temac %08x sdma %08x\n",
+ lp->regs.addr,
+ lp->sdma.addr);
+
+ temac_bd_init(ndev);
+
+ emac_cfg_write(ndev, XTE_RXC0_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_RXC1_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_TXC_OFFSET, 0);
+ emac_cfg_write(ndev, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK);
+
+ /* Sync default options with HW
+ * but leave receiver and transmitter disabled. */
+ temac_setoptions(ndev,
+ lp->options & ~(XTE_OPTION_TXEN | XTE_OPTION_RXEN));
+
+ temac_phy_init(ndev);
+
+ temac_set_mac_address(ndev, 0);
+ /* Set address filter table */
+ temac_set_multicast_list(ndev);
+ if (temac_setoptions(ndev, lp->options))
+ dev_err(&ndev->dev, "Error setting TEMAC options\n");
+
+ /* Init Driver variable */
+ ndev->trans_start = 0;
+ spin_lock_init(&lp->lock);
+ spin_lock_init(&lp->rx_lock);
+}
+
+static void
+temac_hard_start_xmit_done(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct cdmac_bd *cur_p;
+ unsigned int stat = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
+ stat = cur_p->app0;
+
+ while (stat & STS_CTRL_APP0_CMPLT) {
+ pci_unmap_single(NULL,
+ (unsigned long)cur_p->phys,
+ cur_p->len,
+ PCI_DMA_TODEVICE);
+ if (cur_p->app4)
+ dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
+ cur_p->app0 = 0;
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += cur_p->len;
+
+ lp->tx_bd_ci++;
+ if (lp->tx_bd_ci >= TX_BD_NUM)
+ lp->tx_bd_ci = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_ci];
+ stat = cur_p->app0;
+ }
+
+ netif_wake_queue(ndev);
+}
+
+static int
+temac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct cdmac_bd *cur_p, *start_p, *tail_p;
+ int ii;
+ unsigned long num_frag;
+ skb_frag_t *frag;
+
+ num_frag = skb_shinfo(skb)->nr_frags;
+ frag = &skb_shinfo(skb)->frags[0];
+ start_p = &lp->tx_bd_p[lp->tx_bd_tail];
+ cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
+
+ if (cur_p->app0 & STS_CTRL_APP0_CMPLT) {
+ if (!netif_queue_stopped(ndev)) {
+ netif_stop_queue(ndev);
+ return NETDEV_TX_BUSY;
+ }
+ return NETDEV_TX_BUSY;
+ }
+
+ cur_p->app0 = 0;
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ const struct iphdr *ip = ip_hdr(skb);
+ int length = 0, start, insert = 0, headlen;
+
+ switch (ip->protocol) {
+ case IPPROTO_TCP:
+ start = sizeof(struct iphdr) + ETH_HLEN;
+ insert = sizeof(struct iphdr) + ETH_HLEN + 16;
+ length = ip->tot_len - sizeof(struct iphdr);
+ headlen = ETH_HLEN
+ + sizeof(struct iphdr)
+ + sizeof(struct tcphdr);
+ break;
+ case IPPROTO_UDP:
+ start = sizeof(struct iphdr) + ETH_HLEN;
+ insert = sizeof(struct iphdr) + ETH_HLEN + 6;
+ length = ip->tot_len - sizeof(struct iphdr);
+ headlen = ETH_HLEN
+ + sizeof(struct iphdr)
+ + sizeof(struct udphdr);
+ break;
+ default:
+ break;
+ }
+ cur_p->app1 = ((start << 16) | insert);
+ cur_p->app2 = csum_tcpudp_magic(ip->saddr,
+ ip->daddr,
+ length,
+ ip->protocol,
+ 0);
+ skb->data[insert] = 0;
+ skb->data[insert + 1] = 0;
+ }
+ cur_p->app0 |= STS_CTRL_APP0_SOP;
+ cur_p->len = skb_headlen(skb);
+ cur_p->phys = (unsigned char *)pci_map_single(NULL,
+ skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ cur_p->app4 = (unsigned long)skb;
+
+ for (ii = 0; ii < num_frag; ii++) {
+ lp->tx_bd_tail++;
+ if (lp->tx_bd_tail >= TX_BD_NUM)
+ lp->tx_bd_tail = 0;
+
+ cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
+ cur_p->phys = (unsigned char *)pci_map_single(NULL,
+ (void *)page_address(frag->page)
+ + frag->page_offset,
+ frag->size,
+ PCI_DMA_TODEVICE);
+ cur_p->len = frag->size;
+ cur_p->app0 = 0;
+ frag++;
+ }
+ cur_p->app0 |= STS_CTRL_APP0_EOP;
+
+ tail_p = &lp->tx_bd_p[lp->tx_bd_tail];
+ lp->tx_bd_tail++;
+ if (lp->tx_bd_tail >= TX_BD_NUM)
+ lp->tx_bd_tail = 0;
+
+ /* EngBusy ? */
+ if (!(sd_ior(ndev, TX_CHNL_STS) & CHNL_STS_ENGBUSY)) {
+ sd_iow(ndev, TX_CURDESC_PTR, (uintptr_t)start_p);
+ /* DMA start */
+ sd_iow(ndev, TX_TAILDESC_PTR, (uintptr_t)tail_p);
+ }
+
+ return 0;
+}
+
+
+static void
+ll_temac_recv(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ struct sk_buff *skb, *new_skb;
+ unsigned int bdstat;
+ struct cdmac_bd *cur_p, *tail_p;
+ int length;
+ unsigned long skb_vaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lp->rx_lock, flags);
+
+ tail_p = &lp->rx_bd_p[lp->rx_bd_ci];
+ cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
+
+ bdstat = cur_p->app0;
+ while ((bdstat & STS_CTRL_APP0_CMPLT)) {
+
+ skb = lp->rx_skb[lp->rx_bd_ci];
+ length = cur_p->app4;
+
+ skb_vaddr = virt_to_bus(skb->data);
+ pci_unmap_single(NULL, skb_vaddr, length, PCI_DMA_FROMDEVICE);
+
+ skb_put(skb, length);
+ skb->dev = ndev;
+ skb->protocol = eth_type_trans(skb, ndev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(skb);
+
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += length;
+
+ new_skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE + XTE_ALIGN,
+ GFP_ATOMIC);
+ if (new_skb == 0) {
+ dev_err(&ndev->dev, "no memory for new sk_buff\n");
+ spin_unlock_irqrestore(&lp->rx_lock, flags);
+ return;
+ }
+
+ skb_reserve(new_skb, BUFFER_ALIGN(new_skb->data));
+
+ cur_p->app0 = STS_CTRL_APP0_IRQONEND;
+ cur_p->phys = (unsigned char *)
+ pci_map_single(NULL, new_skb->data,
+ XTE_MAX_JUMBO_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ cur_p->len = XTE_MAX_JUMBO_FRAME_SIZE;
+ lp->rx_skb[lp->rx_bd_ci] = new_skb;
+
+ lp->rx_bd_ci++;
+ if (lp->rx_bd_ci >= RX_BD_NUM)
+ lp->rx_bd_ci = 0;
+
+ cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
+ bdstat = cur_p->app0;
+ }
+ sd_iow(ndev, RX_TAILDESC_PTR, (uintptr_t)tail_p);
+
+ spin_unlock_irqrestore(&lp->rx_lock, flags);
+}
+
+static irqreturn_t
+ll_temac_tx_interrupt(int irq, void *dev_id)
+{
+ unsigned int status;
+ struct net_device *ndev = (struct net_device *)dev_id;
+
+ status = sd_ior(ndev, TX_IRQ_REG);
+ sd_iow(ndev, TX_IRQ_REG, status);
+
+ if (status & (IRQ_COAL | IRQ_DLY))
+ temac_hard_start_xmit_done(ndev);
+ if (status & 0x080)
+ dev_err(&ndev->dev, "DMA error 0x%x\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+ll_temac_rx_interrupt(int irq, void *dev_id)
+{
+ unsigned int status;
+ struct net_device *ndev = (struct net_device *)dev_id;
+
+ status = sd_ior(ndev, RX_IRQ_REG);
+ sd_iow(ndev, RX_IRQ_REG, status);
+
+ if (status & (IRQ_COAL | IRQ_DLY))
+ ll_temac_recv(ndev);
+
+ return IRQ_HANDLED;
+}
+
+static struct net_device_stats *
+temac_get_stats(struct net_device *ndev)
+{
+ return netdev_priv(ndev);
+}
+
+static int
+temac_open(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ int erC = 0;
+
+ /* if (netif_device_present(ndev))
+ * raise link
+ */
+ erC = request_irq(lp->tx_irq, ll_temac_tx_interrupt,
+ 0, ndev->name, ndev);
+ erC = request_irq(lp->rx_irq, ll_temac_rx_interrupt,
+ 0, ndev->name, ndev);
+ temac_device_reset(ndev);
+ return 0;
+}
+
+static int
+temac_stop(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+ /* if (netif_device_present(ndev))
+ * drop link
+ */
+ free_irq(lp->tx_irq, ndev);
+ free_irq(lp->rx_irq, ndev);
+ /* free buffers */
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void
+temac_poll_controller(struct net_device *ndev)
+{
+ struct temac_local *lp = netdev_priv(ndev);
+
+ disable_irq(lp->tx_irq);
+ disable_irq(lp->rx_irq);
+
+ ll_temac_rx_interrupt(lp->tx_irq, ndev, 0);
+ ll_temac_tx_interrupt(lp->rx_irq, ndev, 0);
+
+ enable_irq(lp->tx_irq);
+ enable_irq(lp->rx_irq);
+}
+#endif
+
+static int __init
+temac_device_map(struct platform_device *pdev, struct net_device *ndev,
+ int num, struct temac_region *reg)
+{
+ struct resource *res;
+ int erC = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, num);
+ if (res == 0) {
+ erC = -ENODEV;
+ goto fail;
+ }
+ if (request_mem_region(res->start, res->end - res->start,
+ pdev->name) == 0) {
+ dev_err(&pdev->dev,
+ "%s: failed to request registers\n",
+ pdev->name);
+ erC = -ENXIO;
+ goto fail;
+ }
+
+ reg->base = (uintptr_t)res->start;
+ reg->len = res->end - res->start;
+ reg->addr = ioremap_nocache((uintptr_t)reg->base, reg->len);
+ if (reg->addr == 0) {
+ dev_err(&pdev->dev,
+ "%s: failed to remap registers\n", pdev->name);
+ erC = -ENXIO;
+ goto fail_remap;
+ }
+ return 0;
+fail_remap:
+ release_region((u32)reg->addr, reg->len);
+fail:
+ return erC;
+}
+/*
+Search TEMAC board, allocate space and register it
+ */
+static int __init
+temac_device_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev = alloc_etherdev(sizeof(struct temac_local));
+ struct xlltemac_platform_data *pdata =
+ (struct xlltemac_platform_data *) pdev->dev.platform_data;
+ struct temac_local *lp;
+ u8 addr[] = { 0x0, 0x50, 0xc2, 0x44, 0x2f, 0xff };
+ int erC = 0;
+
+ /* Init network device */
+ if (!ndev) {
+ dev_err(&pdev->dev, "could not allocate device.\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ /* setup board info structure */
+ /* Clear memory */
+ lp = netdev_priv(ndev);
+ memset(lp, 0, sizeof(struct temac_local));
+ erC = temac_device_map(pdev, ndev, 0, &lp->regs);
+ if (erC) {
+ dev_err(&pdev->dev, "could not allocate temac regs.\n");
+ goto nodev;
+ }
+ ndev->base_addr = (u32) lp->regs.addr;
+
+ erC = temac_device_map(pdev, ndev, 1, &lp->sdma);
+ if (erC) {
+ dev_err(&pdev->dev, "could not allocate sdma regs.\n");
+ goto nodev;
+ }
+ lp->rx_irq = platform_get_irq(pdev, 0);
+ if (lp->rx_irq < 0) {
+ dev_err(&pdev->dev, "could not allocate rx irq.\n");
+ erC = -ENOMEM;
+ goto nodev;
+ }
+ lp->tx_irq = platform_get_irq(pdev, 1);
+ if (lp->tx_irq < 0) {
+ dev_err(&pdev->dev, "could not allocate tx irq.\n");
+ erC = -ENOMEM;
+ goto nodev;
+ }
+
+ dev_info(&pdev->dev, DRV_NAME ".c:v " __DATE__ " "
+ DRV_AUTHOR " " DRV_EMAIL "\n");
+
+ lp->dev = ndev;
+ lp->emac_num = 0;
+ lp->options = XTE_OPTION_DEFAULTS;
+
+ temac_set_mac_address(ndev, addr);
+ /* from this point we assume that we have found a TEMAC */
+ /* driver system function */
+ ether_setup(ndev);
+ /* The TEMAC-specific entries in the device structure. */
+ ndev->open = &temac_open;
+ ndev->stop = &temac_stop;
+ ndev->get_stats = &temac_get_stats;
+ ndev->set_mac_address = &temac_set_mac_address;
+ ndev->hard_start_xmit = &temac_hard_start_xmit;
+ ndev->set_multicast_list = &temac_set_multicast_list;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ ndev->poll_controller = &temac_poll_controller;
+#endif
+ ndev->flags &= ~IFF_MULTICAST; /* clear multicast */
+
+ /* Scatter/gather IO. */
+ ndev->features = NETIF_F_SG ;
+ /* Scatter/gather IO. */
+ ndev->features |= NETIF_F_FRAGLIST;
+#if 0
+ /* Can checksum TCP/UDP over IPv4. */
+ ndev->features |= NETIF_F_IP_CSUM
+ /* Can checksum all the packets. */
+ ndev->features |= NETIF_F_HW_CSUM
+
+ /* Can checksum TCP/UDP over IPV6 */
+ ndev->features |= NETIF_F_IPV6_CSUM
+ /* Can DMA to high memory. */
+ ndev->features |= NETIF_F_HIGHDMA
+ /* Transmit VLAN hw acceleration */
+ ndev->features |= NETIF_F_HW_VLAN_TX
+ /* Receive VLAN hw acceleration */
+ ndev->features |= NETIF_F_HW_VLAN_RX
+ /* Receive filtering on VLAN */
+ ndev->features |= NETIF_F_HW_VLAN_FILTER
+ /* Device cannot handle VLAN packets */
+ ndev->features |= NETIF_F_VLAN_CHALLENGED
+ /* Enable software GSO. */
+ ndev->features |= NETIF_F_GSO
+ /* Has multiple TX/RX queues */
+ ndev->features |= NETIF_F_MULTI_QUEUE
+ /* large receive offload */
+ ndev->features |= NETIF_F_LRO
+#endif
+
+
+ erC = register_netdev(ndev);
+ if (!erC)
+ return 0;
+
+nodev:
+ free_netdev(ndev);
+ ndev = NULL;
+ return erC;
+}
+
+static int __devexit temac_device_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ unregister_netdev(dev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return 0;
+}
+static struct platform_driver temac_device_driver = {
+ .probe = temac_device_probe,
+ .remove = __devexit_p(temac_device_remove),
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+temac_init_module(void)
+{
+ return platform_driver_register(&temac_device_driver);
+}
+
+static void __exit
+temac_cleanup_module(void)
+{
+ platform_driver_unregister(&temac_device_driver);
+}
+
+module_init(temac_init_module);
+module_exit(temac_cleanup_module);
+
+MODULE_DESCRIPTION("Xilinx Tri-Mode Eth MAC driver");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+/* vim:set ts=4 sts=4 sw=4 noet list nowrap: */
diff --git a/include/linux/xilinx_devices.h b/include/linux/xilinx_devices.h
index 41ad421..40512d5 100755
--- a/include/linux/xilinx_devices.h
+++ b/include/linux/xilinx_devices.h
@@ -94,7 +94,7 @@ struct xtemac_platform_data {
#define XTEMAC_DMA_SGDMA 3 /* scatter gather DMA */
#endif
-#if defined(CONFIG_XILINX_LLTEMAC)
+#if defined(CONFIG_XILINX_LLTEMAC) || defined(CONFIG_XPS_LLTEMAC)
/* LLTEMAC platform data */
struct xlltemac_platform_data {
u8 tx_csum;
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
2008-08-19 9:34 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
@ 2008-08-22 16:10 ` Sergey Temerkhanov
2008-08-22 16:14 ` Sergey Temerkhanov
2008-09-14 0:13 ` Jeff Garzik
2 siblings, 0 replies; 8+ messages in thread
From: Sergey Temerkhanov @ 2008-08-22 16:10 UTC (permalink / raw)
To: linuxppc-embedded
On Tuesday 19 August 2008 13:34:04 David H. Lynch Jr. wrote:
> Pass II
> + cur_p->phys = (unsigned char *)pci_map_single(NULL,
> + skb->data, skb->len,
> + PCI_DMA_TODEVICE);
> + cur_p->app4 = (unsigned long)skb;
> +
> + for (ii = 0; ii < num_frag; ii++) {
> + lp->tx_bd_tail++;
> + if (lp->tx_bd_tail >= TX_BD_NUM)
> + lp->tx_bd_tail = 0;
> +
> + cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
> + cur_p->phys = (unsigned char *)pci_map_single(NULL,
> + (void *)page_address(frag->page)
> + + frag->page_offset,
> + frag->size,
> + PCI_DMA_TODEVICE);
1. Why is pci_* API is used instead of dma_*?
2. Why pci_map_single() in skb_shinfo(skb)->frags processing instead of
dma_map_page()?
> + if (ii == (TX_BD_NUM - 1))
> + lp->tx_bd_v[ii].next = &lp->tx_bd_p[0];
> + else
> + lp->tx_bd_v[ii].next = &lp->tx_bd_p[ii + 1];
3. It would be much simpler to use masking by (TX_BD_NUM - 1) instead of "if
(ii == (TX_BD_NUM - 1))" constructs.
4. There is a need of global locking mechanism for indirect registers access,
as according to Xilinx documentation only one hard core at a a time can be
accessed.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
2008-08-19 9:34 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
2008-08-22 16:10 ` Sergey Temerkhanov
@ 2008-08-22 16:14 ` Sergey Temerkhanov
2008-09-14 0:13 ` Jeff Garzik
2 siblings, 0 replies; 8+ messages in thread
From: Sergey Temerkhanov @ 2008-08-22 16:14 UTC (permalink / raw)
To: linuxppc-embedded
T24gVHVlc2RheSAxOSBBdWd1c3QgMjAwOCAxMzozNDowNCBEYXZpZCBILiBMeW5jaCBKci4gd3Jv
dGU6Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHN3aXRjaCAoaXAtPnByb3RvY29s
KSB7Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGNhc2UgSVBQUk9UT19UQ1A6Cj4g
K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBzdGFydCA9IHNp
emVvZihzdHJ1Y3QgaXBoZHIpICsgRVRIX0hMRU47Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBpbnNlcnQgPSBzaXplb2Yoc3RydWN0IGlwaGRyKSArIEVU
SF9ITEVOICsgMTY7Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqBsZW5ndGggPSBpcC0+dG90X2xlbiAtIHNpemVvZihzdHJ1Y3QgaXBoZHIpOwo+ICvCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaGVhZGxlbiA9IEVUSF9I
TEVOCj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgKyBzaXplb2Yoc3RydWN0IGlwaGRyKQo+ICvCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCsgc2l6ZW9mKHN0cnVj
dCB0Y3BoZHIpOwo+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgYnJlYWs7Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGNhc2UgSVBQUk9UT19V
RFA6Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBzdGFy
dCA9IHNpemVvZihzdHJ1Y3QgaXBoZHIpICsgRVRIX0hMRU47Cj4gK8KgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBpbnNlcnQgPSBzaXplb2Yoc3RydWN0IGlwaGRy
KSArIEVUSF9ITEVOICsgNjsKPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoGxlbmd0aCA9IGlwLT50b3RfbGVuIC0gc2l6ZW9mKHN0cnVjdCBpcGhkcik7Cj4g
K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBoZWFkbGVuID0g
RVRIX0hMRU4KPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqArIHNpemVvZihzdHJ1Y3QgaXBoZHIpCj4gK8KgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgKyBzaXplb2Yo
c3RydWN0IHVkcGhkcik7Cj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqBicmVhazsKPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgZGVmYXVsdDoK
PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGJyZWFrOwo+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqB9CgpXaHkgYWxsIHRoZXNlIGNhbGN1bGF0
aW9ucyBpbnN0ZWFkIG9mIHNrYl90cmFuc3BvcnRfb2Zmc2V0KHNrYikgYW5kIApza2ItPmNzdW1f
b2Zmc2V0IHVzYWdlPwoK
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC
2008-08-19 9:34 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
2008-08-22 16:10 ` Sergey Temerkhanov
2008-08-22 16:14 ` Sergey Temerkhanov
@ 2008-09-14 0:13 ` Jeff Garzik
2 siblings, 0 replies; 8+ messages in thread
From: Jeff Garzik @ 2008-09-14 0:13 UTC (permalink / raw)
To: dhlii; +Cc: netdev, linuxppc-embedded
Patch looks good -- ACK -- but I cannot apply since I do not have
include/linux/xilinx_devices.h
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2008-09-14 0:13 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-17 4:59 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
2008-08-18 12:30 ` Ben Hutchings
2008-08-18 14:36 ` Ben Hutchings
2008-08-18 17:01 ` [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 EthernetNIC Stephen Neuendorffer
-- strict thread matches above, loose matches on Subject: below --
2008-08-19 9:34 [PATCH] Linux Device Driver for Xilinx LL TEMAC 10/100/1000 Ethernet NIC David H. Lynch Jr.
2008-08-22 16:10 ` Sergey Temerkhanov
2008-08-22 16:14 ` Sergey Temerkhanov
2008-09-14 0:13 ` Jeff Garzik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).