Ethernet base device driver for APM X-Gene SoC. Signed-off-by: Iyappan Subramanian <isubramanian@xxxxxxx> Signed-off-by: Ravi Patel <rapatel@xxxxxxx> Signed-off-by: Keyur Chudgar <kchudgar@xxxxxxx> --- MAINTAINERS | 7 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/apm/Kconfig | 1 + drivers/net/ethernet/apm/Makefile | 5 + drivers/net/ethernet/apm/xgene/Kconfig | 10 + drivers/net/ethernet/apm/xgene/Makefile | 10 + drivers/net/ethernet/apm/xgene/xgene_enet_common.c | 497 ++++++ drivers/net/ethernet/apm/xgene/xgene_enet_common.h | 450 ++++++ drivers/net/ethernet/apm/xgene/xgene_enet_csr.h | 162 ++ drivers/net/ethernet/apm/xgene/xgene_enet_mac.c | 520 +++++++ drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 1581 ++++++++++++++++++++ drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 172 +++ 13 files changed, 3417 insertions(+) create mode 100644 drivers/net/ethernet/apm/Kconfig create mode 100644 drivers/net/ethernet/apm/Makefile create mode 100644 drivers/net/ethernet/apm/xgene/Kconfig create mode 100644 drivers/net/ethernet/apm/xgene/Makefile create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_common.c create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_common.h create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_csr.h create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_mac.c create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.c create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.h diff --git a/MAINTAINERS b/MAINTAINERS index 920cae8..01340e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -672,6 +672,13 @@ S: Maintained F: drivers/misc/xgene/ F: include/misc/xgene/xgene_qmtm.h +APPLIEDMICRO (APM) X-GENE SOC ETHERNET DRIVER +M: Keyur Chudgar <kchudgar@xxxxxxx> +M: Iyappan Subramanian <isubramanian@xxxxxxx> +M: Ravi Patel <rapatel@xxxxxxx> +S: Maintained +F: drivers/net/ethernet/apm/ + APTINA CAMERA SENSOR PLL M: Laurent Pinchart <Laurent.pinchart@xxxxxxxxxxxxxxxx> L: linux-media@xxxxxxxxxxxxxxx diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 506b024..f0c4315 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -17,6 +17,7 @@ config MDIO config SUNGEM_PHY tristate +source "drivers/net/ethernet/apm/Kconfig" source "drivers/net/ethernet/3com/Kconfig" source "drivers/net/ethernet/adaptec/Kconfig" source "drivers/net/ethernet/aeroflex/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index c0b8789..bc0d4a5 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_GRETH) += aeroflex/ obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ obj-$(CONFIG_NET_VENDOR_AMD) += amd/ +obj-$(CONFIG_NET_XGENE) += apm/ obj-$(CONFIG_NET_VENDOR_APPLE) += apple/ obj-$(CONFIG_NET_VENDOR_ARC) += arc/ obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/ diff --git a/drivers/net/ethernet/apm/Kconfig b/drivers/net/ethernet/apm/Kconfig new file mode 100644 index 0000000..ec63d70 --- /dev/null +++ b/drivers/net/ethernet/apm/Kconfig @@ -0,0 +1 @@ +source "drivers/net/ethernet/apm/xgene/Kconfig" diff --git a/drivers/net/ethernet/apm/Makefile b/drivers/net/ethernet/apm/Makefile new file mode 100644 index 0000000..65ce32a --- /dev/null +++ b/drivers/net/ethernet/apm/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for APM X-GENE Ethernet driver. +# + +obj-$(CONFIG_NET_XGENE) += xgene/ diff --git a/drivers/net/ethernet/apm/xgene/Kconfig b/drivers/net/ethernet/apm/xgene/Kconfig new file mode 100644 index 0000000..c6ac5f9 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/Kconfig @@ -0,0 +1,10 @@ +config NET_XGENE + tristate "APM X-Gene Ethernet Driver" + depends on XGENE_QMTM + select PHYLIB + default y + help + This is the Ethernet driver for APM X-Gene SoC. + + To compile this driver as a module, choose M here. This module will + be called xgene_enet. diff --git a/drivers/net/ethernet/apm/xgene/Makefile b/drivers/net/ethernet/apm/xgene/Makefile new file mode 100644 index 0000000..16dfc6c --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for APM X-GENE Ethernet driver. +# + +xgene-enet-objs := \ + xgene_enet_common.o \ + xgene_enet_mac.o \ + xgene_enet_main.o + +obj-$(CONFIG_NET_XGENE) += xgene-enet.o diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_common.c b/drivers/net/ethernet/apm/xgene/xgene_enet_common.c new file mode 100644 index 0000000..279f3cd --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_common.c @@ -0,0 +1,497 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel <rapatel@xxxxxxx> + * Iyappan Subramanian <isubramanian@xxxxxxx> + * Fushen Chen <fchen@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "xgene_enet_main.h" +#include "xgene_enet_common.h" +#include "xgene_enet_csr.h" + +/* Indirect Address - read/write commands */ +#define PHY_ADDR_WR(src) (((u32)(src) < 8) & 0x00001f00) +#define REG_ADDR_WR(src) (((u32)(src)) & 0x0000001f) + +int xgene_enet_wr(struct xgene_enet_priv *priv, u8 block_id, + u32 reg_offset, u32 value) +{ + u32 cmd_done; + u32 indirect = 0; + int wait; + void *addr_reg_offst, *cmd_reg_offst, *wr_reg_offst; + void *cmd_done_reg_offst; + + switch (block_id) { + case BLOCK_ETH_CSR: + addr_reg_offst = priv->eth_csr_addr_v + reg_offset; + pr_debug("ETH CSR write\n"); + break; + + case BLOCK_ETH_MDIO_CSR: + addr_reg_offst = priv->vmii_base + reg_offset + + BLOCK_ETH_CSR_OFFSET; + pr_debug("BLOCK_ETH_MDIO_CSR write 0x%p\n", addr_reg_offst); + break; + + case BLOCK_ETH_CLE: + addr_reg_offst = priv->eth_cle_addr_v + reg_offset; + pr_debug("ETH CLE write\n"); + break; + + case BLOCK_ETH_QMI: + addr_reg_offst = priv->eth_qmi_addr_v + reg_offset; + pr_debug("ETH QMI write\n"); + break; + + case BLOCK_ETH_SDS_CSR: + addr_reg_offst = priv->eth_sds_csr_addr_v + reg_offset; + pr_debug("ETH SDS CSR write\n"); + break; + + case BLOCK_ETH_CLKRST_CSR: + addr_reg_offst = priv->eth_clkrst_csr_addr_v + reg_offset; + pr_debug("ETH CLKRST CSR write\n"); + break; + + case BLOCK_ETH_DIAG_CSR: + addr_reg_offst = priv->eth_diag_csr_addr_v + reg_offset; + pr_debug("ETH DIAG CSR write\n"); + break; + + case BLOCK_MCX_MAC: + case BLOCK_ETH_INTPHY: + addr_reg_offst = priv->mcx_mac_addr_v + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->mcx_mac_addr_v + MAC_COMMAND_REG_OFFSET; + wr_reg_offst = priv->mcx_mac_addr_v + MAC_WRITE_REG_OFFSET; + cmd_done_reg_offst = priv->mcx_mac_addr_v + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX MAC/Internal PHY write\n"); + break; + + case BLOCK_ETH_EXTPHY: + addr_reg_offst = priv->vmii_base + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->vmii_base + MAC_COMMAND_REG_OFFSET; + wr_reg_offst = priv->vmii_base + MAC_WRITE_REG_OFFSET; + cmd_done_reg_offst = priv->vmii_base + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX MAC/External PHY write\n"); + break; + + case BLOCK_MCX_STATS: + addr_reg_offst = priv->mcx_stats_addr_v + STAT_ADDR_REG_OFFSET; + cmd_reg_offst = + priv->mcx_stats_addr_v + STAT_COMMAND_REG_OFFSET; + wr_reg_offst = priv->mcx_stats_addr_v + STAT_WRITE_REG_OFFSET; + cmd_done_reg_offst = priv->mcx_stats_addr_v + + STAT_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX STATS write\n"); + break; + + case BLOCK_MCX_MAC_CSR: + addr_reg_offst = priv->mcx_mac_csr_addr_v + reg_offset; + pr_debug("MCX MAC CSR write\n"); + break; + + case BLOCK_SATA_ENET_CSR: + addr_reg_offst = priv->sata_enet_csr_addr_v + reg_offset; + pr_debug("SATA ENET CSR write\n"); + break; + + case BLOCK_AXG_MAC: + addr_reg_offst = priv->axg_mac_addr_v + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->axg_mac_addr_v + MAC_COMMAND_REG_OFFSET; + wr_reg_offst = priv->axg_mac_addr_v + MAC_WRITE_REG_OFFSET; + cmd_done_reg_offst = priv->axg_mac_addr_v + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("AXG MAC write\n"); + break; + + case BLOCK_AXG_STATS: + addr_reg_offst = priv->axg_stats_addr_v + STAT_ADDR_REG_OFFSET; + cmd_reg_offst = + priv->axg_stats_addr_v + STAT_COMMAND_REG_OFFSET; + wr_reg_offst = priv->axg_stats_addr_v + STAT_WRITE_REG_OFFSET; + cmd_done_reg_offst = priv->axg_stats_addr_v + + STAT_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("AXG STATS write\n"); + break; + + case BLOCK_AXG_MAC_CSR: + addr_reg_offst = priv->axg_mac_csr_addr_v + reg_offset; + pr_debug("AXG MAC CSR write\n"); + break; + + case BLOCK_XGENET_PCS: + addr_reg_offst = priv->xgenet_pcs_addr_v + reg_offset; + pr_debug("XGENET PCS write\n"); + break; + + case BLOCK_XGENET_MDIO_CSR: + addr_reg_offst = priv->xgenet_mdio_csr_addr_v + reg_offset; + pr_debug("XGENET MDIO CSR write\n"); + break; + + default: + pr_err("Invalid blockid in write reg: %d\n", block_id); + return -1; + } + + if (indirect) { + xgene_enet_wr32(addr_reg_offst, reg_offset); + xgene_enet_wr32(wr_reg_offst, value); + xgene_enet_wr32(cmd_reg_offst, XGENE_ENET_WR_CMD); + pr_debug("Indirect write: addr: 0x%X, value: 0x%X\n", + reg_offset, value); + + /* wait upto 5 us for completion */ + wait = 5; + do { + xgene_enet_rd32(cmd_done_reg_offst, &cmd_done); + usleep_range(1, 2); + } while (--wait && !cmd_done); + if (!wait) { + pr_err("Write failed for blk: %d\n", block_id); + BUG(); + } + + xgene_enet_wr32(cmd_reg_offst, 0); + } else { + xgene_enet_wr32(addr_reg_offst, value); + pr_debug("Direct write addr: 0x%p, value: 0x%X\n", + addr_reg_offst, value); + } + + return 0; +} + +int xgene_enet_rd(struct xgene_enet_priv *priv, u8 block_id, + u32 reg_offset, u32 *value) +{ + u32 cmd_done; + u32 indirect = 0; + int wait; + void *addr_reg_offst, *cmd_reg_offst, *rd_reg_offst; + void *cmd_done_reg_offst; + + switch (block_id) { + case BLOCK_ETH_CSR: + addr_reg_offst = priv->eth_csr_addr_v + reg_offset; + pr_debug("ETH CSR read\n"); + break; + + case BLOCK_ETH_MDIO_CSR: + addr_reg_offst = priv->vmii_base + reg_offset + + BLOCK_ETH_CSR_OFFSET; + pr_debug("BLOCK_ETH_MDIO_CSR read 0x%p\n", addr_reg_offst); + break; + + case BLOCK_ETH_CLE: + addr_reg_offst = priv->eth_cle_addr_v + reg_offset; + pr_debug("ETH CLE read\n"); + break; + + case BLOCK_ETH_QMI: + addr_reg_offst = priv->eth_qmi_addr_v + reg_offset; + pr_debug("ETH QMI read\n"); + break; + + case BLOCK_ETH_SDS_CSR: + addr_reg_offst = priv->eth_sds_csr_addr_v + reg_offset; + pr_debug("ETH SDS CSR read\n"); + break; + + case BLOCK_ETH_CLKRST_CSR: + addr_reg_offst = priv->eth_clkrst_csr_addr_v + reg_offset; + pr_debug("ETH CLKRST CSR read\n"); + break; + + case BLOCK_ETH_DIAG_CSR: + addr_reg_offst = priv->eth_diag_csr_addr_v + reg_offset; + pr_debug("ETH DIAG CSR read\n"); + break; + + case BLOCK_MCX_MAC: + case BLOCK_ETH_INTPHY: + addr_reg_offst = priv->mcx_mac_addr_v + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->mcx_mac_addr_v + MAC_COMMAND_REG_OFFSET; + rd_reg_offst = priv->mcx_mac_addr_v + MAC_READ_REG_OFFSET; + cmd_done_reg_offst = priv->mcx_mac_addr_v + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX MAC/Internal PHY read\n"); + break; + + case BLOCK_ETH_EXTPHY: + addr_reg_offst = priv->vmii_base + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->vmii_base + MAC_COMMAND_REG_OFFSET; + rd_reg_offst = priv->vmii_base + MAC_READ_REG_OFFSET; + cmd_done_reg_offst = priv->vmii_base + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX MAC/External PHY read\n"); + break; + + case BLOCK_MCX_STATS: + addr_reg_offst = priv->mcx_stats_addr_v + STAT_ADDR_REG_OFFSET; + cmd_reg_offst = + priv->mcx_stats_addr_v + STAT_COMMAND_REG_OFFSET; + rd_reg_offst = priv->mcx_stats_addr_v + STAT_READ_REG_OFFSET; + cmd_done_reg_offst = priv->mcx_stats_addr_v + + STAT_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("MCX STATS read\n"); + break; + + case BLOCK_MCX_MAC_CSR: + addr_reg_offst = priv->mcx_mac_csr_addr_v + reg_offset; + pr_debug("MCX MAC CSR read\n"); + break; + + case BLOCK_SATA_ENET_CSR: + addr_reg_offst = priv->sata_enet_csr_addr_v + reg_offset; + pr_debug("SATA ENET CSR read\n"); + break; + + case BLOCK_AXG_MAC: + addr_reg_offst = priv->axg_mac_addr_v + MAC_ADDR_REG_OFFSET; + cmd_reg_offst = priv->axg_mac_addr_v + MAC_COMMAND_REG_OFFSET; + rd_reg_offst = priv->axg_mac_addr_v + MAC_READ_REG_OFFSET; + cmd_done_reg_offst = priv->axg_mac_addr_v + + MAC_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("AXG MAC read\n"); + break; + + case BLOCK_AXG_STATS: + addr_reg_offst = priv->axg_stats_addr_v + STAT_ADDR_REG_OFFSET; + cmd_reg_offst = + priv->axg_stats_addr_v + STAT_COMMAND_REG_OFFSET; + rd_reg_offst = priv->axg_stats_addr_v + STAT_READ_REG_OFFSET; + cmd_done_reg_offst = priv->axg_stats_addr_v + + STAT_COMMAND_DONE_REG_OFFSET; + indirect = 1; + pr_debug("AXG STATS read\n"); + break; + + case BLOCK_AXG_MAC_CSR: + addr_reg_offst = priv->axg_mac_csr_addr_v + reg_offset; + pr_debug("AXG MAC CSR read\n"); + break; + + case BLOCK_XGENET_PCS: + addr_reg_offst = priv->xgenet_pcs_addr_v + reg_offset; + pr_debug("XGENET PCS read\n"); + break; + + case BLOCK_XGENET_MDIO_CSR: + addr_reg_offst = priv->xgenet_mdio_csr_addr_v + reg_offset; + pr_debug("XGENET MDIO CSR read\n"); + break; + + default: + pr_err("Invalid blockid in read reg: %d\n", block_id); + return -1; + } + + if (indirect) { + xgene_enet_wr32(addr_reg_offst, reg_offset); + xgene_enet_wr32(cmd_reg_offst, XGENE_ENET_RD_CMD); + pr_debug("Indirect read: addr: 0x%X\n", reg_offset); + + /* wait upto 5 us for completion */ + wait = 5; + do { + xgene_enet_rd32(cmd_done_reg_offst, &cmd_done); + } while (--wait && !cmd_done); + if (!wait) { + pr_err("Read failed for blk: %d\n", block_id); + BUG(); + } + + xgene_enet_rd32(rd_reg_offst, value); + pr_debug("Indirect read value: 0x%X\n", *value); + + xgene_enet_wr32(cmd_reg_offst, 0); + } else { + xgene_enet_rd32(addr_reg_offst, value); + pr_debug("Direct read addr: 0x%p, value: 0x%X\n", + addr_reg_offst, *value); + } + + return 0; +} + +void xgene_genericmiiphy_write(struct xgene_enet_priv *priv, u8 phy_id, + unsigned char reg, u32 data) +{ + u32 value; + int wait; + u32 blockid = BLOCK_ETH_EXTPHY; + + if (priv->port == MENET) + phy_id = priv->phy_addr; + + /* All PHYs lie on MII bus of Port0 MAC due to this + * each port should access its PHY through Port0 MAC. + * Hence we allow access to PHY_ID associated with this + * port only. + */ + + /* Write PHY number and address in MII Mgmt Address */ + value = PHY_ADDR_WR(phy_id) | REG_ADDR_WR(reg); + pr_debug("Write MII_MGMT_ADDRESS phy_id=0x%x, reg=0x%x, value=0x%x\n", + phy_id, reg << 2, value); + xgene_enet_wr(priv, blockid, MII_MGMT_ADDRESS_ADDR, value); + + /* Write 16 bit data to MII MGMT CONTROL */ + value = PHY_CONTROL_WR(data); + pr_debug("Write MII_MGMT_CONTROL phy_id=0x%x, reg=0x%x, value=0x%x\n", + phy_id, reg << 2, value); + xgene_enet_wr(priv, blockid, MII_MGMT_CONTROL_ADDR, value); + + /* wait upto 20 us for completion */ + wait = 20; + do { + xgene_enet_rd(priv, blockid, MII_MGMT_INDICATORS_ADDR, &value); + usleep_range(1, 2); + } while (--wait && (value & BUSY_MASK)); + if (!wait) + pr_err("MII_MGMT write failed\n"); +} + +void xgene_genericmiiphy_read(struct xgene_enet_priv *priv, u8 phy_id, + unsigned char reg, u32 *data) +{ + u32 value; + u32 blockid = BLOCK_ETH_EXTPHY; + int wait; + + if (priv->port == MENET) + phy_id = priv->phy_addr; + + /* All PHYs lie on MII bus of Port0 MAC due to this + * each port should access its PHY through Port0 MAC. + * Hence we allow access to PHY_ID associated with this + * port only. + */ + + /* Write PHY number and address in MII Mgmt Address */ + value = PHY_ADDR_WR(phy_id) | REG_ADDR_WR(reg); + pr_debug("Write MII_MGMT_ADDR phy_id=0x%x, reg=0x%x, value=0x%x\n", + phy_id, reg << 2, value); + xgene_enet_wr(priv, blockid, MII_MGMT_ADDRESS_ADDR, value); + + /* Write read command */ + xgene_enet_wr(priv, blockid, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK); + + /* wait upto 20 us for completion */ + wait = 20; + do { + xgene_enet_rd(priv, blockid, MII_MGMT_INDICATORS_ADDR, &value); + if (!(value & BUSY_MASK)) + break; + usleep_range(1, 2); + } while (--wait && (value & BUSY_MASK)); + + xgene_enet_rd(priv, blockid, MII_MGMT_STATUS_ADDR, data); + + /* reset mii_mgmt_command register */ + xgene_enet_wr(priv, blockid, MII_MGMT_COMMAND_ADDR, 0); +} + +inline void xgene_enet_port_reset(struct xgene_enet_priv *priv) +{ + if (priv->port_reset) + priv->port_reset(priv); +} + +inline void xgene_enet_mac_reset(struct xgene_enet_priv *priv) +{ + if (priv->mac_reset) + priv->mac_reset(priv); +} + +inline int xgene_enet_mac_init(struct xgene_enet_priv *priv, + unsigned char *dev_addr, int speed, int mtu, + int crc) +{ + int rc = 0; + if (priv->mac_init) + rc = priv->mac_init(priv, dev_addr, speed, mtu, crc); + return rc; +} + +inline void xgene_enet_mac_tx_state(struct xgene_enet_priv *priv, u32 enable) +{ + if (priv->mac_tx_state) + priv->mac_tx_state(priv, enable); +} + +inline void xgene_enet_mac_rx_state(struct xgene_enet_priv *priv, u32 enable) +{ + if (priv->mac_rx_state) + priv->mac_rx_state(priv, enable); +} + +inline void xgene_enet_mac_change_mtu(struct xgene_enet_priv *priv, u32 new_mtu) +{ + if (priv->mac_change_mtu) + priv->mac_change_mtu(priv, new_mtu); +} + +inline void xgene_enet_mac_set_ipg(struct xgene_enet_priv *priv, u16 ipg) +{ + if (priv->mac_set_ipg) + priv->mac_set_ipg(priv, ipg); +} + +inline void xgene_enet_get_stats(struct xgene_enet_priv *priv, + struct xgene_enet_detailed_stats *stats) +{ + if (priv->get_stats) + priv->get_stats(priv, stats); +} + +inline void xgene_enet_set_mac_addr(struct xgene_enet_priv *priv, + unsigned char *dev_addr) +{ + if (priv->set_mac_addr) + priv->set_mac_addr(priv, dev_addr); +} + +inline void xgene_enet_cle_bypass(struct xgene_enet_priv *priv, + u32 dstqid, u32 fpsel) +{ + if (priv->cle_bypass) + priv->cle_bypass(priv, dstqid, fpsel); +} + +inline void xgene_enet_tx_offload(struct xgene_enet_priv *priv, + u32 command, u32 value) +{ + if (priv->tx_offload) + priv->tx_offload(priv, command, value); +} + +inline void xgene_enet_port_shutdown(struct xgene_enet_priv *priv) +{ + if (priv->port_shutdown) + priv->port_shutdown(priv); +} diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_common.h b/drivers/net/ethernet/apm/xgene/xgene_enet_common.h new file mode 100644 index 0000000..4b0bfad --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_common.h @@ -0,0 +1,450 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel rapatel@xxxxxxx> + * Iyappan Subramanian isubramanian@xxxxxxx> + * Fushen Chen fchen@xxxxxxx> + * Keyur Chudgar kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __XGENE_ENET_COMMON_H__ +#define __XGENE_ENET_COMMON_H__ + +#include <misc/xgene/xgene_qmtm.h> +#define MAX_LOOP_POLL_CNT 10 + +#ifndef UDP_HDR_SIZE +#define UDP_HDR_SIZE 2 +#endif + +/* Ethernet & XGENET port ids */ +enum eth_port_ids { + ENET_0 = 0, + ENET_1, + ENET_2, + ENET_3, + XGENET_0, + XGENET_1, + XGENET_2, + XGENET_3, + MENET, + MAX_ENET_PORTS +}; + +/* TSO Parameters */ +#define TSO_ENABLE 1 +#define TSO_ENABLE_MASK 1 +#define TSO_CHKSUM_ENABLE 1 +#define TSO_INS_CRC_ENABLE 1 +#define TSO_IPPROTO_TCP 1 +#define TSO_IPPROTO_UDP 0 +#define TSO_IP_HLEN_MASK 0X3F +#define TSO_TCP_HLEN_MASK 0X3F +#define TSO_ETH_HLEN_MASK 0XFF +#define TSO_MSS_MASK 0X3 /* 2b */ +#define DEFAULT_TCP_MSS 1448 + +enum { + XGENE_ENET_MSS0 = 0, + XGENE_ENET_MSS1, + XGENE_ENET_MSS2, + XGENE_ENET_MSS3, + XGENE_ENET_TSO_CFG, + XGENE_ENET_INSERT_VLAN +}; + +/* TYPE_SEL for Ethernt egress message */ +#define TYPE_SEL_WORK_MSG 1U + +/* Blocks for defined regions */ +enum { + BLOCK_ETH_CSR = 1, + BLOCK_ETH_CLE, + BLOCK_ETH_QMI, + BLOCK_ETH_SDS_CSR, + BLOCK_ETH_CLKRST_CSR, + BLOCK_ETH_DIAG_CSR, + BLOCK_ETH_MDIO_CSR, + BLOCK_ETH_INTPHY, + BLOCK_ETH_EXTPHY, + BLOCK_MCX_MAC, + BLOCK_MCX_STATS, + BLOCK_MCX_MAC_CSR, + BLOCK_SATA_ENET_CSR, + BLOCK_AXG_MAC, + BLOCK_AXG_STATS, + BLOCK_AXG_MAC_CSR, + BLOCK_XGENET_PCS, + BLOCK_XGENET_MDIO_CSR, + BLOCK_ETH_MAX +}; + +/* Direct Address mode */ +#define BLOCK_ETH_CSR_OFFSET 0x2000 +#define BLOCK_ETH_CLE_OFFSET 0x6000 +#define BLOCK_ETH_QMI_OFFSET 0x9000 +#define BLOCK_ETH_SDS_CSR_OFFSET 0xA000 +#define BLOCK_ETH_CLKRST_CSR_OFFSET 0xC000 +#define BLOCK_ETH_DIAG_CSR_OFFSET 0xD000 + +/* Indirect & Direct Address mode for MCX_MAC and AXG_MAC */ +#define BLOCK_ETH_MAC_OFFSET 0x0000 +#define BLOCK_ETH_STATS_OFFSET 0x0014 +#define BLOCK_ETH_MAC_CSR_OFFSET 0x2800 + +#define BLOCK_SATA_ENET_CSR_OFFSET 0x7000 + +/* Constants for indirect registers */ +#define MAC_ADDR_REG_OFFSET 0 +#define MAC_COMMAND_REG_OFFSET 4 +#define MAC_WRITE_REG_OFFSET 8 +#define MAC_READ_REG_OFFSET 12 +#define MAC_COMMAND_DONE_REG_OFFSET 16 + +#define STAT_ADDR_REG_OFFSET 0 +#define STAT_COMMAND_REG_OFFSET 4 +#define STAT_WRITE_REG_OFFSET 8 +#define STAT_READ_REG_OFFSET 12 +#define STAT_COMMAND_DONE_REG_OFFSET 16 + +/* Address PE_MCXMAC Registers */ +#define MII_MGMT_COMMAND_ADDR 0x00000024 +#define MII_MGMT_ADDRESS_ADDR 0x00000028 +#define MII_MGMT_CONTROL_ADDR 0x0000002c +#define MII_MGMT_STATUS_ADDR 0x00000030 +#define MII_MGMT_INDICATORS_ADDR 0x00000034 + +#define INT_PHY_ADDR 0x1E + +#define BUSY_MASK 0x00000001 +#define READ_CYCLE_MASK 0x00000001 +#define PHY_CONTROL_WR(src) (((u32)(src)) & 0x0000ffff) + +#define HW_MTU(m) ((m) + 12 + 2 + 4 /* MAC + CRC */) + +enum xgene_enum_speed { + XGENE_ENET_SPEED_0 = 0xffff, + XGENE_ENET_SPEED_10 = 10, + XGENE_ENET_SPEED_100 = 100, + XGENE_ENET_SPEED_1000 = 1000, + XGENE_ENET_SPEED_10000 = 10000 +}; + +enum xgene_enet_mode { + HALF_DUPLEX = 1, + FULL_DUPLEX = 2 +}; + +enum xgene_enet_phy_mode { + PHY_MODE_NONE, + PHY_MODE_RGMII, + PHY_MODE_SGMII, + PHY_MODE_XGMII +}; + +enum xgene_enet_cmd { + XGENE_ENET_WR_CMD = 0x80000000, + XGENE_ENET_RD_CMD = 0x40000000 +}; + +#define CMU 0 + +/* ===== MII definitions ===== */ + +#define MII_CRC_LEN 0x4 /* CRC length in bytes */ +#define MII_ETH_MAX_PCK_SZ (ETHERMTU + SIZEOF_ETHERHEADER \ + + MII_CRC_LEN) +#define MII_MAX_PHY_NUM 0x20 /* max number of attached PHYs */ +#define MII_MAX_REG_NUM 0x20 /* max number of registers */ + +#define MII_CTRL_REG 0x0 /* Control Register */ +#define MII_STAT_REG 0x1 /* Status Register */ +#define MII_PHY_ID1_REG 0x2 /* PHY identifier 1 Register */ +#define MII_PHY_ID2_REG 0x3 /* PHY identifier 2 Register */ +#define MII_AN_ADS_REG 0x4 /* Auto-Negotiation */ + /* Advertisement Register */ +#define MII_AN_PRTN_REG 0x5 /* Auto-Negotiation */ + /* partner ability Register */ +#define MII_AN_EXP_REG 0x6 /* Auto-Negotiation */ + /* Expansion Register */ +#define MII_AN_NEXT_REG 0x7 /* Auto-Negotiation */ + /* next-page transmit Register */ + +#define MII_AN_PRTN_NEXT_REG 0x8 /* Link partner received next page */ +#define MII_MASSLA_CTRL_REG 0x9 /* MATER-SLAVE control register */ +#define MII_MASSLA_STAT_REG 0xa /* MATER-SLAVE status register */ +#define MII_EXT_STAT_REG 0xf /* Extented status register */ + +/* MII control register bit */ +#define MII_CR_1000 0x0040 /* 1 = 1000mb when + * MII_CR_100 is also 1 + */ +#define MII_CR_COLL_TEST 0x0080 /* collision test */ +#define MII_CR_FDX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART 0x0200 /* restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* power down */ +#define MII_CR_AUTO_EN 0x1000 /* auto-negotiation enable */ +#define MII_CR_100 0x2000 /* 0 = 10mb, 1 = 100mb */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_NORM_EN 0x0000 /* just enable the PHY */ +#define MII_CR_DEF_0_MASK 0xca7f /* they must return zero */ +#define MII_CR_RES_MASK 0x003f /* reserved bits,return zero */ + +/* MII Status register bit definitions */ +#define MII_SR_LINK_STATUS 0x0004 /* link Status -- 1 = link */ +#define MII_SR_AUTO_SEL 0x0008 /* auto speed select capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote fault detect */ +#define MII_SR_AUTO_NEG 0x0020 /* auto negotiation complete */ +#define MII_SR_EXT_STS 0x0100 /* extended sts in reg 15 */ +#define MII_SR_T2_HALF_DPX 0x0200 /* 100baseT2 HD capable */ +#define MII_SR_T2_FULL_DPX 0x0400 /* 100baseT2 FD capable */ +#define MII_SR_10T_HALF_DPX 0x0800 /* 10BaseT HD capable */ +#define MII_SR_10T_FULL_DPX 0x1000 /* 10BaseT FD capable */ +#define MII_SR_TX_HALF_DPX 0x2000 /* TX HD capable */ +#define MII_SR_TX_FULL_DPX 0x4000 /* TX FD capable */ +#define MII_SR_T4 0x8000 /* T4 capable */ +#define MII_SR_ABIL_MASK 0xff80 /* abilities mask */ +#define MII_SR_EXT_CAP 0x0001 /* extended capabilities */ +#define MII_SR_SPEED_SEL_MASK 0xf800 /* Mask to extract just speed + * capabilities from status + * register. + */ + +/* MII AN advertisement Register bit definition */ +#define MII_ANAR_10TX_HD 0x0020 +#define MII_ANAR_10TX_FD 0x0040 +#define MII_ANAR_100TX_HD 0x0080 +#define MII_ANAR_100TX_FD 0x0100 +#define MII_ANAR_100T_4 0x0200 +#define MII_ANAR_PAUSE 0x0400 +#define MII_ANAR_ASM_PAUSE 0x0800 +#define MII_ANAR_REMORT_FAULT 0x2000 +#define MII_ANAR_NEXT_PAGE 0x8000 +#define MII_ANAR_PAUSE_MASK 0x0c00 + +/* MII Link Code word bit definitions */ +#define MII_BP_FAULT 0x2000 /* remote fault */ +#define MII_BP_ACK 0x4000 /* acknowledge */ +#define MII_BP_NP 0x8000 /* nexp page is supported */ + +/* MII Next Page bit definitions */ +#define MII_NP_TOGGLE 0x0800 /* toggle bit */ +#define MII_NP_ACK2 0x1000 /* acknowledge two */ +#define MII_NP_MSG 0x2000 /* message page */ +#define MII_NP_ACK1 0x4000 /* acknowledge one */ +#define MII_NP_NP 0x8000 /* nexp page will follow */ + +/* MII Master-Slave Control register bit definition */ +#define MII_MASSLA_CTRL_1000T_HD 0x100 +#define MII_MASSLA_CTRL_1000T_FD 0x200 +#define MII_MASSLA_CTRL_PORT_TYPE 0x400 +#define MII_MASSLA_CTRL_CONFIG_VAL 0x800 +#define MII_MASSLA_CTRL_CONFIG_EN 0x1000 + +/* MII Master-Slave Status register bit definition */ +#define MII_MASSLA_STAT_LP1000T_HD 0x400 +#define MII_MASSLA_STAT_LP1000T_FD 0x800 +#define MII_MASSLA_STAT_REMOTE_RCV 0x1000 +#define MII_MASSLA_STAT_LOCAL_RCV 0x2000 +#define MII_MASSLA_STAT_CONF_RES 0x4000 +#define MII_MASSLA_STAT_CONF_FAULT 0x8000 + +/* these values may be used in the default phy mode field of the load + * string, since that is used to force the operating mode of the PHY + * in case any attempt to establish the link failed. + */ + +#define PHY_10BASE_T 0x00 /* 10 Base-T */ +#define PHY_10BASE_T_FDX 0x01 /* 10 Base Tx, full duplex */ +#define PHY_100BASE_TX 0x02 /* 100 Base Tx */ +#define PHY_100BASE_TX_FDX 0x03 /* 100 Base TX, full duplex */ +#define PHY_100BASE_T4 0x04 /* 100 Base T4 */ +#define PHY_AN_ENABLE 0x05 /* re-enable auto-negotiation */ + +#define MII_AN_TBL_MAX 20 /* max number of entries in the table */ + +/* Realtek PHY definitions */ +#define PHY_SPEED_RES 3 +#define PHY_SPEED_1000 2 +#define PHY_SPEED_100 1 +#define PHY_SPEED_10 0 +#define RTL_PHYSR_ADR 0X11 +#define RTL_PHYSR_SPEED_RD(src) (((src) & 0x0000C000) >> 14) +#define RTL_PHYSR_LINK_RD(src) (((src) & 0x00000400) >> 10) + +#define RTL_PHYSR_ADR 0X11 + +/* LErr(3b) Decoding */ +enum xgene_enet_lerr { + ENET_NO_ERR = 0, /* No Error */ + ENET_AXI_WR_ERR = 1, /* AXI write data error due to RSIF */ + ENET_ING_CRC_ERR = 2, /* Rx packet had CRC */ + ENET_AXI_RD_ERR = 3, /* AXI read data error when processing + * a work message in TSIF + */ + ENET_LL_RD_ERR = 4, /* AXI Link List read error when + * processing a work message in TSIF + */ + ENET_ING_ERR = 5, /* Rx packet had ingress processing error */ + ENET_CHKSUM_ERR = 5, /* Checksum error */ + ENET_BAD_MSG_ERR = 6, /* Bad message to subsytem */ + ENET_MISC_ERR = 7, /* Other ingress processing error */ + ENET_MAC_TRUNC_ERR = 7, /* MAX truncated */ + ENET_MAC_LEN_ERR = 8, /* Packet length error */ + ENET_PKT_LESS64_ERR = 9, /* MAC length lesser than 64B */ + ENET_MAC_OVERRUN_ERR = 10, /* FIFO overrun on ingress */ + ENET_UNISEC_CHKSUM_ERR = 11, /* Rx pacekt checksum error */ + ENET_UNISEC_LEN_ERR = 12, /* Rx pkt length mismatch QM message */ + ENET_UNISEC_ICV_ERR = 13, /* Rx pkt ICV error */ + ENET_UNISEC_PROTO_ERR = 14, /* Rx pkt protocol field mismatch */ + ENET_FP_TIMEOUT_ERR = 15 /* Free pool buffer timeout */ +}; + +/* Error TX/RX Statistics - maintained by software */ +struct xgene_mac_error_stats { + u64 rx_hw_errors; + u64 rx_hw_overrun; + u64 tx_dropped; +}; + +struct xgene_enet_rx_stats { + u32 rx_byte_count; /* Receive Byte Counter */ + u32 rx_packet_count; /* Receive Packet Counter */ + u32 rx_fcs_err_count; /* Receive FCS Error Counter */ + u32 rx_alignment_err_pkt_count; /* Rx Alignment Err Packet Counter */ + u32 rx_frm_len_err_pkt_count; /* Rx Frame Len Err Packet Counter */ + u32 rx_undersize_pkt_count; /* Receive Undersize Packet Counter */ + u32 rx_oversize_pkt_count; /* Receive Oversize Packet Counter */ + u32 rx_drop_pkt_count; /* Receive Drop Packet Counter */ +}; + +struct xgene_enet_tx_stats { + u32 tx_byte_count; /* Tx Byte cnt */ + u32 tx_pkt_count; /* Tx pkt cnt */ + u32 tx_drop_frm_count; /* Tx Drop Frame cnt */ + u32 tx_fcs_err_frm_count; /* Tx FCS Error Frame cnt */ + u32 tx_undersize_frm_count; /* Tx Undersize Frame cnt */ +}; + +struct xgene_enet_detailed_stats { + struct xgene_enet_rx_stats rx_stats; + struct xgene_enet_tx_stats tx_stats; + struct xgene_mac_error_stats estats; +}; + +/* Ethernet private structure */ +struct xgene_enet_priv { + void *eth_csr_addr_v; + void *eth_cle_addr_v; + void *eth_qmi_addr_v; + void *eth_sds_csr_addr_v; + void *eth_clkrst_csr_addr_v; + void *eth_diag_csr_addr_v; + void *mcx_mac_addr_v; + void *mcx_stats_addr_v; + void *mcx_mac_csr_addr_v; + void *sata_enet_csr_addr_v; + void *axg_mac_addr_v; + void *axg_stats_addr_v; + void *axg_mac_csr_addr_v; + void *xgenet_pcs_addr_v; + void *xgenet_mdio_csr_addr_v; + + u64 paddr_base; /* Base physical address of device */ + void *vaddr_base; /* Base Virtual address for the device */ + u64 ppaddr_base; /* Per port physical address of device */ + void *vpaddr_base; /* Per port Virtual address of device */ + void *vmii_base; /* Base MII Virtual address of device */ + + u32 phy_addr; /* Virtual address for PHY */ + u32 phy_mode; + u32 port; + u32 speed; /* Forced Link Speed */ + u32 link_status; + u32 crc; + u32 autoneg_set; + u32 mac_to_mac; /* Tell traffic is MAC-to-MAC */ + u32 desired_speed; /* In case of MAC-to-MAC, no autoneg, + * tell the desired speed to setup + */ + u32 phyless; /* PHY stays away from board on + * common server board design + */ + u32 force_serdes_reset; /* Force analog reset till stable state */ + + /* Function pointers */ + void (*port_reset) (struct xgene_enet_priv *priv); + int (*phy_autoneg_done) (struct xgene_enet_priv *priv); + void (*phy_link_mode) (struct xgene_enet_priv *priv, + u32 *speed, u32 *state); + void (*mac_reset) (struct xgene_enet_priv *priv); + int (*mac_init) (struct xgene_enet_priv *priv, + unsigned char *dev_addr, int speed, int mtu, int crc); + void (*mac_rx_state) (struct xgene_enet_priv *priv, u32 enable); + void (*mac_tx_state) (struct xgene_enet_priv *priv, u32 enable); + void (*mac_change_mtu) (struct xgene_enet_priv *priv, u32 new_mtu); + void (*mac_set_ipg) (struct xgene_enet_priv *priv, u16 new_ipg); + void (*get_stats) (struct xgene_enet_priv *priv, + struct xgene_enet_detailed_stats *stats); + void (*set_mac_addr) (struct xgene_enet_priv *priv, + unsigned char *dev_addr); + void (*cle_bypass) (struct xgene_enet_priv *priv, u32 dstqid, + u32 fpsel); + void (*tx_offload) (struct xgene_enet_priv *priv, u32 command, + u32 value); + void (*qmi_assoc) (struct xgene_enet_priv *priv); + void (*port_shutdown) (struct xgene_enet_priv *priv); +}; + +int xgene_enet_wr(struct xgene_enet_priv *priv, u8 block_id, + u32 reg_offset, u32 value); + +int xgene_enet_rd(struct xgene_enet_priv *priv, u8 block_id, + u32 reg_offset, u32 *value); + +void xgene_enet_port_reset(struct xgene_enet_priv *priv); + +/* This function resets the entire part of MAC and minimal init for phy access + * It will put both Transmit and Receive MAC Control block in reset + * and then init. + */ +void xgene_enet_mac_reset(struct xgene_enet_priv *priv); + +int xgene_enet_mac_init(struct xgene_enet_priv *priv, unsigned char *dev_addr, + int speed, int mtu, int crc); + +void xgene_enet_mac_rx_state(struct xgene_enet_priv *priv, u32 enable); +void xgene_enet_mac_tx_state(struct xgene_enet_priv *priv, u32 enable); + +void xgene_enet_mac_change_mtu(struct xgene_enet_priv *priv, u32 new_mtu); +void xgene_enet_mac_set_ipg(struct xgene_enet_priv *priv, u16 ipg); + +void xgene_enet_set_mac_addr(struct xgene_enet_priv *priv, + unsigned char *dev_addr); + +void xgene_enet_cle_bypass(struct xgene_enet_priv *priv, u32 dstqid, u32 fpsel); + +void xgene_enet_tx_offload(struct xgene_enet_priv *priv, + u32 command, u32 value); + +void xgene_enet_port_shutdown(struct xgene_enet_priv *priv); +enum xgene_qmtm_qaccess xgene_enet_get_qacess(void); +void xgene_genericmiiphy_read(struct xgene_enet_priv *priv, u8 phy_id, + unsigned char reg, u32 *data); +void xgene_genericmiiphy_write(struct xgene_enet_priv *priv, u8 phy_id, + unsigned char reg, u32 data); + +void xgene_enet_get_stats(struct xgene_enet_priv *priv, + struct xgene_enet_detailed_stats *detailed_stats); +#endif /* __XGENE_ENET_COMMON_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_csr.h b/drivers/net/ethernet/apm/xgene/xgene_enet_csr.h new file mode 100644 index 0000000..c6b49c9 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_csr.h @@ -0,0 +1,162 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel <rapatel@xxxxxxx> + * Iyappan Subramanian <isubramanian@xxxxxxx> + * Fushen Chen <fchen@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __XGENE_ENET_CSR_H__ +#define __XGENE_ENET_CSR_H__ + +#define ENET_SPARE_CFG_REG_ADDR 0x00000750 +#define RSIF_CONFIG_REG_ADDR 0x00000010 +#define RSIF_RAM_DBG_REG0_ADDR 0x00000048 +#define RGMII_REG_0_ADDR 0x000007e0 +#define CFG_LINK_AGGR_RESUME_0_ADDR 0x000007c8 +#define DEBUG_REG_ADDR 0x00000700 +#define CFG_BYPASS_ADDR 0x00000294 +#define CLE_BYPASS_REG0_0_ADDR 0x00000490 +#define CLE_BYPASS_REG1_0_ADDR 0x00000494 +#define CLE_BYPASS_REG8_0_ADDR 0x000004b0 +#define TSIF_MSS_REG0_0_ADDR 0x00000108 +#define TSIF_MSS_REG1_0_ADDR 0x00000110 +#define TSO_CFG_0_ADDR 0x00000314 +#define TSO_CFG_INSERT_VLAN_0_ADDR 0x0000031c +#define CFG_RSIF_FPBUFF_TIMEOUT_EN_WR(src) (((u32)(src)<<31) & 0x80000000) +#define CFG_TSIF_MSS_SZ10_SET(dst, src) \ + (((dst) & ~0x3fff0000) | (((u32)(src)<<16) & 0x3fff0000)) +#define CFG_TSIF_MSS_SZ00_SET(dst, src) \ + (((dst) & ~0x00003fff) | (((u32)(src)) & 0x00003fff)) +#define CFG_TSIF_MSS_SZ20_SET(dst, src) \ + (((dst) & ~0x00003fff) | (((u32)(src)) & 0x00003fff)) +#define CFG_TSIF_MSS_SZ30_SET(dst, src) \ + (((dst) & ~0x3fff0000) | (((u32)(src)<<16) & 0x3fff0000)) +#define RESUME_TX_WR(src) (((u32)(src)) & 0x00000001) +#define CFG_SPEED_1250_WR(src) (((u32)(src)<<24) & 0x01000000) +#define CFG_TXCLK_MUXSEL0_WR(src) (((u32)(src)<<29) & 0xe0000000) +#define TX_PORT0_WR(src) (((u32)(src)) & 0x00000001) +#define CFG_BYPASS_UNISEC_TX_WR(src) (((u32)(src)<<2) & 0x00000004) +#define CFG_BYPASS_UNISEC_RX_WR(src) (((u32)(src)<<1) & 0x00000002) +#define CFG_CLE_BYPASS_EN0_SET(dst, src) \ + (((dst) & ~0x80000000) | (((u32)(src)<<31) & 0x80000000)) +#define CFG_CLE_IP_PROTOCOL0_SET(dst, src) \ + (((dst) & ~0x00030000) | (((u32)(src)<<16) & 0x00030000)) +#define CFG_CLE_DSTQID0_SET(dst, src) \ + (((dst) & ~0x00000fff) | (((u32)(src)) & 0x00000fff)) +#define CFG_CLE_FPSEL0_SET(dst, src) \ + (((dst) & ~0x000f0000) | (((u32)(src)<<16) & 0x000f0000)) +#define CFG_CLE_HENQNUM0_SET(dst, src) \ + (((dst) & ~0x0fff0000) | (((u32)(src)<<16) & 0x0fff0000)) +#define ICM_CONFIG0_REG_0_ADDR 0x00000400 +#define ICM_CONFIG2_REG_0_ADDR 0x00000410 +#define ECM_CONFIG0_REG_0_ADDR 0x00000500 +#define RX_DV_GATE_REG_0_ADDR 0x000005fc +#define TX_DV_GATE_EN0_SET(dst, src) \ + (((dst) & ~0x00000004) | (((u32)(src)<<2) & 0x00000004)) +#define RX_DV_GATE_EN0_SET(dst, src) \ + (((dst) & ~0x00000002) | (((u32)(src)<<1) & 0x00000002)) +#define RESUME_RX0_SET(dst, src) \ + (((dst) & ~0x00000001) | (((u32)(src)) & 0x00000001)) +#define ENET_CFGSSQMIWQASSOC_ADDR 0x000000e0 +#define ENET_CFGSSQMIFPQASSOC_ADDR 0x000000dc +#define ENET_CFGSSQMIQMLITEFPQASSOC_ADDR 0x000000f0 +#define ENET_CFGSSQMIQMLITEWQASSOC_ADDR 0x000000f4 +#define ENET_CLKEN_ADDR 0x00000008 +#define ENET_SRST_ADDR 0x00000000 +#define CSR0_RESET_WR(src) (((u32)(src)) & 0x00000001) +#define ENET0_RESET_WR(src) (((u32)(src)<<1) & 0x00000002) +#define CSR1_RESET_WR(src) (((u32)(src)<<2) & 0x00000004) +#define ENET1_RESET_WR(src) (((u32)(src)<<3) & 0x00000008) +#define CSR0_CLKEN_WR(src) (((u32)(src)) & 0x00000001) +#define ENET0_CLKEN_WR(src) (((u32)(src)<<1) & 0x00000002) +#define CSR1_CLKEN_WR(src) (((u32)(src)<<2) & 0x00000004) +#define ENET1_CLKEN_WR(src) (((u32)(src)<<3) & 0x00000008) +#define ENET_CFG_MEM_RAM_SHUTDOWN_ADDR 0x00000070 +#define ENET_BLOCK_MEM_RDY_ADDR 0x00000074 +#define MAC_CONFIG_1_ADDR 0x00000000 +#define MAC_CONFIG_2_ADDR 0x00000004 +#define MAX_FRAME_LEN_ADDR 0x00000010 +#define MII_MGMT_CONFIG_ADDR 0x00000020 +#define MII_MGMT_COMMAND_ADDR 0x00000024 +#define MII_MGMT_ADDRESS_ADDR 0x00000028 +#define MII_MGMT_CONTROL_ADDR 0x0000002c +#define MII_MGMT_STATUS_ADDR 0x00000030 +#define MII_MGMT_INDICATORS_ADDR 0x00000034 +#define INTERFACE_CONTROL_ADDR 0x00000038 +#define STATION_ADDR0_ADDR 0x00000040 +#define STATION_ADDR1_ADDR 0x00000044 +#define SCAN_CYCLE_MASK 0x00000002 +#define SOFT_RESET1_MASK 0x80000000 +#define MAX_FRAME_LEN_SET(dst, src) \ + (((dst) & ~0x0000ffff) | (((u32)(src)) & 0x0000ffff)) +#define PHY_ADDR_SET(dst, src) \ + (((dst) & ~0x00001f00) | (((u32)(src)<<8) & 0x00001f00)) +#define REG_ADDR_SET(dst, src) \ + (((dst) & ~0x0000001f) | (((u32)(src)) & 0x0000001f)) +#define RESET_TX_FUN1_WR(src) (((u32)(src)<<16) & 0x00010000) +#define RESET_RX_FUN1_WR(src) (((u32)(src)<<17) & 0x00020000) +#define RESET_TX_MC1_WR(src) (((u32)(src)<<18) & 0x00040000) +#define RESET_RX_MC1_WR(src) (((u32)(src)<<19) & 0x00080000) +#define SIM_RESET1_WR(src) (((u32)(src)<<30) & 0x40000000) +#define SOFT_RESET1_WR(src) (((u32)(src)<<31) & 0x80000000) +#define TX_EN1_WR(src) (((u32)(src)) & 0x00000001) +#define RX_EN1_WR(src) (((u32)(src)<<2) & 0x00000004) +#define TX_FLOW_EN1_WR(src) (((u32)(src)<<4) & 0x00000010) +#define LOOP_BACK1_WR(src) (((u32)(src)<<8) & 0x00000100) +#define RX_FLOW_EN1_WR(src) (((u32)(src)<<5) & 0x00000020) +#define ENET_LHD_MODE_WR(src) (((u32)(src)<<25) & 0x02000000) +#define ENET_GHD_MODE_WR(src) (((u32)(src)<<26) & 0x04000000) +#define FULL_DUPLEX2_WR(src) (((u32)(src)) & 0x00000001) +#define LENGTH_CHECK2_WR(src) (((u32)(src)<<4) & 0x00000010) +#define HUGE_FRAME_EN2_WR(src) (((u32)(src)<<5) & 0x00000020) +#define ENET_INTERFACE_MODE2_WR(src) (((u32)(src)<<8) & 0x00000300) +#define PAD_CRC2_WR(src) (((u32)(src)<<2) & 0x00000004) +#define CRC_EN2_WR(src) (((u32)(src)<<1) & 0x00000002) +#define PREAMBLE_LENGTH2_WR(src) (((u32)(src)<<12) & 0x0000f000) +#define MAX_FRAME_LEN_WR(src) (((u32)(src)) & 0x0000ffff) +#define MGMT_CLOCK_SEL_SET(dst, src) \ + (((dst) & ~0x00000007) | (((u32)(src)) & 0x00000007)) +#define RX_EN1_SET(dst, src) \ + (((dst) & ~0x00000004) | (((u32)(src)<<2) & 0x00000004)) +#define TX_EN1_SET(dst, src) \ + (((dst) & ~0x00000001) | (((u32)(src)) & 0x00000001)) +#define SCAN_AUTO_INCR_MASK 0x00000020 +#define RBYT_ADDR 0x00000027 +#define RPKT_ADDR 0x00000028 +#define RFCS_ADDR 0x00000029 +#define RALN_ADDR 0x0000002f +#define RFLR_ADDR 0x00000030 +#define RUND_ADDR 0x00000033 +#define ROVR_ADDR 0x00000034 +#define RDRP_ADDR 0x00000037 +#define TBYT_ADDR 0x00000038 +#define TPKT_ADDR 0x00000039 +#define TDRP_ADDR 0x00000045 +#define TFCS_ADDR 0x00000047 +#define TUND_ADDR 0x0000004a +#define RX_BYTE_CNTR_MASK 0x7fffffff +#define RX_PKT_CNTR_MASK 0x7fffffff +#define RX_FCS_ERROR_CNTR_MASK 0x0000ffff +#define RX_ALIGN_ERR_CNTR_MASK 0x0000ffff +#define RX_LEN_ERR_CNTR_MASK 0x0000ffff +#define RX_UNDRSIZE_PKT_CNTR_MASK 0x0000ffff +#define RX_OVRSIZE_PKT_CNTR_MASK 0x0000ffff +#define RX_DROPPED_PKT_CNTR_MASK 0x0000ffff +#define TX_BYTE_CNTR_MASK 0x7fffffff +#define TX_PKT_CNTR_MASK 0x7fffffff +#define TX_DROP_FRAME_CNTR_MASK 0x0000ffff +#define TX_FCS_ERROR_CNTR_MASK 0x00000fff +#define TX_UNDSIZE_FRAME_CNTR_MASK 0x00000fff + +#endif /* __XGENE_ENET_CSR_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_mac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_mac.c new file mode 100644 index 0000000..68f8851 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_mac.c @@ -0,0 +1,520 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel <rapatel@xxxxxxx> + * Iyappan Subramanian <isubramanian@xxxxxxx> + * Fushen Chen <fchen@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "xgene_enet_main.h" +#include "xgene_enet_csr.h" + +static void xgene_gmac_set_mac_addr(struct xgene_enet_priv *priv, + unsigned char *dev_addr) +{ + u32 a_hi = *(u32 *)&dev_addr[0]; + u32 a_lo = (u32) *(u16 *)&dev_addr[4]; + xgene_enet_wr(priv, BLOCK_MCX_MAC, STATION_ADDR0_ADDR, a_hi); + + a_lo <<= 16; + a_lo |= (priv->phy_addr & 0xFFFF); + xgene_enet_wr(priv, BLOCK_MCX_MAC, STATION_ADDR1_ADDR, a_lo); +} + +static int xgene_enet_ecc_init(struct xgene_enet_priv *priv) +{ + u32 data; + int wait; + + xgene_enet_wr(priv, BLOCK_ETH_DIAG_CSR, + ENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x0); + /* check for at leaset 1 ms */ + wait = 1000; + do { + xgene_enet_rd(priv, BLOCK_ETH_DIAG_CSR, + ENET_BLOCK_MEM_RDY_ADDR, &data); + usleep_range(1, 100); + } while (--wait && data != 0xffffffff); + if (!wait) { + pr_err("Failed to release memory from shutdown\n"); + return -ENODEV; + } + + return 0; +} + +static void xgene_gmac_change_mtu(struct xgene_enet_priv *priv, u32 new_mtu) +{ + u32 data; + + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAX_FRAME_LEN_ADDR, &data); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAX_FRAME_LEN_ADDR, + MAX_FRAME_LEN_SET(data, new_mtu)); +} + +static void xgene_gmac_phy_enable_scan_cycle(struct xgene_enet_priv *priv, + int enable) +{ + u32 val; + + xgene_enet_rd(priv, BLOCK_MCX_MAC, MII_MGMT_COMMAND_ADDR, &val); + if (enable) + val |= SCAN_CYCLE_MASK; + else + val &= ~SCAN_CYCLE_MASK; + xgene_enet_wr(priv, BLOCK_MCX_MAC, MII_MGMT_COMMAND_ADDR, val); + + /* Program phy address start scan from 0 and register at address 0x1 */ + xgene_enet_rd(priv, BLOCK_MCX_MAC, MII_MGMT_ADDRESS_ADDR, &val); + val = PHY_ADDR_SET(val, 0); + val = REG_ADDR_SET(val, 1); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MII_MGMT_ADDRESS_ADDR, val); +} + +static void xgene_gmac_reset(struct xgene_enet_priv *priv) +{ + u32 value; + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &value); + if (!(value & SOFT_RESET1_MASK)) + return; + + value = RESET_TX_FUN1_WR(1) + | RESET_RX_FUN1_WR(1) + | RESET_TX_MC1_WR(1) + | RESET_RX_MC1_WR(1) + | SIM_RESET1_WR(1) + | SOFT_RESET1_WR(1); + + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, value); + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &value); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, 0); +} + +int xgene_gmac_init(struct xgene_enet_priv *priv, unsigned char *dev_addr, + int speed, int mtu, int crc) +{ + u32 value, temp; + u32 addr_hi, addr_lo; + + u32 interface_control; + u32 mac_config_2; + u32 rgmii; + u32 icm_config0 = 0x0008503f; + u32 icm_config2 = 0x0010000f; + u32 ecm_config0 = 0x00000032; + u32 enet_spare_cfg = 0x00006040; + + /* Reset subsystem */ + value = RESET_TX_FUN1_WR(1) + | RESET_RX_FUN1_WR(1) + | RESET_TX_MC1_WR(1) + | RESET_RX_MC1_WR(1) + | SIM_RESET1_WR(1) + | SOFT_RESET1_WR(1); + + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, value); + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &temp); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, 0); + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &temp); + + value = TX_EN1_WR(1) + | RX_EN1_WR(1) + | TX_FLOW_EN1_WR(0) + | LOOP_BACK1_WR(0) + | RX_FLOW_EN1_WR(0); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, value); + xgene_enet_rd(priv, BLOCK_ETH_CSR, + ENET_SPARE_CFG_REG_ADDR, &enet_spare_cfg); + + if (speed == XGENE_ENET_SPEED_10) { + interface_control = ENET_LHD_MODE_WR(0) + | ENET_GHD_MODE_WR(0); + mac_config_2 = FULL_DUPLEX2_WR(1) + | LENGTH_CHECK2_WR(0) + | HUGE_FRAME_EN2_WR(0) + | ENET_INTERFACE_MODE2_WR(1) /* 10Mbps */ + |PAD_CRC2_WR(crc) + | CRC_EN2_WR(crc) + | PREAMBLE_LENGTH2_WR(7); + rgmii = 0; + icm_config0 = 0x0000503f; + icm_config2 = 0x000101f4; + ecm_config0 = 0x600032; + enet_spare_cfg = enet_spare_cfg | (0x0000c040); + } else if (speed == XGENE_ENET_SPEED_100) { + interface_control = ENET_LHD_MODE_WR(1); + mac_config_2 = FULL_DUPLEX2_WR(1) + | LENGTH_CHECK2_WR(0) + | HUGE_FRAME_EN2_WR(0) + | ENET_INTERFACE_MODE2_WR(1) /* 100Mbps */ + |PAD_CRC2_WR(crc) + | CRC_EN2_WR(crc) + | PREAMBLE_LENGTH2_WR(7); + rgmii = 0; + icm_config0 = 0x0004503f; + icm_config2 = 0x00010050; + ecm_config0 = 0x600032; + enet_spare_cfg = enet_spare_cfg | (0x0000c040); + } else { + interface_control = ENET_GHD_MODE_WR(1); + mac_config_2 = FULL_DUPLEX2_WR(1) + | LENGTH_CHECK2_WR(0) + | HUGE_FRAME_EN2_WR(0) + | ENET_INTERFACE_MODE2_WR(2) /* 1Gbps */ + |PAD_CRC2_WR(crc) + | CRC_EN2_WR(crc) + | PREAMBLE_LENGTH2_WR(7); + rgmii = CFG_SPEED_1250_WR(1) | CFG_TXCLK_MUXSEL0_WR(4); + icm_config0 = 0x0008503f; + icm_config2 = 0x0001000f; + ecm_config0 = 0x32; + enet_spare_cfg = (enet_spare_cfg & ~0x0000c000) + | (0x00000040); + } + + enet_spare_cfg |= 0x00006040; + + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_2_ADDR, mac_config_2); + + xgene_enet_wr(priv, BLOCK_MCX_MAC, INTERFACE_CONTROL_ADDR, + interface_control); + + value = MAX_FRAME_LEN_WR(0x0600); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAX_FRAME_LEN_ADDR, value); + + /* Program the station MAC address */ + addr_hi = *(u32 *) &dev_addr[0]; + addr_lo = *(u16 *) &dev_addr[4]; + addr_lo <<= 16; + addr_lo |= (priv->phy_addr & 0xFFFF); + + xgene_enet_wr(priv, BLOCK_MCX_MAC, STATION_ADDR0_ADDR, addr_hi); + xgene_enet_wr(priv, BLOCK_MCX_MAC, STATION_ADDR1_ADDR, addr_lo); + + /* Adjust MDC clock frequency */ + xgene_enet_rd(priv, BLOCK_MCX_MAC, MII_MGMT_CONFIG_ADDR, &value); + value = MGMT_CLOCK_SEL_SET(value, 7); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MII_MGMT_CONFIG_ADDR, value); + + /* Enable drop if FP not available */ + xgene_enet_rd(priv, BLOCK_ETH_CSR, RSIF_CONFIG_REG_ADDR, &value); + value |= CFG_RSIF_FPBUFF_TIMEOUT_EN_WR(1); + xgene_enet_wr(priv, BLOCK_ETH_CSR, RSIF_CONFIG_REG_ADDR, value); + + /* Rtype should be copied from FP */ + value = 0; + xgene_enet_wr(priv, BLOCK_ETH_CSR, RSIF_RAM_DBG_REG0_ADDR, value); + /* Initialize RGMII PHY */ + if (priv->phy_mode == PHY_MODE_RGMII) + xgene_enet_wr(priv, BLOCK_ETH_CSR, RGMII_REG_0_ADDR, rgmii); + + xgene_enet_wr(priv, BLOCK_MCX_MAC_CSR, ICM_CONFIG0_REG_0_ADDR, + icm_config0); + xgene_enet_wr(priv, BLOCK_MCX_MAC_CSR, ICM_CONFIG2_REG_0_ADDR, + icm_config2); + xgene_enet_wr(priv, BLOCK_MCX_MAC_CSR, ECM_CONFIG0_REG_0_ADDR, + ecm_config0); + xgene_enet_wr(priv, BLOCK_ETH_CSR, ENET_SPARE_CFG_REG_ADDR, + enet_spare_cfg); + + /* Rx-Tx traffic resume */ + xgene_enet_wr(priv, BLOCK_ETH_CSR, + CFG_LINK_AGGR_RESUME_0_ADDR, TX_PORT0_WR(0x1)); + + if (speed != XGENE_ENET_SPEED_10 && speed != XGENE_ENET_SPEED_100) { + xgene_enet_rd(priv, BLOCK_ETH_CSR, DEBUG_REG_ADDR, &value); + value |= CFG_BYPASS_UNISEC_TX_WR(1) + | CFG_BYPASS_UNISEC_RX_WR(1); + xgene_enet_wr(priv, BLOCK_ETH_CSR, DEBUG_REG_ADDR, value); + } + + xgene_enet_rd(priv, BLOCK_MCX_MAC_CSR, RX_DV_GATE_REG_0_ADDR, &value); + value = TX_DV_GATE_EN0_SET(value, 0); + value = RX_DV_GATE_EN0_SET(value, 0); + value = RESUME_RX0_SET(value, 1); + xgene_enet_wr(priv, BLOCK_MCX_MAC_CSR, RX_DV_GATE_REG_0_ADDR, value); + + xgene_enet_wr(priv, BLOCK_ETH_CSR, CFG_BYPASS_ADDR, RESUME_TX_WR(1)); + return 0; +} + +/* Start Statistics related functions */ +static void xgene_gmac_get_rx_stats(struct xgene_enet_priv *priv, + struct xgene_enet_rx_stats *rx_stat) +{ + xgene_enet_rd(priv, BLOCK_MCX_STATS, RBYT_ADDR, + &rx_stat->rx_byte_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RPKT_ADDR, + &rx_stat->rx_packet_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RDRP_ADDR, + &rx_stat->rx_drop_pkt_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RFCS_ADDR, + &rx_stat->rx_fcs_err_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RFLR_ADDR, + &rx_stat->rx_frm_len_err_pkt_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RALN_ADDR, + &rx_stat->rx_alignment_err_pkt_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, ROVR_ADDR, + &rx_stat->rx_oversize_pkt_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, RUND_ADDR, + &rx_stat->rx_undersize_pkt_count); + + rx_stat->rx_byte_count &= RX_BYTE_CNTR_MASK; + rx_stat->rx_packet_count &= RX_PKT_CNTR_MASK; + rx_stat->rx_drop_pkt_count &= RX_DROPPED_PKT_CNTR_MASK; + rx_stat->rx_fcs_err_count &= RX_FCS_ERROR_CNTR_MASK; + rx_stat->rx_frm_len_err_pkt_count &= RX_LEN_ERR_CNTR_MASK; + rx_stat->rx_alignment_err_pkt_count &= RX_ALIGN_ERR_CNTR_MASK; + rx_stat->rx_oversize_pkt_count &= RX_OVRSIZE_PKT_CNTR_MASK; + rx_stat->rx_undersize_pkt_count &= RX_UNDRSIZE_PKT_CNTR_MASK; +} + +static void xgene_gmac_get_tx_stats(struct xgene_enet_priv *priv, + struct xgene_enet_tx_stats *tx_stats) +{ + xgene_enet_rd(priv, BLOCK_MCX_STATS, TBYT_ADDR, + &tx_stats->tx_byte_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, TPKT_ADDR, + &tx_stats->tx_pkt_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, TDRP_ADDR, + &tx_stats->tx_drop_frm_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, TFCS_ADDR, + &tx_stats->tx_fcs_err_frm_count); + xgene_enet_rd(priv, BLOCK_MCX_STATS, TUND_ADDR, + &tx_stats->tx_undersize_frm_count); + + tx_stats->tx_byte_count &= TX_BYTE_CNTR_MASK; + tx_stats->tx_pkt_count &= TX_PKT_CNTR_MASK; + tx_stats->tx_drop_frm_count &= TX_DROP_FRAME_CNTR_MASK; + tx_stats->tx_fcs_err_frm_count &= TX_FCS_ERROR_CNTR_MASK; + tx_stats->tx_undersize_frm_count &= TX_UNDSIZE_FRAME_CNTR_MASK; +} + +static void xgene_gmac_get_detailed_stats(struct xgene_enet_priv *priv, + struct xgene_enet_detailed_stats *stats) +{ + xgene_gmac_get_rx_stats(priv, &(stats->rx_stats)); + xgene_gmac_get_tx_stats(priv, &(stats->tx_stats)); +} + +/* Configure Ethernet QMI: WQ and FPQ association to QML */ +static void xgene_enet_config_qmi_assoc(struct xgene_enet_priv *priv) +{ + xgene_enet_wr(priv, BLOCK_ETH_QMI, ENET_CFGSSQMIWQASSOC_ADDR, + 0xffffffff); + xgene_enet_wr(priv, BLOCK_ETH_QMI, ENET_CFGSSQMIFPQASSOC_ADDR, + 0xffffffff); + xgene_enet_wr(priv, BLOCK_ETH_QMI, ENET_CFGSSQMIQMLITEFPQASSOC_ADDR, + 0xffffffff); + xgene_enet_wr(priv, BLOCK_ETH_QMI, ENET_CFGSSQMIQMLITEWQASSOC_ADDR, + 0xffffffff); +} + +static void xgene_enet_cle_bypass_mode_cfg(struct xgene_enet_priv *priv, + u32 cle_dstqid, u32 cle_fpsel) +{ + u32 reg; + + xgene_enet_rd(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG0_0_ADDR, ®); + reg = CFG_CLE_BYPASS_EN0_SET(reg, 1); + reg = CFG_CLE_IP_PROTOCOL0_SET(reg, 3); + xgene_enet_wr(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG0_0_ADDR, reg); + + xgene_enet_rd(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG1_0_ADDR, ®); + reg = CFG_CLE_DSTQID0_SET(reg, cle_dstqid); + reg = CFG_CLE_FPSEL0_SET(reg, cle_fpsel); + xgene_enet_wr(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG1_0_ADDR, reg); + + xgene_enet_rd(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG8_0_ADDR, ®); + reg = CFG_CLE_HENQNUM0_SET(reg, cle_dstqid); + xgene_enet_wr(priv, BLOCK_ETH_CSR, CLE_BYPASS_REG8_0_ADDR, reg); +} + +static void xgene_gmac_rx_state(struct xgene_enet_priv *priv, u32 enable) +{ + u32 data, rx_en; + + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &data); + rx_en = (enable) ? RX_EN1_SET(data, 1) : RX_EN1_SET(data, 0); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, rx_en); +} + +static void xgene_gmac_tx_state(struct xgene_enet_priv *priv, u32 enable) +{ + u32 data, tx_en; + + xgene_enet_rd(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, &data); + tx_en = (enable) ? TX_EN1_SET(data, 1) : TX_EN1_SET(data, 0); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MAC_CONFIG_1_ADDR, tx_en); +} + +static void xgene_gmac_tx_offload(struct xgene_enet_priv *priv, + u32 command, u32 value) +{ + u32 data; + + switch (priv->port) { + case MENET: + switch (command) { + /* TCP MSS 0 */ + case XGENE_ENET_MSS0: + xgene_enet_rd(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG0_0_ADDR, &data); + xgene_enet_wr(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG0_0_ADDR, + CFG_TSIF_MSS_SZ00_SET(data, value)); + break; + /* TCP MSS 1 */ + case XGENE_ENET_MSS1: + xgene_enet_rd(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG0_0_ADDR, &data); + xgene_enet_wr(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG0_0_ADDR, + CFG_TSIF_MSS_SZ10_SET(data, value)); + break; + /* TCP MSS 2 */ + case XGENE_ENET_MSS2: + xgene_enet_rd(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG1_0_ADDR, &data); + xgene_enet_wr(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG1_0_ADDR, + CFG_TSIF_MSS_SZ20_SET(data, value)); + break; + /* TCP MSS 3 */ + case XGENE_ENET_MSS3: + xgene_enet_rd(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG1_0_ADDR, &data); + xgene_enet_wr(priv, BLOCK_ETH_CSR, + TSIF_MSS_REG1_0_ADDR, + CFG_TSIF_MSS_SZ30_SET(data, value)); + break; + /* Program TSO config */ + case XGENE_ENET_TSO_CFG: + xgene_enet_wr(priv, BLOCK_ETH_CSR, TSO_CFG_0_ADDR, + value); + break; + /* Insert Inser tVLAN TAG */ + case XGENE_ENET_INSERT_VLAN: + xgene_enet_wr(priv, BLOCK_ETH_CSR, + TSO_CFG_INSERT_VLAN_0_ADDR, value); + break; + } + break; + default: + break; + } +} + +static void xgene_enet_clkrst_cfg(struct xgene_enet_priv *priv) +{ + u32 data; + + /* disable all clocks */ + data = CSR0_CLKEN_WR(0) | ENET0_CLKEN_WR(0) | + CSR1_CLKEN_WR(0) | ENET1_CLKEN_WR(0); + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_CLKEN_ADDR, data); + + /* enable all clocks */ + data = CSR0_CLKEN_WR(1) | ENET0_CLKEN_WR(1) | + CSR1_CLKEN_WR(1) | ENET1_CLKEN_WR(1); + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_CLKEN_ADDR, data); + + /* put csr and core reset */ + data = CSR0_RESET_WR(1) | ENET0_RESET_WR(1) | + CSR1_RESET_WR(1) | ENET1_RESET_WR(1); + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_SRST_ADDR, data); + + /* release csr and core reset */ + data = CSR0_RESET_WR(0) | ENET0_RESET_WR(0) | + CSR1_RESET_WR(0) | ENET1_RESET_WR(0); + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_SRST_ADDR, data); + + xgene_enet_ecc_init(priv); +} + +static void xgene_gport_reset(struct xgene_enet_priv *priv) +{ + u32 val; + + xgene_enet_clkrst_cfg(priv); + xgene_enet_config_qmi_assoc(priv); + + /* Enable auto-incr for scanning */ + xgene_enet_rd(priv, BLOCK_MCX_MAC, MII_MGMT_CONFIG_ADDR, &val); + val |= SCAN_AUTO_INCR_MASK; + val = MGMT_CLOCK_SEL_SET(val, 1); + xgene_enet_wr(priv, BLOCK_MCX_MAC, MII_MGMT_CONFIG_ADDR, val); + xgene_gmac_phy_enable_scan_cycle(priv, 1); +} + +static void xgene_gport_shutdown(struct xgene_enet_priv *priv) +{ + u32 clk, rst; + + rst = CSR0_RESET_WR(1) | ENET0_RESET_WR(1); + clk = CSR0_CLKEN_WR(0) | ENET0_CLKEN_WR(0); + + /* reset ethernet csr, core and disable clock */ + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_SRST_ADDR, rst); + xgene_enet_wr(priv, BLOCK_ETH_CLKRST_CSR, ENET_CLKEN_ADDR, clk); +} + +void xgene_enet_init_priv(struct xgene_enet_priv *priv) +{ + void *gbl_vaddr = priv->vaddr_base; + void *port_vaddr = priv->vpaddr_base; + + /* Initialize base addresses for direct access */ + priv->eth_csr_addr_v = gbl_vaddr + BLOCK_ETH_CSR_OFFSET; + priv->eth_cle_addr_v = gbl_vaddr + BLOCK_ETH_CLE_OFFSET; + priv->eth_qmi_addr_v = gbl_vaddr + BLOCK_ETH_QMI_OFFSET; + priv->eth_sds_csr_addr_v = gbl_vaddr + BLOCK_ETH_SDS_CSR_OFFSET; + priv->eth_clkrst_csr_addr_v = gbl_vaddr + BLOCK_ETH_CLKRST_CSR_OFFSET; + priv->eth_diag_csr_addr_v = gbl_vaddr + BLOCK_ETH_DIAG_CSR_OFFSET; + + /* Initialize per port base addr for indirect & direct MCX MAC access */ + priv->mcx_mac_addr_v = port_vaddr + BLOCK_ETH_MAC_OFFSET; + priv->mcx_stats_addr_v = port_vaddr + BLOCK_ETH_STATS_OFFSET; + priv->mcx_mac_csr_addr_v = gbl_vaddr + BLOCK_ETH_MAC_CSR_OFFSET; + priv->sata_enet_csr_addr_v = gbl_vaddr + BLOCK_SATA_ENET_CSR_OFFSET; + + /* Enable autonegotiation by default */ + priv->autoneg_set = 1; + + pr_debug(" ETH%d VADDR: 0x%p\n", priv->port, priv->vpaddr_base); + pr_debug(" ETH VADDR: 0x%p\n", priv->vaddr_base); + pr_debug(" ETH CSR VADDR: 0x%p\n", priv->eth_csr_addr_v); + pr_debug(" ETH CLE VADDR: 0x%p\n", priv->eth_cle_addr_v); + pr_debug(" ETH QMI VADDR: 0x%p\n", priv->eth_qmi_addr_v); + pr_debug(" ETH SDS CSR VADDR: 0x%p\n", priv->eth_sds_csr_addr_v); + pr_debug("ETH CLKRST CSR VADDR: 0x%p\n", priv->eth_clkrst_csr_addr_v); + pr_debug(" ETH DIAG VADDR: 0x%p\n", priv->eth_diag_csr_addr_v); + pr_debug(" MAC MII VADDR: 0x%p\n", priv->vmii_base); + pr_debug(" MCX MAC VADDR: 0x%p\n", priv->mcx_mac_addr_v); + pr_debug(" MCX STAT VADDR: 0x%p\n", priv->mcx_stats_addr_v); + pr_debug(" MCX MAC CSR VADDR: 0x%p\n", priv->mcx_mac_csr_addr_v); + pr_debug(" SATA ENET CSR VADDR: 0x%p\n", priv->sata_enet_csr_addr_v); + + /* Initialize priv handlers */ + priv->port_reset = xgene_gport_reset; + priv->mac_reset = xgene_gmac_reset; + priv->mac_init = xgene_gmac_init; + priv->mac_rx_state = xgene_gmac_rx_state; + priv->mac_tx_state = xgene_gmac_tx_state; + priv->mac_change_mtu = xgene_gmac_change_mtu; + priv->set_mac_addr = xgene_gmac_set_mac_addr; + priv->cle_bypass = xgene_enet_cle_bypass_mode_cfg; + priv->tx_offload = xgene_gmac_tx_offload; + priv->port_shutdown = xgene_gport_shutdown; + priv->get_stats = xgene_gmac_get_detailed_stats; +} diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c new file mode 100644 index 0000000..2aa1808 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -0,0 +1,1581 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel <rapatel@xxxxxxx> + * Iyappan Subramanian <isubramanian@xxxxxxx> + * Fushen Chen <fchen@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "xgene_enet_main.h" +#include "xgene_enet_csr.h" + +inline void xgene_enet_wr32(void *addr, u32 data) +{ + pr_debug("Write addr 0x%p data 0x%08X\n", addr, data); + writel(data, (void __iomem *)addr); +} + +inline void xgene_enet_rd32(void *addr, u32 *data) +{ + *data = readl((void __iomem *)addr); + pr_debug("data 0x%08X\n", *data); +} + +inline u32 xgene_enet_get_port(struct xgene_enet_pdev *pdev) +{ + return pdev->priv.port; +} + +inline phys_addr_t xgene_enet_enc_addr(void *vaddr) +{ + return __pa(vaddr); +} + +inline void *xgene_enet_dec_addr(phys_addr_t paddr) +{ + return __va(paddr); +} + +inline void xgene_enet_set_skb_data(struct xgene_qmtm_msg16 *msg16, + struct sk_buff *skb) +{ + u64 pa; + + pa = xgene_enet_enc_addr((void *)skb); + msg16->UserInfo = (u32) pa; + pa >>= 32; + msg16->Rv6 = (u8) pa; + msg16->Rv2 = (u8) (pa >> 6); + + pa = xgene_enet_enc_addr(skb->data); + msg16->DataAddr = pa; +} + +inline struct sk_buff *xgene_enet_get_skb(struct xgene_qmtm_msg16 *msg16) +{ + u64 pa = ((u64) msg16->Rv2 << 6) | (u64) msg16->Rv6; + pa <<= 32; + pa |= (u64) msg16->UserInfo; + return (struct sk_buff *)xgene_enet_dec_addr(pa); +} + +static int xgene_enet_init_fp(struct xgene_enet_qcontext *c2e, u32 nbuf) +{ + struct xgene_enet_pdev *pdev = c2e->pdev; + struct sk_buff *skb; + struct xgene_qmtm_msg16 *msg16; + u32 i; + + /* Initializing common fields */ + for (i = 0; i < c2e->qdesc->count; i++) { + msg16 = &c2e->qdesc->msg16[i]; + memset(msg16, 0, sizeof(struct xgene_qmtm_msg16)); + msg16->C = 1; + msg16->BufDataLen = xgene_qmtm_encode_bufdatalen(c2e->buf_size); + msg16->FPQNum = c2e->eqnum; + msg16->PB = 0; + msg16->HB = 1; + } + + if (nbuf > c2e->qdesc->count) { + netdev_warn(pdev->ndev, + "Limiting number of skb alloc to queue size\n"); + nbuf = c2e->qdesc->count; + } + + for (i = 0; i < nbuf; i++) { + msg16 = &c2e->qdesc->msg16[i]; + skb = dev_alloc_skb(c2e->buf_size); + if (unlikely(!skb)) { + netdev_err(pdev->ndev, + "Failed to allocate new skb size %d", + c2e->buf_size); + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + xgene_enet_set_skb_data(msg16, skb); + } + + writel(nbuf, c2e->qdesc->command); + + if (nbuf == c2e->qdesc->count) + nbuf = 0; + c2e->qdesc->qtail = nbuf; + + return 0; +} + +static int xgene_enet_refill_fp(struct xgene_enet_qcontext *c2e, u32 nbuf) +{ + register u32 qtail = c2e->qdesc->qtail; + struct xgene_enet_pdev *pdev = c2e->pdev; + u32 i; + + for (i = 0; i < nbuf; i++) { + struct sk_buff *skb; + struct xgene_qmtm_msg16 *msg16 = &c2e->qdesc->msg16[qtail]; + + msg16->BufDataLen = xgene_qmtm_encode_bufdatalen(c2e->buf_size); + skb = dev_alloc_skb(c2e->buf_size); + if (unlikely(!skb)) { + netdev_err(pdev->ndev, + "Failed to allocate new skb size %d", + c2e->buf_size); + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + xgene_enet_set_skb_data(msg16, skb); + if (++qtail == c2e->qdesc->count) + qtail = 0; + } + + writel(nbuf, c2e->qdesc->command); + c2e->qdesc->qtail = qtail; + + return 0; +} + +static void xgene_enet_deinit_fp(struct xgene_enet_qcontext *c2e, int qid) +{ + u32 qtail = c2e->qdesc->qtail; + u32 count = c2e->qdesc->count; + u32 command = 0; + struct xgene_enet_pdev *pdev = c2e->pdev; + struct xgene_qmtm_msg16 *msg16; + struct xgene_qmtm_qinfo qinfo; + struct sk_buff *skb; + int i; + + memset(&qinfo, 0, sizeof(qinfo)); + qinfo.qmtm = pdev->sdev->qmtm; + qinfo.queue_id = qid; + xgene_qmtm_read_qstate(&qinfo); + + for (i = 0; i < qinfo.nummsgs; i++) { + if (qtail == 0) + qtail = count; + + qtail--; + msg16 = &c2e->qdesc->msg16[qtail]; + if (msg16->UserInfo) { + skb = xgene_enet_get_skb(msg16); + kfree_skb(skb); + } + command--; + } + + writel(command, c2e->qdesc->command); + c2e->qdesc->qtail = qtail; +} + +static int xgene_enet_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &pdev->priv; + int eth_running; + + if (HW_MTU(new_mtu) < XGENE_ENET_MIN_MTU + || HW_MTU(new_mtu) > XGENE_ENET_MAX_MTU) { + netdev_err(ndev, "Invalid MTU: %d\n", new_mtu); + return -EINVAL; + } + + netdev_info(ndev, "changing MTU from %d to %d\n", ndev->mtu, new_mtu); + eth_running = netif_running(ndev); + if (eth_running) { + netif_stop_queue(ndev); + xgene_enet_mac_rx_state(priv, 0); + xgene_enet_mac_tx_state(priv, 0); + } + ndev->mtu = new_mtu; + xgene_enet_mac_change_mtu(priv, HW_MTU(new_mtu)); + if (eth_running) { + xgene_enet_mac_rx_state(priv, 1); + xgene_enet_mac_tx_state(priv, 1); + netif_start_queue(ndev); + } + return 0; +} + +static int xgene_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct xgene_enet_pdev *pdev = bus->priv; + struct xgene_enet_priv *priv = &pdev->priv; + u32 regval1; + + xgene_genericmiiphy_read(priv, mii_id, regnum, ®val1); + pr_debug("%s: bus=%d reg=%d val=%x\n", __func__, mii_id, + regnum, regval1); + return (int)regval1; +} + +static int xgene_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 regval) +{ + struct xgene_enet_pdev *pdev = bus->priv; + struct xgene_enet_priv *priv = &pdev->priv; + + pr_debug("%s: bus=%d reg=%d val=%x\n", __func__, mii_id, + regnum, regval); + xgene_genericmiiphy_write(priv, mii_id, regnum, regval); + + return 0; +} + +static void xgene_enet_mdio_link_change(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &pdev->priv; + struct phy_device *phydev = pdev->phy_dev; + int status_change = 0; + + if (phydev->link) { + if (pdev->phy_speed != phydev->speed) { + xgene_enet_mac_init(priv, ndev->dev_addr, phydev->speed, + HW_MTU(ndev->mtu), priv->crc); + pdev->phy_speed = phydev->speed; + status_change = 1; + } + } + + if (phydev->link != pdev->phy_link) { + if (!phydev->link) + pdev->phy_speed = 0; + pdev->phy_link = phydev->link; + status_change = 1; + } + + if (status_change) { + xgene_enet_mac_rx_state(priv, phydev->link); + xgene_enet_mac_tx_state(priv, phydev->link); + if (phydev->link) + netdev_info(ndev, "%s: link up %d Mbps\n", + ndev->name, phydev->speed); + else + netdev_info(ndev, "%s: link down\n", ndev->name); + } +} + +static int xgene_enet_mdio_probe(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct phy_device *phydev = NULL; + int phy_addr; + + /* find the first phy */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { + if (pdev->mdio_bus->phy_map[phy_addr]) { + phydev = pdev->mdio_bus->phy_map[phy_addr]; + break; + } + } + + if (!phydev) { + netdev_info(ndev, "%s: no PHY found\n", ndev->name); + return -1; + } + + /* attach the mac to the phy */ + phydev = phy_connect(ndev, dev_name(&phydev->dev), + &xgene_enet_mdio_link_change, + PHY_INTERFACE_MODE_RGMII); + + pdev->phy_link = 0; + pdev->phy_speed = 0; + + if (IS_ERR(phydev)) { + pdev->phy_dev = NULL; + netdev_err(ndev, "%s: Could not attach to PHY\n", ndev->name); + return PTR_ERR(phydev); + } + pdev->phy_dev = phydev; + + netdev_info(ndev, "%s: phy_id=0x%08x phy_drv=\"%s\"", + ndev->name, phydev->phy_id, phydev->drv->name); + + return 0; +} + +static int xgene_enet_mdio_remove(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = + (struct xgene_enet_pdev *)netdev_priv(ndev); + struct mii_bus *mdio_bus; + + mdio_bus = pdev->mdio_bus; + mdiobus_unregister(mdio_bus); + mdiobus_free(mdio_bus); + pdev->mdio_bus = NULL; + + return 0; +} + +static inline u32 xgene_enet_hdr_len(const void *data) +{ + const struct ethhdr *eth = data; + return (eth->h_proto == htons(ETH_P_8021Q)) ? VLAN_ETH_HLEN : ETH_HLEN; +} + +irqreturn_t xgene_enet_e2c_irq(const int irq, void *data) +{ + struct xgene_enet_qcontext *e2c = (struct xgene_enet_qcontext *)data; + + if (napi_schedule_prep(&e2c->napi)) { + disable_irq_nosync(irq); + __napi_schedule(&e2c->napi); + } + + return IRQ_HANDLED; +} + +static int xgene_enet_tx_completion(struct xgene_enet_qcontext *e2c, + struct xgene_qmtm_msg32 *msg32_1) +{ + struct sk_buff *skb; + int rc = 0; + + skb = (struct sk_buff *)xgene_enet_dec_addr( + msg32_1->msgup16.H0Info_msb); + + if (likely(skb)) { + dev_kfree_skb_any(skb); + } else { + netdev_info(e2c->pdev->ndev, "completion skb is NULL\n"); + rc = -1; + } + + return rc; +} + +static inline u16 xgene_enet_select_queue(struct net_device *ndev, + struct sk_buff *skb) +{ + return skb_tx_hash(ndev, skb); +} + +/* Checksum offload processing */ +static int xgene_enet_checksum_offload(struct net_device *ndev, + struct sk_buff *skb, + struct xgene_qmtm_msg_up16 *msg_up16) +{ + u32 maclen, nr_frags, ihl; + struct iphdr *iph; + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + int rc = 0; + + if (unlikely(!(ndev->features & NETIF_F_IP_CSUM))) + goto out; + + if (unlikely(skb->protocol != htons(ETH_P_IP)) && + unlikely(skb->protocol != htons(ETH_P_8021Q))) + goto out; + + nr_frags = skb_shinfo(skb)->nr_frags; + maclen = xgene_enet_hdr_len(skb->data); + iph = ip_hdr(skb); + ihl = ip_hdrlen(skb) >> 2; + + if (unlikely(iph->frag_off & htons(IP_MF | IP_OFFSET))) + goto out; + + if (likely(iph->protocol == IPPROTO_TCP)) { + int xhlen, mss_len; + u32 mss, all_hdr_len; + + xhlen = tcp_hdrlen(skb) / 4; + msg_up16->H0Info_lsb |= + (xhlen & TSO_TCP_HLEN_MASK) | + ((ihl & TSO_IP_HLEN_MASK) << 6) | + (TSO_CHKSUM_ENABLE << 22) | (TSO_IPPROTO_TCP << 24); + + netdev_dbg(ndev, + "Checksum Offload H0Info 0x%llX H1Info 0x%0llX\n", + (unsigned long long)msg_up16->H0Info_lsb, + (unsigned long long)msg_up16->H0Info_msb); + + if (unlikely(!(ndev->features & NETIF_F_TSO))) + goto out; + + /* TCP Segmentation offload processing */ + mss = skb_shinfo(skb)->gso_size; + all_hdr_len = maclen + ip_hdrlen(skb) + tcp_hdrlen(skb); + mss_len = skb->len - all_hdr_len; + + /* HW requires all header resides in the first buffer */ + if (nr_frags && (skb_headlen(skb) < all_hdr_len)) { + netdev_err(ndev, + "Unsupported header len location by Eth HW\n"); + pdev->stats.estats.tx_dropped++; + dev_kfree_skb(skb); + rc = -1; + goto out; + } + + if (!mss || mss_len <= mss) + goto out; + + if (mss != pdev->mss) { + xgene_enet_tx_offload(&pdev->priv, XGENE_ENET_MSS0, + mss); + pdev->mss = mss; + } + + msg_up16->H0Info_lsb |= ((0 & TSO_MSS_MASK) << 20) | + ((TSO_ENABLE & TSO_ENABLE_MASK) << 23); + netdev_dbg(ndev, "TSO H0Info 0x%llX H1Info 0x%0llX mss %d\n", + (unsigned long long)msg_up16->H0Info_lsb, + (unsigned long long)msg_up16->H0Info_msb, mss); + } else if (iph->protocol == IPPROTO_UDP) { + msg_up16->H0Info_lsb |= (UDP_HDR_SIZE & TSO_TCP_HLEN_MASK) + | ((ihl & TSO_IP_HLEN_MASK) << 6) + | (TSO_CHKSUM_ENABLE << 22) + | (TSO_IPPROTO_UDP << 24); + netdev_dbg(ndev, "Csum Offload H0Info 0x%llX H1Info 0x%0llX\n", + (unsigned long long)msg_up16->H0Info_lsb, + (unsigned long long)msg_up16->H0Info_msb); + } else { + msg_up16->H0Info_lsb |= ((ihl & TSO_IP_HLEN_MASK) << 6); + } +out: + return rc; +} + +static void xgene_enet_process_frags(struct net_device *ndev, + struct xgene_qmtm_msg16 *msg16, + struct xgene_enet_qcontext *c2e, + struct sk_buff *skb) +{ + struct xgene_qmtm_msg_up16 *msg_up16; + struct xgene_qmtm_msg_ext32 *msg32_2; + struct xgene_qmtm_msg_ext8 *ext_msg; + struct xgene_qmtm_msg_ll8 *ext_msg_ll8; + u32 qtail = c2e->qdesc->qtail; + phys_addr_t paddr = virt_to_phys(skb->data); + u32 nr_frags = skb_shinfo(skb)->nr_frags; + skb_frag_t *frag = NULL; + u8 *vaddr = NULL; + int frag_no = 0, len = 0, offset = 0; + int ell_bcnt = 0, ell_cnt = 0, i; + + msg_up16 = (struct xgene_qmtm_msg_up16 *)&msg16[1]; + msg32_2 = (struct xgene_qmtm_msg_ext32 *)&c2e->qdesc->msg32[qtail]; + + if (++qtail == c2e->qdesc->count) + qtail = 0; + + memset(msg32_2, 0, sizeof(struct xgene_qmtm_msg_ext32)); + + /* First Fragment, 64B message */ + msg16->BufDataLen = xgene_qmtm_encode_datalen(skb_headlen(skb)); + msg16->DataAddr = paddr; + msg16->NV = 1; + + /* 2nd, 3rd, and 4th fragments */ + ext_msg = &msg32_2->msg8_1; + + /* Terminate next pointers, will be updated later as required */ + msg32_2->msg8_2.NxtBufDataLength = 0x7800; + msg32_2->msg8_3.NxtBufDataLength = 0x7800; + msg32_2->msg8_4.NxtBufDataLength = 0x7800; + + for (i = 0; i < 3 && frag_no < nr_frags; i++) { + if (!vaddr) { + frag = &skb_shinfo(skb)->frags[frag_no]; + len = frag->size; + vaddr = skb_frag_address(frag); + offset = 0; + netdev_dbg(ndev, "SKB Frag[%d] 0x%p len %d\n", + frag_no, vaddr, len); + } + paddr = virt_to_phys(vaddr + offset); + ext_msg->NxtDataAddr = paddr; + + if (len <= 16 * 1024) { + /* Encode using 16K buffer size format */ + ext_msg->NxtBufDataLength = + xgene_qmtm_encode_datalen(len); + vaddr = NULL; + frag_no++; + } else { + len -= 16 * 1024; + offset += 16 * 1024; + /* Encode using 16K buffer size format */ + ext_msg->NxtBufDataLength = 0; + } + + netdev_dbg(ndev, "Frag[%d] PADDR 0x%llX len %d\n", i, + (unsigned long long)ext_msg->NxtDataAddr, + ext_msg->NxtBufDataLength); + ext_msg = (struct xgene_qmtm_msg_ext8 *) + (((u8 *) msg32_2) + (8 * ((i + 1) ^ 1))); + } + + /* Determine no more fragment, last one, or more than one */ + if (!vaddr) { + /* Check next fragment */ + if (frag_no >= nr_frags) { + goto out; + } else { + frag = &skb_shinfo(skb)->frags[frag_no]; + if (frag->size <= 16 * 1024 + && (frag_no + 1) >= nr_frags) + goto one_more_frag; + else + goto more_than_one_frag; + } + } else if (len <= 16 * 1024) { + /* Current fragment <= 16K, check if last fragment */ + if ((frag_no + 1) >= nr_frags) + goto one_more_frag; + else + goto more_than_one_frag; + } else { + /* Current fragment requires two pointers */ + goto more_than_one_frag; + } + +one_more_frag: + if (!vaddr) { + frag = &skb_shinfo(skb)->frags[frag_no]; + len = frag->size; + vaddr = skb_frag_address(frag); + offset = 0; + netdev_dbg(ndev, "SKB Frag[%d] 0x%p len %d\n", + frag_no, vaddr, len); + } + + paddr = virt_to_phys(vaddr + offset); + ext_msg->NxtDataAddr = paddr; + /* Encode using 16K buffer size format */ + ext_msg->NxtBufDataLength = xgene_qmtm_encode_datalen(len); + netdev_dbg(ndev, "Frag[%d] PADDR 0x%llX len %d\n", i, + (unsigned long long)ext_msg->NxtDataAddr, + ext_msg->NxtBufDataLength); + goto out; + +more_than_one_frag: + msg16->LL = 1; /* Extended link list */ + ext_msg_ll8 = &msg32_2->msg8_ll; + ext_msg = &c2e->msg8[qtail * 256]; + memset(ext_msg, 0, 255 * sizeof(struct xgene_qmtm_msg_ext8)); + paddr = virt_to_phys(ext_msg); + ext_msg_ll8->NxtDataPtr = paddr; + + for (i = 0; i < 255 && frag_no < nr_frags;) { + if (vaddr == NULL) { + frag = &skb_shinfo(skb)->frags[frag_no]; + len = frag->size; + vaddr = skb_frag_address(frag); + offset = 0; + netdev_dbg(ndev, "SKB Frag[%d] 0x%p len %d\n", + frag_no, vaddr, len); + } + paddr = virt_to_phys(vaddr + offset); + ext_msg[i ^ 1].NxtDataAddr = paddr; + + if (len <= 16 * 1024) { + /* Encode using 16K buffer size format */ + ext_msg[i ^ 1].NxtBufDataLength = + xgene_qmtm_encode_datalen(len); + ell_bcnt += len; + vaddr = NULL; + frag_no++; + } else { + len -= 16 * 1024; + offset += 16 * 1024; + ell_bcnt += 16 * 1024; + } + + ell_cnt++; + netdev_dbg(ndev, "Frag ELL[%d] PADDR 0x%llX len %d\n", i, + (unsigned long long)ext_msg[i ^ 1].NxtDataAddr, + ext_msg[i ^ 1].NxtBufDataLength); + i++; + } + + /* Encode the extended link list byte count and link count */ + ext_msg_ll8->NxtLinkListength = ell_cnt; + msg_up16->TotDataLengthLinkListLSBs = (ell_bcnt & 0xFFF); + ext_msg_ll8->TotDataLengthLinkListMSBs = ((ell_bcnt & 0xFF000) >> 12); + +out: + c2e->qdesc->qtail = qtail; +} + +/* Packet transmit function */ +static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_qcontext *c2e = pdev->tx[skb->queue_mapping]; + struct xgene_qmtm_msg16 *msg16; + struct xgene_qmtm_msg_up16 *msg_up16; + u32 nr_frags = skb_shinfo(skb)->nr_frags; + u32 nummsgs = (readl(c2e->nummsgs) & 0x1fffe) >> 1; + u32 cmd = 1; + + msg16 = + (struct xgene_qmtm_msg16 *)&c2e->qdesc->msg32[c2e->qdesc->qtail]; + msg_up16 = (struct xgene_qmtm_msg_up16 *)&msg16[1]; + + if (nummsgs > pdev->tx_cqt_hi) { + do { + nummsgs = (readl(c2e->nummsgs) & 0x1fffe) >> 1; + } while (nummsgs < pdev->tx_cqt_low); + } + + if (++c2e->qdesc->qtail == c2e->qdesc->count) + c2e->qdesc->qtail = 0; + + memset(msg16, 0, sizeof(struct xgene_qmtm_msg32)); + + if (likely(nr_frags == 0)) { + skb->len = (skb->len < 60) ? 60 : skb->len; + msg16->BufDataLen = xgene_qmtm_encode_datalen(skb->len); + msg16->DataAddr = virt_to_phys(skb->data); + } else { + xgene_enet_process_frags(ndev, msg16, c2e, skb); + cmd = 2; + } + + msg_up16->H0Info_msb = xgene_enet_enc_addr((void *)skb); + msg_up16->H0Enq_Num = c2e->eqnum; + msg16->C = 1; + + /* Set TYPE_SEL for egress work message */ + msg_up16->H0Info_lsb = (u64) TYPE_SEL_WORK_MSG << 44; + + /* Enable CRC insertion */ + if (!pdev->priv.crc) + msg_up16->H0Info_lsb |= (u64) ((u64) TSO_INS_CRC_ENABLE << 35); + + /* Setup mac header length H0Info */ + msg_up16->H0Info_lsb |= + ((xgene_enet_hdr_len(skb->data) & TSO_ETH_HLEN_MASK) << 12); + + if (unlikely(xgene_enet_checksum_offload(ndev, skb, msg_up16))) + return NETDEV_TX_OK; + + /* xmit: Push the work message to ENET HW */ + netdev_dbg(ndev, "TX CQID %d Addr 0x%llx len %d\n", + msg_up16->H0Enq_Num, + (unsigned long long)msg16->DataAddr, msg16->BufDataLen); + writel(cmd, c2e->qdesc->command); + + ndev->trans_start = jiffies; + return NETDEV_TX_OK; +} + +int xgene_enet_check_skb(struct net_device *ndev, + struct sk_buff *skb, + struct xgene_qmtm_msg32 *msg32_1, u32 qid) +{ + struct xgene_qmtm_msg16 *msg16 = &msg32_1->msg16; + u32 UserInfo = msg16->UserInfo; + u8 NV = msg16->NV; + int rc = 0; + + if (unlikely(!skb)) { + netdev_err(ndev, "ENET skb NULL UserInfo %d QID %d FP 0x%x\n", + UserInfo, qid, msg16->FPQNum); + print_hex_dump(KERN_INFO, "QM msg:", + DUMP_PREFIX_ADDRESS, 16, 4, msg32_1, + NV ? 64 : 32, 1); + rc = -1; + goto out; + } + + if (unlikely(!skb->head) || unlikely(!skb->data)) { + netdev_err(ndev, "ENET skb 0x%p head 0x%p data 0x%p FP 0x%x\n", + skb, skb->head, skb->data, msg16->FPQNum); + print_hex_dump(KERN_INFO, "QM msg:", + DUMP_PREFIX_ADDRESS, 16, 4, msg32_1, + NV ? 64 : 32, 1); + rc = -1; + goto out; + } + + if (unlikely(skb->len)) { + netdev_err(ndev, "ENET skb 0x%p len %d FP 0x%x\n", skb, + skb->len, msg16->FPQNum); + print_hex_dump(KERN_INFO, "QM msg:", + DUMP_PREFIX_ADDRESS, 16, 4, msg32_1, + NV ? 64 : 32, 1); + rc = -1; + goto out; + } + +out: + return rc; +} + +inline void xgene_enet_skip_csum(struct sk_buff *skb) +{ + struct iphdr *iph = (struct iphdr *)skb->data; + if (likely(!(iph->frag_off & htons(IP_MF | IP_OFFSET))) + || likely(iph->protocol != IPPROTO_TCP + && iph->protocol != IPPROTO_UDP)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } +} + +/* Process received frame */ +static int xgene_enet_rx_frame(struct xgene_enet_qcontext *e2c, + struct xgene_qmtm_msg32 *msg32_1) +{ + struct xgene_enet_qcontext *c2e = e2c->c2e_skb; + struct xgene_enet_pdev *pdev = e2c->pdev; + struct net_device *ndev = pdev->ndev; + struct xgene_qmtm_msg16 *msg16 = &msg32_1->msg16; + struct sk_buff *skb = NULL; + u32 data_len = xgene_qmtm_decode_datalen(msg16->BufDataLen); + u8 NV = msg16->NV; + u8 LErr = ((u8) msg16->ELErr << 3) | msg16->LErr; + u32 UserInfo = msg16->UserInfo; + u32 qid = pdev->qm_queues.rx[e2c->queue_index].qid; + + if (unlikely(UserInfo == 0)) { + netdev_err(ndev, "ENET UserInfo NULL QID %d FP 0x%x\n", + qid, msg16->FPQNum); + print_hex_dump(KERN_INFO, "QM msg:", + DUMP_PREFIX_ADDRESS, 16, 4, msg32_1, + NV ? 64 : 32, 1); + goto err_refill; + } + + skb = xgene_enet_get_skb(msg16); + if (unlikely(xgene_enet_check_skb(ndev, skb, msg32_1, qid))) + goto err_refill; + + /* Check for error, if packet received with error */ + if (unlikely(LErr)) { + if (LErr == 0x15) /* ignore rx queue full error */ + goto process_pkt; + if (LErr == 0x10 || LErr == 0x11) { + LErr = 0; + goto process_pkt; + } + if (LErr == 0x10 || LErr == 5) { + LErr = 0; + goto process_pkt; + } + + netdev_dbg(ndev, "ENET LErr 0x%x skb 0x%p FP 0x%x\n", + LErr, skb, msg16->FPQNum); + print_hex_dump(KERN_ERR, "QM Msg: ", + DUMP_PREFIX_ADDRESS, 16, 4, msg32_1, + NV ? 64 : 32, 1); + goto err_refill; + } + +process_pkt: + prefetch(skb->data - NET_IP_ALIGN); + + if (likely(!NV)) { + /* Strip off CRC as HW isn't doing this */ + data_len -= 4; + skb_put(skb, data_len); + netdev_dbg(ndev, "RX port %d SKB len %d\n", + xgene_enet_get_port(pdev), data_len); + } + + if (--e2c->c2e_count == 0) { + xgene_enet_refill_fp(c2e, 32); + e2c->c2e_count = 32; + } + + if (pdev->num_rx_queues > 1) + skb_record_rx_queue(skb, e2c->queue_index); + + skb->protocol = eth_type_trans(skb, ndev); + if (likely(ndev->features & NETIF_F_IP_CSUM) + && likely(LErr == 0) + && likely(skb->protocol == htons(ETH_P_IP))) { + xgene_enet_skip_csum(skb); + } + + napi_gro_receive(&e2c->napi, skb); + return 0; + +err_refill: + if (skb != NULL) + dev_kfree_skb_any(skb); + + xgene_enet_refill_fp(e2c->c2e_skb, 1); + + if (LErr != 0x15) + pdev->stats.estats.rx_hw_errors++; + else + pdev->stats.estats.rx_hw_overrun++; + + return -1; +} + +static int xgene_enet_dequeue_msg(struct xgene_enet_qcontext *e2c, int budget) +{ + u32 processed = 0; + u32 command = 0; + u32 qhead = e2c->qdesc->qhead; + u32 count = e2c->qdesc->count; + u16 nummsgs; + + while (budget--) { + struct xgene_qmtm_msg32 *msg32_1 = &e2c->qdesc->msg32[qhead]; + struct xgene_qmtm_msg_ext32 *msg32_2 = NULL; + + if (unlikely(((u32 *) msg32_1)[EMPTY_SLOT_INDEX] == EMPTY_SLOT)) + break; + + command--; + + if (msg32_1->msg16.FPQNum) + xgene_enet_rx_frame(e2c, msg32_1); + else + xgene_enet_tx_completion(e2c, msg32_1); + + if (++qhead == count) + qhead = 0; + + if (msg32_1->msg16.NV) { + msg32_2 = (struct xgene_qmtm_msg_ext32 *) + &e2c->qdesc->msg32[qhead]; + if (unlikely(((u32 *) msg32_2)[EMPTY_SLOT_INDEX] + == EMPTY_SLOT)) { + command++; + if (!qhead) + qhead = count; + qhead--; + break; + } + command--; + if (++qhead == count) + qhead = 0; + } + + ((u32 *) msg32_1)[EMPTY_SLOT_INDEX] = EMPTY_SLOT; + if (msg32_2) + ((u32 *) msg32_2)[EMPTY_SLOT_INDEX] = EMPTY_SLOT; + processed++; + } + + do { + nummsgs = (readl(e2c->nummsgs) & 0x1fffe) >> 1; + } while (nummsgs < (1 + ~command)); + writel(command, e2c->qdesc->command); + e2c->qdesc->qhead = qhead; + + return processed; +} + +static int xgene_enet_napi(struct napi_struct *napi, const int budget) +{ + struct xgene_enet_qcontext *e2c = + container_of(napi, struct xgene_enet_qcontext, napi); + int processed = xgene_enet_dequeue_msg(e2c, budget); + + if (processed != budget) { + napi_complete(napi); + enable_irq(e2c->qdesc->irq); + } + + return processed; +} + +static void xgene_enet_timeout(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + xgene_enet_mac_reset(&pdev->priv); +} + +static void xgene_enet_napi_add(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + netif_napi_add(pdev->ndev, &pdev->rx[qindex]->napi, + xgene_enet_napi, 64); +} + +static void xgene_enet_napi_del(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + netif_napi_del(&pdev->rx[qindex]->napi); +} + +static void xgene_enet_napi_enable(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + napi_enable(&pdev->rx[qindex]->napi); +} + +static void xgene_enet_napi_disable(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + napi_disable(&pdev->rx[qindex]->napi); +} + +static void xgene_enet_irq_enable(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + enable_irq(pdev->rx[qindex]->qdesc->irq); +} + +static void xgene_enet_irq_disable_all(struct xgene_enet_pdev *pdev) +{ + u32 qindex; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + disable_irq_nosync(pdev->rx[qindex]->qdesc->irq); +} + +static int xgene_enet_open(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &pdev->priv; + + xgene_enet_napi_enable(pdev); + xgene_enet_irq_enable(pdev); + + netif_tx_start_all_queues(ndev); + netif_carrier_on(ndev); + + if (pdev->phy_dev) + phy_start(pdev->phy_dev); + + xgene_enet_mac_tx_state(priv, 1); + xgene_enet_mac_rx_state(priv, 1); + + return 0; +} + +static int xgene_enet_close(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &pdev->priv; + u32 qindex; + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + netif_tx_disable(ndev); + + if (pdev->phy_dev) + phy_stop(pdev->phy_dev); + + xgene_enet_mac_tx_state(priv, 0); + xgene_enet_mac_rx_state(priv, 0); + + xgene_enet_irq_disable_all(pdev); + xgene_enet_napi_disable(pdev); + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) + xgene_enet_dequeue_msg(pdev->rx[qindex], -1); + + return 0; +} + +static struct xgene_enet_qcontext *xgene_enet_allocq(struct xgene_enet_pdev + *pdev, + struct xgene_qmtm_qinfo + *qinfo, + struct xgene_qmtm_sdev + *sdev, u8 qtype, u8 qsize) +{ + struct xgene_enet_qcontext *qc; + + memset(qinfo, 0, sizeof(struct xgene_qmtm_qinfo)); + qinfo->sdev = sdev; + qinfo->qaccess = QACCESS_ALT; + qinfo->qtype = qtype; + qinfo->qsize = qsize; + qinfo->flags = XGENE_SLAVE_DEFAULT_FLAGS; + + if (xgene_qmtm_set_qinfo(qinfo)) { + netdev_err(pdev->ndev, "Could not allocate queue\n"); + return NULL; + } + + qc = (struct xgene_enet_qcontext *) + kmalloc(sizeof(struct xgene_enet_qcontext), + GFP_KERNEL | __GFP_ZERO); + qc->nummsgs = &(((u32 *) qinfo->qfabric)[1]); + qc->qdesc = qinfo->qdesc; + qc->pdev = pdev; + + return qc; +} + +static int xgene_enet_qconfig(struct xgene_enet_pdev *pdev) +{ + struct xgene_qmtm_qinfo qinfo; + struct xgene_qmtm_sdev *sdev = pdev->sdev; + struct xgene_qmtm_sdev *idev = pdev->sdev->idev; + int qmtm_ip = sdev->qmtm_ip; + int port = pdev->priv.port; + int rc = 0; + u32 qindex; + struct xgene_enet_qcontext *e2c; + struct xgene_enet_qcontext *c2e; + + memset(&pdev->qm_queues, 0, sizeof(struct eth_queue_ids)); + pdev->qm_queues.qm_ip = qmtm_ip; + + for (qindex = 0; qindex < pdev->num_tx_queues; qindex++) { + /* Allocate EGRESS work queues from CPUx to ETHx */ + c2e = xgene_enet_allocq(pdev, &qinfo, sdev, + QTYPE_PQ, QSIZE_64KB); + if (!c2e) + goto out; + + pdev->qm_queues.tx[qindex].qid = qinfo.queue_id; + + /* Setup TX Frame cpu_to_enet info */ + c2e->msg8 = + (struct xgene_qmtm_msg_ext8 *) + kmalloc(sizeof(struct xgene_qmtm_msg_ext8) * 256 * + c2e->qdesc->count, GFP_KERNEL); + c2e->queue_index = qindex; + pdev->tx[qindex] = c2e; + /* Assign TX completn queue threshold based on rx queue size */ + pdev->tx_cqt_hi = c2e->qdesc->count / 4; + pdev->tx_cqt_low = pdev->tx_cqt_low / 16; + } + + pdev->qm_queues.default_tx_qid = pdev->qm_queues.tx[0].qid; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) { + /* Allocate INGRESS work queue from ETHx to CPUx */ + u8 qsize = QSIZE_512KB; + e2c = xgene_enet_allocq(pdev, &qinfo, idev, + QTYPE_PQ, qsize); + if (!e2c) + goto out; + + pdev->qm_queues.rx[qindex].qid = qinfo.queue_id; + e2c->queue_index = qindex; + snprintf(e2c->irq_name, sizeof(e2c->irq_name), "%s-rx%d", + pdev->ndev->name, qindex); + e2c->c2e_count = 1; + pdev->rx[qindex] = e2c; + + /* Allocate free pool for ETHx from CPUx */ + c2e = xgene_enet_allocq(pdev, &qinfo, sdev, + QTYPE_FP, QSIZE_16KB); + if (!c2e) + goto out; + + pdev->qm_queues.rx_fp[qindex].qid = qinfo.queue_id; + pdev->qm_queues.rx_fp[qindex].pbn = qinfo.pbn; + + c2e->eqnum = QMTM_QUEUE_ID(qmtm_ip, qinfo.queue_id); + c2e->buf_size = XGENE_ENET_PKT_BUF_SIZE; + pdev->rx_skb_pool[qindex] = c2e; + pdev->rx[qindex]->c2e_skb = pdev->rx_skb_pool[qindex]; + + /* Configure free pool */ + xgene_enet_init_fp(pdev->rx_skb_pool[qindex], + pdev->rx_buff_cnt); + } + + for (qindex = 0; qindex < pdev->num_tx_queues; qindex++) { + u32 cqindex = pdev->num_tx_queues - qindex - 1; + u32 rqindex = qindex % pdev->num_rx_queues; + + pdev->tx[cqindex]->nummsgs = pdev->rx[rqindex]->nummsgs; + pdev->tx[cqindex]->eqnum = QMTM_QUEUE_ID(qmtm_ip, + pdev->qm_queues. + rx[rqindex].qid); + } + + pdev->qm_queues.default_hw_tx_qid = pdev->qm_queues.hw_tx[0].qid; + pdev->qm_queues.default_rx_qid = pdev->qm_queues.rx[0].qid; + pdev->qm_queues.default_rx_fp_qid = pdev->qm_queues.rx_fp[0].qid; + pdev->qm_queues.default_rx_fp_pbn = pdev->qm_queues.rx_fp[0].pbn; + pdev->qm_queues.default_rx_nxtfp_qid = pdev->qm_queues.rx_nxtfp[0].qid; + pdev->qm_queues.default_rx_nxtfp_pbn = pdev->qm_queues.rx_nxtfp[0].pbn; + + netdev_dbg(pdev->ndev, "Port %d CQID %d FP %d FP PBN %d\n", + port, pdev->qm_queues.default_comp_qid, + pdev->qm_queues.default_rx_fp_qid, + pdev->qm_queues.default_rx_fp_pbn); + +out: + return rc; +} + +static void xgene_enet_delete_queue(struct xgene_enet_pdev *pdev) +{ + struct xgene_qmtm_qinfo qinfo; + u32 qindex; + u8 qmtm_ip = pdev->sdev->qmtm_ip; + u16 queue_id; + + qinfo.qmtm_ip = qmtm_ip; + + for (qindex = 0; qindex < pdev->num_tx_queues; qindex++) { + queue_id = pdev->qm_queues.tx[qindex].qid; + + if (queue_id) { + qinfo.queue_id = queue_id; + xgene_qmtm_clr_qinfo(&qinfo); + } + } + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) { + queue_id = pdev->qm_queues.rx[qindex].qid; + + if (queue_id) { + qinfo.queue_id = queue_id; + xgene_qmtm_clr_qinfo(&qinfo); + } + + queue_id = pdev->qm_queues.rx_fp[qindex].qid; + + if (queue_id) { + qinfo.queue_id = queue_id; + xgene_qmtm_clr_qinfo(&qinfo); + } + } +} + +static struct net_device_stats *xgene_enet_stats(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &(pdev->priv); + struct net_device_stats *nst = &pdev->nstats; + struct xgene_enet_detailed_stats detailed_stats; + struct xgene_enet_rx_stats *rx_stats; + struct xgene_enet_tx_stats *tx_stats; + u32 pkt_bytes, crc_bytes = 4; + + memset(&detailed_stats, 0, sizeof(struct xgene_enet_detailed_stats)); + + rx_stats = &detailed_stats.rx_stats; + tx_stats = &detailed_stats.tx_stats; + + local_irq_disable(); + xgene_enet_get_stats(priv, &detailed_stats); + + pkt_bytes = rx_stats->rx_byte_count; + pkt_bytes -= (rx_stats->rx_packet_count * crc_bytes); + nst->rx_packets += rx_stats->rx_packet_count; + nst->rx_bytes += pkt_bytes; + + pkt_bytes = tx_stats->tx_byte_count; + pkt_bytes -= (tx_stats->tx_pkt_count * crc_bytes); + nst->tx_packets += tx_stats->tx_pkt_count; + nst->tx_bytes += pkt_bytes; + + nst->rx_dropped += rx_stats->rx_drop_pkt_count; + nst->tx_dropped += tx_stats->tx_drop_frm_count; + + nst->rx_crc_errors += rx_stats->rx_fcs_err_count; + nst->rx_length_errors += rx_stats->rx_frm_len_err_pkt_count; + nst->rx_frame_errors += rx_stats->rx_alignment_err_pkt_count; + nst->rx_over_errors += (rx_stats->rx_oversize_pkt_count + + pdev->stats.estats.rx_hw_overrun); + + nst->rx_errors += (rx_stats->rx_fcs_err_count + + rx_stats->rx_frm_len_err_pkt_count + + rx_stats->rx_oversize_pkt_count + + rx_stats->rx_undersize_pkt_count + + pdev->stats.estats.rx_hw_overrun + + pdev->stats.estats.rx_hw_errors); + + nst->tx_errors += tx_stats->tx_fcs_err_frm_count + + tx_stats->tx_undersize_frm_count; + + local_irq_enable(); + + pdev->stats.estats.rx_hw_errors = 0; + pdev->stats.estats.rx_hw_overrun = 0; + + return nst; +} + +static int xgene_enet_set_mac_address(struct net_device *ndev, void *p) +{ + struct xgene_enet_pdev *pdev = netdev_priv(ndev); + struct xgene_enet_priv *priv = &(pdev->priv); + struct sockaddr *addr = p; + + if (netif_running(ndev)) + return -EBUSY; + + memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len); + xgene_enet_set_mac_addr(priv, (unsigned char *)(ndev->dev_addr)); + return 0; +} + +/* net_device_ops structure for data path ethernet */ +static const struct net_device_ops apm_dnetdev_ops = { + .ndo_open = xgene_enet_open, + .ndo_stop = xgene_enet_close, + .ndo_select_queue = xgene_enet_select_queue, + .ndo_start_xmit = xgene_enet_start_xmit, + .ndo_tx_timeout = xgene_enet_timeout, + .ndo_get_stats = xgene_enet_stats, + .ndo_change_mtu = xgene_enet_change_mtu, + .ndo_set_mac_address = xgene_enet_set_mac_address, +}; + +static void xgene_enet_register_irq(struct net_device *ndev) +{ + struct xgene_enet_pdev *pdev; + struct device *dev; + u32 qindex; + + pdev = (struct xgene_enet_pdev *)netdev_priv(ndev); + dev = &pdev->plat_dev->dev; + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) { + if (devm_request_irq(dev, pdev->rx[qindex]->qdesc->irq, + xgene_enet_e2c_irq, 0, + pdev->rx[qindex]->irq_name, + (void *)pdev->rx[qindex]) != 0) { + netdev_err(ndev, "request_irq failed %d for RX Frame\n", + pdev->rx[qindex]->qdesc->irq); + return; + } + + /* Disable interrupts for RX queue mailboxes */ + disable_irq_nosync(pdev->rx[qindex]->qdesc->irq); + } +} + +static int xgene_enet_get_resources(struct xgene_enet_pdev *pdev) +{ + struct platform_device *plat_dev; + struct net_device *ndev; + struct device *dev; + struct xgene_enet_priv *priv; + struct xgene_qmtm_sdev *sdev; + struct xgene_enet_platform_data pdata; + struct resource *res; + u64 csr_paddr; + void *csr_addr; + int i, rc; + + plat_dev = pdev->plat_dev; + dev = &plat_dev->dev; + ndev = pdev->ndev; + priv = &pdev->priv; + + rc = of_property_read_u32(plat_dev->dev.of_node, "devid", + &pdata.port_id); + if (rc || pdata.port_id >= MAX_ENET_PORTS) { + dev_err(dev, "No device ID or invalid value %d\n", + pdata.port_id); + goto out; + } + priv->port = pdata.port_id; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); + rc = -ENODEV; + goto out; + } + csr_paddr = res->start; + csr_addr = devm_ioremap(&plat_dev->dev, csr_paddr, resource_size(res)); + priv->ppaddr_base = csr_paddr; + priv->vpaddr_base = csr_addr; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "Unable to retrieve ENET Global CSR region\n"); + rc = -ENODEV; + goto out; + } + csr_paddr = res->start; + csr_addr = devm_ioremap(&plat_dev->dev, csr_paddr, resource_size(res)); + priv->paddr_base = csr_paddr; + priv->vaddr_base = csr_addr; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(dev, "Unable to retrieve ENET MII access region\n"); + rc = -ENODEV; + goto out; + } + csr_paddr = res->start; + csr_addr = devm_ioremap(&plat_dev->dev, csr_paddr, resource_size(res)); + priv->vmii_base = csr_addr; + + rc = of_property_read_string(plat_dev->dev.of_node, "slave-name", + &pdata.sname); + + sdev = xgene_qmtm_get_sdev((char *)pdata.sname); + if (!sdev) { + dev_err(dev, "QMTM Slave %s error\n", pdata.sname); + rc = -ENODEV; + goto out; + } + pdev->sdev = sdev; + + rc = of_property_read_u32(plat_dev->dev.of_node, "phyid", + &pdata.phy_id); + if (rc || pdata.phy_id > 0x1F) { + dev_err(dev, "No phy ID or invalid value in DTS\n"); + rc = -EINVAL; + goto out; + } + priv->phy_addr = pdata.phy_id; + + rc = of_property_read_u8_array(plat_dev->dev.of_node, + "local-mac-address", pdata.ethaddr, + ARRAY_SIZE(pdata.ethaddr)); + if (rc) { + dev_err(dev, "Can't get Device MAC address\n"); + } else { + for (i = 0; i < ETH_ALEN; i++) + ndev->dev_addr[i] = pdata.ethaddr[i] & 0xff; + } + + pdev->clk = clk_get(&plat_dev->dev, NULL); + + if (IS_ERR(pdev->clk)) + dev_err(&plat_dev->dev, "can't get clock\n"); + else if (clk_prepare_enable(pdev->clk)) + dev_err(&plat_dev->dev, "clock prepare enable failed"); + + priv->phy_mode = PHY_MODE_RGMII; + pdev->rx_buff_cnt = XGENE_NUM_PKT_BUF; + +out: + return rc; +} + +static int xgene_enet_init_hw(struct xgene_enet_pdev *pdev) +{ + struct net_device *ndev; + struct xgene_enet_priv *priv; + struct mii_bus *mdio_bus; + int rc = 0; + + ndev = pdev->ndev; + priv = &pdev->priv; + + xgene_enet_port_reset(priv); + + /* To ensure no packet enters the system, disable Rx/Tx */ + xgene_enet_mac_tx_state(priv, 0); + xgene_enet_mac_rx_state(priv, 0); + + ndev->netdev_ops = &apm_dnetdev_ops; + + ndev->features |= NETIF_F_IP_CSUM; + ndev->features |= NETIF_F_TSO | NETIF_F_SG; + pdev->mss = DEFAULT_TCP_MSS; + xgene_enet_tx_offload(priv, XGENE_ENET_MSS0, pdev->mss); + ndev->features |= NETIF_F_GRO; + + /* Ethtool checks the capabilities/features in hw_features flag */ + ndev->hw_features = ndev->features; + + rc = register_netdev(ndev); + if (rc) { + netdev_err(ndev, "Failed to register net dev(%d)!\n", rc); + goto out; + } + + rc = xgene_enet_qconfig(pdev); + if (rc) { + netdev_err(ndev, "Error in QM configuration\n"); + goto out; + } + + xgene_enet_napi_add(pdev); + + xgene_enet_cle_bypass(priv, QMTM_QUEUE_ID(pdev->sdev->qmtm_ip, + pdev->qm_queues. + default_rx_qid), + pdev->qm_queues.default_rx_fp_pbn - 0x20); + + /* Default MAC initialization */ + xgene_enet_mac_init(priv, ndev->dev_addr, SPEED_1000, + HW_MTU(ndev->mtu), priv->crc); + + /* Setup MDIO bus */ + mdio_bus = mdiobus_alloc(); + if (!mdio_bus) { + netdev_err(ndev, "Not able to allocate memory for MDIO bus\n"); + rc = -ENOMEM; + goto out; + } + + pdev->mdio_bus = mdio_bus; + mdio_bus->name = "APM Ethernet MII Bus"; + mdio_bus->read = xgene_enet_mdio_read; + mdio_bus->write = xgene_enet_mdio_write; + snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%x", priv->port); + mdio_bus->priv = pdev; + mdio_bus->parent = &ndev->dev; + mdio_bus->phy_mask = ~(1 << priv->phy_addr); + rc = mdiobus_register(mdio_bus); + if (rc) { + netdev_err(ndev, "Failed to register MDIO bus(%d)!\n", rc); + return rc; + } + + rc = xgene_enet_mdio_probe(ndev); + xgene_enet_register_irq(ndev); + +out: + return rc; +} + +static int xgene_enet_probe(struct platform_device *plat_dev) +{ + struct net_device *ndev; + struct xgene_enet_pdev *pdev; + struct device *dev; + struct xgene_enet_priv *priv; + u32 num_tx_queues, num_rx_queues; + int rc; + + dev = &plat_dev->dev; + num_tx_queues = MAX_TX_QUEUES; + num_rx_queues = MAX_RX_QUEUES; + + ndev = alloc_etherdev_mqs(sizeof(struct xgene_enet_pdev), + num_tx_queues, num_rx_queues); + + if (!ndev) { + dev_err(dev, "Not able to allocate memory for netdev\n"); + rc = -ENOMEM; + goto out; + } + + pdev = (struct xgene_enet_pdev *)netdev_priv(ndev); + priv = &pdev->priv; + pdev->ndev = ndev; + pdev->num_tx_queues = num_tx_queues; + pdev->num_rx_queues = num_rx_queues; + pdev->plat_dev = plat_dev; + pdev->node = plat_dev->dev.of_node; + SET_NETDEV_DEV(ndev, &plat_dev->dev); + dev_set_drvdata(&plat_dev->dev, pdev); + + xgene_enet_get_resources(pdev); + + xgene_enet_init_priv(priv); + rc = xgene_enet_init_hw(pdev); + +out: + return rc; +} + +static int xgene_enet_remove(struct platform_device *plat_dev) +{ + struct xgene_enet_pdev *pdev; + struct xgene_enet_priv *priv; + struct net_device *ndev; + int port; + u32 qindex; + u8 qmtm_ip; + + pdev = platform_get_drvdata(plat_dev); + qmtm_ip = pdev->sdev->qmtm_ip; + ndev = pdev->ndev; + priv = &pdev->priv; + + port = xgene_enet_get_port(pdev); + + /* Stop any traffic and disable MAC */ + xgene_enet_mac_rx_state(priv, 0); + xgene_enet_mac_tx_state(priv, 0); + + if (netif_running(ndev)) { + netif_device_detach(ndev); + netif_stop_queue(ndev); + xgene_enet_napi_disable(pdev); + } + + xgene_enet_napi_del(pdev); + xgene_enet_mdio_remove(ndev); + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) { + if (pdev->qm_queues.rx_fp[qindex].qid > 0) + xgene_enet_deinit_fp(pdev->rx_skb_pool[qindex], + pdev->qm_queues.rx_fp[qindex].qid); + } + + xgene_enet_delete_queue(pdev); + + for (qindex = 0; qindex < pdev->num_rx_queues; qindex++) { + kfree(pdev->rx_skb_pool[qindex]); + kfree(pdev->rx[qindex]); + } + for (qindex = 0; qindex < pdev->num_tx_queues; qindex++) { + kfree(pdev->tx[qindex]->msg8); + kfree(pdev->tx[qindex]); + } + + unregister_netdev(ndev); + xgene_enet_port_shutdown(priv); + + free_netdev(ndev); + + return 0; +} + +static struct of_device_id xgene_enet_match[] = { + { + .compatible = "apm,xgene-enet", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, xgene_enet_match); + +static struct platform_driver xgene_enet_driver = { + .driver = { + .name = XGENE_ENET_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = xgene_enet_match, + }, + .probe = xgene_enet_probe, + .remove = xgene_enet_remove, +}; + +static int __init xgene_enet_init(void) +{ + if (!platform_driver_register(&xgene_enet_driver)) + pr_info("%s v%s loaded\n", XGENE_ENET_DRIVER_DESC, + XGENE_ENET_DRIVER_VERSION); + + return 0; +} + +static void __exit xgene_enet_exit(void) +{ + platform_driver_unregister(&xgene_enet_driver); + pr_info("%s v%s unloaded\n", XGENE_ENET_DRIVER_DESC, + XGENE_ENET_DRIVER_VERSION); +} + +module_init(xgene_enet_init); +module_exit(xgene_enet_exit); + +MODULE_DESCRIPTION(XGENE_ENET_DRIVER_DESC); +MODULE_VERSION(XGENE_ENET_DRIVER_VERSION); +MODULE_AUTHOR("Keyur Chudgar <kchudgar@xxxxxxx>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h new file mode 100644 index 0000000..15ea995 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -0,0 +1,172 @@ +/* AppliedMicro X-Gene SoC Ethernet Driver + * + * Copyright (c) 2013, Applied Micro Circuits Corporation + * Authors: Ravi Patel <rapatel@xxxxxxx> + * Iyappan Subramanian <isubramanian@xxxxxxx> + * Fushen Chen <fchen@xxxxxxx> + * Keyur Chudgar <kchudgar@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __XGENE_ENET_MAIN_H__ +#define __XGENE_ENET_MAIN_H__ + +#include <linux/clk.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/module.h> +#include <net/ip.h> +#include <linux/tcp.h> +#include <linux/interrupt.h> +#include <linux/if_vlan.h> +#include <linux/phy.h> +#include <linux/netdevice.h> +#include <linux/io.h> +#include <misc/xgene/xgene_qmtm.h> +#include "xgene_enet_common.h" + +#define XGENE_ENET_DRIVER_NAME "xgene-enet" +#define XGENE_ENET_DRIVER_VERSION "1.0" +#define XGENE_ENET_DRIVER_DESC "APM X-Gene SoC Ethernet driver" + +#define XGENE_ENET_MIN_MTU 64 +#define XGENE_ENET_MAX_MTU 10000 + +/* Note: PKT_BUF_SIZE & PKT_NXTBUF_SIZE has to be one of the following: + * 256, 1K, 2K, 4K, 16K for ethernet to work with optimum performance. + */ +#define XGENE_ENET_PKT_BUF_SIZE 2048 +#define XGENE_NUM_PKT_BUF 256 + +/* define Enet system struct */ +struct xgene_enet_dev { + int refcnt; + struct timer_list link_poll_timer; + int ipp_loaded; + int ipp_hw_mtu; +}; + +enum xgene_enet_phy_poll_interval { + PHY_POLL_LINK_ON = HZ, + PHY_POLL_LINK_OFF = (HZ / 5) +}; + +enum xgene_enet_debug_cmd { + XGENE_ENET_READ_CMD, + XGENE_ENET_WRITE_CMD, + XGENE_ENET_MAX_CMD +}; + +#define MAX_TX_QUEUES 1 +#define MAX_RX_QUEUES 1 + +/* This is soft flow context of queue */ +struct xgene_enet_qcontext { + struct xgene_enet_pdev *pdev; + struct xgene_qmtm_qdesc *qdesc; + struct xgene_qmtm_msg_ext8 *msg8; + u32 *nummsgs; + unsigned int queue_index; + unsigned int eqnum; + u32 buf_size; + unsigned int c2e_count; + struct xgene_enet_qcontext *c2e_skb; + struct xgene_enet_qcontext *c2e_page; + struct napi_struct napi; + char irq_name[16]; +}; + +/* Queues related parameters per Enet port */ +#define ENET_MAX_PBN 8 +#define ENET_MAX_QSEL 8 + +struct eth_wqids { + u16 qtype; + u16 qid; + u16 arb; + u16 qcount; + u16 qsel[ENET_MAX_QSEL]; +}; + +struct eth_fqids { + u16 qid; + u16 pbn; +}; + +struct eth_queue_ids { + u16 default_tx_qid; + u16 tx_count; + u16 tx_idx; + struct eth_wqids tx[ENET_MAX_PBN]; + u16 default_rx_qid; + u16 rx_count; + u16 rx_idx; + struct eth_wqids rx[ENET_MAX_PBN]; + u16 default_rx_fp_qid; + u16 default_rx_fp_pbn; + struct eth_fqids rx_fp[ENET_MAX_PBN]; + u16 default_rx_nxtfp_qid; + u16 default_rx_nxtfp_pbn; + struct eth_fqids rx_nxtfp[ENET_MAX_PBN]; + struct eth_fqids hw_fp; + u16 default_hw_tx_qid; + struct eth_fqids hw_tx[ENET_MAX_PBN]; + struct eth_wqids comp[ENET_MAX_PBN]; + u16 default_comp_qid; + u32 qm_ip; +}; + +struct xgene_enet_platform_data { + u32 port_id; + const char *sname; + u32 phy_id; + u8 ethaddr[6]; +}; + +/* APM ethernet per port data */ +struct xgene_enet_pdev { + struct net_device *ndev; + struct mii_bus *mdio_bus; + struct phy_device *phy_dev; + int phy_link; + int phy_speed; + struct clk *clk; + struct device_node *node; + struct platform_device *plat_dev; + struct xgene_qmtm_sdev *sdev; + struct xgene_enet_qcontext *tx[MAX_TX_QUEUES]; + struct xgene_enet_qcontext *rx_skb_pool[MAX_RX_QUEUES]; + u32 num_tx_queues; + struct xgene_enet_qcontext *rx[MAX_RX_QUEUES]; + struct xgene_enet_qcontext *tx_completion[MAX_TX_QUEUES]; + u32 num_rx_queues; + struct net_device_stats nstats; + struct xgene_enet_detailed_stats stats; + char *dev_name; + int uc_count; + struct eth_queue_ids qm_queues; + u32 rx_buff_cnt, tx_cqt_low, tx_cqt_hi; + int mss; + struct xgene_enet_priv priv; +}; + +/* Ethernet raw register write, read routines */ +void xgene_enet_wr32(void *addr, u32 data); +void xgene_enet_rd32(void *addr, u32 *data); + +u32 xgene_enet_get_port(struct xgene_enet_pdev *pdev); + +void xgene_enet_init_priv(struct xgene_enet_priv *priv); + +int xgene_enet_parse_error(u8 LErr, int qid); +void xgene_enet_register_err_irqs(struct net_device *ndev); + +#endif /* __XGENE_ENET_MAIN_H__ */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html