From: Jeff Kirsher Date: Wed, 15 Jun 2011 18:23:00 +0000 (-0700) Subject: xilinx/ll_temac: Move the Xilinx drivers X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=b13ad8f498793dc582b7f42f27b8f44490bd608d;p=deliverable%2Flinux.git xilinx/ll_temac: Move the Xilinx drivers Move the Xilinx drivers into drivers/net/ethernet/xilinx/ and make the necessary Kconfig and Makefile changes. CC: John Williams CC: "David H. Lynch Jr." Signed-off-by: Jeff Kirsher --- diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 996bae006fc3..2607a44a270f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -493,13 +493,6 @@ config NET_POCKET the questions about this class of network devices. If you say Y, you will be asked for your specific device in the following questions. -config XILINX_EMACLITE - tristate "Xilinx 10/100 Ethernet Lite support" - depends on PPC32 || MICROBLAZE - select PHYLIB - help - This driver supports the 10/100 Ethernet Lite from Xilinx. - config LANTIQ_ETOP tristate "Lantiq SoC ETOP driver" depends on SOC_TYPE_XWAY @@ -539,14 +532,6 @@ config IP1000 To compile this driver as a module, choose M here: the module will be called ipg. This is recommended. -config XILINX_LL_TEMAC - tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" - depends on PPC || MICROBLAZE - select PHYLIB - help - This driver supports the Xilinx 10/100/1000 LocalLink TEMAC - core used in Xilinx Spartan and Virtex FPGAs - endif # NETDEV_1000 # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 271fc52138de..4c7af0286ccf 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -33,10 +33,6 @@ obj-$(CONFIG_NET_SB1000) += sb1000.o obj-$(CONFIG_HP100) += hp100.o obj-$(CONFIG_FORCEDETH) += forcedeth.o -ll_temac-objs := ll_temac_main.o ll_temac_mdio.o -obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o -obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o - obj-$(CONFIG_PPP) += ppp_generic.o obj-$(CONFIG_PPP_ASYNC) += ppp_async.o obj-$(CONFIG_PPP_SYNC_TTY) += ppp_synctty.o diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index c29c1457aa9a..922f4d1243a6 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -82,5 +82,6 @@ source "drivers/net/ethernet/ti/Kconfig" source "drivers/net/ethernet/toshiba/Kconfig" source "drivers/net/ethernet/tundra/Kconfig" source "drivers/net/ethernet/via/Kconfig" +source "drivers/net/ethernet/xilinx/Kconfig" endif # ETHERNET diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 8495c507725c..fcecd5f474b4 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -50,3 +50,4 @@ obj-$(CONFIG_NET_VENDOR_TI) += ti/ obj-$(CONFIG_NET_VENDOR_TOSHIBA) += toshiba/ obj-$(CONFIG_NET_VENDOR_TUNDRA) += tundra/ obj-$(CONFIG_NET_VENDOR_VIA) += via/ +obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig new file mode 100644 index 000000000000..4e3aad401cd8 --- /dev/null +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -0,0 +1,35 @@ +# +# Xilink device configuration +# + +config NET_VENDOR_XILINX + bool "Xilinx devices" + depends on PPC || PPC32 || MICROBLAZE + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Xilinx devices. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_XILINX + +config XILINX_EMACLITE + tristate "Xilinx 10/100 Ethernet Lite support" + depends on (PPC32 || MICROBLAZE) + select PHYLIB + ---help--- + This driver supports the 10/100 Ethernet Lite from Xilinx. + +config XILINX_LL_TEMAC + tristate "Xilinx LL TEMAC (LocalLink Tri-mode Ethernet MAC) driver" + depends on (PPC || MICROBLAZE) + select PHYLIB + ---help--- + This driver supports the Xilinx 10/100/1000 LocalLink TEMAC + core used in Xilinx Spartan and Virtex FPGAs + +endif # NET_VENDOR_XILINX diff --git a/drivers/net/ethernet/xilinx/Makefile b/drivers/net/ethernet/xilinx/Makefile new file mode 100644 index 000000000000..5feac734ea45 --- /dev/null +++ b/drivers/net/ethernet/xilinx/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Xilink network device drivers. +# + +ll_temac-objs := ll_temac_main.o ll_temac_mdio.o +obj-$(CONFIG_XILINX_LL_TEMAC) += ll_temac.o +obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h new file mode 100644 index 000000000000..522abe2ff25a --- /dev/null +++ b/drivers/net/ethernet/xilinx/ll_temac.h @@ -0,0 +1,385 @@ + +#ifndef XILINX_LL_TEMAC_H +#define XILINX_LL_TEMAC_H + +#include +#include +#include + +#ifdef CONFIG_PPC_DCR +#include +#include +#endif + +/* packet size info */ +#define XTE_HDR_SIZE 14 /* size of Ethernet header */ +#define XTE_TRL_SIZE 4 /* size of Ethernet trailer (FCS) */ +#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 0x01 /* r */ +#define TX_CURBUF_LENGTH 0x02 /* r */ +#define TX_CURDESC_PTR 0x03 /* rw */ +#define TX_TAILDESC_PTR 0x04 /* rw */ +#define TX_CHNL_CTRL 0x05 /* 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 0x06 /* 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 0x07 /* 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 0x08 /* r */ +#define RX_CURBUF_ADDR 0x09 /* r */ +#define RX_CURBUF_LENGTH 0x0a /* r */ +#define RX_CURDESC_PTR 0x0b /* rw */ +#define RX_TAILDESC_PTR 0x0c /* rw */ +#define RX_CHNL_CTRL 0x0d /* 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 0x0e /* 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 0x0f /* 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 0x10 /* rw */ +#define DMA_CONTROL_RST (1 << 0) +#define DMA_TAIL_ENABLE (1 << 2) + +/* 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 +#define XTE_RDY0_HARD_ACS_RDY_MASK (0x10000) + +/* 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 config */ +#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 /* 10 Mbit LINKSPD_MASK */ +#define XTE_EMCFG_LINKSPD_100 (1 << 30) /* 100 Mbit LINKSPD_MASK */ +#define XTE_EMCFG_LINKSPD_1000 (1 << 31) /* 1000 Mbit LINKSPD_MASK */ + +#define XTE_GMIC_OFFSET 0x00000320 /* RGMII/SGMII config */ +#define XTE_MC_OFFSET 0x00000340 /* MDIO configuration */ +#define XTE_UAW0_OFFSET 0x00000380 /* Unicast address word 0 */ +#define XTE_UAW1_OFFSET 0x00000384 /* Unicast address word 1 */ + +#define XTE_MAW0_OFFSET 0x00000388 /* Multicast addr word 0 */ +#define XTE_MAW1_OFFSET 0x0000038C /* Multicast addr word 1 */ +#define XTE_AFM_OFFSET 0x00000390 /* Promiscuous mode */ +#define XTE_AFM_EPPRM_MASK (1 << 31) /* Promiscuous mode enable */ + +/* 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) + +#define XTE_TIE_OFFSET 0x000003A4 /* Interrupt 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) +/* 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 MULTICAST_CAM_TABLE_NUM 4 + +/* TEMAC Synthesis features */ +#define TEMAC_FEATURE_RX_CSUM (1 << 0) +#define TEMAC_FEATURE_TX_CSUM (1 << 1) + +/* TX/RX CURDESC_PTR points to first descriptor */ +/* TX/RX TAILDESC_PTR points to last descriptor in linked list */ + +/** + * struct cdmac_bd - LocalLink buffer descriptor format + * + * 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 cdmac_bd { + u32 next; /* Physical address of next buffer descriptor */ + u32 phys; + u32 len; + u32 app0; + u32 app1; /* TX start << 16 | insert */ + u32 app2; /* TX csum */ + u32 app3; + u32 app4; /* skb for TX length for RX */ +}; + +struct temac_local { + struct net_device *ndev; + struct device *dev; + + /* Connection to PHY device */ + struct phy_device *phy_dev; /* Pointer to PHY device */ + struct device_node *phy_node; + + /* MDIO bus data */ + struct mii_bus *mii_bus; /* MII bus reference */ + int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */ + + /* IO registers, dma functions and IRQs */ + void __iomem *regs; + void __iomem *sdma_regs; +#ifdef CONFIG_PPC_DCR + dcr_host_t sdma_dcrs; +#endif + u32 (*dma_in)(struct temac_local *, int); + void (*dma_out)(struct temac_local *, int, u32); + + int tx_irq; + int rx_irq; + int emac_num; + + struct sk_buff **rx_skb; + spinlock_t rx_lock; + struct mutex indirect_mutex; + u32 options; /* Current options word */ + int last_link; + unsigned int temac_features; + + /* Buffer descriptors */ + struct cdmac_bd *tx_bd_v; + dma_addr_t tx_bd_p; + struct cdmac_bd *rx_bd_v; + dma_addr_t rx_bd_p; + int tx_bd_ci; + int tx_bd_next; + int tx_bd_tail; + int rx_bd_ci; +}; + +/* xilinx_temac.c */ +u32 temac_ior(struct temac_local *lp, int offset); +void temac_iow(struct temac_local *lp, int offset, u32 value); +int temac_indirect_busywait(struct temac_local *lp); +u32 temac_indirect_in32(struct temac_local *lp, int reg); +void temac_indirect_out32(struct temac_local *lp, int reg, u32 value); + + +/* xilinx_temac_mdio.c */ +int temac_mdio_setup(struct temac_local *lp, struct device_node *np); +void temac_mdio_teardown(struct temac_local *lp); + +#endif /* XILINX_LL_TEMAC_H */ diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c new file mode 100644 index 000000000000..728fe414147a --- /dev/null +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -0,0 +1,1154 @@ +/* + * Driver for Xilinx TEMAC Ethernet device + * + * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi + * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. + * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. + * + * This is a driver for the Xilinx ll_temac ipcore which is often used + * in the Virtex and Spartan series of chips. + * + * Notes: + * - The ll_temac hardware uses indirect access for many of the TEMAC + * registers, include the MDIO bus. However, indirect access to MDIO + * registers take considerably more clock cycles than to TEMAC registers. + * MDIO accesses are long, so threads doing them should probably sleep + * rather than busywait. However, since only one indirect access can be + * in progress at any given time, that means that *all* indirect accesses + * could end up sleeping (to wait for an MDIO access to complete). + * Fortunately none of the indirect accesses are on the 'hot' path for tx + * or rx, so this should be okay. + * + * TODO: + * - Factor out locallink DMA code into separate driver + * - Fix multicast assignment. + * - Fix support for hardware checksumming. + * - Testing. Lots and lots of testing. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* needed for sizeof(tcphdr) */ +#include /* needed for sizeof(udphdr) */ +#include +#include +#include +#include +#include +#include +#include + +#include "ll_temac.h" + +#define TX_BD_NUM 64 +#define RX_BD_NUM 128 + +/* --------------------------------------------------------------------- + * Low level register access functions + */ + +u32 temac_ior(struct temac_local *lp, int offset) +{ + return in_be32((u32 *)(lp->regs + offset)); +} + +void temac_iow(struct temac_local *lp, int offset, u32 value) +{ + out_be32((u32 *) (lp->regs + offset), value); +} + +int temac_indirect_busywait(struct temac_local *lp) +{ + long end = jiffies + 2; + + while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) { + if (end - jiffies <= 0) { + WARN_ON(1); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +/** + * temac_indirect_in32 + * + * lp->indirect_mutex must be held when calling this function + */ +u32 temac_indirect_in32(struct temac_local *lp, int reg) +{ + u32 val; + + if (temac_indirect_busywait(lp)) + return -ETIMEDOUT; + temac_iow(lp, XTE_CTL0_OFFSET, reg); + if (temac_indirect_busywait(lp)) + return -ETIMEDOUT; + val = temac_ior(lp, XTE_LSW0_OFFSET); + + return val; +} + +/** + * temac_indirect_out32 + * + * lp->indirect_mutex must be held when calling this function + */ +void temac_indirect_out32(struct temac_local *lp, int reg, u32 value) +{ + if (temac_indirect_busywait(lp)) + return; + temac_iow(lp, XTE_LSW0_OFFSET, value); + temac_iow(lp, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK | reg); +} + +/** + * temac_dma_in32 - Memory mapped DMA read, this function expects a + * register input that is based on DCR word addresses which + * are then converted to memory mapped byte addresses + */ +static u32 temac_dma_in32(struct temac_local *lp, int reg) +{ + return in_be32((u32 *)(lp->sdma_regs + (reg << 2))); +} + +/** + * temac_dma_out32 - Memory mapped DMA read, this function expects a + * register input that is based on DCR word addresses which + * are then converted to memory mapped byte addresses + */ +static void temac_dma_out32(struct temac_local *lp, int reg, u32 value) +{ + out_be32((u32 *)(lp->sdma_regs + (reg << 2)), value); +} + +/* DMA register access functions can be DCR based or memory mapped. + * The PowerPC 440 is DCR based, the PowerPC 405 and MicroBlaze are both + * memory mapped. + */ +#ifdef CONFIG_PPC_DCR + +/** + * temac_dma_dcr_in32 - DCR based DMA read + */ +static u32 temac_dma_dcr_in(struct temac_local *lp, int reg) +{ + return dcr_read(lp->sdma_dcrs, reg); +} + +/** + * temac_dma_dcr_out32 - DCR based DMA write + */ +static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value) +{ + dcr_write(lp->sdma_dcrs, reg, value); +} + +/** + * temac_dcr_setup - If the DMA is DCR based, then setup the address and + * I/O functions + */ +static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, + struct device_node *np) +{ + unsigned int dcrs; + + /* setup the dcr address mapping if it's in the device tree */ + + dcrs = dcr_resource_start(np, 0); + if (dcrs != 0) { + lp->sdma_dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); + lp->dma_in = temac_dma_dcr_in; + lp->dma_out = temac_dma_dcr_out; + dev_dbg(&op->dev, "DCR base: %x\n", dcrs); + return 0; + } + /* no DCR in the device tree, indicate a failure */ + return -1; +} + +#else + +/* + * temac_dcr_setup - This is a stub for when DCR is not supported, + * such as with MicroBlaze + */ +static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, + struct device_node *np) +{ + return -1; +} + +#endif + +/** + * * temac_dma_bd_release - Release buffer descriptor rings + */ +static void temac_dma_bd_release(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + int i; + + for (i = 0; i < RX_BD_NUM; i++) { + if (!lp->rx_skb[i]) + break; + else { + dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys, + XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE); + dev_kfree_skb(lp->rx_skb[i]); + } + } + if (lp->rx_bd_v) + dma_free_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + lp->rx_bd_v, lp->rx_bd_p); + if (lp->tx_bd_v) + dma_free_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + lp->tx_bd_v, lp->tx_bd_p); + if (lp->rx_skb) + kfree(lp->rx_skb); +} + +/** + * temac_dma_bd_init - Setup buffer descriptor rings + */ +static int temac_dma_bd_init(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + struct sk_buff *skb; + int i; + + lp->rx_skb = kzalloc(sizeof(*lp->rx_skb) * RX_BD_NUM, GFP_KERNEL); + if (!lp->rx_skb) { + dev_err(&ndev->dev, + "can't allocate memory for DMA RX buffer\n"); + goto out; + } + /* allocate the tx and rx ring buffer descriptors. */ + /* returns a virtual address and a physical address. */ + lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->tx_bd_v) * TX_BD_NUM, + &lp->tx_bd_p, GFP_KERNEL); + if (!lp->tx_bd_v) { + dev_err(&ndev->dev, + "unable to allocate DMA TX buffer descriptors"); + goto out; + } + lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, + sizeof(*lp->rx_bd_v) * RX_BD_NUM, + &lp->rx_bd_p, GFP_KERNEL); + if (!lp->rx_bd_v) { + dev_err(&ndev->dev, + "unable to allocate DMA RX buffer descriptors"); + goto out; + } + + memset(lp->tx_bd_v, 0, sizeof(*lp->tx_bd_v) * TX_BD_NUM); + for (i = 0; i < TX_BD_NUM; i++) { + lp->tx_bd_v[i].next = lp->tx_bd_p + + sizeof(*lp->tx_bd_v) * ((i + 1) % TX_BD_NUM); + } + + memset(lp->rx_bd_v, 0, sizeof(*lp->rx_bd_v) * RX_BD_NUM); + for (i = 0; i < RX_BD_NUM; i++) { + lp->rx_bd_v[i].next = lp->rx_bd_p + + sizeof(*lp->rx_bd_v) * ((i + 1) % RX_BD_NUM); + + skb = netdev_alloc_skb_ip_align(ndev, + XTE_MAX_JUMBO_FRAME_SIZE); + + if (skb == 0) { + dev_err(&ndev->dev, "alloc_skb error %d\n", i); + goto out; + } + lp->rx_skb[i] = skb; + /* returns physical address of skb->data */ + lp->rx_bd_v[i].phys = dma_map_single(ndev->dev.parent, + skb->data, + XTE_MAX_JUMBO_FRAME_SIZE, + DMA_FROM_DEVICE); + lp->rx_bd_v[i].len = XTE_MAX_JUMBO_FRAME_SIZE; + lp->rx_bd_v[i].app0 = STS_CTRL_APP0_IRQONEND; + } + + lp->dma_out(lp, TX_CHNL_CTRL, 0x10220400 | + CHNL_CTRL_IRQ_EN | + CHNL_CTRL_IRQ_DLY_EN | + CHNL_CTRL_IRQ_COAL_EN); + /* 0x10220483 */ + /* 0x00100483 */ + lp->dma_out(lp, RX_CHNL_CTRL, 0xff070000 | + CHNL_CTRL_IRQ_EN | + CHNL_CTRL_IRQ_DLY_EN | + CHNL_CTRL_IRQ_COAL_EN | + CHNL_CTRL_IRQ_IOE); + /* 0xff010283 */ + + lp->dma_out(lp, RX_CURDESC_PTR, lp->rx_bd_p); + lp->dma_out(lp, RX_TAILDESC_PTR, + lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); + lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); + + return 0; + +out: + temac_dma_bd_release(ndev); + return -ENOMEM; +} + +/* --------------------------------------------------------------------- + * net_device_ops + */ + +static int temac_set_mac_address(struct net_device *ndev, void *address) +{ + struct temac_local *lp = netdev_priv(ndev); + + 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 */ + mutex_lock(&lp->indirect_mutex); + temac_indirect_out32(lp, 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 */ + temac_indirect_out32(lp, XTE_UAW1_OFFSET, + (ndev->dev_addr[4] & 0x000000ff) | + (ndev->dev_addr[5] << 8)); + mutex_unlock(&lp->indirect_mutex); + + return 0; +} + +static int netdev_set_mac_address(struct net_device *ndev, void *p) +{ + struct sockaddr *addr = p; + + return temac_set_mac_address(ndev, addr->sa_data); +} + +static void temac_set_multicast_list(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + u32 multi_addr_msw, multi_addr_lsw, val; + int i; + + mutex_lock(&lp->indirect_mutex); + if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) || + netdev_mc_count(ndev) > 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; + temac_indirect_out32(lp, XTE_AFM_OFFSET, XTE_AFM_EPPRM_MASK); + dev_info(&ndev->dev, "Promiscuous mode enabled.\n"); + } else if (!netdev_mc_empty(ndev)) { + struct netdev_hw_addr *ha; + + i = 0; + netdev_for_each_mc_addr(ha, ndev) { + if (i >= MULTICAST_CAM_TABLE_NUM) + break; + multi_addr_msw = ((ha->addr[3] << 24) | + (ha->addr[2] << 16) | + (ha->addr[1] << 8) | + (ha->addr[0])); + temac_indirect_out32(lp, XTE_MAW0_OFFSET, + multi_addr_msw); + multi_addr_lsw = ((ha->addr[5] << 8) | + (ha->addr[4]) | (i << 16)); + temac_indirect_out32(lp, XTE_MAW1_OFFSET, + multi_addr_lsw); + i++; + } + } else { + val = temac_indirect_in32(lp, XTE_AFM_OFFSET); + temac_indirect_out32(lp, XTE_AFM_OFFSET, + val & ~XTE_AFM_EPPRM_MASK); + temac_indirect_out32(lp, XTE_MAW0_OFFSET, 0); + temac_indirect_out32(lp, XTE_MAW1_OFFSET, 0); + dev_info(&ndev->dev, "Promiscuous mode disabled.\n"); + } + mutex_unlock(&lp->indirect_mutex); +} + +struct temac_option { + int flg; + u32 opt; + u32 reg; + u32 m_or; + u32 m_and; +} temac_options[] = { + /* Turn on jumbo packet support for both Rx and Tx */ + { + .opt = XTE_OPTION_JUMBO, + .reg = XTE_TXC_OFFSET, + .m_or = XTE_TXC_TXJMBO_MASK, + }, + { + .opt = XTE_OPTION_JUMBO, + .reg = XTE_RXC1_OFFSET, + .m_or =XTE_RXC1_RXJMBO_MASK, + }, + /* Turn on VLAN packet support for both Rx and Tx */ + { + .opt = XTE_OPTION_VLAN, + .reg = XTE_TXC_OFFSET, + .m_or =XTE_TXC_TXVLAN_MASK, + }, + { + .opt = XTE_OPTION_VLAN, + .reg = XTE_RXC1_OFFSET, + .m_or =XTE_RXC1_RXVLAN_MASK, + }, + /* Turn on FCS stripping on receive packets */ + { + .opt = XTE_OPTION_FCS_STRIP, + .reg = XTE_RXC1_OFFSET, + .m_or =XTE_RXC1_RXFCS_MASK, + }, + /* Turn on FCS insertion on transmit packets */ + { + .opt = XTE_OPTION_FCS_INSERT, + .reg = XTE_TXC_OFFSET, + .m_or =XTE_TXC_TXFCS_MASK, + }, + /* Turn on length/type field checking on receive packets */ + { + .opt = XTE_OPTION_LENTYPE_ERR, + .reg = XTE_RXC1_OFFSET, + .m_or =XTE_RXC1_RXLT_MASK, + }, + /* Turn on flow control */ + { + .opt = XTE_OPTION_FLOW_CONTROL, + .reg = XTE_FCC_OFFSET, + .m_or =XTE_FCC_RXFLO_MASK, + }, + /* Turn on flow control */ + { + .opt = XTE_OPTION_FLOW_CONTROL, + .reg = XTE_FCC_OFFSET, + .m_or =XTE_FCC_TXFLO_MASK, + }, + /* Turn on promiscuous frame filtering (all frames are received ) */ + { + .opt = XTE_OPTION_PROMISC, + .reg = XTE_AFM_OFFSET, + .m_or =XTE_AFM_EPPRM_MASK, + }, + /* Enable transmitter if not already enabled */ + { + .opt = XTE_OPTION_TXEN, + .reg = XTE_TXC_OFFSET, + .m_or =XTE_TXC_TXEN_MASK, + }, + /* Enable receiver? */ + { + .opt = XTE_OPTION_RXEN, + .reg = XTE_RXC1_OFFSET, + .m_or =XTE_RXC1_RXEN_MASK, + }, + {} +}; + +/** + * temac_setoptions + */ +static u32 temac_setoptions(struct net_device *ndev, u32 options) +{ + struct temac_local *lp = netdev_priv(ndev); + struct temac_option *tp = &temac_options[0]; + int reg; + + mutex_lock(&lp->indirect_mutex); + while (tp->opt) { + reg = temac_indirect_in32(lp, tp->reg) & ~tp->m_or; + if (options & tp->opt) + reg |= tp->m_or; + temac_indirect_out32(lp, tp->reg, reg); + tp++; + } + lp->options |= options; + mutex_unlock(&lp->indirect_mutex); + + return 0; +} + +/* Initialize temac */ +static void temac_device_reset(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + u32 timeout; + u32 val; + + /* Perform a software reset */ + + /* 0x300 host enable bit ? */ + /* reset PHY through control register ?:1 */ + + dev_dbg(&ndev->dev, "%s()\n", __func__); + + mutex_lock(&lp->indirect_mutex); + /* Reset the receiver and wait for it to finish reset */ + temac_indirect_out32(lp, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK); + timeout = 1000; + while (temac_indirect_in32(lp, XTE_RXC1_OFFSET) & XTE_RXC1_RXRST_MASK) { + udelay(1); + if (--timeout == 0) { + dev_err(&ndev->dev, + "temac_device_reset RX reset timeout!!\n"); + break; + } + } + + /* Reset the transmitter and wait for it to finish reset */ + temac_indirect_out32(lp, XTE_TXC_OFFSET, XTE_TXC_TXRST_MASK); + timeout = 1000; + while (temac_indirect_in32(lp, 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 */ + val = temac_indirect_in32(lp, XTE_RXC1_OFFSET); + temac_indirect_out32(lp, XTE_RXC1_OFFSET, val & ~XTE_RXC1_RXEN_MASK); + + /* Reset Local Link (DMA) */ + lp->dma_out(lp, DMA_CONTROL_REG, DMA_CONTROL_RST); + timeout = 1000; + while (lp->dma_in(lp, DMA_CONTROL_REG) & DMA_CONTROL_RST) { + udelay(1); + if (--timeout == 0) { + dev_err(&ndev->dev, + "temac_device_reset DMA reset timeout!!\n"); + break; + } + } + lp->dma_out(lp, DMA_CONTROL_REG, DMA_TAIL_ENABLE); + + if (temac_dma_bd_init(ndev)) { + dev_err(&ndev->dev, + "temac_device_reset descriptor allocation failed\n"); + } + + temac_indirect_out32(lp, XTE_RXC0_OFFSET, 0); + temac_indirect_out32(lp, XTE_RXC1_OFFSET, 0); + temac_indirect_out32(lp, XTE_TXC_OFFSET, 0); + temac_indirect_out32(lp, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK); + + mutex_unlock(&lp->indirect_mutex); + + /* Sync default options with HW + * but leave receiver and transmitter disabled. */ + temac_setoptions(ndev, + lp->options & ~(XTE_OPTION_TXEN | XTE_OPTION_RXEN)); + + temac_set_mac_address(ndev, NULL); + + /* 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 = jiffies; /* prevent tx timeout */ +} + +void temac_adjust_link(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + struct phy_device *phy = lp->phy_dev; + u32 mii_speed; + int link_state; + + /* hash together the state values to decide if something has changed */ + link_state = phy->speed | (phy->duplex << 1) | phy->link; + + mutex_lock(&lp->indirect_mutex); + if (lp->last_link != link_state) { + mii_speed = temac_indirect_in32(lp, XTE_EMCFG_OFFSET); + mii_speed &= ~XTE_EMCFG_LINKSPD_MASK; + + switch (phy->speed) { + case SPEED_1000: mii_speed |= XTE_EMCFG_LINKSPD_1000; break; + case SPEED_100: mii_speed |= XTE_EMCFG_LINKSPD_100; break; + case SPEED_10: mii_speed |= XTE_EMCFG_LINKSPD_10; break; + } + + /* Write new speed setting out to TEMAC */ + temac_indirect_out32(lp, XTE_EMCFG_OFFSET, mii_speed); + lp->last_link = link_state; + phy_print_status(phy); + } + mutex_unlock(&lp->indirect_mutex); +} + +static void temac_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) { + dma_unmap_single(ndev->dev.parent, cur_p->phys, cur_p->len, + DMA_TO_DEVICE); + if (cur_p->app4) + dev_kfree_skb_irq((struct sk_buff *)cur_p->app4); + cur_p->app0 = 0; + cur_p->app1 = 0; + cur_p->app2 = 0; + cur_p->app3 = 0; + cur_p->app4 = 0; + + ndev->stats.tx_packets++; + ndev->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 inline int temac_check_tx_bd_space(struct temac_local *lp, int num_frag) +{ + struct cdmac_bd *cur_p; + int tail; + + tail = lp->tx_bd_tail; + cur_p = &lp->tx_bd_v[tail]; + + do { + if (cur_p->app0) + return NETDEV_TX_BUSY; + + tail++; + if (tail >= TX_BD_NUM) + tail = 0; + + cur_p = &lp->tx_bd_v[tail]; + num_frag--; + } while (num_frag >= 0); + + return 0; +} + +static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + struct cdmac_bd *cur_p; + dma_addr_t 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 + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; + cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; + + if (temac_check_tx_bd_space(lp, num_frag)) { + 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) { + unsigned int csum_start_off = skb_checksum_start_offset(skb); + unsigned int csum_index_off = csum_start_off + skb->csum_offset; + + cur_p->app0 |= 1; /* TX Checksum Enabled */ + cur_p->app1 = (csum_start_off << 16) | csum_index_off; + cur_p->app2 = 0; /* initial checksum seed */ + } + + cur_p->app0 |= STS_CTRL_APP0_SOP; + cur_p->len = skb_headlen(skb); + cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len, + DMA_TO_DEVICE); + 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 = dma_map_single(ndev->dev.parent, + (void *)page_address(frag->page) + + frag->page_offset, + frag->size, DMA_TO_DEVICE); + cur_p->len = frag->size; + cur_p->app0 = 0; + frag++; + } + cur_p->app0 |= STS_CTRL_APP0_EOP; + + tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; + lp->tx_bd_tail++; + if (lp->tx_bd_tail >= TX_BD_NUM) + lp->tx_bd_tail = 0; + + skb_tx_timestamp(skb); + + /* Kick off the transfer */ + lp->dma_out(lp, TX_TAILDESC_PTR, tail_p); /* DMA start */ + + return NETDEV_TX_OK; +} + + +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; + dma_addr_t tail_p; + int length; + unsigned long flags; + + spin_lock_irqsave(&lp->rx_lock, flags); + + tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * 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 & 0x3FFF; + + dma_unmap_single(ndev->dev.parent, cur_p->phys, length, + DMA_FROM_DEVICE); + + skb_put(skb, length); + skb->dev = ndev; + skb->protocol = eth_type_trans(skb, ndev); + skb_checksum_none_assert(skb); + + /* if we're doing rx csum offload, set it up */ + if (((lp->temac_features & TEMAC_FEATURE_RX_CSUM) != 0) && + (skb->protocol == __constant_htons(ETH_P_IP)) && + (skb->len > 64)) { + + skb->csum = cur_p->app3 & 0xFFFF; + skb->ip_summed = CHECKSUM_COMPLETE; + } + + if (!skb_defer_rx_timestamp(skb)) + netif_rx(skb); + + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += length; + + new_skb = netdev_alloc_skb_ip_align(ndev, + XTE_MAX_JUMBO_FRAME_SIZE); + + if (new_skb == 0) { + dev_err(&ndev->dev, "no memory for new sk_buff\n"); + spin_unlock_irqrestore(&lp->rx_lock, flags); + return; + } + + cur_p->app0 = STS_CTRL_APP0_IRQONEND; + cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data, + XTE_MAX_JUMBO_FRAME_SIZE, + DMA_FROM_DEVICE); + 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; + } + lp->dma_out(lp, RX_TAILDESC_PTR, tail_p); + + spin_unlock_irqrestore(&lp->rx_lock, flags); +} + +static irqreturn_t ll_temac_tx_irq(int irq, void *_ndev) +{ + struct net_device *ndev = _ndev; + struct temac_local *lp = netdev_priv(ndev); + unsigned int status; + + status = lp->dma_in(lp, TX_IRQ_REG); + lp->dma_out(lp, TX_IRQ_REG, status); + + if (status & (IRQ_COAL | IRQ_DLY)) + temac_start_xmit_done(lp->ndev); + if (status & 0x080) + dev_err(&ndev->dev, "DMA error 0x%x\n", status); + + return IRQ_HANDLED; +} + +static irqreturn_t ll_temac_rx_irq(int irq, void *_ndev) +{ + struct net_device *ndev = _ndev; + struct temac_local *lp = netdev_priv(ndev); + unsigned int status; + + /* Read and clear the status registers */ + status = lp->dma_in(lp, RX_IRQ_REG); + lp->dma_out(lp, RX_IRQ_REG, status); + + if (status & (IRQ_COAL | IRQ_DLY)) + ll_temac_recv(lp->ndev); + + return IRQ_HANDLED; +} + +static int temac_open(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + int rc; + + dev_dbg(&ndev->dev, "temac_open()\n"); + + if (lp->phy_node) { + lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + temac_adjust_link, 0, 0); + if (!lp->phy_dev) { + dev_err(lp->dev, "of_phy_connect() failed\n"); + return -ENODEV; + } + + phy_start(lp->phy_dev); + } + + rc = request_irq(lp->tx_irq, ll_temac_tx_irq, 0, ndev->name, ndev); + if (rc) + goto err_tx_irq; + rc = request_irq(lp->rx_irq, ll_temac_rx_irq, 0, ndev->name, ndev); + if (rc) + goto err_rx_irq; + + temac_device_reset(ndev); + return 0; + + err_rx_irq: + free_irq(lp->tx_irq, ndev); + err_tx_irq: + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + dev_err(lp->dev, "request_irq() failed\n"); + return rc; +} + +static int temac_stop(struct net_device *ndev) +{ + struct temac_local *lp = netdev_priv(ndev); + + dev_dbg(&ndev->dev, "temac_close()\n"); + + free_irq(lp->tx_irq, ndev); + free_irq(lp->rx_irq, ndev); + + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + + temac_dma_bd_release(ndev); + + 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_irq(lp->tx_irq, ndev); + ll_temac_tx_irq(lp->rx_irq, ndev); + + enable_irq(lp->tx_irq); + enable_irq(lp->rx_irq); +} +#endif + +static const struct net_device_ops temac_netdev_ops = { + .ndo_open = temac_open, + .ndo_stop = temac_stop, + .ndo_start_xmit = temac_start_xmit, + .ndo_set_mac_address = netdev_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + //.ndo_set_multicast_list = temac_set_multicast_list, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = temac_poll_controller, +#endif +}; + +/* --------------------------------------------------------------------- + * SYSFS device attributes + */ +static ssize_t temac_show_llink_regs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct temac_local *lp = netdev_priv(ndev); + int i, len = 0; + + for (i = 0; i < 0x11; i++) + len += sprintf(buf + len, "%.8x%s", lp->dma_in(lp, i), + (i % 8) == 7 ? "\n" : " "); + len += sprintf(buf + len, "\n"); + + return len; +} + +static DEVICE_ATTR(llink_regs, 0440, temac_show_llink_regs, NULL); + +static struct attribute *temac_device_attrs[] = { + &dev_attr_llink_regs.attr, + NULL, +}; + +static const struct attribute_group temac_attr_group = { + .attrs = temac_device_attrs, +}; + +static int __devinit temac_of_probe(struct platform_device *op) +{ + struct device_node *np; + struct temac_local *lp; + struct net_device *ndev; + const void *addr; + __be32 *p; + int size, rc = 0; + + /* Init network device structure */ + ndev = alloc_etherdev(sizeof(*lp)); + if (!ndev) { + dev_err(&op->dev, "could not allocate device.\n"); + return -ENOMEM; + } + ether_setup(ndev); + dev_set_drvdata(&op->dev, ndev); + SET_NETDEV_DEV(ndev, &op->dev); + ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ + ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; + ndev->netdev_ops = &temac_netdev_ops; +#if 0 + ndev->features |= NETIF_F_IP_CSUM; /* Can checksum TCP/UDP over IPv4. */ + ndev->features |= NETIF_F_HW_CSUM; /* Can checksum all the packets. */ + ndev->features |= NETIF_F_IPV6_CSUM; /* Can checksum IPV6 TCP/UDP */ + ndev->features |= NETIF_F_HIGHDMA; /* Can DMA to high memory. */ + ndev->features |= NETIF_F_HW_VLAN_TX; /* Transmit VLAN hw accel */ + ndev->features |= NETIF_F_HW_VLAN_RX; /* Receive VLAN hw acceleration */ + ndev->features |= NETIF_F_HW_VLAN_FILTER; /* Receive VLAN filtering */ + ndev->features |= NETIF_F_VLAN_CHALLENGED; /* cannot handle VLAN pkts */ + ndev->features |= NETIF_F_GSO; /* Enable software GSO. */ + ndev->features |= NETIF_F_MULTI_QUEUE; /* Has multiple TX/RX queues */ + ndev->features |= NETIF_F_LRO; /* large receive offload */ +#endif + + /* setup temac private info structure */ + lp = netdev_priv(ndev); + lp->ndev = ndev; + lp->dev = &op->dev; + lp->options = XTE_OPTION_DEFAULTS; + spin_lock_init(&lp->rx_lock); + mutex_init(&lp->indirect_mutex); + + /* map device registers */ + lp->regs = of_iomap(op->dev.of_node, 0); + if (!lp->regs) { + dev_err(&op->dev, "could not map temac regs.\n"); + goto nodev; + } + + /* Setup checksum offload, but default to off if not specified */ + lp->temac_features = 0; + p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); + if (p && be32_to_cpu(*p)) { + lp->temac_features |= TEMAC_FEATURE_TX_CSUM; + /* Can checksum TCP/UDP over IPv4. */ + ndev->features |= NETIF_F_IP_CSUM; + } + p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); + if (p && be32_to_cpu(*p)) + lp->temac_features |= TEMAC_FEATURE_RX_CSUM; + + /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ + np = of_parse_phandle(op->dev.of_node, "llink-connected", 0); + if (!np) { + dev_err(&op->dev, "could not find DMA node\n"); + goto err_iounmap; + } + + /* Setup the DMA register accesses, could be DCR or memory mapped */ + if (temac_dcr_setup(lp, op, np)) { + + /* no DCR in the device tree, try non-DCR */ + lp->sdma_regs = of_iomap(np, 0); + if (lp->sdma_regs) { + lp->dma_in = temac_dma_in32; + lp->dma_out = temac_dma_out32; + dev_dbg(&op->dev, "MEM base: %p\n", lp->sdma_regs); + } else { + dev_err(&op->dev, "unable to map DMA registers\n"); + of_node_put(np); + goto err_iounmap; + } + } + + lp->rx_irq = irq_of_parse_and_map(np, 0); + lp->tx_irq = irq_of_parse_and_map(np, 1); + + of_node_put(np); /* Finished with the DMA node; drop the reference */ + + if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { + dev_err(&op->dev, "could not determine irqs\n"); + rc = -ENOMEM; + goto err_iounmap_2; + } + + + /* Retrieve the MAC address */ + addr = of_get_property(op->dev.of_node, "local-mac-address", &size); + if ((!addr) || (size != 6)) { + dev_err(&op->dev, "could not find MAC address\n"); + rc = -ENODEV; + goto err_iounmap_2; + } + temac_set_mac_address(ndev, (void *)addr); + + rc = temac_mdio_setup(lp, op->dev.of_node); + if (rc) + dev_warn(&op->dev, "error registering MDIO bus\n"); + + lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); + if (lp->phy_node) + dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); + + /* Add the device attributes */ + rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); + if (rc) { + dev_err(lp->dev, "Error creating sysfs files\n"); + goto err_iounmap_2; + } + + rc = register_netdev(lp->ndev); + if (rc) { + dev_err(lp->dev, "register_netdev() error (%i)\n", rc); + goto err_register_ndev; + } + + return 0; + + err_register_ndev: + sysfs_remove_group(&lp->dev->kobj, &temac_attr_group); + err_iounmap_2: + if (lp->sdma_regs) + iounmap(lp->sdma_regs); + err_iounmap: + iounmap(lp->regs); + nodev: + free_netdev(ndev); + ndev = NULL; + return rc; +} + +static int __devexit temac_of_remove(struct platform_device *op) +{ + struct net_device *ndev = dev_get_drvdata(&op->dev); + struct temac_local *lp = netdev_priv(ndev); + + temac_mdio_teardown(lp); + unregister_netdev(ndev); + sysfs_remove_group(&lp->dev->kobj, &temac_attr_group); + if (lp->phy_node) + of_node_put(lp->phy_node); + lp->phy_node = NULL; + dev_set_drvdata(&op->dev, NULL); + iounmap(lp->regs); + if (lp->sdma_regs) + iounmap(lp->sdma_regs); + free_netdev(ndev); + return 0; +} + +static struct of_device_id temac_of_match[] __devinitdata = { + { .compatible = "xlnx,xps-ll-temac-1.01.b", }, + { .compatible = "xlnx,xps-ll-temac-2.00.a", }, + { .compatible = "xlnx,xps-ll-temac-2.02.a", }, + { .compatible = "xlnx,xps-ll-temac-2.03.a", }, + {}, +}; +MODULE_DEVICE_TABLE(of, temac_of_match); + +static struct platform_driver temac_of_driver = { + .probe = temac_of_probe, + .remove = __devexit_p(temac_of_remove), + .driver = { + .owner = THIS_MODULE, + .name = "xilinx_temac", + .of_match_table = temac_of_match, + }, +}; + +static int __init temac_init(void) +{ + return platform_driver_register(&temac_of_driver); +} +module_init(temac_init); + +static void __exit temac_exit(void) +{ + platform_driver_unregister(&temac_of_driver); +} +module_exit(temac_exit); + +MODULE_DESCRIPTION("Xilinx LL_TEMAC Ethernet driver"); +MODULE_AUTHOR("Yoshio Kashiwagi"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c new file mode 100644 index 000000000000..8cf9d4f56bb2 --- /dev/null +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -0,0 +1,122 @@ +/* + * MDIO bus driver for the Xilinx TEMAC device + * + * Copyright (c) 2009 Secret Lab Technologies, Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ll_temac.h" + +/* --------------------------------------------------------------------- + * MDIO Bus functions + */ +static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct temac_local *lp = bus->priv; + u32 rc; + + /* Write the PHY address to the MIIM Access Initiator register. + * When the transfer completes, the PHY register value will appear + * in the LSW0 register */ + mutex_lock(&lp->indirect_mutex); + temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); + rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); + mutex_unlock(&lp->indirect_mutex); + + dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", + phy_id, reg, rc); + + return rc; +} + +static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) +{ + struct temac_local *lp = bus->priv; + + dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", + phy_id, reg, val); + + /* First write the desired value into the write data register + * and then write the address into the access initiator register + */ + mutex_lock(&lp->indirect_mutex); + temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); + temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); + mutex_unlock(&lp->indirect_mutex); + + return 0; +} + +int temac_mdio_setup(struct temac_local *lp, struct device_node *np) +{ + struct mii_bus *bus; + const u32 *bus_hz; + int clk_div; + int rc, size; + struct resource res; + + /* Calculate a reasonable divisor for the clock rate */ + clk_div = 0x3f; /* worst-case default setting */ + bus_hz = of_get_property(np, "clock-frequency", &size); + if (bus_hz && size >= sizeof(*bus_hz)) { + clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1; + if (clk_div < 1) + clk_div = 1; + if (clk_div > 0x3f) + clk_div = 0x3f; + } + + /* Enable the MDIO bus by asserting the enable bit and writing + * in the clock config */ + mutex_lock(&lp->indirect_mutex); + temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); + mutex_unlock(&lp->indirect_mutex); + + bus = mdiobus_alloc(); + if (!bus) + return -ENOMEM; + + of_address_to_resource(np, 0, &res); + snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long)res.start); + bus->priv = lp; + bus->name = "Xilinx TEMAC MDIO"; + bus->read = temac_mdio_read; + bus->write = temac_mdio_write; + bus->parent = lp->dev; + bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ + + lp->mii_bus = bus; + + rc = of_mdiobus_register(bus, np); + if (rc) + goto err_register; + + mutex_lock(&lp->indirect_mutex); + dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", + temac_indirect_in32(lp, XTE_MC_OFFSET)); + mutex_unlock(&lp->indirect_mutex); + return 0; + + err_register: + mdiobus_free(bus); + return rc; +} + +void temac_mdio_teardown(struct temac_local *lp) +{ + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + lp->mii_bus = NULL; +} + diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c new file mode 100644 index 000000000000..8018d7d045b0 --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -0,0 +1,1330 @@ +/* + * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. + * + * This is a new flat driver which is based on the original emac_lite + * driver from John Williams . + * + * 2007-2009 (c) Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "xilinx_emaclite" + +/* Register offsets for the EmacLite Core */ +#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ +#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ +#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ +#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ +#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ +#define XEL_GIER_OFFSET 0x07F8 /* GIE Register */ +#define XEL_TSR_OFFSET 0x07FC /* Tx status */ +#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ + +#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ +#define XEL_RPLR_OFFSET 0x100C /* Rx packet length */ +#define XEL_RSR_OFFSET 0x17FC /* Rx status */ + +#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ + +/* MDIO Address Register Bit Masks */ +#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ +#define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ +#define XEL_MDIOADDR_PHYADR_SHIFT 5 +#define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ + +/* MDIO Write Data Register Bit Masks */ +#define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ + +/* MDIO Read Data Register Bit Masks */ +#define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ + +/* MDIO Control Register Bit Masks */ +#define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ +#define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ + +/* Global Interrupt Enable Register (GIER) Bit Masks */ +#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ + +/* Transmit Status Register (TSR) Bit Masks */ +#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ +#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ +#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ +#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit + * only. This is not documented + * in the HW spec */ + +/* Define for programming the MAC address into the EmacLite */ +#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) + +/* Receive Status Register (RSR) */ +#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ +#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ + +/* Transmit Packet Length Register (TPLR) */ +#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ + +/* Receive Packet Length Register (RPLR) */ +#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ + +#define XEL_HEADER_OFFSET 12 /* Offset to length field */ +#define XEL_HEADER_SHIFT 16 /* Shift value for length */ + +/* General Ethernet Definitions */ +#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ +#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ + + + +#define TX_TIMEOUT (60*HZ) /* Tx timeout is 60 seconds. */ +#define ALIGNMENT 4 + +/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ +#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT) + +/** + * struct net_local - Our private per device data + * @ndev: instance of the network device + * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW + * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW + * @next_tx_buf_to_use: next Tx buffer to write to + * @next_rx_buf_to_use: next Rx buffer to read from + * @base_addr: base address of the Emaclite device + * @reset_lock: lock used for synchronization + * @deferred_skb: holds an skb (for transmission at a later time) when the + * Tx buffer is not free + * @phy_dev: pointer to the PHY device + * @phy_node: pointer to the PHY device node + * @mii_bus: pointer to the MII bus + * @mdio_irqs: IRQs table for MDIO bus + * @last_link: last link status + * @has_mdio: indicates whether MDIO is included in the HW + */ +struct net_local { + + struct net_device *ndev; + + bool tx_ping_pong; + bool rx_ping_pong; + u32 next_tx_buf_to_use; + u32 next_rx_buf_to_use; + void __iomem *base_addr; + + spinlock_t reset_lock; + struct sk_buff *deferred_skb; + + struct phy_device *phy_dev; + struct device_node *phy_node; + + struct mii_bus *mii_bus; + int mdio_irqs[PHY_MAX_ADDR]; + + int last_link; + bool has_mdio; +}; + + +/*************************/ +/* EmacLite driver calls */ +/*************************/ + +/** + * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device + * @drvdata: Pointer to the Emaclite device private data + * + * This function enables the Tx and Rx interrupts for the Emaclite device along + * with the Global Interrupt Enable. + */ +static void xemaclite_enable_interrupts(struct net_local *drvdata) +{ + u32 reg_data; + + /* Enable the Tx interrupts for the first Buffer */ + reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); + out_be32(drvdata->base_addr + XEL_TSR_OFFSET, + reg_data | XEL_TSR_XMIT_IE_MASK); + + /* Enable the Tx interrupts for the second Buffer if + * configured in HW */ + if (drvdata->tx_ping_pong != 0) { + reg_data = in_be32(drvdata->base_addr + + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET, + reg_data | XEL_TSR_XMIT_IE_MASK); + } + + /* Enable the Rx interrupts for the first buffer */ + out_be32(drvdata->base_addr + XEL_RSR_OFFSET, + XEL_RSR_RECV_IE_MASK); + + /* Enable the Rx interrupts for the second Buffer if + * configured in HW */ + if (drvdata->rx_ping_pong != 0) { + out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET, + XEL_RSR_RECV_IE_MASK); + } + + /* Enable the Global Interrupt Enable */ + out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); +} + +/** + * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device + * @drvdata: Pointer to the Emaclite device private data + * + * This function disables the Tx and Rx interrupts for the Emaclite device, + * along with the Global Interrupt Enable. + */ +static void xemaclite_disable_interrupts(struct net_local *drvdata) +{ + u32 reg_data; + + /* Disable the Global Interrupt Enable */ + out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); + + /* Disable the Tx interrupts for the first buffer */ + reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); + out_be32(drvdata->base_addr + XEL_TSR_OFFSET, + reg_data & (~XEL_TSR_XMIT_IE_MASK)); + + /* Disable the Tx interrupts for the second Buffer + * if configured in HW */ + if (drvdata->tx_ping_pong != 0) { + reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); + out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET, + reg_data & (~XEL_TSR_XMIT_IE_MASK)); + } + + /* Disable the Rx interrupts for the first buffer */ + reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); + out_be32(drvdata->base_addr + XEL_RSR_OFFSET, + reg_data & (~XEL_RSR_RECV_IE_MASK)); + + /* Disable the Rx interrupts for the second buffer + * if configured in HW */ + if (drvdata->rx_ping_pong != 0) { + + reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET); + out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET, + reg_data & (~XEL_RSR_RECV_IE_MASK)); + } +} + +/** + * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address + * @src_ptr: Void pointer to the 16-bit aligned source address + * @dest_ptr: Pointer to the 32-bit aligned destination address + * @length: Number bytes to write from source to destination + * + * This function writes data from a 16-bit aligned buffer to a 32-bit aligned + * address in the EmacLite device. + */ +static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr, + unsigned length) +{ + u32 align_buffer; + u32 *to_u32_ptr; + u16 *from_u16_ptr, *to_u16_ptr; + + to_u32_ptr = dest_ptr; + from_u16_ptr = src_ptr; + align_buffer = 0; + + for (; length > 3; length -= 4) { + to_u16_ptr = (u16 *)&align_buffer; + *to_u16_ptr++ = *from_u16_ptr++; + *to_u16_ptr++ = *from_u16_ptr++; + + /* Output a word */ + *to_u32_ptr++ = align_buffer; + } + if (length) { + u8 *from_u8_ptr, *to_u8_ptr; + + /* Set up to output the remaining data */ + align_buffer = 0; + to_u8_ptr = (u8 *) &align_buffer; + from_u8_ptr = (u8 *) from_u16_ptr; + + /* Output the remaining data */ + for (; length > 0; length--) + *to_u8_ptr++ = *from_u8_ptr++; + + *to_u32_ptr = align_buffer; + } +} + +/** + * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer + * @src_ptr: Pointer to the 32-bit aligned source address + * @dest_ptr: Pointer to the 16-bit aligned destination address + * @length: Number bytes to read from source to destination + * + * This function reads data from a 32-bit aligned address in the EmacLite device + * to a 16-bit aligned buffer. + */ +static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, + unsigned length) +{ + u16 *to_u16_ptr, *from_u16_ptr; + u32 *from_u32_ptr; + u32 align_buffer; + + from_u32_ptr = src_ptr; + to_u16_ptr = (u16 *) dest_ptr; + + for (; length > 3; length -= 4) { + /* Copy each word into the temporary buffer */ + align_buffer = *from_u32_ptr++; + from_u16_ptr = (u16 *)&align_buffer; + + /* Read data from source */ + *to_u16_ptr++ = *from_u16_ptr++; + *to_u16_ptr++ = *from_u16_ptr++; + } + + if (length) { + u8 *to_u8_ptr, *from_u8_ptr; + + /* Set up to read the remaining data */ + to_u8_ptr = (u8 *) to_u16_ptr; + align_buffer = *from_u32_ptr++; + from_u8_ptr = (u8 *) &align_buffer; + + /* Read the remaining data */ + for (; length > 0; length--) + *to_u8_ptr = *from_u8_ptr; + } +} + +/** + * xemaclite_send_data - Send an Ethernet frame + * @drvdata: Pointer to the Emaclite device private data + * @data: Pointer to the data to be sent + * @byte_count: Total frame size, including header + * + * This function checks if the Tx buffer of the Emaclite device is free to send + * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it + * returns an error. + * + * Return: 0 upon success or -1 if the buffer(s) are full. + * + * Note: The maximum Tx packet size can not be more than Ethernet header + * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. + */ +static int xemaclite_send_data(struct net_local *drvdata, u8 *data, + unsigned int byte_count) +{ + u32 reg_data; + void __iomem *addr; + + /* Determine the expected Tx buffer address */ + addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; + + /* If the length is too large, truncate it */ + if (byte_count > ETH_FRAME_LEN) + byte_count = ETH_FRAME_LEN; + + /* Check if the expected buffer is available */ + reg_data = in_be32(addr + XEL_TSR_OFFSET); + if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | + XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { + + /* Switch to next buffer if configured */ + if (drvdata->tx_ping_pong != 0) + drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET; + } else if (drvdata->tx_ping_pong != 0) { + /* If the expected buffer is full, try the other buffer, + * if it is configured in HW */ + + addr = (void __iomem __force *)((u32 __force)addr ^ + XEL_BUFFER_OFFSET); + reg_data = in_be32(addr + XEL_TSR_OFFSET); + + if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | + XEL_TSR_XMIT_ACTIVE_MASK)) != 0) + return -1; /* Buffers were full, return failure */ + } else + return -1; /* Buffer was full, return failure */ + + /* Write the frame to the buffer */ + xemaclite_aligned_write(data, (u32 __force *) addr, byte_count); + + out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK)); + + /* Update the Tx Status Register to indicate that there is a + * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which + * is used by the interrupt handler to check whether a frame + * has been transmitted */ + reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); + out_be32(addr + XEL_TSR_OFFSET, reg_data); + + return 0; +} + +/** + * xemaclite_recv_data - Receive a frame + * @drvdata: Pointer to the Emaclite device private data + * @data: Address where the data is to be received + * + * This function is intended to be called from the interrupt context or + * with a wrapper which waits for the receive frame to be available. + * + * Return: Total number of bytes received + */ +static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) +{ + void __iomem *addr; + u16 length, proto_type; + u32 reg_data; + + /* Determine the expected buffer address */ + addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); + + /* Verify which buffer has valid data */ + reg_data = in_be32(addr + XEL_RSR_OFFSET); + + if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { + if (drvdata->rx_ping_pong != 0) + drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET; + } else { + /* The instance is out of sync, try other buffer if other + * buffer is configured, return 0 otherwise. If the instance is + * out of sync, do not update the 'next_rx_buf_to_use' since it + * will correct on subsequent calls */ + if (drvdata->rx_ping_pong != 0) + addr = (void __iomem __force *)((u32 __force)addr ^ + XEL_BUFFER_OFFSET); + else + return 0; /* No data was available */ + + /* Verify that buffer has valid data */ + reg_data = in_be32(addr + XEL_RSR_OFFSET); + if ((reg_data & XEL_RSR_RECV_DONE_MASK) != + XEL_RSR_RECV_DONE_MASK) + return 0; /* No data was available */ + } + + /* Get the protocol type of the ethernet frame that arrived */ + proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & + XEL_RPLR_LENGTH_MASK); + + /* Check if received ethernet frame is a raw ethernet frame + * or an IP packet or an ARP packet */ + if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { + + if (proto_type == ETH_P_IP) { + length = ((ntohl(in_be32(addr + + XEL_HEADER_IP_LENGTH_OFFSET + + XEL_RXBUFF_OFFSET)) >> + XEL_HEADER_SHIFT) & + XEL_RPLR_LENGTH_MASK); + length += ETH_HLEN + ETH_FCS_LEN; + + } else if (proto_type == ETH_P_ARP) + length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN; + else + /* Field contains type other than IP or ARP, use max + * frame size and let user parse it */ + length = ETH_FRAME_LEN + ETH_FCS_LEN; + } else + /* Use the length in the frame, plus the header and trailer */ + length = proto_type + ETH_HLEN + ETH_FCS_LEN; + + /* Read from the EmacLite device */ + xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET), + data, length); + + /* Acknowledge the frame */ + reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data &= ~XEL_RSR_RECV_DONE_MASK; + out_be32(addr + XEL_RSR_OFFSET, reg_data); + + return length; +} + +/** + * xemaclite_update_address - Update the MAC address in the device + * @drvdata: Pointer to the Emaclite device private data + * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) + * + * Tx must be idle and Rx should be idle for deterministic results. + * It is recommended that this function should be called after the + * initialization and before transmission of any packets from the device. + * The MAC address can be programmed using any of the two transmit + * buffers (if configured). + */ +static void xemaclite_update_address(struct net_local *drvdata, + u8 *address_ptr) +{ + void __iomem *addr; + u32 reg_data; + + /* Determine the expected Tx buffer address */ + addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; + + xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN); + + out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN); + + /* Update the MAC address in the EmacLite */ + reg_data = in_be32(addr + XEL_TSR_OFFSET); + out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR); + + /* Wait for EmacLite to finish with the MAC address update */ + while ((in_be32(addr + XEL_TSR_OFFSET) & + XEL_TSR_PROG_MAC_ADDR) != 0) + ; +} + +/** + * xemaclite_set_mac_address - Set the MAC address for this device + * @dev: Pointer to the network device instance + * @addr: Void pointer to the sockaddr structure + * + * This function copies the HW address from the sockaddr strucutre to the + * net_device structure and updates the address in HW. + * + * Return: Error if the net device is busy or 0 if the addr is set + * successfully + */ +static int xemaclite_set_mac_address(struct net_device *dev, void *address) +{ + struct net_local *lp = netdev_priv(dev); + struct sockaddr *addr = address; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + xemaclite_update_address(lp, dev->dev_addr); + return 0; +} + +/** + * xemaclite_tx_timeout - Callback for Tx Timeout + * @dev: Pointer to the network device + * + * This function is called when Tx time out occurs for Emaclite device. + */ +static void xemaclite_tx_timeout(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + unsigned long flags; + + dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n", + TX_TIMEOUT * 1000UL / HZ); + + dev->stats.tx_errors++; + + /* Reset the device */ + spin_lock_irqsave(&lp->reset_lock, flags); + + /* Shouldn't really be necessary, but shouldn't hurt */ + netif_stop_queue(dev); + + xemaclite_disable_interrupts(lp); + xemaclite_enable_interrupts(lp); + + if (lp->deferred_skb) { + dev_kfree_skb(lp->deferred_skb); + lp->deferred_skb = NULL; + dev->stats.tx_errors++; + } + + /* To exclude tx timeout */ + dev->trans_start = jiffies; /* prevent tx timeout */ + + /* We're all ready to go. Start the queue */ + netif_wake_queue(dev); + spin_unlock_irqrestore(&lp->reset_lock, flags); +} + +/**********************/ +/* Interrupt Handlers */ +/**********************/ + +/** + * xemaclite_tx_handler - Interrupt handler for frames sent + * @dev: Pointer to the network device + * + * This function updates the number of packets transmitted and handles the + * deferred skb, if there is one. + */ +static void xemaclite_tx_handler(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + + dev->stats.tx_packets++; + if (lp->deferred_skb) { + if (xemaclite_send_data(lp, + (u8 *) lp->deferred_skb->data, + lp->deferred_skb->len) != 0) + return; + else { + dev->stats.tx_bytes += lp->deferred_skb->len; + dev_kfree_skb_irq(lp->deferred_skb); + lp->deferred_skb = NULL; + dev->trans_start = jiffies; /* prevent tx timeout */ + netif_wake_queue(dev); + } + } +} + +/** + * xemaclite_rx_handler- Interrupt handler for frames received + * @dev: Pointer to the network device + * + * This function allocates memory for a socket buffer, fills it with data + * received and hands it over to the TCP/IP stack. + */ +static void xemaclite_rx_handler(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + struct sk_buff *skb; + unsigned int align; + u32 len; + + len = ETH_FRAME_LEN + ETH_FCS_LEN; + skb = dev_alloc_skb(len + ALIGNMENT); + if (!skb) { + /* Couldn't get memory. */ + dev->stats.rx_dropped++; + dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n"); + return; + } + + /* + * A new skb should have the data halfword aligned, but this code is + * here just in case that isn't true. Calculate how many + * bytes we should reserve to get the data to start on a word + * boundary */ + align = BUFFER_ALIGN(skb->data); + if (align) + skb_reserve(skb, align); + + skb_reserve(skb, 2); + + len = xemaclite_recv_data(lp, (u8 *) skb->data); + + if (!len) { + dev->stats.rx_errors++; + dev_kfree_skb_irq(skb); + return; + } + + skb_put(skb, len); /* Tell the skb how much data we got */ + + skb->protocol = eth_type_trans(skb, dev); + skb_checksum_none_assert(skb); + + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + + if (!skb_defer_rx_timestamp(skb)) + netif_rx(skb); /* Send the packet upstream */ +} + +/** + * xemaclite_interrupt - Interrupt handler for this driver + * @irq: Irq of the Emaclite device + * @dev_id: Void pointer to the network device instance used as callback + * reference + * + * This function handles the Tx and Rx interrupts of the EmacLite device. + */ +static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) +{ + bool tx_complete = 0; + struct net_device *dev = dev_id; + struct net_local *lp = netdev_priv(dev); + void __iomem *base_addr = lp->base_addr; + u32 tx_status; + + /* Check if there is Rx Data available */ + if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) || + (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) + & XEL_RSR_RECV_DONE_MASK)) + + xemaclite_rx_handler(dev); + + /* Check if the Transmission for the first buffer is completed */ + tx_status = in_be32(base_addr + XEL_TSR_OFFSET); + if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && + (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { + + tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; + out_be32(base_addr + XEL_TSR_OFFSET, tx_status); + + tx_complete = 1; + } + + /* Check if the Transmission for the second buffer is completed */ + tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && + (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { + + tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; + out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, + tx_status); + + tx_complete = 1; + } + + /* If there was a Tx interrupt, call the Tx Handler */ + if (tx_complete != 0) + xemaclite_tx_handler(dev); + + return IRQ_HANDLED; +} + +/**********************/ +/* MDIO Bus functions */ +/**********************/ + +/** + * xemaclite_mdio_wait - Wait for the MDIO to be ready to use + * @lp: Pointer to the Emaclite device private data + * + * This function waits till the device is ready to accept a new MDIO + * request. + * + * Return: 0 for success or ETIMEDOUT for a timeout + */ + +static int xemaclite_mdio_wait(struct net_local *lp) +{ + long end = jiffies + 2; + + /* wait for the MDIO interface to not be busy or timeout + after some time. + */ + while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) & + XEL_MDIOCTRL_MDIOSTS_MASK) { + if (end - jiffies <= 0) { + WARN_ON(1); + return -ETIMEDOUT; + } + msleep(1); + } + return 0; +} + +/** + * xemaclite_mdio_read - Read from a given MII management register + * @bus: the mii_bus struct + * @phy_id: the phy address + * @reg: register number to read from + * + * This function waits till the device is ready to accept a new MDIO + * request and then writes the phy address to the MDIO Address register + * and reads data from MDIO Read Data register, when its available. + * + * Return: Value read from the MII management register + */ +static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct net_local *lp = bus->priv; + u32 ctrl_reg; + u32 rc; + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + /* Write the PHY address, register number and set the OP bit in the + * MDIO Address register. Set the Status bit in the MDIO Control + * register to start a MDIO read transaction. + */ + ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); + out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, + XEL_MDIOADDR_OP_MASK | + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET); + + dev_dbg(&lp->ndev->dev, + "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", + phy_id, reg, rc); + + return rc; +} + +/** + * xemaclite_mdio_write - Write to a given MII management register + * @bus: the mii_bus struct + * @phy_id: the phy address + * @reg: register number to write to + * @val: value to write to the register number specified by reg + * + * This function waits till the device is ready to accept a new MDIO + * request and then writes the val to the MDIO Write Data register. + */ +static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, + u16 val) +{ + struct net_local *lp = bus->priv; + u32 ctrl_reg; + + dev_dbg(&lp->ndev->dev, + "xemaclite_mdio_write(phy_id=%i, reg=%x, val=%x)\n", + phy_id, reg, val); + + if (xemaclite_mdio_wait(lp)) + return -ETIMEDOUT; + + /* Write the PHY address, register number and clear the OP bit in the + * MDIO Address register and then write the value into the MDIO Write + * Data register. Finally, set the Status bit in the MDIO Control + * register to start a MDIO write transaction. + */ + ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); + out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, + ~XEL_MDIOADDR_OP_MASK & + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); + out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val); + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + + return 0; +} + +/** + * xemaclite_mdio_reset - Reset the mdio bus. + * @bus: Pointer to the MII bus + * + * This function is required(?) as per Documentation/networking/phy.txt. + * There is no reset in this device; this function always returns 0. + */ +static int xemaclite_mdio_reset(struct mii_bus *bus) +{ + return 0; +} + +/** + * xemaclite_mdio_setup - Register mii_bus for the Emaclite device + * @lp: Pointer to the Emaclite device private data + * @ofdev: Pointer to OF device structure + * + * This function enables MDIO bus in the Emaclite device and registers a + * mii_bus. + * + * Return: 0 upon success or a negative error upon failure + */ +static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) +{ + struct mii_bus *bus; + int rc; + struct resource res; + struct device_node *np = of_get_parent(lp->phy_node); + + /* Don't register the MDIO bus if the phy_node or its parent node + * can't be found. + */ + if (!np) + return -ENODEV; + + /* Enable the MDIO bus by asserting the enable bit in MDIO Control + * register. + */ + out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, + XEL_MDIOCTRL_MDIOEN_MASK); + + bus = mdiobus_alloc(); + if (!bus) + return -ENOMEM; + + of_address_to_resource(np, 0, &res); + snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", + (unsigned long long)res.start); + bus->priv = lp; + bus->name = "Xilinx Emaclite MDIO"; + bus->read = xemaclite_mdio_read; + bus->write = xemaclite_mdio_write; + bus->reset = xemaclite_mdio_reset; + bus->parent = dev; + bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ + + lp->mii_bus = bus; + + rc = of_mdiobus_register(bus, np); + if (rc) + goto err_register; + + return 0; + +err_register: + mdiobus_free(bus); + return rc; +} + +/** + * xemaclite_adjust_link - Link state callback for the Emaclite device + * @ndev: pointer to net_device struct + * + * There's nothing in the Emaclite device to be configured when the link + * state changes. We just print the status. + */ +void xemaclite_adjust_link(struct net_device *ndev) +{ + struct net_local *lp = netdev_priv(ndev); + struct phy_device *phy = lp->phy_dev; + int link_state; + + /* hash together the state values to decide if something has changed */ + link_state = phy->speed | (phy->duplex << 1) | phy->link; + + if (lp->last_link != link_state) { + lp->last_link = link_state; + phy_print_status(phy); + } +} + +/** + * xemaclite_open - Open the network device + * @dev: Pointer to the network device + * + * This function sets the MAC address, requests an IRQ and enables interrupts + * for the Emaclite device and starts the Tx queue. + * It also connects to the phy device, if MDIO is included in Emaclite device. + */ +static int xemaclite_open(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + int retval; + + /* Just to be safe, stop the device first */ + xemaclite_disable_interrupts(lp); + + if (lp->phy_node) { + u32 bmcr; + + lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, + xemaclite_adjust_link, 0, + PHY_INTERFACE_MODE_MII); + if (!lp->phy_dev) { + dev_err(&lp->ndev->dev, "of_phy_connect() failed\n"); + return -ENODEV; + } + + /* EmacLite doesn't support giga-bit speeds */ + lp->phy_dev->supported &= (PHY_BASIC_FEATURES); + lp->phy_dev->advertising = lp->phy_dev->supported; + + /* Don't advertise 1000BASE-T Full/Half duplex speeds */ + phy_write(lp->phy_dev, MII_CTRL1000, 0); + + /* Advertise only 10 and 100mbps full/half duplex speeds */ + phy_write(lp->phy_dev, MII_ADVERTISE, ADVERTISE_ALL); + + /* Restart auto negotiation */ + bmcr = phy_read(lp->phy_dev, MII_BMCR); + bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); + phy_write(lp->phy_dev, MII_BMCR, bmcr); + + phy_start(lp->phy_dev); + } + + /* Set the MAC address each time opened */ + xemaclite_update_address(lp, dev->dev_addr); + + /* Grab the IRQ */ + retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); + if (retval) { + dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", + dev->irq); + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + + return retval; + } + + /* Enable Interrupts */ + xemaclite_enable_interrupts(lp); + + /* We're ready to go */ + netif_start_queue(dev); + + return 0; +} + +/** + * xemaclite_close - Close the network device + * @dev: Pointer to the network device + * + * This function stops the Tx queue, disables interrupts and frees the IRQ for + * the Emaclite device. + * It also disconnects the phy device associated with the Emaclite device. + */ +static int xemaclite_close(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + + netif_stop_queue(dev); + xemaclite_disable_interrupts(lp); + free_irq(dev->irq, dev); + + if (lp->phy_dev) + phy_disconnect(lp->phy_dev); + lp->phy_dev = NULL; + + return 0; +} + +/** + * xemaclite_send - Transmit a frame + * @orig_skb: Pointer to the socket buffer to be transmitted + * @dev: Pointer to the network device + * + * This function checks if the Tx buffer of the Emaclite device is free to send + * data. If so, it fills the Tx buffer with data from socket buffer data, + * updates the stats and frees the socket buffer. The Tx completion is signaled + * by an interrupt. If the Tx buffer isn't free, then the socket buffer is + * deferred and the Tx queue is stopped so that the deferred socket buffer can + * be transmitted when the Emaclite device is free to transmit data. + * + * Return: 0, always. + */ +static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + struct sk_buff *new_skb; + unsigned int len; + unsigned long flags; + + len = orig_skb->len; + + new_skb = orig_skb; + + spin_lock_irqsave(&lp->reset_lock, flags); + if (xemaclite_send_data(lp, (u8 *) new_skb->data, len) != 0) { + /* If the Emaclite Tx buffer is busy, stop the Tx queue and + * defer the skb for transmission during the ISR, after the + * current transmission is complete */ + netif_stop_queue(dev); + lp->deferred_skb = new_skb; + /* Take the time stamp now, since we can't do this in an ISR. */ + skb_tx_timestamp(new_skb); + spin_unlock_irqrestore(&lp->reset_lock, flags); + return 0; + } + spin_unlock_irqrestore(&lp->reset_lock, flags); + + skb_tx_timestamp(new_skb); + + dev->stats.tx_bytes += len; + dev_kfree_skb(new_skb); + + return 0; +} + +/** + * xemaclite_remove_ndev - Free the network device + * @ndev: Pointer to the network device to be freed + * + * This function un maps the IO region of the Emaclite device and frees the net + * device. + */ +static void xemaclite_remove_ndev(struct net_device *ndev) +{ + if (ndev) { + struct net_local *lp = netdev_priv(ndev); + + if (lp->base_addr) + iounmap((void __iomem __force *) (lp->base_addr)); + free_netdev(ndev); + } +} + +/** + * get_bool - Get a parameter from the OF device + * @ofdev: Pointer to OF device structure + * @s: Property to be retrieved + * + * This function looks for a property in the device node and returns the value + * of the property if its found or 0 if the property is not found. + * + * Return: Value of the parameter if the parameter is found, or 0 otherwise + */ +static bool get_bool(struct platform_device *ofdev, const char *s) +{ + u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL); + + if (p) { + return (bool)*p; + } else { + dev_warn(&ofdev->dev, "Parameter %s not found," + "defaulting to false\n", s); + return 0; + } +} + +static struct net_device_ops xemaclite_netdev_ops; + +/** + * xemaclite_of_probe - Probe method for the Emaclite device. + * @ofdev: Pointer to OF device structure + * @match: Pointer to the structure used for matching a device + * + * This function probes for the Emaclite device in the device tree. + * It initializes the driver data structure and the hardware, sets the MAC + * address and registers the network device. + * It also registers a mii_bus for the Emaclite device, if MDIO is included + * in the device. + * + * Return: 0, if the driver is bound to the Emaclite device, or + * a negative error if there is failure. + */ +static int __devinit xemaclite_of_probe(struct platform_device *ofdev) +{ + struct resource r_irq; /* Interrupt resources */ + struct resource r_mem; /* IO mem resources */ + struct net_device *ndev = NULL; + struct net_local *lp = NULL; + struct device *dev = &ofdev->dev; + const void *mac_address; + + int rc = 0; + + dev_info(dev, "Device Tree Probing\n"); + + /* Get iospace for the device */ + rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); + if (rc) { + dev_err(dev, "invalid address\n"); + return rc; + } + + /* Get IRQ for the device */ + rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq); + if (rc == NO_IRQ) { + dev_err(dev, "no IRQ found\n"); + return rc; + } + + /* Create an ethernet device instance */ + ndev = alloc_etherdev(sizeof(struct net_local)); + if (!ndev) { + dev_err(dev, "Could not allocate network device\n"); + return -ENOMEM; + } + + dev_set_drvdata(dev, ndev); + SET_NETDEV_DEV(ndev, &ofdev->dev); + + ndev->irq = r_irq.start; + ndev->mem_start = r_mem.start; + ndev->mem_end = r_mem.end; + + lp = netdev_priv(ndev); + lp->ndev = ndev; + + if (!request_mem_region(ndev->mem_start, + ndev->mem_end - ndev->mem_start + 1, + DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at %p\n", + (void *)ndev->mem_start); + rc = -EBUSY; + goto error2; + } + + /* Get the virtual base address for the device */ + lp->base_addr = ioremap(r_mem.start, resource_size(&r_mem)); + if (NULL == lp->base_addr) { + dev_err(dev, "EmacLite: Could not allocate iomem\n"); + rc = -EIO; + goto error1; + } + + spin_lock_init(&lp->reset_lock); + lp->next_tx_buf_to_use = 0x0; + lp->next_rx_buf_to_use = 0x0; + lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong"); + lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong"); + mac_address = of_get_mac_address(ofdev->dev.of_node); + + if (mac_address) + /* Set the MAC address. */ + memcpy(ndev->dev_addr, mac_address, 6); + else + dev_warn(dev, "No MAC address found\n"); + + /* Clear the Tx CSR's in case this is a restart */ + out_be32(lp->base_addr + XEL_TSR_OFFSET, 0); + out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0); + + /* Set the MAC address in the EmacLite device */ + xemaclite_update_address(lp, ndev->dev_addr); + + lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); + rc = xemaclite_mdio_setup(lp, &ofdev->dev); + if (rc) + dev_warn(&ofdev->dev, "error registering MDIO bus\n"); + + dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr); + + ndev->netdev_ops = &xemaclite_netdev_ops; + ndev->flags &= ~IFF_MULTICAST; + ndev->watchdog_timeo = TX_TIMEOUT; + + /* Finally, register the device */ + rc = register_netdev(ndev); + if (rc) { + dev_err(dev, + "Cannot register network device, aborting\n"); + goto error1; + } + + dev_info(dev, + "Xilinx EmacLite at 0x%08X mapped to 0x%08X, irq=%d\n", + (unsigned int __force)ndev->mem_start, + (unsigned int __force)lp->base_addr, ndev->irq); + return 0; + +error1: + release_mem_region(ndev->mem_start, resource_size(&r_mem)); + +error2: + xemaclite_remove_ndev(ndev); + return rc; +} + +/** + * xemaclite_of_remove - Unbind the driver from the Emaclite device. + * @of_dev: Pointer to OF device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees any resources allocated to + * the device. + * + * Return: 0, always. + */ +static int __devexit xemaclite_of_remove(struct platform_device *of_dev) +{ + struct device *dev = &of_dev->dev; + struct net_device *ndev = dev_get_drvdata(dev); + + struct net_local *lp = netdev_priv(ndev); + + /* Un-register the mii_bus, if configured */ + if (lp->has_mdio) { + mdiobus_unregister(lp->mii_bus); + kfree(lp->mii_bus->irq); + mdiobus_free(lp->mii_bus); + lp->mii_bus = NULL; + } + + unregister_netdev(ndev); + + if (lp->phy_node) + of_node_put(lp->phy_node); + lp->phy_node = NULL; + + release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1); + + xemaclite_remove_ndev(ndev); + dev_set_drvdata(dev, NULL); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void +xemaclite_poll_controller(struct net_device *ndev) +{ + disable_irq(ndev->irq); + xemaclite_interrupt(ndev->irq, ndev); + enable_irq(ndev->irq); +} +#endif + +static struct net_device_ops xemaclite_netdev_ops = { + .ndo_open = xemaclite_open, + .ndo_stop = xemaclite_close, + .ndo_start_xmit = xemaclite_send, + .ndo_set_mac_address = xemaclite_set_mac_address, + .ndo_tx_timeout = xemaclite_tx_timeout, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = xemaclite_poll_controller, +#endif +}; + +/* Match table for OF platform binding */ +static struct of_device_id xemaclite_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-ethernetlite-1.01.a", }, + { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, + { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, + { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, + { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, + { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, xemaclite_of_match); + +static struct platform_driver xemaclite_of_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = xemaclite_of_match, + }, + .probe = xemaclite_of_probe, + .remove = __devexit_p(xemaclite_of_remove), +}; + +/** + * xgpiopss_init - Initial driver registration call + * + * Return: 0 upon success, or a negative error upon failure. + */ +static int __init xemaclite_init(void) +{ + /* No kernel boot options used, we just need to register the driver */ + return platform_driver_register(&xemaclite_of_driver); +} + +/** + * xemaclite_cleanup - Driver un-registration call + */ +static void __exit xemaclite_cleanup(void) +{ + platform_driver_unregister(&xemaclite_of_driver); +} + +module_init(xemaclite_init); +module_exit(xemaclite_cleanup); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ll_temac.h b/drivers/net/ll_temac.h deleted file mode 100644 index 522abe2ff25a..000000000000 --- a/drivers/net/ll_temac.h +++ /dev/null @@ -1,385 +0,0 @@ - -#ifndef XILINX_LL_TEMAC_H -#define XILINX_LL_TEMAC_H - -#include -#include -#include - -#ifdef CONFIG_PPC_DCR -#include -#include -#endif - -/* packet size info */ -#define XTE_HDR_SIZE 14 /* size of Ethernet header */ -#define XTE_TRL_SIZE 4 /* size of Ethernet trailer (FCS) */ -#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 0x01 /* r */ -#define TX_CURBUF_LENGTH 0x02 /* r */ -#define TX_CURDESC_PTR 0x03 /* rw */ -#define TX_TAILDESC_PTR 0x04 /* rw */ -#define TX_CHNL_CTRL 0x05 /* 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 0x06 /* 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 0x07 /* 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 0x08 /* r */ -#define RX_CURBUF_ADDR 0x09 /* r */ -#define RX_CURBUF_LENGTH 0x0a /* r */ -#define RX_CURDESC_PTR 0x0b /* rw */ -#define RX_TAILDESC_PTR 0x0c /* rw */ -#define RX_CHNL_CTRL 0x0d /* 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 0x0e /* 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 0x0f /* 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 0x10 /* rw */ -#define DMA_CONTROL_RST (1 << 0) -#define DMA_TAIL_ENABLE (1 << 2) - -/* 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 -#define XTE_RDY0_HARD_ACS_RDY_MASK (0x10000) - -/* 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 config */ -#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 /* 10 Mbit LINKSPD_MASK */ -#define XTE_EMCFG_LINKSPD_100 (1 << 30) /* 100 Mbit LINKSPD_MASK */ -#define XTE_EMCFG_LINKSPD_1000 (1 << 31) /* 1000 Mbit LINKSPD_MASK */ - -#define XTE_GMIC_OFFSET 0x00000320 /* RGMII/SGMII config */ -#define XTE_MC_OFFSET 0x00000340 /* MDIO configuration */ -#define XTE_UAW0_OFFSET 0x00000380 /* Unicast address word 0 */ -#define XTE_UAW1_OFFSET 0x00000384 /* Unicast address word 1 */ - -#define XTE_MAW0_OFFSET 0x00000388 /* Multicast addr word 0 */ -#define XTE_MAW1_OFFSET 0x0000038C /* Multicast addr word 1 */ -#define XTE_AFM_OFFSET 0x00000390 /* Promiscuous mode */ -#define XTE_AFM_EPPRM_MASK (1 << 31) /* Promiscuous mode enable */ - -/* 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) - -#define XTE_TIE_OFFSET 0x000003A4 /* Interrupt 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) -/* 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 MULTICAST_CAM_TABLE_NUM 4 - -/* TEMAC Synthesis features */ -#define TEMAC_FEATURE_RX_CSUM (1 << 0) -#define TEMAC_FEATURE_TX_CSUM (1 << 1) - -/* TX/RX CURDESC_PTR points to first descriptor */ -/* TX/RX TAILDESC_PTR points to last descriptor in linked list */ - -/** - * struct cdmac_bd - LocalLink buffer descriptor format - * - * 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 cdmac_bd { - u32 next; /* Physical address of next buffer descriptor */ - u32 phys; - u32 len; - u32 app0; - u32 app1; /* TX start << 16 | insert */ - u32 app2; /* TX csum */ - u32 app3; - u32 app4; /* skb for TX length for RX */ -}; - -struct temac_local { - struct net_device *ndev; - struct device *dev; - - /* Connection to PHY device */ - struct phy_device *phy_dev; /* Pointer to PHY device */ - struct device_node *phy_node; - - /* MDIO bus data */ - struct mii_bus *mii_bus; /* MII bus reference */ - int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */ - - /* IO registers, dma functions and IRQs */ - void __iomem *regs; - void __iomem *sdma_regs; -#ifdef CONFIG_PPC_DCR - dcr_host_t sdma_dcrs; -#endif - u32 (*dma_in)(struct temac_local *, int); - void (*dma_out)(struct temac_local *, int, u32); - - int tx_irq; - int rx_irq; - int emac_num; - - struct sk_buff **rx_skb; - spinlock_t rx_lock; - struct mutex indirect_mutex; - u32 options; /* Current options word */ - int last_link; - unsigned int temac_features; - - /* Buffer descriptors */ - struct cdmac_bd *tx_bd_v; - dma_addr_t tx_bd_p; - struct cdmac_bd *rx_bd_v; - dma_addr_t rx_bd_p; - int tx_bd_ci; - int tx_bd_next; - int tx_bd_tail; - int rx_bd_ci; -}; - -/* xilinx_temac.c */ -u32 temac_ior(struct temac_local *lp, int offset); -void temac_iow(struct temac_local *lp, int offset, u32 value); -int temac_indirect_busywait(struct temac_local *lp); -u32 temac_indirect_in32(struct temac_local *lp, int reg); -void temac_indirect_out32(struct temac_local *lp, int reg, u32 value); - - -/* xilinx_temac_mdio.c */ -int temac_mdio_setup(struct temac_local *lp, struct device_node *np); -void temac_mdio_teardown(struct temac_local *lp); - -#endif /* XILINX_LL_TEMAC_H */ diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c deleted file mode 100644 index 728fe414147a..000000000000 --- a/drivers/net/ll_temac_main.c +++ /dev/null @@ -1,1154 +0,0 @@ -/* - * Driver for Xilinx TEMAC Ethernet device - * - * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi - * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. - * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. - * - * This is a driver for the Xilinx ll_temac ipcore which is often used - * in the Virtex and Spartan series of chips. - * - * Notes: - * - The ll_temac hardware uses indirect access for many of the TEMAC - * registers, include the MDIO bus. However, indirect access to MDIO - * registers take considerably more clock cycles than to TEMAC registers. - * MDIO accesses are long, so threads doing them should probably sleep - * rather than busywait. However, since only one indirect access can be - * in progress at any given time, that means that *all* indirect accesses - * could end up sleeping (to wait for an MDIO access to complete). - * Fortunately none of the indirect accesses are on the 'hot' path for tx - * or rx, so this should be okay. - * - * TODO: - * - Factor out locallink DMA code into separate driver - * - Fix multicast assignment. - * - Fix support for hardware checksumming. - * - Testing. Lots and lots of testing. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* needed for sizeof(tcphdr) */ -#include /* needed for sizeof(udphdr) */ -#include -#include -#include -#include -#include -#include -#include - -#include "ll_temac.h" - -#define TX_BD_NUM 64 -#define RX_BD_NUM 128 - -/* --------------------------------------------------------------------- - * Low level register access functions - */ - -u32 temac_ior(struct temac_local *lp, int offset) -{ - return in_be32((u32 *)(lp->regs + offset)); -} - -void temac_iow(struct temac_local *lp, int offset, u32 value) -{ - out_be32((u32 *) (lp->regs + offset), value); -} - -int temac_indirect_busywait(struct temac_local *lp) -{ - long end = jiffies + 2; - - while (!(temac_ior(lp, XTE_RDY0_OFFSET) & XTE_RDY0_HARD_ACS_RDY_MASK)) { - if (end - jiffies <= 0) { - WARN_ON(1); - return -ETIMEDOUT; - } - msleep(1); - } - return 0; -} - -/** - * temac_indirect_in32 - * - * lp->indirect_mutex must be held when calling this function - */ -u32 temac_indirect_in32(struct temac_local *lp, int reg) -{ - u32 val; - - if (temac_indirect_busywait(lp)) - return -ETIMEDOUT; - temac_iow(lp, XTE_CTL0_OFFSET, reg); - if (temac_indirect_busywait(lp)) - return -ETIMEDOUT; - val = temac_ior(lp, XTE_LSW0_OFFSET); - - return val; -} - -/** - * temac_indirect_out32 - * - * lp->indirect_mutex must be held when calling this function - */ -void temac_indirect_out32(struct temac_local *lp, int reg, u32 value) -{ - if (temac_indirect_busywait(lp)) - return; - temac_iow(lp, XTE_LSW0_OFFSET, value); - temac_iow(lp, XTE_CTL0_OFFSET, CNTLREG_WRITE_ENABLE_MASK | reg); -} - -/** - * temac_dma_in32 - Memory mapped DMA read, this function expects a - * register input that is based on DCR word addresses which - * are then converted to memory mapped byte addresses - */ -static u32 temac_dma_in32(struct temac_local *lp, int reg) -{ - return in_be32((u32 *)(lp->sdma_regs + (reg << 2))); -} - -/** - * temac_dma_out32 - Memory mapped DMA read, this function expects a - * register input that is based on DCR word addresses which - * are then converted to memory mapped byte addresses - */ -static void temac_dma_out32(struct temac_local *lp, int reg, u32 value) -{ - out_be32((u32 *)(lp->sdma_regs + (reg << 2)), value); -} - -/* DMA register access functions can be DCR based or memory mapped. - * The PowerPC 440 is DCR based, the PowerPC 405 and MicroBlaze are both - * memory mapped. - */ -#ifdef CONFIG_PPC_DCR - -/** - * temac_dma_dcr_in32 - DCR based DMA read - */ -static u32 temac_dma_dcr_in(struct temac_local *lp, int reg) -{ - return dcr_read(lp->sdma_dcrs, reg); -} - -/** - * temac_dma_dcr_out32 - DCR based DMA write - */ -static void temac_dma_dcr_out(struct temac_local *lp, int reg, u32 value) -{ - dcr_write(lp->sdma_dcrs, reg, value); -} - -/** - * temac_dcr_setup - If the DMA is DCR based, then setup the address and - * I/O functions - */ -static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, - struct device_node *np) -{ - unsigned int dcrs; - - /* setup the dcr address mapping if it's in the device tree */ - - dcrs = dcr_resource_start(np, 0); - if (dcrs != 0) { - lp->sdma_dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); - lp->dma_in = temac_dma_dcr_in; - lp->dma_out = temac_dma_dcr_out; - dev_dbg(&op->dev, "DCR base: %x\n", dcrs); - return 0; - } - /* no DCR in the device tree, indicate a failure */ - return -1; -} - -#else - -/* - * temac_dcr_setup - This is a stub for when DCR is not supported, - * such as with MicroBlaze - */ -static int temac_dcr_setup(struct temac_local *lp, struct platform_device *op, - struct device_node *np) -{ - return -1; -} - -#endif - -/** - * * temac_dma_bd_release - Release buffer descriptor rings - */ -static void temac_dma_bd_release(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - int i; - - for (i = 0; i < RX_BD_NUM; i++) { - if (!lp->rx_skb[i]) - break; - else { - dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys, - XTE_MAX_JUMBO_FRAME_SIZE, DMA_FROM_DEVICE); - dev_kfree_skb(lp->rx_skb[i]); - } - } - if (lp->rx_bd_v) - dma_free_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - lp->rx_bd_v, lp->rx_bd_p); - if (lp->tx_bd_v) - dma_free_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - lp->tx_bd_v, lp->tx_bd_p); - if (lp->rx_skb) - kfree(lp->rx_skb); -} - -/** - * temac_dma_bd_init - Setup buffer descriptor rings - */ -static int temac_dma_bd_init(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - struct sk_buff *skb; - int i; - - lp->rx_skb = kzalloc(sizeof(*lp->rx_skb) * RX_BD_NUM, GFP_KERNEL); - if (!lp->rx_skb) { - dev_err(&ndev->dev, - "can't allocate memory for DMA RX buffer\n"); - goto out; - } - /* allocate the tx and rx ring buffer descriptors. */ - /* returns a virtual address and a physical address. */ - lp->tx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->tx_bd_v) * TX_BD_NUM, - &lp->tx_bd_p, GFP_KERNEL); - if (!lp->tx_bd_v) { - dev_err(&ndev->dev, - "unable to allocate DMA TX buffer descriptors"); - goto out; - } - lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, - sizeof(*lp->rx_bd_v) * RX_BD_NUM, - &lp->rx_bd_p, GFP_KERNEL); - if (!lp->rx_bd_v) { - dev_err(&ndev->dev, - "unable to allocate DMA RX buffer descriptors"); - goto out; - } - - memset(lp->tx_bd_v, 0, sizeof(*lp->tx_bd_v) * TX_BD_NUM); - for (i = 0; i < TX_BD_NUM; i++) { - lp->tx_bd_v[i].next = lp->tx_bd_p + - sizeof(*lp->tx_bd_v) * ((i + 1) % TX_BD_NUM); - } - - memset(lp->rx_bd_v, 0, sizeof(*lp->rx_bd_v) * RX_BD_NUM); - for (i = 0; i < RX_BD_NUM; i++) { - lp->rx_bd_v[i].next = lp->rx_bd_p + - sizeof(*lp->rx_bd_v) * ((i + 1) % RX_BD_NUM); - - skb = netdev_alloc_skb_ip_align(ndev, - XTE_MAX_JUMBO_FRAME_SIZE); - - if (skb == 0) { - dev_err(&ndev->dev, "alloc_skb error %d\n", i); - goto out; - } - lp->rx_skb[i] = skb; - /* returns physical address of skb->data */ - lp->rx_bd_v[i].phys = dma_map_single(ndev->dev.parent, - skb->data, - XTE_MAX_JUMBO_FRAME_SIZE, - DMA_FROM_DEVICE); - lp->rx_bd_v[i].len = XTE_MAX_JUMBO_FRAME_SIZE; - lp->rx_bd_v[i].app0 = STS_CTRL_APP0_IRQONEND; - } - - lp->dma_out(lp, TX_CHNL_CTRL, 0x10220400 | - CHNL_CTRL_IRQ_EN | - CHNL_CTRL_IRQ_DLY_EN | - CHNL_CTRL_IRQ_COAL_EN); - /* 0x10220483 */ - /* 0x00100483 */ - lp->dma_out(lp, RX_CHNL_CTRL, 0xff070000 | - CHNL_CTRL_IRQ_EN | - CHNL_CTRL_IRQ_DLY_EN | - CHNL_CTRL_IRQ_COAL_EN | - CHNL_CTRL_IRQ_IOE); - /* 0xff010283 */ - - lp->dma_out(lp, RX_CURDESC_PTR, lp->rx_bd_p); - lp->dma_out(lp, RX_TAILDESC_PTR, - lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); - lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); - - return 0; - -out: - temac_dma_bd_release(ndev); - return -ENOMEM; -} - -/* --------------------------------------------------------------------- - * net_device_ops - */ - -static int temac_set_mac_address(struct net_device *ndev, void *address) -{ - struct temac_local *lp = netdev_priv(ndev); - - 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 */ - mutex_lock(&lp->indirect_mutex); - temac_indirect_out32(lp, 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 */ - temac_indirect_out32(lp, XTE_UAW1_OFFSET, - (ndev->dev_addr[4] & 0x000000ff) | - (ndev->dev_addr[5] << 8)); - mutex_unlock(&lp->indirect_mutex); - - return 0; -} - -static int netdev_set_mac_address(struct net_device *ndev, void *p) -{ - struct sockaddr *addr = p; - - return temac_set_mac_address(ndev, addr->sa_data); -} - -static void temac_set_multicast_list(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - u32 multi_addr_msw, multi_addr_lsw, val; - int i; - - mutex_lock(&lp->indirect_mutex); - if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) || - netdev_mc_count(ndev) > 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; - temac_indirect_out32(lp, XTE_AFM_OFFSET, XTE_AFM_EPPRM_MASK); - dev_info(&ndev->dev, "Promiscuous mode enabled.\n"); - } else if (!netdev_mc_empty(ndev)) { - struct netdev_hw_addr *ha; - - i = 0; - netdev_for_each_mc_addr(ha, ndev) { - if (i >= MULTICAST_CAM_TABLE_NUM) - break; - multi_addr_msw = ((ha->addr[3] << 24) | - (ha->addr[2] << 16) | - (ha->addr[1] << 8) | - (ha->addr[0])); - temac_indirect_out32(lp, XTE_MAW0_OFFSET, - multi_addr_msw); - multi_addr_lsw = ((ha->addr[5] << 8) | - (ha->addr[4]) | (i << 16)); - temac_indirect_out32(lp, XTE_MAW1_OFFSET, - multi_addr_lsw); - i++; - } - } else { - val = temac_indirect_in32(lp, XTE_AFM_OFFSET); - temac_indirect_out32(lp, XTE_AFM_OFFSET, - val & ~XTE_AFM_EPPRM_MASK); - temac_indirect_out32(lp, XTE_MAW0_OFFSET, 0); - temac_indirect_out32(lp, XTE_MAW1_OFFSET, 0); - dev_info(&ndev->dev, "Promiscuous mode disabled.\n"); - } - mutex_unlock(&lp->indirect_mutex); -} - -struct temac_option { - int flg; - u32 opt; - u32 reg; - u32 m_or; - u32 m_and; -} temac_options[] = { - /* Turn on jumbo packet support for both Rx and Tx */ - { - .opt = XTE_OPTION_JUMBO, - .reg = XTE_TXC_OFFSET, - .m_or = XTE_TXC_TXJMBO_MASK, - }, - { - .opt = XTE_OPTION_JUMBO, - .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXJMBO_MASK, - }, - /* Turn on VLAN packet support for both Rx and Tx */ - { - .opt = XTE_OPTION_VLAN, - .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXVLAN_MASK, - }, - { - .opt = XTE_OPTION_VLAN, - .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXVLAN_MASK, - }, - /* Turn on FCS stripping on receive packets */ - { - .opt = XTE_OPTION_FCS_STRIP, - .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXFCS_MASK, - }, - /* Turn on FCS insertion on transmit packets */ - { - .opt = XTE_OPTION_FCS_INSERT, - .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXFCS_MASK, - }, - /* Turn on length/type field checking on receive packets */ - { - .opt = XTE_OPTION_LENTYPE_ERR, - .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXLT_MASK, - }, - /* Turn on flow control */ - { - .opt = XTE_OPTION_FLOW_CONTROL, - .reg = XTE_FCC_OFFSET, - .m_or =XTE_FCC_RXFLO_MASK, - }, - /* Turn on flow control */ - { - .opt = XTE_OPTION_FLOW_CONTROL, - .reg = XTE_FCC_OFFSET, - .m_or =XTE_FCC_TXFLO_MASK, - }, - /* Turn on promiscuous frame filtering (all frames are received ) */ - { - .opt = XTE_OPTION_PROMISC, - .reg = XTE_AFM_OFFSET, - .m_or =XTE_AFM_EPPRM_MASK, - }, - /* Enable transmitter if not already enabled */ - { - .opt = XTE_OPTION_TXEN, - .reg = XTE_TXC_OFFSET, - .m_or =XTE_TXC_TXEN_MASK, - }, - /* Enable receiver? */ - { - .opt = XTE_OPTION_RXEN, - .reg = XTE_RXC1_OFFSET, - .m_or =XTE_RXC1_RXEN_MASK, - }, - {} -}; - -/** - * temac_setoptions - */ -static u32 temac_setoptions(struct net_device *ndev, u32 options) -{ - struct temac_local *lp = netdev_priv(ndev); - struct temac_option *tp = &temac_options[0]; - int reg; - - mutex_lock(&lp->indirect_mutex); - while (tp->opt) { - reg = temac_indirect_in32(lp, tp->reg) & ~tp->m_or; - if (options & tp->opt) - reg |= tp->m_or; - temac_indirect_out32(lp, tp->reg, reg); - tp++; - } - lp->options |= options; - mutex_unlock(&lp->indirect_mutex); - - return 0; -} - -/* Initialize temac */ -static void temac_device_reset(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - u32 timeout; - u32 val; - - /* Perform a software reset */ - - /* 0x300 host enable bit ? */ - /* reset PHY through control register ?:1 */ - - dev_dbg(&ndev->dev, "%s()\n", __func__); - - mutex_lock(&lp->indirect_mutex); - /* Reset the receiver and wait for it to finish reset */ - temac_indirect_out32(lp, XTE_RXC1_OFFSET, XTE_RXC1_RXRST_MASK); - timeout = 1000; - while (temac_indirect_in32(lp, XTE_RXC1_OFFSET) & XTE_RXC1_RXRST_MASK) { - udelay(1); - if (--timeout == 0) { - dev_err(&ndev->dev, - "temac_device_reset RX reset timeout!!\n"); - break; - } - } - - /* Reset the transmitter and wait for it to finish reset */ - temac_indirect_out32(lp, XTE_TXC_OFFSET, XTE_TXC_TXRST_MASK); - timeout = 1000; - while (temac_indirect_in32(lp, 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 */ - val = temac_indirect_in32(lp, XTE_RXC1_OFFSET); - temac_indirect_out32(lp, XTE_RXC1_OFFSET, val & ~XTE_RXC1_RXEN_MASK); - - /* Reset Local Link (DMA) */ - lp->dma_out(lp, DMA_CONTROL_REG, DMA_CONTROL_RST); - timeout = 1000; - while (lp->dma_in(lp, DMA_CONTROL_REG) & DMA_CONTROL_RST) { - udelay(1); - if (--timeout == 0) { - dev_err(&ndev->dev, - "temac_device_reset DMA reset timeout!!\n"); - break; - } - } - lp->dma_out(lp, DMA_CONTROL_REG, DMA_TAIL_ENABLE); - - if (temac_dma_bd_init(ndev)) { - dev_err(&ndev->dev, - "temac_device_reset descriptor allocation failed\n"); - } - - temac_indirect_out32(lp, XTE_RXC0_OFFSET, 0); - temac_indirect_out32(lp, XTE_RXC1_OFFSET, 0); - temac_indirect_out32(lp, XTE_TXC_OFFSET, 0); - temac_indirect_out32(lp, XTE_FCC_OFFSET, XTE_FCC_RXFLO_MASK); - - mutex_unlock(&lp->indirect_mutex); - - /* Sync default options with HW - * but leave receiver and transmitter disabled. */ - temac_setoptions(ndev, - lp->options & ~(XTE_OPTION_TXEN | XTE_OPTION_RXEN)); - - temac_set_mac_address(ndev, NULL); - - /* 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 = jiffies; /* prevent tx timeout */ -} - -void temac_adjust_link(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - struct phy_device *phy = lp->phy_dev; - u32 mii_speed; - int link_state; - - /* hash together the state values to decide if something has changed */ - link_state = phy->speed | (phy->duplex << 1) | phy->link; - - mutex_lock(&lp->indirect_mutex); - if (lp->last_link != link_state) { - mii_speed = temac_indirect_in32(lp, XTE_EMCFG_OFFSET); - mii_speed &= ~XTE_EMCFG_LINKSPD_MASK; - - switch (phy->speed) { - case SPEED_1000: mii_speed |= XTE_EMCFG_LINKSPD_1000; break; - case SPEED_100: mii_speed |= XTE_EMCFG_LINKSPD_100; break; - case SPEED_10: mii_speed |= XTE_EMCFG_LINKSPD_10; break; - } - - /* Write new speed setting out to TEMAC */ - temac_indirect_out32(lp, XTE_EMCFG_OFFSET, mii_speed); - lp->last_link = link_state; - phy_print_status(phy); - } - mutex_unlock(&lp->indirect_mutex); -} - -static void temac_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) { - dma_unmap_single(ndev->dev.parent, cur_p->phys, cur_p->len, - DMA_TO_DEVICE); - if (cur_p->app4) - dev_kfree_skb_irq((struct sk_buff *)cur_p->app4); - cur_p->app0 = 0; - cur_p->app1 = 0; - cur_p->app2 = 0; - cur_p->app3 = 0; - cur_p->app4 = 0; - - ndev->stats.tx_packets++; - ndev->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 inline int temac_check_tx_bd_space(struct temac_local *lp, int num_frag) -{ - struct cdmac_bd *cur_p; - int tail; - - tail = lp->tx_bd_tail; - cur_p = &lp->tx_bd_v[tail]; - - do { - if (cur_p->app0) - return NETDEV_TX_BUSY; - - tail++; - if (tail >= TX_BD_NUM) - tail = 0; - - cur_p = &lp->tx_bd_v[tail]; - num_frag--; - } while (num_frag >= 0); - - return 0; -} - -static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - struct cdmac_bd *cur_p; - dma_addr_t 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 + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; - cur_p = &lp->tx_bd_v[lp->tx_bd_tail]; - - if (temac_check_tx_bd_space(lp, num_frag)) { - 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) { - unsigned int csum_start_off = skb_checksum_start_offset(skb); - unsigned int csum_index_off = csum_start_off + skb->csum_offset; - - cur_p->app0 |= 1; /* TX Checksum Enabled */ - cur_p->app1 = (csum_start_off << 16) | csum_index_off; - cur_p->app2 = 0; /* initial checksum seed */ - } - - cur_p->app0 |= STS_CTRL_APP0_SOP; - cur_p->len = skb_headlen(skb); - cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len, - DMA_TO_DEVICE); - 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 = dma_map_single(ndev->dev.parent, - (void *)page_address(frag->page) + - frag->page_offset, - frag->size, DMA_TO_DEVICE); - cur_p->len = frag->size; - cur_p->app0 = 0; - frag++; - } - cur_p->app0 |= STS_CTRL_APP0_EOP; - - tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; - lp->tx_bd_tail++; - if (lp->tx_bd_tail >= TX_BD_NUM) - lp->tx_bd_tail = 0; - - skb_tx_timestamp(skb); - - /* Kick off the transfer */ - lp->dma_out(lp, TX_TAILDESC_PTR, tail_p); /* DMA start */ - - return NETDEV_TX_OK; -} - - -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; - dma_addr_t tail_p; - int length; - unsigned long flags; - - spin_lock_irqsave(&lp->rx_lock, flags); - - tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * 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 & 0x3FFF; - - dma_unmap_single(ndev->dev.parent, cur_p->phys, length, - DMA_FROM_DEVICE); - - skb_put(skb, length); - skb->dev = ndev; - skb->protocol = eth_type_trans(skb, ndev); - skb_checksum_none_assert(skb); - - /* if we're doing rx csum offload, set it up */ - if (((lp->temac_features & TEMAC_FEATURE_RX_CSUM) != 0) && - (skb->protocol == __constant_htons(ETH_P_IP)) && - (skb->len > 64)) { - - skb->csum = cur_p->app3 & 0xFFFF; - skb->ip_summed = CHECKSUM_COMPLETE; - } - - if (!skb_defer_rx_timestamp(skb)) - netif_rx(skb); - - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += length; - - new_skb = netdev_alloc_skb_ip_align(ndev, - XTE_MAX_JUMBO_FRAME_SIZE); - - if (new_skb == 0) { - dev_err(&ndev->dev, "no memory for new sk_buff\n"); - spin_unlock_irqrestore(&lp->rx_lock, flags); - return; - } - - cur_p->app0 = STS_CTRL_APP0_IRQONEND; - cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data, - XTE_MAX_JUMBO_FRAME_SIZE, - DMA_FROM_DEVICE); - 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; - } - lp->dma_out(lp, RX_TAILDESC_PTR, tail_p); - - spin_unlock_irqrestore(&lp->rx_lock, flags); -} - -static irqreturn_t ll_temac_tx_irq(int irq, void *_ndev) -{ - struct net_device *ndev = _ndev; - struct temac_local *lp = netdev_priv(ndev); - unsigned int status; - - status = lp->dma_in(lp, TX_IRQ_REG); - lp->dma_out(lp, TX_IRQ_REG, status); - - if (status & (IRQ_COAL | IRQ_DLY)) - temac_start_xmit_done(lp->ndev); - if (status & 0x080) - dev_err(&ndev->dev, "DMA error 0x%x\n", status); - - return IRQ_HANDLED; -} - -static irqreturn_t ll_temac_rx_irq(int irq, void *_ndev) -{ - struct net_device *ndev = _ndev; - struct temac_local *lp = netdev_priv(ndev); - unsigned int status; - - /* Read and clear the status registers */ - status = lp->dma_in(lp, RX_IRQ_REG); - lp->dma_out(lp, RX_IRQ_REG, status); - - if (status & (IRQ_COAL | IRQ_DLY)) - ll_temac_recv(lp->ndev); - - return IRQ_HANDLED; -} - -static int temac_open(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - int rc; - - dev_dbg(&ndev->dev, "temac_open()\n"); - - if (lp->phy_node) { - lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, - temac_adjust_link, 0, 0); - if (!lp->phy_dev) { - dev_err(lp->dev, "of_phy_connect() failed\n"); - return -ENODEV; - } - - phy_start(lp->phy_dev); - } - - rc = request_irq(lp->tx_irq, ll_temac_tx_irq, 0, ndev->name, ndev); - if (rc) - goto err_tx_irq; - rc = request_irq(lp->rx_irq, ll_temac_rx_irq, 0, ndev->name, ndev); - if (rc) - goto err_rx_irq; - - temac_device_reset(ndev); - return 0; - - err_rx_irq: - free_irq(lp->tx_irq, ndev); - err_tx_irq: - if (lp->phy_dev) - phy_disconnect(lp->phy_dev); - lp->phy_dev = NULL; - dev_err(lp->dev, "request_irq() failed\n"); - return rc; -} - -static int temac_stop(struct net_device *ndev) -{ - struct temac_local *lp = netdev_priv(ndev); - - dev_dbg(&ndev->dev, "temac_close()\n"); - - free_irq(lp->tx_irq, ndev); - free_irq(lp->rx_irq, ndev); - - if (lp->phy_dev) - phy_disconnect(lp->phy_dev); - lp->phy_dev = NULL; - - temac_dma_bd_release(ndev); - - 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_irq(lp->tx_irq, ndev); - ll_temac_tx_irq(lp->rx_irq, ndev); - - enable_irq(lp->tx_irq); - enable_irq(lp->rx_irq); -} -#endif - -static const struct net_device_ops temac_netdev_ops = { - .ndo_open = temac_open, - .ndo_stop = temac_stop, - .ndo_start_xmit = temac_start_xmit, - .ndo_set_mac_address = netdev_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - //.ndo_set_multicast_list = temac_set_multicast_list, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = temac_poll_controller, -#endif -}; - -/* --------------------------------------------------------------------- - * SYSFS device attributes - */ -static ssize_t temac_show_llink_regs(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct net_device *ndev = dev_get_drvdata(dev); - struct temac_local *lp = netdev_priv(ndev); - int i, len = 0; - - for (i = 0; i < 0x11; i++) - len += sprintf(buf + len, "%.8x%s", lp->dma_in(lp, i), - (i % 8) == 7 ? "\n" : " "); - len += sprintf(buf + len, "\n"); - - return len; -} - -static DEVICE_ATTR(llink_regs, 0440, temac_show_llink_regs, NULL); - -static struct attribute *temac_device_attrs[] = { - &dev_attr_llink_regs.attr, - NULL, -}; - -static const struct attribute_group temac_attr_group = { - .attrs = temac_device_attrs, -}; - -static int __devinit temac_of_probe(struct platform_device *op) -{ - struct device_node *np; - struct temac_local *lp; - struct net_device *ndev; - const void *addr; - __be32 *p; - int size, rc = 0; - - /* Init network device structure */ - ndev = alloc_etherdev(sizeof(*lp)); - if (!ndev) { - dev_err(&op->dev, "could not allocate device.\n"); - return -ENOMEM; - } - ether_setup(ndev); - dev_set_drvdata(&op->dev, ndev); - SET_NETDEV_DEV(ndev, &op->dev); - ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ - ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; - ndev->netdev_ops = &temac_netdev_ops; -#if 0 - ndev->features |= NETIF_F_IP_CSUM; /* Can checksum TCP/UDP over IPv4. */ - ndev->features |= NETIF_F_HW_CSUM; /* Can checksum all the packets. */ - ndev->features |= NETIF_F_IPV6_CSUM; /* Can checksum IPV6 TCP/UDP */ - ndev->features |= NETIF_F_HIGHDMA; /* Can DMA to high memory. */ - ndev->features |= NETIF_F_HW_VLAN_TX; /* Transmit VLAN hw accel */ - ndev->features |= NETIF_F_HW_VLAN_RX; /* Receive VLAN hw acceleration */ - ndev->features |= NETIF_F_HW_VLAN_FILTER; /* Receive VLAN filtering */ - ndev->features |= NETIF_F_VLAN_CHALLENGED; /* cannot handle VLAN pkts */ - ndev->features |= NETIF_F_GSO; /* Enable software GSO. */ - ndev->features |= NETIF_F_MULTI_QUEUE; /* Has multiple TX/RX queues */ - ndev->features |= NETIF_F_LRO; /* large receive offload */ -#endif - - /* setup temac private info structure */ - lp = netdev_priv(ndev); - lp->ndev = ndev; - lp->dev = &op->dev; - lp->options = XTE_OPTION_DEFAULTS; - spin_lock_init(&lp->rx_lock); - mutex_init(&lp->indirect_mutex); - - /* map device registers */ - lp->regs = of_iomap(op->dev.of_node, 0); - if (!lp->regs) { - dev_err(&op->dev, "could not map temac regs.\n"); - goto nodev; - } - - /* Setup checksum offload, but default to off if not specified */ - lp->temac_features = 0; - p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL); - if (p && be32_to_cpu(*p)) { - lp->temac_features |= TEMAC_FEATURE_TX_CSUM; - /* Can checksum TCP/UDP over IPv4. */ - ndev->features |= NETIF_F_IP_CSUM; - } - p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL); - if (p && be32_to_cpu(*p)) - lp->temac_features |= TEMAC_FEATURE_RX_CSUM; - - /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ - np = of_parse_phandle(op->dev.of_node, "llink-connected", 0); - if (!np) { - dev_err(&op->dev, "could not find DMA node\n"); - goto err_iounmap; - } - - /* Setup the DMA register accesses, could be DCR or memory mapped */ - if (temac_dcr_setup(lp, op, np)) { - - /* no DCR in the device tree, try non-DCR */ - lp->sdma_regs = of_iomap(np, 0); - if (lp->sdma_regs) { - lp->dma_in = temac_dma_in32; - lp->dma_out = temac_dma_out32; - dev_dbg(&op->dev, "MEM base: %p\n", lp->sdma_regs); - } else { - dev_err(&op->dev, "unable to map DMA registers\n"); - of_node_put(np); - goto err_iounmap; - } - } - - lp->rx_irq = irq_of_parse_and_map(np, 0); - lp->tx_irq = irq_of_parse_and_map(np, 1); - - of_node_put(np); /* Finished with the DMA node; drop the reference */ - - if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { - dev_err(&op->dev, "could not determine irqs\n"); - rc = -ENOMEM; - goto err_iounmap_2; - } - - - /* Retrieve the MAC address */ - addr = of_get_property(op->dev.of_node, "local-mac-address", &size); - if ((!addr) || (size != 6)) { - dev_err(&op->dev, "could not find MAC address\n"); - rc = -ENODEV; - goto err_iounmap_2; - } - temac_set_mac_address(ndev, (void *)addr); - - rc = temac_mdio_setup(lp, op->dev.of_node); - if (rc) - dev_warn(&op->dev, "error registering MDIO bus\n"); - - lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); - if (lp->phy_node) - dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); - - /* Add the device attributes */ - rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); - if (rc) { - dev_err(lp->dev, "Error creating sysfs files\n"); - goto err_iounmap_2; - } - - rc = register_netdev(lp->ndev); - if (rc) { - dev_err(lp->dev, "register_netdev() error (%i)\n", rc); - goto err_register_ndev; - } - - return 0; - - err_register_ndev: - sysfs_remove_group(&lp->dev->kobj, &temac_attr_group); - err_iounmap_2: - if (lp->sdma_regs) - iounmap(lp->sdma_regs); - err_iounmap: - iounmap(lp->regs); - nodev: - free_netdev(ndev); - ndev = NULL; - return rc; -} - -static int __devexit temac_of_remove(struct platform_device *op) -{ - struct net_device *ndev = dev_get_drvdata(&op->dev); - struct temac_local *lp = netdev_priv(ndev); - - temac_mdio_teardown(lp); - unregister_netdev(ndev); - sysfs_remove_group(&lp->dev->kobj, &temac_attr_group); - if (lp->phy_node) - of_node_put(lp->phy_node); - lp->phy_node = NULL; - dev_set_drvdata(&op->dev, NULL); - iounmap(lp->regs); - if (lp->sdma_regs) - iounmap(lp->sdma_regs); - free_netdev(ndev); - return 0; -} - -static struct of_device_id temac_of_match[] __devinitdata = { - { .compatible = "xlnx,xps-ll-temac-1.01.b", }, - { .compatible = "xlnx,xps-ll-temac-2.00.a", }, - { .compatible = "xlnx,xps-ll-temac-2.02.a", }, - { .compatible = "xlnx,xps-ll-temac-2.03.a", }, - {}, -}; -MODULE_DEVICE_TABLE(of, temac_of_match); - -static struct platform_driver temac_of_driver = { - .probe = temac_of_probe, - .remove = __devexit_p(temac_of_remove), - .driver = { - .owner = THIS_MODULE, - .name = "xilinx_temac", - .of_match_table = temac_of_match, - }, -}; - -static int __init temac_init(void) -{ - return platform_driver_register(&temac_of_driver); -} -module_init(temac_init); - -static void __exit temac_exit(void) -{ - platform_driver_unregister(&temac_of_driver); -} -module_exit(temac_exit); - -MODULE_DESCRIPTION("Xilinx LL_TEMAC Ethernet driver"); -MODULE_AUTHOR("Yoshio Kashiwagi"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ll_temac_mdio.c b/drivers/net/ll_temac_mdio.c deleted file mode 100644 index 8cf9d4f56bb2..000000000000 --- a/drivers/net/ll_temac_mdio.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MDIO bus driver for the Xilinx TEMAC device - * - * Copyright (c) 2009 Secret Lab Technologies, Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ll_temac.h" - -/* --------------------------------------------------------------------- - * MDIO Bus functions - */ -static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) -{ - struct temac_local *lp = bus->priv; - u32 rc; - - /* Write the PHY address to the MIIM Access Initiator register. - * When the transfer completes, the PHY register value will appear - * in the LSW0 register */ - mutex_lock(&lp->indirect_mutex); - temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); - rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); - mutex_unlock(&lp->indirect_mutex); - - dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", - phy_id, reg, rc); - - return rc; -} - -static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) -{ - struct temac_local *lp = bus->priv; - - dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", - phy_id, reg, val); - - /* First write the desired value into the write data register - * and then write the address into the access initiator register - */ - mutex_lock(&lp->indirect_mutex); - temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); - temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); - mutex_unlock(&lp->indirect_mutex); - - return 0; -} - -int temac_mdio_setup(struct temac_local *lp, struct device_node *np) -{ - struct mii_bus *bus; - const u32 *bus_hz; - int clk_div; - int rc, size; - struct resource res; - - /* Calculate a reasonable divisor for the clock rate */ - clk_div = 0x3f; /* worst-case default setting */ - bus_hz = of_get_property(np, "clock-frequency", &size); - if (bus_hz && size >= sizeof(*bus_hz)) { - clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1; - if (clk_div < 1) - clk_div = 1; - if (clk_div > 0x3f) - clk_div = 0x3f; - } - - /* Enable the MDIO bus by asserting the enable bit and writing - * in the clock config */ - mutex_lock(&lp->indirect_mutex); - temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); - mutex_unlock(&lp->indirect_mutex); - - bus = mdiobus_alloc(); - if (!bus) - return -ENOMEM; - - of_address_to_resource(np, 0, &res); - snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", - (unsigned long long)res.start); - bus->priv = lp; - bus->name = "Xilinx TEMAC MDIO"; - bus->read = temac_mdio_read; - bus->write = temac_mdio_write; - bus->parent = lp->dev; - bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ - - lp->mii_bus = bus; - - rc = of_mdiobus_register(bus, np); - if (rc) - goto err_register; - - mutex_lock(&lp->indirect_mutex); - dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", - temac_indirect_in32(lp, XTE_MC_OFFSET)); - mutex_unlock(&lp->indirect_mutex); - return 0; - - err_register: - mdiobus_free(bus); - return rc; -} - -void temac_mdio_teardown(struct temac_local *lp) -{ - mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); - mdiobus_free(lp->mii_bus); - lp->mii_bus = NULL; -} - diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c deleted file mode 100644 index 8018d7d045b0..000000000000 --- a/drivers/net/xilinx_emaclite.c +++ /dev/null @@ -1,1330 +0,0 @@ -/* - * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. - * - * This is a new flat driver which is based on the original emac_lite - * driver from John Williams . - * - * 2007-2009 (c) Xilinx, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "xilinx_emaclite" - -/* Register offsets for the EmacLite Core */ -#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ -#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ -#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ -#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ -#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ -#define XEL_GIER_OFFSET 0x07F8 /* GIE Register */ -#define XEL_TSR_OFFSET 0x07FC /* Tx status */ -#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ - -#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ -#define XEL_RPLR_OFFSET 0x100C /* Rx packet length */ -#define XEL_RSR_OFFSET 0x17FC /* Rx status */ - -#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ - -/* MDIO Address Register Bit Masks */ -#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ -#define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ -#define XEL_MDIOADDR_PHYADR_SHIFT 5 -#define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ - -/* MDIO Write Data Register Bit Masks */ -#define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ - -/* MDIO Read Data Register Bit Masks */ -#define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ - -/* MDIO Control Register Bit Masks */ -#define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ -#define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ - -/* Global Interrupt Enable Register (GIER) Bit Masks */ -#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ - -/* Transmit Status Register (TSR) Bit Masks */ -#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ -#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ -#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ -#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit - * only. This is not documented - * in the HW spec */ - -/* Define for programming the MAC address into the EmacLite */ -#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) - -/* Receive Status Register (RSR) */ -#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ -#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ - -/* Transmit Packet Length Register (TPLR) */ -#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ - -/* Receive Packet Length Register (RPLR) */ -#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ - -#define XEL_HEADER_OFFSET 12 /* Offset to length field */ -#define XEL_HEADER_SHIFT 16 /* Shift value for length */ - -/* General Ethernet Definitions */ -#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ -#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ - - - -#define TX_TIMEOUT (60*HZ) /* Tx timeout is 60 seconds. */ -#define ALIGNMENT 4 - -/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ -#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT) - -/** - * struct net_local - Our private per device data - * @ndev: instance of the network device - * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW - * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW - * @next_tx_buf_to_use: next Tx buffer to write to - * @next_rx_buf_to_use: next Rx buffer to read from - * @base_addr: base address of the Emaclite device - * @reset_lock: lock used for synchronization - * @deferred_skb: holds an skb (for transmission at a later time) when the - * Tx buffer is not free - * @phy_dev: pointer to the PHY device - * @phy_node: pointer to the PHY device node - * @mii_bus: pointer to the MII bus - * @mdio_irqs: IRQs table for MDIO bus - * @last_link: last link status - * @has_mdio: indicates whether MDIO is included in the HW - */ -struct net_local { - - struct net_device *ndev; - - bool tx_ping_pong; - bool rx_ping_pong; - u32 next_tx_buf_to_use; - u32 next_rx_buf_to_use; - void __iomem *base_addr; - - spinlock_t reset_lock; - struct sk_buff *deferred_skb; - - struct phy_device *phy_dev; - struct device_node *phy_node; - - struct mii_bus *mii_bus; - int mdio_irqs[PHY_MAX_ADDR]; - - int last_link; - bool has_mdio; -}; - - -/*************************/ -/* EmacLite driver calls */ -/*************************/ - -/** - * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device - * @drvdata: Pointer to the Emaclite device private data - * - * This function enables the Tx and Rx interrupts for the Emaclite device along - * with the Global Interrupt Enable. - */ -static void xemaclite_enable_interrupts(struct net_local *drvdata) -{ - u32 reg_data; - - /* Enable the Tx interrupts for the first Buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); - - /* Enable the Tx interrupts for the second Buffer if - * configured in HW */ - if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + - XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); - } - - /* Enable the Rx interrupts for the first buffer */ - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); - - /* Enable the Rx interrupts for the second Buffer if - * configured in HW */ - if (drvdata->rx_ping_pong != 0) { - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); - } - - /* Enable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); -} - -/** - * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device - * @drvdata: Pointer to the Emaclite device private data - * - * This function disables the Tx and Rx interrupts for the Emaclite device, - * along with the Global Interrupt Enable. - */ -static void xemaclite_disable_interrupts(struct net_local *drvdata) -{ - u32 reg_data; - - /* Disable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); - - /* Disable the Tx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); - - /* Disable the Tx interrupts for the second Buffer - * if configured in HW */ - if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); - } - - /* Disable the Rx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); - - /* Disable the Rx interrupts for the second buffer - * if configured in HW */ - if (drvdata->rx_ping_pong != 0) { - - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); - } -} - -/** - * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address - * @src_ptr: Void pointer to the 16-bit aligned source address - * @dest_ptr: Pointer to the 32-bit aligned destination address - * @length: Number bytes to write from source to destination - * - * This function writes data from a 16-bit aligned buffer to a 32-bit aligned - * address in the EmacLite device. - */ -static void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr, - unsigned length) -{ - u32 align_buffer; - u32 *to_u32_ptr; - u16 *from_u16_ptr, *to_u16_ptr; - - to_u32_ptr = dest_ptr; - from_u16_ptr = src_ptr; - align_buffer = 0; - - for (; length > 3; length -= 4) { - to_u16_ptr = (u16 *)&align_buffer; - *to_u16_ptr++ = *from_u16_ptr++; - *to_u16_ptr++ = *from_u16_ptr++; - - /* Output a word */ - *to_u32_ptr++ = align_buffer; - } - if (length) { - u8 *from_u8_ptr, *to_u8_ptr; - - /* Set up to output the remaining data */ - align_buffer = 0; - to_u8_ptr = (u8 *) &align_buffer; - from_u8_ptr = (u8 *) from_u16_ptr; - - /* Output the remaining data */ - for (; length > 0; length--) - *to_u8_ptr++ = *from_u8_ptr++; - - *to_u32_ptr = align_buffer; - } -} - -/** - * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer - * @src_ptr: Pointer to the 32-bit aligned source address - * @dest_ptr: Pointer to the 16-bit aligned destination address - * @length: Number bytes to read from source to destination - * - * This function reads data from a 32-bit aligned address in the EmacLite device - * to a 16-bit aligned buffer. - */ -static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, - unsigned length) -{ - u16 *to_u16_ptr, *from_u16_ptr; - u32 *from_u32_ptr; - u32 align_buffer; - - from_u32_ptr = src_ptr; - to_u16_ptr = (u16 *) dest_ptr; - - for (; length > 3; length -= 4) { - /* Copy each word into the temporary buffer */ - align_buffer = *from_u32_ptr++; - from_u16_ptr = (u16 *)&align_buffer; - - /* Read data from source */ - *to_u16_ptr++ = *from_u16_ptr++; - *to_u16_ptr++ = *from_u16_ptr++; - } - - if (length) { - u8 *to_u8_ptr, *from_u8_ptr; - - /* Set up to read the remaining data */ - to_u8_ptr = (u8 *) to_u16_ptr; - align_buffer = *from_u32_ptr++; - from_u8_ptr = (u8 *) &align_buffer; - - /* Read the remaining data */ - for (; length > 0; length--) - *to_u8_ptr = *from_u8_ptr; - } -} - -/** - * xemaclite_send_data - Send an Ethernet frame - * @drvdata: Pointer to the Emaclite device private data - * @data: Pointer to the data to be sent - * @byte_count: Total frame size, including header - * - * This function checks if the Tx buffer of the Emaclite device is free to send - * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it - * returns an error. - * - * Return: 0 upon success or -1 if the buffer(s) are full. - * - * Note: The maximum Tx packet size can not be more than Ethernet header - * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. - */ -static int xemaclite_send_data(struct net_local *drvdata, u8 *data, - unsigned int byte_count) -{ - u32 reg_data; - void __iomem *addr; - - /* Determine the expected Tx buffer address */ - addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; - - /* If the length is too large, truncate it */ - if (byte_count > ETH_FRAME_LEN) - byte_count = ETH_FRAME_LEN; - - /* Check if the expected buffer is available */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); - if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | - XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { - - /* Switch to next buffer if configured */ - if (drvdata->tx_ping_pong != 0) - drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET; - } else if (drvdata->tx_ping_pong != 0) { - /* If the expected buffer is full, try the other buffer, - * if it is configured in HW */ - - addr = (void __iomem __force *)((u32 __force)addr ^ - XEL_BUFFER_OFFSET); - reg_data = in_be32(addr + XEL_TSR_OFFSET); - - if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | - XEL_TSR_XMIT_ACTIVE_MASK)) != 0) - return -1; /* Buffers were full, return failure */ - } else - return -1; /* Buffer was full, return failure */ - - /* Write the frame to the buffer */ - xemaclite_aligned_write(data, (u32 __force *) addr, byte_count); - - out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK)); - - /* Update the Tx Status Register to indicate that there is a - * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which - * is used by the interrupt handler to check whether a frame - * has been transmitted */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); - reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); - out_be32(addr + XEL_TSR_OFFSET, reg_data); - - return 0; -} - -/** - * xemaclite_recv_data - Receive a frame - * @drvdata: Pointer to the Emaclite device private data - * @data: Address where the data is to be received - * - * This function is intended to be called from the interrupt context or - * with a wrapper which waits for the receive frame to be available. - * - * Return: Total number of bytes received - */ -static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) -{ - void __iomem *addr; - u16 length, proto_type; - u32 reg_data; - - /* Determine the expected buffer address */ - addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); - - /* Verify which buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); - - if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { - if (drvdata->rx_ping_pong != 0) - drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET; - } else { - /* The instance is out of sync, try other buffer if other - * buffer is configured, return 0 otherwise. If the instance is - * out of sync, do not update the 'next_rx_buf_to_use' since it - * will correct on subsequent calls */ - if (drvdata->rx_ping_pong != 0) - addr = (void __iomem __force *)((u32 __force)addr ^ - XEL_BUFFER_OFFSET); - else - return 0; /* No data was available */ - - /* Verify that buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); - if ((reg_data & XEL_RSR_RECV_DONE_MASK) != - XEL_RSR_RECV_DONE_MASK) - return 0; /* No data was available */ - } - - /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + - XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & - XEL_RPLR_LENGTH_MASK); - - /* Check if received ethernet frame is a raw ethernet frame - * or an IP packet or an ARP packet */ - if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { - - if (proto_type == ETH_P_IP) { - length = ((ntohl(in_be32(addr + - XEL_HEADER_IP_LENGTH_OFFSET + - XEL_RXBUFF_OFFSET)) >> - XEL_HEADER_SHIFT) & - XEL_RPLR_LENGTH_MASK); - length += ETH_HLEN + ETH_FCS_LEN; - - } else if (proto_type == ETH_P_ARP) - length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN; - else - /* Field contains type other than IP or ARP, use max - * frame size and let user parse it */ - length = ETH_FRAME_LEN + ETH_FCS_LEN; - } else - /* Use the length in the frame, plus the header and trailer */ - length = proto_type + ETH_HLEN + ETH_FCS_LEN; - - /* Read from the EmacLite device */ - xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET), - data, length); - - /* Acknowledge the frame */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); - reg_data &= ~XEL_RSR_RECV_DONE_MASK; - out_be32(addr + XEL_RSR_OFFSET, reg_data); - - return length; -} - -/** - * xemaclite_update_address - Update the MAC address in the device - * @drvdata: Pointer to the Emaclite device private data - * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) - * - * Tx must be idle and Rx should be idle for deterministic results. - * It is recommended that this function should be called after the - * initialization and before transmission of any packets from the device. - * The MAC address can be programmed using any of the two transmit - * buffers (if configured). - */ -static void xemaclite_update_address(struct net_local *drvdata, - u8 *address_ptr) -{ - void __iomem *addr; - u32 reg_data; - - /* Determine the expected Tx buffer address */ - addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; - - xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN); - - out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN); - - /* Update the MAC address in the EmacLite */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); - out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR); - - /* Wait for EmacLite to finish with the MAC address update */ - while ((in_be32(addr + XEL_TSR_OFFSET) & - XEL_TSR_PROG_MAC_ADDR) != 0) - ; -} - -/** - * xemaclite_set_mac_address - Set the MAC address for this device - * @dev: Pointer to the network device instance - * @addr: Void pointer to the sockaddr structure - * - * This function copies the HW address from the sockaddr strucutre to the - * net_device structure and updates the address in HW. - * - * Return: Error if the net device is busy or 0 if the addr is set - * successfully - */ -static int xemaclite_set_mac_address(struct net_device *dev, void *address) -{ - struct net_local *lp = netdev_priv(dev); - struct sockaddr *addr = address; - - if (netif_running(dev)) - return -EBUSY; - - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - xemaclite_update_address(lp, dev->dev_addr); - return 0; -} - -/** - * xemaclite_tx_timeout - Callback for Tx Timeout - * @dev: Pointer to the network device - * - * This function is called when Tx time out occurs for Emaclite device. - */ -static void xemaclite_tx_timeout(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - unsigned long flags; - - dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n", - TX_TIMEOUT * 1000UL / HZ); - - dev->stats.tx_errors++; - - /* Reset the device */ - spin_lock_irqsave(&lp->reset_lock, flags); - - /* Shouldn't really be necessary, but shouldn't hurt */ - netif_stop_queue(dev); - - xemaclite_disable_interrupts(lp); - xemaclite_enable_interrupts(lp); - - if (lp->deferred_skb) { - dev_kfree_skb(lp->deferred_skb); - lp->deferred_skb = NULL; - dev->stats.tx_errors++; - } - - /* To exclude tx timeout */ - dev->trans_start = jiffies; /* prevent tx timeout */ - - /* We're all ready to go. Start the queue */ - netif_wake_queue(dev); - spin_unlock_irqrestore(&lp->reset_lock, flags); -} - -/**********************/ -/* Interrupt Handlers */ -/**********************/ - -/** - * xemaclite_tx_handler - Interrupt handler for frames sent - * @dev: Pointer to the network device - * - * This function updates the number of packets transmitted and handles the - * deferred skb, if there is one. - */ -static void xemaclite_tx_handler(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - - dev->stats.tx_packets++; - if (lp->deferred_skb) { - if (xemaclite_send_data(lp, - (u8 *) lp->deferred_skb->data, - lp->deferred_skb->len) != 0) - return; - else { - dev->stats.tx_bytes += lp->deferred_skb->len; - dev_kfree_skb_irq(lp->deferred_skb); - lp->deferred_skb = NULL; - dev->trans_start = jiffies; /* prevent tx timeout */ - netif_wake_queue(dev); - } - } -} - -/** - * xemaclite_rx_handler- Interrupt handler for frames received - * @dev: Pointer to the network device - * - * This function allocates memory for a socket buffer, fills it with data - * received and hands it over to the TCP/IP stack. - */ -static void xemaclite_rx_handler(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - struct sk_buff *skb; - unsigned int align; - u32 len; - - len = ETH_FRAME_LEN + ETH_FCS_LEN; - skb = dev_alloc_skb(len + ALIGNMENT); - if (!skb) { - /* Couldn't get memory. */ - dev->stats.rx_dropped++; - dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n"); - return; - } - - /* - * A new skb should have the data halfword aligned, but this code is - * here just in case that isn't true. Calculate how many - * bytes we should reserve to get the data to start on a word - * boundary */ - align = BUFFER_ALIGN(skb->data); - if (align) - skb_reserve(skb, align); - - skb_reserve(skb, 2); - - len = xemaclite_recv_data(lp, (u8 *) skb->data); - - if (!len) { - dev->stats.rx_errors++; - dev_kfree_skb_irq(skb); - return; - } - - skb_put(skb, len); /* Tell the skb how much data we got */ - - skb->protocol = eth_type_trans(skb, dev); - skb_checksum_none_assert(skb); - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - if (!skb_defer_rx_timestamp(skb)) - netif_rx(skb); /* Send the packet upstream */ -} - -/** - * xemaclite_interrupt - Interrupt handler for this driver - * @irq: Irq of the Emaclite device - * @dev_id: Void pointer to the network device instance used as callback - * reference - * - * This function handles the Tx and Rx interrupts of the EmacLite device. - */ -static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) -{ - bool tx_complete = 0; - struct net_device *dev = dev_id; - struct net_local *lp = netdev_priv(dev); - void __iomem *base_addr = lp->base_addr; - u32 tx_status; - - /* Check if there is Rx Data available */ - if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) || - (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) - & XEL_RSR_RECV_DONE_MASK)) - - xemaclite_rx_handler(dev); - - /* Check if the Transmission for the first buffer is completed */ - tx_status = in_be32(base_addr + XEL_TSR_OFFSET); - if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && - (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { - - tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_TSR_OFFSET, tx_status); - - tx_complete = 1; - } - - /* Check if the Transmission for the second buffer is completed */ - tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && - (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { - - tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, - tx_status); - - tx_complete = 1; - } - - /* If there was a Tx interrupt, call the Tx Handler */ - if (tx_complete != 0) - xemaclite_tx_handler(dev); - - return IRQ_HANDLED; -} - -/**********************/ -/* MDIO Bus functions */ -/**********************/ - -/** - * xemaclite_mdio_wait - Wait for the MDIO to be ready to use - * @lp: Pointer to the Emaclite device private data - * - * This function waits till the device is ready to accept a new MDIO - * request. - * - * Return: 0 for success or ETIMEDOUT for a timeout - */ - -static int xemaclite_mdio_wait(struct net_local *lp) -{ - long end = jiffies + 2; - - /* wait for the MDIO interface to not be busy or timeout - after some time. - */ - while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) & - XEL_MDIOCTRL_MDIOSTS_MASK) { - if (end - jiffies <= 0) { - WARN_ON(1); - return -ETIMEDOUT; - } - msleep(1); - } - return 0; -} - -/** - * xemaclite_mdio_read - Read from a given MII management register - * @bus: the mii_bus struct - * @phy_id: the phy address - * @reg: register number to read from - * - * This function waits till the device is ready to accept a new MDIO - * request and then writes the phy address to the MDIO Address register - * and reads data from MDIO Read Data register, when its available. - * - * Return: Value read from the MII management register - */ -static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) -{ - struct net_local *lp = bus->priv; - u32 ctrl_reg; - u32 rc; - - if (xemaclite_mdio_wait(lp)) - return -ETIMEDOUT; - - /* Write the PHY address, register number and set the OP bit in the - * MDIO Address register. Set the Status bit in the MDIO Control - * register to start a MDIO read transaction. - */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - XEL_MDIOADDR_OP_MASK | - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); - - if (xemaclite_mdio_wait(lp)) - return -ETIMEDOUT; - - rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET); - - dev_dbg(&lp->ndev->dev, - "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", - phy_id, reg, rc); - - return rc; -} - -/** - * xemaclite_mdio_write - Write to a given MII management register - * @bus: the mii_bus struct - * @phy_id: the phy address - * @reg: register number to write to - * @val: value to write to the register number specified by reg - * - * This function waits till the device is ready to accept a new MDIO - * request and then writes the val to the MDIO Write Data register. - */ -static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, - u16 val) -{ - struct net_local *lp = bus->priv; - u32 ctrl_reg; - - dev_dbg(&lp->ndev->dev, - "xemaclite_mdio_write(phy_id=%i, reg=%x, val=%x)\n", - phy_id, reg, val); - - if (xemaclite_mdio_wait(lp)) - return -ETIMEDOUT; - - /* Write the PHY address, register number and clear the OP bit in the - * MDIO Address register and then write the value into the MDIO Write - * Data register. Finally, set the Status bit in the MDIO Control - * register to start a MDIO write transaction. - */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - ~XEL_MDIOADDR_OP_MASK & - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); - - return 0; -} - -/** - * xemaclite_mdio_reset - Reset the mdio bus. - * @bus: Pointer to the MII bus - * - * This function is required(?) as per Documentation/networking/phy.txt. - * There is no reset in this device; this function always returns 0. - */ -static int xemaclite_mdio_reset(struct mii_bus *bus) -{ - return 0; -} - -/** - * xemaclite_mdio_setup - Register mii_bus for the Emaclite device - * @lp: Pointer to the Emaclite device private data - * @ofdev: Pointer to OF device structure - * - * This function enables MDIO bus in the Emaclite device and registers a - * mii_bus. - * - * Return: 0 upon success or a negative error upon failure - */ -static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) -{ - struct mii_bus *bus; - int rc; - struct resource res; - struct device_node *np = of_get_parent(lp->phy_node); - - /* Don't register the MDIO bus if the phy_node or its parent node - * can't be found. - */ - if (!np) - return -ENODEV; - - /* Enable the MDIO bus by asserting the enable bit in MDIO Control - * register. - */ - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - XEL_MDIOCTRL_MDIOEN_MASK); - - bus = mdiobus_alloc(); - if (!bus) - return -ENOMEM; - - of_address_to_resource(np, 0, &res); - snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", - (unsigned long long)res.start); - bus->priv = lp; - bus->name = "Xilinx Emaclite MDIO"; - bus->read = xemaclite_mdio_read; - bus->write = xemaclite_mdio_write; - bus->reset = xemaclite_mdio_reset; - bus->parent = dev; - bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ - - lp->mii_bus = bus; - - rc = of_mdiobus_register(bus, np); - if (rc) - goto err_register; - - return 0; - -err_register: - mdiobus_free(bus); - return rc; -} - -/** - * xemaclite_adjust_link - Link state callback for the Emaclite device - * @ndev: pointer to net_device struct - * - * There's nothing in the Emaclite device to be configured when the link - * state changes. We just print the status. - */ -void xemaclite_adjust_link(struct net_device *ndev) -{ - struct net_local *lp = netdev_priv(ndev); - struct phy_device *phy = lp->phy_dev; - int link_state; - - /* hash together the state values to decide if something has changed */ - link_state = phy->speed | (phy->duplex << 1) | phy->link; - - if (lp->last_link != link_state) { - lp->last_link = link_state; - phy_print_status(phy); - } -} - -/** - * xemaclite_open - Open the network device - * @dev: Pointer to the network device - * - * This function sets the MAC address, requests an IRQ and enables interrupts - * for the Emaclite device and starts the Tx queue. - * It also connects to the phy device, if MDIO is included in Emaclite device. - */ -static int xemaclite_open(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - int retval; - - /* Just to be safe, stop the device first */ - xemaclite_disable_interrupts(lp); - - if (lp->phy_node) { - u32 bmcr; - - lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, - xemaclite_adjust_link, 0, - PHY_INTERFACE_MODE_MII); - if (!lp->phy_dev) { - dev_err(&lp->ndev->dev, "of_phy_connect() failed\n"); - return -ENODEV; - } - - /* EmacLite doesn't support giga-bit speeds */ - lp->phy_dev->supported &= (PHY_BASIC_FEATURES); - lp->phy_dev->advertising = lp->phy_dev->supported; - - /* Don't advertise 1000BASE-T Full/Half duplex speeds */ - phy_write(lp->phy_dev, MII_CTRL1000, 0); - - /* Advertise only 10 and 100mbps full/half duplex speeds */ - phy_write(lp->phy_dev, MII_ADVERTISE, ADVERTISE_ALL); - - /* Restart auto negotiation */ - bmcr = phy_read(lp->phy_dev, MII_BMCR); - bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); - phy_write(lp->phy_dev, MII_BMCR, bmcr); - - phy_start(lp->phy_dev); - } - - /* Set the MAC address each time opened */ - xemaclite_update_address(lp, dev->dev_addr); - - /* Grab the IRQ */ - retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); - if (retval) { - dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", - dev->irq); - if (lp->phy_dev) - phy_disconnect(lp->phy_dev); - lp->phy_dev = NULL; - - return retval; - } - - /* Enable Interrupts */ - xemaclite_enable_interrupts(lp); - - /* We're ready to go */ - netif_start_queue(dev); - - return 0; -} - -/** - * xemaclite_close - Close the network device - * @dev: Pointer to the network device - * - * This function stops the Tx queue, disables interrupts and frees the IRQ for - * the Emaclite device. - * It also disconnects the phy device associated with the Emaclite device. - */ -static int xemaclite_close(struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - - netif_stop_queue(dev); - xemaclite_disable_interrupts(lp); - free_irq(dev->irq, dev); - - if (lp->phy_dev) - phy_disconnect(lp->phy_dev); - lp->phy_dev = NULL; - - return 0; -} - -/** - * xemaclite_send - Transmit a frame - * @orig_skb: Pointer to the socket buffer to be transmitted - * @dev: Pointer to the network device - * - * This function checks if the Tx buffer of the Emaclite device is free to send - * data. If so, it fills the Tx buffer with data from socket buffer data, - * updates the stats and frees the socket buffer. The Tx completion is signaled - * by an interrupt. If the Tx buffer isn't free, then the socket buffer is - * deferred and the Tx queue is stopped so that the deferred socket buffer can - * be transmitted when the Emaclite device is free to transmit data. - * - * Return: 0, always. - */ -static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) -{ - struct net_local *lp = netdev_priv(dev); - struct sk_buff *new_skb; - unsigned int len; - unsigned long flags; - - len = orig_skb->len; - - new_skb = orig_skb; - - spin_lock_irqsave(&lp->reset_lock, flags); - if (xemaclite_send_data(lp, (u8 *) new_skb->data, len) != 0) { - /* If the Emaclite Tx buffer is busy, stop the Tx queue and - * defer the skb for transmission during the ISR, after the - * current transmission is complete */ - netif_stop_queue(dev); - lp->deferred_skb = new_skb; - /* Take the time stamp now, since we can't do this in an ISR. */ - skb_tx_timestamp(new_skb); - spin_unlock_irqrestore(&lp->reset_lock, flags); - return 0; - } - spin_unlock_irqrestore(&lp->reset_lock, flags); - - skb_tx_timestamp(new_skb); - - dev->stats.tx_bytes += len; - dev_kfree_skb(new_skb); - - return 0; -} - -/** - * xemaclite_remove_ndev - Free the network device - * @ndev: Pointer to the network device to be freed - * - * This function un maps the IO region of the Emaclite device and frees the net - * device. - */ -static void xemaclite_remove_ndev(struct net_device *ndev) -{ - if (ndev) { - struct net_local *lp = netdev_priv(ndev); - - if (lp->base_addr) - iounmap((void __iomem __force *) (lp->base_addr)); - free_netdev(ndev); - } -} - -/** - * get_bool - Get a parameter from the OF device - * @ofdev: Pointer to OF device structure - * @s: Property to be retrieved - * - * This function looks for a property in the device node and returns the value - * of the property if its found or 0 if the property is not found. - * - * Return: Value of the parameter if the parameter is found, or 0 otherwise - */ -static bool get_bool(struct platform_device *ofdev, const char *s) -{ - u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL); - - if (p) { - return (bool)*p; - } else { - dev_warn(&ofdev->dev, "Parameter %s not found," - "defaulting to false\n", s); - return 0; - } -} - -static struct net_device_ops xemaclite_netdev_ops; - -/** - * xemaclite_of_probe - Probe method for the Emaclite device. - * @ofdev: Pointer to OF device structure - * @match: Pointer to the structure used for matching a device - * - * This function probes for the Emaclite device in the device tree. - * It initializes the driver data structure and the hardware, sets the MAC - * address and registers the network device. - * It also registers a mii_bus for the Emaclite device, if MDIO is included - * in the device. - * - * Return: 0, if the driver is bound to the Emaclite device, or - * a negative error if there is failure. - */ -static int __devinit xemaclite_of_probe(struct platform_device *ofdev) -{ - struct resource r_irq; /* Interrupt resources */ - struct resource r_mem; /* IO mem resources */ - struct net_device *ndev = NULL; - struct net_local *lp = NULL; - struct device *dev = &ofdev->dev; - const void *mac_address; - - int rc = 0; - - dev_info(dev, "Device Tree Probing\n"); - - /* Get iospace for the device */ - rc = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem); - if (rc) { - dev_err(dev, "invalid address\n"); - return rc; - } - - /* Get IRQ for the device */ - rc = of_irq_to_resource(ofdev->dev.of_node, 0, &r_irq); - if (rc == NO_IRQ) { - dev_err(dev, "no IRQ found\n"); - return rc; - } - - /* Create an ethernet device instance */ - ndev = alloc_etherdev(sizeof(struct net_local)); - if (!ndev) { - dev_err(dev, "Could not allocate network device\n"); - return -ENOMEM; - } - - dev_set_drvdata(dev, ndev); - SET_NETDEV_DEV(ndev, &ofdev->dev); - - ndev->irq = r_irq.start; - ndev->mem_start = r_mem.start; - ndev->mem_end = r_mem.end; - - lp = netdev_priv(ndev); - lp->ndev = ndev; - - if (!request_mem_region(ndev->mem_start, - ndev->mem_end - ndev->mem_start + 1, - DRIVER_NAME)) { - dev_err(dev, "Couldn't lock memory region at %p\n", - (void *)ndev->mem_start); - rc = -EBUSY; - goto error2; - } - - /* Get the virtual base address for the device */ - lp->base_addr = ioremap(r_mem.start, resource_size(&r_mem)); - if (NULL == lp->base_addr) { - dev_err(dev, "EmacLite: Could not allocate iomem\n"); - rc = -EIO; - goto error1; - } - - spin_lock_init(&lp->reset_lock); - lp->next_tx_buf_to_use = 0x0; - lp->next_rx_buf_to_use = 0x0; - lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong"); - lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong"); - mac_address = of_get_mac_address(ofdev->dev.of_node); - - if (mac_address) - /* Set the MAC address. */ - memcpy(ndev->dev_addr, mac_address, 6); - else - dev_warn(dev, "No MAC address found\n"); - - /* Clear the Tx CSR's in case this is a restart */ - out_be32(lp->base_addr + XEL_TSR_OFFSET, 0); - out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0); - - /* Set the MAC address in the EmacLite device */ - xemaclite_update_address(lp, ndev->dev_addr); - - lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); - rc = xemaclite_mdio_setup(lp, &ofdev->dev); - if (rc) - dev_warn(&ofdev->dev, "error registering MDIO bus\n"); - - dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr); - - ndev->netdev_ops = &xemaclite_netdev_ops; - ndev->flags &= ~IFF_MULTICAST; - ndev->watchdog_timeo = TX_TIMEOUT; - - /* Finally, register the device */ - rc = register_netdev(ndev); - if (rc) { - dev_err(dev, - "Cannot register network device, aborting\n"); - goto error1; - } - - dev_info(dev, - "Xilinx EmacLite at 0x%08X mapped to 0x%08X, irq=%d\n", - (unsigned int __force)ndev->mem_start, - (unsigned int __force)lp->base_addr, ndev->irq); - return 0; - -error1: - release_mem_region(ndev->mem_start, resource_size(&r_mem)); - -error2: - xemaclite_remove_ndev(ndev); - return rc; -} - -/** - * xemaclite_of_remove - Unbind the driver from the Emaclite device. - * @of_dev: Pointer to OF device structure - * - * This function is called if a device is physically removed from the system or - * if the driver module is being unloaded. It frees any resources allocated to - * the device. - * - * Return: 0, always. - */ -static int __devexit xemaclite_of_remove(struct platform_device *of_dev) -{ - struct device *dev = &of_dev->dev; - struct net_device *ndev = dev_get_drvdata(dev); - - struct net_local *lp = netdev_priv(ndev); - - /* Un-register the mii_bus, if configured */ - if (lp->has_mdio) { - mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); - mdiobus_free(lp->mii_bus); - lp->mii_bus = NULL; - } - - unregister_netdev(ndev); - - if (lp->phy_node) - of_node_put(lp->phy_node); - lp->phy_node = NULL; - - release_mem_region(ndev->mem_start, ndev->mem_end-ndev->mem_start + 1); - - xemaclite_remove_ndev(ndev); - dev_set_drvdata(dev, NULL); - - return 0; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void -xemaclite_poll_controller(struct net_device *ndev) -{ - disable_irq(ndev->irq); - xemaclite_interrupt(ndev->irq, ndev); - enable_irq(ndev->irq); -} -#endif - -static struct net_device_ops xemaclite_netdev_ops = { - .ndo_open = xemaclite_open, - .ndo_stop = xemaclite_close, - .ndo_start_xmit = xemaclite_send, - .ndo_set_mac_address = xemaclite_set_mac_address, - .ndo_tx_timeout = xemaclite_tx_timeout, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = xemaclite_poll_controller, -#endif -}; - -/* Match table for OF platform binding */ -static struct of_device_id xemaclite_of_match[] __devinitdata = { - { .compatible = "xlnx,opb-ethernetlite-1.01.a", }, - { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, - { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, - { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, - { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, - { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, - { /* end of list */ }, -}; -MODULE_DEVICE_TABLE(of, xemaclite_of_match); - -static struct platform_driver xemaclite_of_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = xemaclite_of_match, - }, - .probe = xemaclite_of_probe, - .remove = __devexit_p(xemaclite_of_remove), -}; - -/** - * xgpiopss_init - Initial driver registration call - * - * Return: 0 upon success, or a negative error upon failure. - */ -static int __init xemaclite_init(void) -{ - /* No kernel boot options used, we just need to register the driver */ - return platform_driver_register(&xemaclite_of_driver); -} - -/** - * xemaclite_cleanup - Driver un-registration call - */ -static void __exit xemaclite_cleanup(void) -{ - platform_driver_unregister(&xemaclite_of_driver); -} - -module_init(xemaclite_init); -module_exit(xemaclite_cleanup); - -MODULE_AUTHOR("Xilinx, Inc."); -MODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver"); -MODULE_LICENSE("GPL");