diff options
Diffstat (limited to 'drivers/net/ethernet/apm')
23 files changed, 2352 insertions, 348 deletions
diff --git a/drivers/net/ethernet/apm/Kconfig b/drivers/net/ethernet/apm/Kconfig index ec63d706d464..59efe5b145dd 100644 --- a/drivers/net/ethernet/apm/Kconfig +++ b/drivers/net/ethernet/apm/Kconfig @@ -1 +1,2 @@ source "drivers/net/ethernet/apm/xgene/Kconfig" +source "drivers/net/ethernet/apm/xgene-v2/Kconfig" diff --git a/drivers/net/ethernet/apm/Makefile b/drivers/net/ethernet/apm/Makefile index 65ce32ad1b2c..946b2a4c882d 100644 --- a/drivers/net/ethernet/apm/Makefile +++ b/drivers/net/ethernet/apm/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_NET_XGENE) += xgene/ +obj-$(CONFIG_NET_XGENE_V2) += xgene-v2/ diff --git a/drivers/net/ethernet/apm/xgene-v2/Kconfig b/drivers/net/ethernet/apm/xgene-v2/Kconfig new file mode 100644 index 000000000000..1205861b6318 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/Kconfig @@ -0,0 +1,11 @@ +config NET_XGENE_V2 + tristate "APM X-Gene SoC Ethernet-v2 Driver" + depends on HAS_DMA + depends on ARCH_XGENE || COMPILE_TEST + help + This is the Ethernet driver for the on-chip ethernet interface + which uses a linked list of DMA descriptor architecture (v2) for + APM X-Gene SoCs. + + To compile this driver as a module, choose M here. This module will + be called xgene-enet-v2. diff --git a/drivers/net/ethernet/apm/xgene-v2/Makefile b/drivers/net/ethernet/apm/xgene-v2/Makefile new file mode 100644 index 000000000000..f16a2b3dde8b --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for APM X-Gene Ethernet v2 driver +# + +xgene-enet-v2-objs := main.o mac.o enet.o ring.o mdio.o ethtool.o +obj-$(CONFIG_NET_XGENE_V2) += xgene-enet-v2.o diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.c b/drivers/net/ethernet/apm/xgene-v2/enet.c new file mode 100644 index 000000000000..5998da014923 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/enet.c @@ -0,0 +1,83 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +void xge_wr_csr(struct xge_pdata *pdata, u32 offset, u32 val) +{ + void __iomem *addr = pdata->resources.base_addr + offset; + + iowrite32(val, addr); +} + +u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset) +{ + void __iomem *addr = pdata->resources.base_addr + offset; + + return ioread32(addr); +} + +int xge_port_reset(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + u32 data, wait = 10; + + xge_wr_csr(pdata, ENET_CLKEN, 0x3); + xge_wr_csr(pdata, ENET_SRST, 0xf); + xge_wr_csr(pdata, ENET_SRST, 0); + xge_wr_csr(pdata, CFG_MEM_RAM_SHUTDOWN, 1); + xge_wr_csr(pdata, CFG_MEM_RAM_SHUTDOWN, 0); + + do { + usleep_range(100, 110); + data = xge_rd_csr(pdata, BLOCK_MEM_RDY); + } while (data != MEM_RDY && wait--); + + if (data != MEM_RDY) { + dev_err(dev, "ECC init failed: %x\n", data); + return -ETIMEDOUT; + } + + xge_wr_csr(pdata, ENET_SHIM, DEVM_ARAUX_COH | DEVM_AWAUX_COH); + + return 0; +} + +static void xge_traffic_resume(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + xge_wr_csr(pdata, CFG_FORCE_LINK_STATUS_EN, 1); + xge_wr_csr(pdata, FORCE_LINK_STATUS, 1); + + xge_wr_csr(pdata, CFG_LINK_AGGR_RESUME, 1); + xge_wr_csr(pdata, RX_DV_GATE_REG, 1); +} + +void xge_port_init(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + pdata->phy_speed = SPEED_1000; + xge_mac_init(pdata); + xge_traffic_resume(ndev); +} diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.h b/drivers/net/ethernet/apm/xgene-v2/enet.h new file mode 100644 index 000000000000..3fd36dc66a23 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/enet.h @@ -0,0 +1,45 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __XGENE_ENET_V2_ENET_H__ +#define __XGENE_ENET_V2_ENET_H__ + +#define ENET_CLKEN 0xc008 +#define ENET_SRST 0xc000 +#define ENET_SHIM 0xc010 +#define CFG_MEM_RAM_SHUTDOWN 0xd070 +#define BLOCK_MEM_RDY 0xd074 + +#define MEM_RDY 0xffffffff +#define DEVM_ARAUX_COH BIT(19) +#define DEVM_AWAUX_COH BIT(3) + +#define CFG_FORCE_LINK_STATUS_EN 0x229c +#define FORCE_LINK_STATUS 0x22a0 +#define CFG_LINK_AGGR_RESUME 0x27c8 +#define RX_DV_GATE_REG 0x2dfc + +void xge_wr_csr(struct xge_pdata *pdata, u32 offset, u32 val); +u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset); +int xge_port_reset(struct net_device *ndev); +void xge_port_init(struct net_device *ndev); + +#endif /* __XGENE_ENET_V2_ENET__H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/ethtool.c b/drivers/net/ethernet/apm/xgene-v2/ethtool.c new file mode 100644 index 000000000000..d31ad8270d93 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.c @@ -0,0 +1,189 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +#define XGE_STAT(m) { #m, offsetof(struct xge_pdata, stats.m) } +#define XGE_EXTD_STAT(m, n) \ + { \ + #m, \ + n, \ + 0 \ + } + +static const struct xge_gstrings_stats gstrings_stats[] = { + XGE_STAT(rx_packets), + XGE_STAT(tx_packets), + XGE_STAT(rx_bytes), + XGE_STAT(tx_bytes), + XGE_STAT(rx_errors) +}; + +static struct xge_gstrings_extd_stats gstrings_extd_stats[] = { + XGE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64), + XGE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127), + XGE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255), + XGE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511), + XGE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K), + XGE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX), + XGE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV), + XGE_EXTD_STAT(rx_fcs_error_cntr, RFCS), + XGE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA), + XGE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA), + XGE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF), + XGE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF), + XGE_EXTD_STAT(rx_unk_opcode_cntr, RXUO), + XGE_EXTD_STAT(rx_align_err_cntr, RALN), + XGE_EXTD_STAT(rx_frame_len_err_cntr, RFLR), + XGE_EXTD_STAT(rx_code_err_cntr, RCDE), + XGE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE), + XGE_EXTD_STAT(rx_undersize_pkt_cntr, RUND), + XGE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR), + XGE_EXTD_STAT(rx_fragments_cntr, RFRG), + XGE_EXTD_STAT(rx_jabber_cntr, RJBR), + XGE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP), + XGE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA), + XGE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA), + XGE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF), + XGE_EXTD_STAT(tx_defer_pkt_cntr, TDFR), + XGE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF), + XGE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL), + XGE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL), + XGE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL), + XGE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL), + XGE_EXTD_STAT(tx_total_col_cntr, TNCL), + XGE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH), + XGE_EXTD_STAT(tx_drop_frame_cntr, TDRP), + XGE_EXTD_STAT(tx_jabber_frame_cntr, TJBR), + XGE_EXTD_STAT(tx_fcs_error_cntr, TFCS), + XGE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF), + XGE_EXTD_STAT(tx_oversize_frame_cntr, TOVR), + XGE_EXTD_STAT(tx_undersize_frame_cntr, TUND), + XGE_EXTD_STAT(tx_fragments_cntr, TFRG) +}; + +#define XGE_STATS_LEN ARRAY_SIZE(gstrings_stats) +#define XGE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) + +static void xge_mac_get_extd_stats(struct xge_pdata *pdata) +{ + u32 data; + int i; + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) { + data = xge_rd_csr(pdata, gstrings_extd_stats[i].addr); + gstrings_extd_stats[i].value += data; + } +} + +static void xge_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct platform_device *pdev = pdata->pdev; + + strcpy(info->driver, "xgene-enet-v2"); + strcpy(info->version, XGENE_ENET_V2_VERSION); + snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A"); + sprintf(info->bus_info, "%s", pdev->name); +} + +static void xge_get_strings(struct net_device *ndev, u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < XGE_STATS_LEN; i++) { + memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) { + memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } +} + +static int xge_get_sset_count(struct net_device *ndev, int sset) +{ + if (sset != ETH_SS_STATS) + return -EINVAL; + + return XGE_STATS_LEN + XGE_EXTD_STATS_LEN; +} + +static void xge_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *dummy, + u64 *data) +{ + void *pdata = netdev_priv(ndev); + int i; + + for (i = 0; i < XGE_STATS_LEN; i++) + *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); + + xge_mac_get_extd_stats(pdata); + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) + *data++ = gstrings_extd_stats[i].value; +} + +static int xge_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + phy_ethtool_ksettings_get(phydev, cmd); + + return 0; +} + +static int xge_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_ksettings_set(phydev, cmd); +} + +static const struct ethtool_ops xge_ethtool_ops = { + .get_drvinfo = xge_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = xge_get_strings, + .get_sset_count = xge_get_sset_count, + .get_ethtool_stats = xge_get_ethtool_stats, + .get_link_ksettings = xge_get_link_ksettings, + .set_link_ksettings = xge_set_link_ksettings, +}; + +void xge_set_ethtool_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &xge_ethtool_ops; +} diff --git a/drivers/net/ethernet/apm/xgene-v2/ethtool.h b/drivers/net/ethernet/apm/xgene-v2/ethtool.h new file mode 100644 index 000000000000..54b48d5561b8 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.h @@ -0,0 +1,78 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __XGENE_ENET_V2_ETHTOOL_H__ +#define __XGENE_ENET_V2_ETHTOOL_H__ + +struct xge_gstrings_stats { + char name[ETH_GSTRING_LEN]; + int offset; +}; + +struct xge_gstrings_extd_stats { + char name[ETH_GSTRING_LEN]; + u32 addr; + u32 value; +}; + +#define TR64 0xa080 +#define TR127 0xa084 +#define TR255 0xa088 +#define TR511 0xa08c +#define TR1K 0xa090 +#define TRMAX 0xa094 +#define TRMGV 0xa098 +#define RFCS 0xa0a4 +#define RMCA 0xa0a8 +#define RBCA 0xa0ac +#define RXCF 0xa0b0 +#define RXPF 0xa0b4 +#define RXUO 0xa0b8 +#define RALN 0xa0bc +#define RFLR 0xa0c0 +#define RCDE 0xa0c4 +#define RCSE 0xa0c8 +#define RUND 0xa0cc +#define ROVR 0xa0d0 +#define RFRG 0xa0d4 +#define RJBR 0xa0d8 +#define RDRP 0xa0dc +#define TMCA 0xa0e8 +#define TBCA 0xa0ec +#define TXPF 0xa0f0 +#define TDFR 0xa0f4 +#define TEDF 0xa0f8 +#define TSCL 0xa0fc +#define TMCL 0xa100 +#define TLCL 0xa104 +#define TXCL 0xa108 +#define TNCL 0xa10c +#define TPFH 0xa110 +#define TDRP 0xa114 +#define TJBR 0xa118 +#define TFCS 0xa11c +#define TXCF 0xa120 +#define TOVR 0xa124 +#define TUND 0xa128 +#define TFRG 0xa12c + +void xge_set_ethtool_ops(struct net_device *ndev); + +#endif /* __XGENE_ENET_V2_ETHTOOL_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.c b/drivers/net/ethernet/apm/xgene-v2/mac.c new file mode 100644 index 000000000000..ee431e397e57 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mac.c @@ -0,0 +1,116 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +void xge_mac_reset(struct xge_pdata *pdata) +{ + xge_wr_csr(pdata, MAC_CONFIG_1, SOFT_RESET); + xge_wr_csr(pdata, MAC_CONFIG_1, 0); +} + +void xge_mac_set_speed(struct xge_pdata *pdata) +{ + u32 icm0, icm2, ecm0, mc2; + u32 intf_ctrl, rgmii; + + icm0 = xge_rd_csr(pdata, ICM_CONFIG0_REG_0); + icm2 = xge_rd_csr(pdata, ICM_CONFIG2_REG_0); + ecm0 = xge_rd_csr(pdata, ECM_CONFIG0_REG_0); + rgmii = xge_rd_csr(pdata, RGMII_REG_0); + mc2 = xge_rd_csr(pdata, MAC_CONFIG_2); + intf_ctrl = xge_rd_csr(pdata, INTERFACE_CONTROL); + icm2 |= CFG_WAITASYNCRD_EN; + + switch (pdata->phy_speed) { + case SPEED_10: + SET_REG_BITS(&mc2, INTF_MODE, 1); + SET_REG_BITS(&intf_ctrl, HD_MODE, 0); + SET_REG_BITS(&icm0, CFG_MACMODE, 0); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 500); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 0); + break; + case SPEED_100: + SET_REG_BITS(&mc2, INTF_MODE, 1); + SET_REG_BITS(&intf_ctrl, HD_MODE, 1); + SET_REG_BITS(&icm0, CFG_MACMODE, 1); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 80); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 0); + break; + default: + SET_REG_BITS(&mc2, INTF_MODE, 2); + SET_REG_BITS(&intf_ctrl, HD_MODE, 2); + SET_REG_BITS(&icm0, CFG_MACMODE, 2); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 16); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 1); + break; + } + + mc2 |= FULL_DUPLEX | CRC_EN | PAD_CRC; + SET_REG_BITS(&ecm0, CFG_WFIFOFULLTHR, 0x32); + + xge_wr_csr(pdata, MAC_CONFIG_2, mc2); + xge_wr_csr(pdata, INTERFACE_CONTROL, intf_ctrl); + xge_wr_csr(pdata, RGMII_REG_0, rgmii); + xge_wr_csr(pdata, ICM_CONFIG0_REG_0, icm0); + xge_wr_csr(pdata, ICM_CONFIG2_REG_0, icm2); + xge_wr_csr(pdata, ECM_CONFIG0_REG_0, ecm0); +} + +void xge_mac_set_station_addr(struct xge_pdata *pdata) +{ + u8 *dev_addr = pdata->ndev->dev_addr; + u32 addr0, addr1; + + addr0 = (dev_addr[3] << 24) | (dev_addr[2] << 16) | + (dev_addr[1] << 8) | dev_addr[0]; + addr1 = (dev_addr[5] << 24) | (dev_addr[4] << 16); + + xge_wr_csr(pdata, STATION_ADDR0, addr0); + xge_wr_csr(pdata, STATION_ADDR1, addr1); +} + +void xge_mac_init(struct xge_pdata *pdata) +{ + xge_mac_reset(pdata); + xge_mac_set_speed(pdata); + xge_mac_set_station_addr(pdata); +} + +void xge_mac_enable(struct xge_pdata *pdata) +{ + u32 data; + + data = xge_rd_csr(pdata, MAC_CONFIG_1); + data |= TX_EN | RX_EN; + xge_wr_csr(pdata, MAC_CONFIG_1, data); + + data = xge_rd_csr(pdata, MAC_CONFIG_1); +} + +void xge_mac_disable(struct xge_pdata *pdata) +{ + u32 data; + + data = xge_rd_csr(pdata, MAC_CONFIG_1); + data &= ~(TX_EN | RX_EN); + xge_wr_csr(pdata, MAC_CONFIG_1, data); +} diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h new file mode 100644 index 000000000000..3c83fa617356 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -0,0 +1,107 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __XGENE_ENET_V2_MAC_H__ +#define __XGENE_ENET_V2_MAC_H__ + +/* Register offsets */ +#define MAC_CONFIG_1 0xa000 +#define MAC_CONFIG_2 0xa004 +#define MII_MGMT_CONFIG 0xa020 +#define MII_MGMT_COMMAND 0xa024 +#define MII_MGMT_ADDRESS 0xa028 +#define MII_MGMT_CONTROL 0xa02c +#define MII_MGMT_STATUS 0xa030 +#define MII_MGMT_INDICATORS 0xa034 +#define INTERFACE_CONTROL 0xa038 +#define STATION_ADDR0 0xa040 +#define STATION_ADDR1 0xa044 + +#define RGMII_REG_0 0x27e0 +#define ICM_CONFIG0_REG_0 0x2c00 +#define ICM_CONFIG2_REG_0 0x2c08 +#define ECM_CONFIG0_REG_0 0x2d00 + +/* Register fields */ +#define SOFT_RESET BIT(31) +#define TX_EN BIT(0) +#define RX_EN BIT(2) +#define PAD_CRC BIT(2) +#define CRC_EN BIT(1) +#define FULL_DUPLEX BIT(0) + +#define INTF_MODE_POS 8 +#define INTF_MODE_LEN 2 +#define HD_MODE_POS 25 +#define HD_MODE_LEN 2 +#define CFG_MACMODE_POS 18 +#define CFG_MACMODE_LEN 2 +#define CFG_WAITASYNCRD_POS 0 +#define CFG_WAITASYNCRD_LEN 16 +#define CFG_SPEED_125_POS 24 +#define CFG_WFIFOFULLTHR_POS 0 +#define CFG_WFIFOFULLTHR_LEN 7 +#define MGMT_CLOCK_SEL_POS 0 +#define MGMT_CLOCK_SEL_LEN 3 +#define PHY_ADDR_POS 8 +#define PHY_ADDR_LEN 5 +#define REG_ADDR_POS 0 +#define REG_ADDR_LEN 5 +#define MII_MGMT_BUSY BIT(0) +#define MII_READ_CYCLE BIT(0) +#define CFG_WAITASYNCRD_EN BIT(16) + +static inline void xgene_set_reg_bits(u32 *var, int pos, int len, u32 val) +{ + u32 mask = GENMASK(pos + len, pos); + + *var &= ~mask; + *var |= ((val << pos) & mask); +} + +static inline u32 xgene_get_reg_bits(u32 var, int pos, int len) +{ + u32 mask = GENMASK(pos + len, pos); + + return (var & mask) >> pos; +} + +#define SET_REG_BITS(var, field, val) \ + xgene_set_reg_bits(var, field ## _POS, field ## _LEN, val) + +#define SET_REG_BIT(var, field, val) \ + xgene_set_reg_bits(var, field ## _POS, 1, val) + +#define GET_REG_BITS(var, field) \ + xgene_get_reg_bits(var, field ## _POS, field ## _LEN) + +#define GET_REG_BIT(var, field) ((var) & (field)) + +struct xge_pdata; + +void xge_mac_reset(struct xge_pdata *pdata); +void xge_mac_set_speed(struct xge_pdata *pdata); +void xge_mac_enable(struct xge_pdata *pdata); +void xge_mac_disable(struct xge_pdata *pdata); +void xge_mac_init(struct xge_pdata *pdata); +void xge_mac_set_station_addr(struct xge_pdata *pdata); + +#endif /* __XGENE_ENET_V2_MAC_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c new file mode 100644 index 000000000000..0f2ad50f3bd7 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -0,0 +1,759 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +static const struct acpi_device_id xge_acpi_match[]; + +static int xge_get_resources(struct xge_pdata *pdata) +{ + struct platform_device *pdev; + struct net_device *ndev; + int phy_mode, ret = 0; + struct resource *res; + struct device *dev; + + pdev = pdata->pdev; + dev = &pdev->dev; + ndev = pdata->ndev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Resource enet_csr not defined\n"); + return -ENODEV; + } + + pdata->resources.base_addr = devm_ioremap(dev, res->start, + resource_size(res)); + if (!pdata->resources.base_addr) { + dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); + return -ENOMEM; + } + + if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN)) + eth_hw_addr_random(ndev); + + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); + + phy_mode = device_get_phy_mode(dev); + if (phy_mode < 0) { + dev_err(dev, "Unable to get phy-connection-type\n"); + return phy_mode; + } + pdata->resources.phy_mode = phy_mode; + + if (pdata->resources.phy_mode != PHY_INTERFACE_MODE_RGMII) { + dev_err(dev, "Incorrect phy-connection-type specified\n"); + return -ENODEV; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "Unable to get irq\n"); + return ret; + } + pdata->resources.irq = ret; + + return 0; +} + +static int xge_refill_buffers(struct net_device *ndev, u32 nbuf) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring = pdata->rx_ring; + const u8 slots = XGENE_ENET_NUM_DESC - 1; + struct device *dev = &pdata->pdev->dev; + struct xge_raw_desc *raw_desc; + u64 addr_lo, addr_hi; + u8 tail = ring->tail; + struct sk_buff *skb; + dma_addr_t dma_addr; + u16 len; + int i; + + for (i = 0; i < nbuf; i++) { + raw_desc = &ring->raw_desc[tail]; + + len = XGENE_ENET_STD_MTU; + skb = netdev_alloc_skb(ndev, len); + if (unlikely(!skb)) + return -ENOMEM; + + dma_addr = dma_map_single(dev, skb->data, len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + netdev_err(ndev, "DMA mapping error\n"); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + ring->pkt_info[tail].skb = skb; + ring->pkt_info[tail].dma_addr = dma_addr; + + addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); + addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | + SET_BITS(NEXT_DESC_ADDRH, addr_hi) | + SET_BITS(PKT_ADDRH, + upper_32_bits(dma_addr))); + + dma_wmb(); + raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | + SET_BITS(E, 1)); + tail = (tail + 1) & slots; + } + + ring->tail = tail; + + return 0; +} + +static int xge_init_hw(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = xge_port_reset(ndev); + if (ret) + return ret; + + xge_port_init(ndev); + pdata->nbufs = NUM_BUFS; + + return 0; +} + +static irqreturn_t xge_irq(const int irq, void *data) +{ + struct xge_pdata *pdata = data; + + if (napi_schedule_prep(&pdata->napi)) { + xge_intr_disable(pdata); + __napi_schedule(&pdata->napi); + } + + return IRQ_HANDLED; +} + +static int xge_request_irq(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + snprintf(pdata->irq_name, IRQ_ID_SIZE, "%s", ndev->name); + + ret = request_irq(pdata->resources.irq, xge_irq, 0, pdata->irq_name, + pdata); + if (ret) + netdev_err(ndev, "Failed to request irq %s\n", pdata->irq_name); + + return ret; +} + +static void xge_free_irq(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + free_irq(pdata->resources.irq, pdata); +} + +static bool is_tx_slot_available(struct xge_raw_desc *raw_desc) +{ + if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && + (GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)) == SLOT_EMPTY)) + return true; + + return false; +} + +static netdev_tx_t xge_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + static dma_addr_t dma_addr; + u64 addr_lo, addr_hi; + void *pkt_buf; + u8 tail; + u16 len; + + tx_ring = pdata->tx_ring; + tail = tx_ring->tail; + len = skb_headlen(skb); + raw_desc = &tx_ring->raw_desc[tail]; + + if (!is_tx_slot_available(raw_desc)) { + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + /* Packet buffers should be 64B aligned */ + pkt_buf = dma_zalloc_coherent(dev, XGENE_ENET_STD_MTU, &dma_addr, + GFP_ATOMIC); + if (unlikely(!pkt_buf)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + memcpy(pkt_buf, skb->data, len); + + addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); + addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | + SET_BITS(NEXT_DESC_ADDRH, addr_hi) | + SET_BITS(PKT_ADDRH, + upper_32_bits(dma_addr))); + + tx_ring->pkt_info[tail].skb = skb; + tx_ring->pkt_info[tail].dma_addr = dma_addr; + tx_ring->pkt_info[tail].pkt_buf = pkt_buf; + + dma_wmb(); + + raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | + SET_BITS(PKT_SIZE, len) | + SET_BITS(E, 0)); + skb_tx_timestamp(skb); + xge_wr_csr(pdata, DMATXCTRL, 1); + + tx_ring->tail = (tail + 1) & (XGENE_ENET_NUM_DESC - 1); + + return NETDEV_TX_OK; +} + +static bool is_tx_hw_done(struct xge_raw_desc *raw_desc) +{ + if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && + !GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0))) + return true; + + return false; +} + +static void xge_txc_poll(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + void *pkt_buf; + u32 data; + u8 head; + + tx_ring = pdata->tx_ring; + head = tx_ring->head; + + data = xge_rd_csr(pdata, DMATXSTATUS); + if (!GET_BITS(TXPKTCOUNT, data)) + return; + + while (1) { + raw_desc = &tx_ring->raw_desc[head]; + + if (!is_tx_hw_done(raw_desc)) + break; + + dma_rmb(); + + skb = tx_ring->pkt_info[head].skb; + dma_addr = tx_ring->pkt_info[head].dma_addr; + pkt_buf = tx_ring->pkt_info[head].pkt_buf; + pdata->stats.tx_packets++; + pdata->stats.tx_bytes += skb->len; + dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); + dev_kfree_skb_any(skb); + + /* clear pktstart address and pktsize */ + raw_desc->m0 = cpu_to_le64(SET_BITS(E, 1) | + SET_BITS(PKT_SIZE, SLOT_EMPTY)); + xge_wr_csr(pdata, DMATXSTATUS, 1); + + head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); + } + + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + tx_ring->head = head; +} + +static int xge_rx_poll(struct net_device *ndev, unsigned int budget) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *rx_ring; + struct xge_raw_desc *raw_desc; + struct sk_buff *skb; + dma_addr_t dma_addr; + int processed = 0; + u8 head, rx_error; + int i, ret; + u32 data; + u16 len; + + rx_ring = pdata->rx_ring; + head = rx_ring->head; + + data = xge_rd_csr(pdata, DMARXSTATUS); + if (!GET_BITS(RXPKTCOUNT, data)) + return 0; + + for (i = 0; i < budget; i++) { + raw_desc = &rx_ring->raw_desc[head]; + + if (GET_BITS(E, le64_to_cpu(raw_desc->m0))) + break; + + dma_rmb(); + + skb = rx_ring->pkt_info[head].skb; + rx_ring->pkt_info[head].skb = NULL; + dma_addr = rx_ring->pkt_info[head].dma_addr; + len = GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)); + dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, + DMA_FROM_DEVICE); + + rx_error = GET_BITS(D, le64_to_cpu(raw_desc->m2)); + if (unlikely(rx_error)) { + pdata->stats.rx_errors++; + dev_kfree_skb_any(skb); + goto out; + } + + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, ndev); + + pdata->stats.rx_packets++; + pdata->stats.rx_bytes += len; + napi_gro_receive(&pdata->napi, skb); +out: + ret = xge_refill_buffers(ndev, 1); + xge_wr_csr(pdata, DMARXSTATUS, 1); + xge_wr_csr(pdata, DMARXCTRL, 1); + + if (ret) + break; + + head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); + processed++; + } + + rx_ring->head = head; + + return processed; +} + +static void xge_delete_desc_ring(struct net_device *ndev, + struct xge_desc_ring *ring) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + u16 size; + + if (!ring) + return; + + size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; + if (ring->desc_addr) + dma_free_coherent(dev, size, ring->desc_addr, ring->dma_addr); + + kfree(ring->pkt_info); + kfree(ring); +} + +static void xge_free_buffers(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring = pdata->rx_ring; + struct device *dev = &pdata->pdev->dev; + struct sk_buff *skb; + dma_addr_t dma_addr; + int i; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + skb = ring->pkt_info[i].skb; + dma_addr = ring->pkt_info[i].dma_addr; + + if (!skb) + continue; + + dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } +} + +static void xge_delete_desc_rings(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + xge_txc_poll(ndev); + xge_delete_desc_ring(ndev, pdata->tx_ring); + + xge_rx_poll(ndev, 64); + xge_free_buffers(ndev); + xge_delete_desc_ring(ndev, pdata->rx_ring); +} + +static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *ring; + u16 size; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + return NULL; + + ring->ndev = ndev; + + size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; + ring->desc_addr = dma_zalloc_coherent(dev, size, &ring->dma_addr, + GFP_KERNEL); + if (!ring->desc_addr) + goto err; + + ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(*ring->pkt_info), + GFP_KERNEL); + if (!ring->pkt_info) + goto err; + + xge_setup_desc(ring); + + return ring; + +err: + xge_delete_desc_ring(ndev, ring); + + return NULL; +} + +static int xge_create_desc_rings(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring; + int ret; + + /* create tx ring */ + ring = xge_create_desc_ring(ndev); + if (!ring) + goto err; + + pdata->tx_ring = ring; + xge_update_tx_desc_addr(pdata); + + /* create rx ring */ + ring = xge_create_desc_ring(ndev); + if (!ring) + goto err; + + pdata->rx_ring = ring; + xge_update_rx_desc_addr(pdata); + + ret = xge_refill_buffers(ndev, XGENE_ENET_NUM_DESC); + if (ret) + goto err; + + return 0; +err: + xge_delete_desc_rings(ndev); + + return -ENOMEM; +} + +static int xge_open(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = xge_create_desc_rings(ndev); + if (ret) + return ret; + + napi_enable(&pdata->napi); + ret = xge_request_irq(ndev); + if (ret) + return ret; + + xge_intr_enable(pdata); + xge_wr_csr(pdata, DMARXCTRL, 1); + + phy_start(ndev->phydev); + xge_mac_enable(pdata); + netif_start_queue(ndev); + + return 0; +} + +static int xge_close(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + netif_stop_queue(ndev); + xge_mac_disable(pdata); + phy_stop(ndev->phydev); + + xge_intr_disable(pdata); + xge_free_irq(ndev); + napi_disable(&pdata->napi); + xge_delete_desc_rings(ndev); + + return 0; +} + +static int xge_napi(struct napi_struct *napi, const int budget) +{ + struct net_device *ndev = napi->dev; + struct xge_pdata *pdata; + int processed; + + pdata = netdev_priv(ndev); + + xge_txc_poll(ndev); + processed = xge_rx_poll(ndev, budget); + + if (processed < budget) { + napi_complete_done(napi, processed); + xge_intr_enable(pdata); + } + + return processed; +} + +static int xge_set_mac_addr(struct net_device *ndev, void *addr) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = eth_mac_addr(ndev, addr); + if (ret) + return ret; + + xge_mac_set_station_addr(pdata); + + return 0; +} + +static bool is_tx_pending(struct xge_raw_desc *raw_desc) +{ + if (!GET_BITS(E, le64_to_cpu(raw_desc->m0))) + return true; + + return false; +} + +static void xge_free_pending_skb(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + void *pkt_buf; + int i; + + tx_ring = pdata->tx_ring; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + raw_desc = &tx_ring->raw_desc[i]; + + if (!is_tx_pending(raw_desc)) + continue; + + skb = tx_ring->pkt_info[i].skb; + dma_addr = tx_ring->pkt_info[i].dma_addr; + pkt_buf = tx_ring->pkt_info[i].pkt_buf; + dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); + dev_kfree_skb_any(skb); + } +} + +static void xge_timeout(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + rtnl_lock(); + + if (!netif_running(ndev)) + goto out; + + netif_stop_queue(ndev); + xge_intr_disable(pdata); + napi_disable(&pdata->napi); + + xge_wr_csr(pdata, DMATXCTRL, 0); + xge_txc_poll(ndev); + xge_free_pending_skb(ndev); + xge_wr_csr(pdata, DMATXSTATUS, ~0U); + + xge_setup_desc(pdata->tx_ring); + xge_update_tx_desc_addr(pdata); + xge_mac_init(pdata); + + napi_enable(&pdata->napi); + xge_intr_enable(pdata); + xge_mac_enable(pdata); + netif_start_queue(ndev); + +out: + rtnl_unlock(); +} + +static void xge_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *storage) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_stats *stats = &pdata->stats; + + storage->tx_packets += stats->tx_packets; + storage->tx_bytes += stats->tx_bytes; + + storage->rx_packets += stats->rx_packets; + storage->rx_bytes += stats->rx_bytes; + storage->rx_errors += stats->rx_errors; +} + +static const struct net_device_ops xgene_ndev_ops = { + .ndo_open = xge_open, + .ndo_stop = xge_close, + .ndo_start_xmit = xge_start_xmit, + .ndo_set_mac_address = xge_set_mac_addr, + .ndo_tx_timeout = xge_timeout, + .ndo_get_stats64 = xge_get_stats64, +}; + +static int xge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct xge_pdata *pdata; + int ret; + + ndev = alloc_etherdev(sizeof(*pdata)); + if (!ndev) + return -ENOMEM; + + pdata = netdev_priv(ndev); + + pdata->pdev = pdev; + pdata->ndev = ndev; + SET_NETDEV_DEV(ndev, dev); + platform_set_drvdata(pdev, pdata); + ndev->netdev_ops = &xgene_ndev_ops; + + ndev->features |= NETIF_F_GSO | + NETIF_F_GRO; + + ret = xge_get_resources(pdata); + if (ret) + goto err; + + ndev->hw_features = ndev->features; + xge_set_ethtool_ops(ndev); + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + netdev_err(ndev, "No usable DMA configuration\n"); + goto err; + } + + ret = xge_init_hw(ndev); + if (ret) + goto err; + + ret = xge_mdio_config(ndev); + if (ret) + goto err; + + netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); + + ret = register_netdev(ndev); + if (ret) { + netdev_err(ndev, "Failed to register netdev\n"); + goto err; + } + + return 0; + +err: + free_netdev(ndev); + + return ret; +} + +static int xge_remove(struct platform_device *pdev) +{ + struct xge_pdata *pdata; + struct net_device *ndev; + + pdata = platform_get_drvdata(pdev); + ndev = pdata->ndev; + + rtnl_lock(); + if (netif_running(ndev)) + dev_close(ndev); + rtnl_unlock(); + + xge_mdio_remove(ndev); + unregister_netdev(ndev); + free_netdev(ndev); + + return 0; +} + +static void xge_shutdown(struct platform_device *pdev) +{ + struct xge_pdata *pdata; + + pdata = platform_get_drvdata(pdev); + if (!pdata) + return; + + if (!pdata->ndev) + return; + + xge_remove(pdev); +} + +static const struct acpi_device_id xge_acpi_match[] = { + { "APMC0D80" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xge_acpi_match); + +static struct platform_driver xge_driver = { + .driver = { + .name = "xgene-enet-v2", + .acpi_match_table = ACPI_PTR(xge_acpi_match), + }, + .probe = xge_probe, + .remove = xge_remove, + .shutdown = xge_shutdown, +}; +module_platform_driver(xge_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC Ethernet v2 driver"); +MODULE_AUTHOR("Iyappan Subramanian <[email protected]>"); +MODULE_VERSION(XGENE_ENET_V2_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h new file mode 100644 index 000000000000..969b258cb7de --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -0,0 +1,80 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __XGENE_ENET_V2_MAIN_H__ +#define __XGENE_ENET_V2_MAIN_H__ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/efi.h> +#include <linux/if_vlan.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> +#include <linux/prefetch.h> +#include <linux/phy.h> +#include <net/ip.h> +#include "mac.h" +#include "enet.h" +#include "ring.h" +#include "ethtool.h" + +#define XGENE_ENET_V2_VERSION "v1.0" +#define XGENE_ENET_STD_MTU 1536 +#define XGENE_ENET_MIN_FRAME 60 +#define IRQ_ID_SIZE 16 + +struct xge_resource { + void __iomem *base_addr; + int phy_mode; + u32 irq; +}; + +struct xge_stats { + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; + u64 rx_errors; +}; + +/* ethernet private data */ +struct xge_pdata { + struct xge_resource resources; + struct xge_desc_ring *tx_ring; + struct xge_desc_ring *rx_ring; + struct platform_device *pdev; + char irq_name[IRQ_ID_SIZE]; + struct mii_bus *mdio_bus; + struct net_device *ndev; + struct napi_struct napi; + struct xge_stats stats; + int phy_speed; + u8 nbufs; +}; + +int xge_mdio_config(struct net_device *ndev); +void xge_mdio_remove(struct net_device *ndev); + +#endif /* __XGENE_ENET_V2_MAIN_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/mdio.c b/drivers/net/ethernet/apm/xgene-v2/mdio.c new file mode 100644 index 000000000000..f5fe3bb2e59d --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mdio.c @@ -0,0 +1,168 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +static int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) +{ + struct xge_pdata *pdata = bus->priv; + u32 done, val = 0; + u8 wait = 10; + + SET_REG_BITS(&val, PHY_ADDR, phy_id); + SET_REG_BITS(&val, REG_ADDR, reg); + xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); + + xge_wr_csr(pdata, MII_MGMT_CONTROL, data); + do { + usleep_range(5, 10); + done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); + } while ((done & MII_MGMT_BUSY) && wait--); + + if (done & MII_MGMT_BUSY) { + dev_err(&bus->dev, "MII_MGMT write failed\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct xge_pdata *pdata = bus->priv; + u32 data, done, val = 0; + u8 wait = 10; + + SET_REG_BITS(&val, PHY_ADDR, phy_id); + SET_REG_BITS(&val, REG_ADDR, reg); + xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); + + xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE); + do { + usleep_range(5, 10); + done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); + } while ((done & MII_MGMT_BUSY) && wait--); + + if (done & MII_MGMT_BUSY) { + dev_err(&bus->dev, "MII_MGMT read failed\n"); + return -ETIMEDOUT; + } + + data = xge_rd_csr(pdata, MII_MGMT_STATUS); + xge_wr_csr(pdata, MII_MGMT_COMMAND, 0); + + return data; +} + +static void xge_adjust_link(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + + if (phydev->link) { + if (pdata->phy_speed != phydev->speed) { + pdata->phy_speed = phydev->speed; + xge_mac_set_speed(pdata); + xge_mac_enable(pdata); + phy_print_status(phydev); + } + } else { + if (pdata->phy_speed != SPEED_UNKNOWN) { + pdata->phy_speed = SPEED_UNKNOWN; + xge_mac_disable(pdata); + phy_print_status(phydev); + } + } +} + +void xge_mdio_remove(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct mii_bus *mdio_bus = pdata->mdio_bus; + + if (ndev->phydev) + phy_disconnect(ndev->phydev); + + if (mdio_bus->state == MDIOBUS_REGISTERED) + mdiobus_unregister(mdio_bus); + + mdiobus_free(mdio_bus); +} + +int xge_mdio_config(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct mii_bus *mdio_bus; + struct phy_device *phydev; + int ret; + + mdio_bus = mdiobus_alloc(); + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus"; + mdio_bus->read = xge_mdio_read; + mdio_bus->write = xge_mdio_write; + mdio_bus->priv = pdata; + mdio_bus->parent = dev; + snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); + pdata->mdio_bus = mdio_bus; + + mdio_bus->phy_mask = 0x1; + ret = mdiobus_register(mdio_bus); + if (ret) + goto err; + + phydev = phy_find_first(mdio_bus); + if (!phydev) { + dev_err(dev, "no PHY found\n"); + ret = -ENODEV; + goto err; + } + phydev = phy_connect(ndev, phydev_name(phydev), + &xge_adjust_link, + pdata->resources.phy_mode); + + if (IS_ERR(phydev)) { + netdev_err(ndev, "Could not attach to PHY\n"); + ret = PTR_ERR(phydev); + goto err; + } + + phydev->supported &= ~(SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_AUI | + SUPPORTED_MII | + SUPPORTED_FIBRE | + SUPPORTED_BNC); + phydev->advertising = phydev->supported; + pdata->phy_speed = SPEED_UNKNOWN; + + return 0; +err: + xge_mdio_remove(ndev); + + return ret; +} diff --git a/drivers/net/ethernet/apm/xgene-v2/ring.c b/drivers/net/ethernet/apm/xgene-v2/ring.c new file mode 100644 index 000000000000..38810828f8f0 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ring.c @@ -0,0 +1,81 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" + +/* create circular linked list of descriptors */ +void xge_setup_desc(struct xge_desc_ring *ring) +{ + struct xge_raw_desc *raw_desc; + dma_addr_t dma_h, next_dma; + u16 offset; + int i; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + raw_desc = &ring->raw_desc[i]; + + offset = (i + 1) & (XGENE_ENET_NUM_DESC - 1); + next_dma = ring->dma_addr + (offset * XGENE_ENET_DESC_SIZE); + + raw_desc->m0 = cpu_to_le64(SET_BITS(E, 1) | + SET_BITS(PKT_SIZE, SLOT_EMPTY)); + dma_h = upper_32_bits(next_dma); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, next_dma) | + SET_BITS(NEXT_DESC_ADDRH, dma_h)); + } +} + +void xge_update_tx_desc_addr(struct xge_pdata *pdata) +{ + struct xge_desc_ring *ring = pdata->tx_ring; + dma_addr_t dma_addr = ring->dma_addr; + + xge_wr_csr(pdata, DMATXDESCL, dma_addr); + xge_wr_csr(pdata, DMATXDESCH, upper_32_bits(dma_addr)); + + ring->head = 0; + ring->tail = 0; +} + +void xge_update_rx_desc_addr(struct xge_pdata *pdata) +{ + struct xge_desc_ring *ring = pdata->rx_ring; + dma_addr_t dma_addr = ring->dma_addr; + + xge_wr_csr(pdata, DMARXDESCL, dma_addr); + xge_wr_csr(pdata, DMARXDESCH, upper_32_bits(dma_addr)); + + ring->head = 0; + ring->tail = 0; +} + +void xge_intr_enable(struct xge_pdata *pdata) +{ + u32 data; + + data = RX_PKT_RCVD | TX_PKT_SENT; + xge_wr_csr(pdata, DMAINTRMASK, data); +} + +void xge_intr_disable(struct xge_pdata *pdata) +{ + xge_wr_csr(pdata, DMAINTRMASK, 0); +} diff --git a/drivers/net/ethernet/apm/xgene-v2/ring.h b/drivers/net/ethernet/apm/xgene-v2/ring.h new file mode 100644 index 000000000000..abc8c9a84954 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ring.h @@ -0,0 +1,119 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <[email protected]> + * Keyur Chudgar <[email protected]> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __XGENE_ENET_V2_RING_H__ +#define __XGENE_ENET_V2_RING_H__ + +#define XGENE_ENET_DESC_SIZE 64 +#define XGENE_ENET_NUM_DESC 256 +#define NUM_BUFS 8 +#define SLOT_EMPTY 0xfff + +#define DMATXCTRL 0xa180 +#define DMATXDESCL 0xa184 +#define DMATXDESCH 0xa1a0 +#define DMATXSTATUS 0xa188 +#define DMARXCTRL 0xa18c +#define DMARXDESCL 0xa190 +#define DMARXDESCH 0xa1a4 +#define DMARXSTATUS 0xa194 +#define DMAINTRMASK 0xa198 +#define DMAINTERRUPT 0xa19c + +#define D_POS 62 +#define D_LEN 2 +#define E_POS 63 +#define E_LEN 1 +#define PKT_ADDRL_POS 0 +#define PKT_ADDRL_LEN 32 +#define PKT_ADDRH_POS 32 +#define PKT_ADDRH_LEN 10 +#define PKT_SIZE_POS 32 +#define PKT_SIZE_LEN 12 +#define NEXT_DESC_ADDRL_POS 0 +#define NEXT_DESC_ADDRL_LEN 32 +#define NEXT_DESC_ADDRH_POS 48 +#define NEXT_DESC_ADDRH_LEN 10 + +#define TXPKTCOUNT_POS 16 +#define TXPKTCOUNT_LEN 8 +#define RXPKTCOUNT_POS 16 +#define RXPKTCOUNT_LEN 8 + +#define TX_PKT_SENT BIT(0) +#define TX_BUS_ERROR BIT(3) +#define RX_PKT_RCVD BIT(4) +#define RX_BUS_ERROR BIT(7) +#define RXSTATUS_RXPKTRCVD BIT(0) + +struct xge_raw_desc { + __le64 m0; + __le64 m1; + __le64 m2; + __le64 m3; + __le64 m4; + __le64 m5; + __le64 m6; + __le64 m7; +}; + +struct pkt_info { + struct sk_buff *skb; + dma_addr_t dma_addr; + void *pkt_buf; +}; + +/* software context of a descriptor ring */ +struct xge_desc_ring { + struct net_device *ndev; + dma_addr_t dma_addr; + u8 head; + u8 tail; + union { + void *desc_addr; + struct xge_raw_desc *raw_desc; + }; + struct pkt_info (*pkt_info); +}; + +static inline u64 xge_set_desc_bits(int pos, int len, u64 val) +{ + return (val & ((1ULL << len) - 1)) << pos; +} + +static inline u64 xge_get_desc_bits(int pos, int len, u64 src) +{ + return (src >> pos) & ((1ULL << len) - 1); +} + +#define SET_BITS(field, val) \ + xge_set_desc_bits(field ## _POS, field ## _LEN, val) + +#define GET_BITS(field, src) \ + xge_get_desc_bits(field ## _POS, field ## _LEN, src) + +void xge_setup_desc(struct xge_desc_ring *ring); +void xge_update_tx_desc_addr(struct xge_pdata *pdata); +void xge_update_rx_desc_addr(struct xge_pdata *pdata); +void xge_intr_enable(struct xge_pdata *pdata); +void xge_intr_disable(struct xge_pdata *pdata); + +#endif /* __XGENE_ENET_V2_RING_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c index 28fdedc30b74..4f50f11718f4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c @@ -23,9 +23,17 @@ struct xgene_gstrings_stats { char name[ETH_GSTRING_LEN]; int offset; + u32 addr; + u32 mask; }; -#define XGENE_STAT(m) { #m, offsetof(struct xgene_enet_pdata, stats.m) } +#define XGENE_STAT(m) { #m, offsetof(struct rtnl_link_stats64, m) } +#define XGENE_EXTD_STAT(s, a, m) \ + { \ + .name = #s, \ + .addr = a ## _ADDR, \ + .mask = m \ + } static const struct xgene_gstrings_stats gstrings_stats[] = { XGENE_STAT(rx_packets), @@ -40,7 +48,65 @@ static const struct xgene_gstrings_stats gstrings_stats[] = { XGENE_STAT(rx_fifo_errors) }; +static const struct xgene_gstrings_stats gstrings_extd_stats[] = { + XGENE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64, 31), + XGENE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127, 31), + XGENE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255, 31), + XGENE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511, 31), + XGENE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K, 31), + XGENE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX, 31), + XGENE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV, 31), + XGENE_EXTD_STAT(rx_fcs_error_cntr, RFCS, 16), + XGENE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA, 31), + XGENE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA, 31), + XGENE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF, 16), + XGENE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF, 16), + XGENE_EXTD_STAT(rx_unk_opcode_cntr, RXUO, 16), + XGENE_EXTD_STAT(rx_align_err_cntr, RALN, 16), + XGENE_EXTD_STAT(rx_frame_len_err_cntr, RFLR, 16), + XGENE_EXTD_STAT(rx_frame_len_err_recov_cntr, DUMP, 0), + XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16), + XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16), + XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16), + XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16), + XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16), + XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16), + XGENE_EXTD_STAT(rx_jabber_recov_cntr, DUMP, 0), + XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16), + XGENE_EXTD_STAT(rx_overrun_cntr, DUMP, 0), + XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31), + XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31), + XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16), + XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31), + XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31), + XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31), + XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31), + XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31), + XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31), + XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31), + XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16), + XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16), + XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12), + XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12), + XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12), + XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12), + XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12), + XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12), + XGENE_EXTD_STAT(tx_underrun_cntr, DUMP, 0) +}; + #define XGENE_STATS_LEN ARRAY_SIZE(gstrings_stats) +#define XGENE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) +#define RFCS_IDX 7 +#define RALN_IDX 13 +#define RFLR_IDX 14 +#define FALSE_RFLR_IDX 15 +#define RUND_IDX 18 +#define FALSE_RJBR_IDX 22 +#define RX_OVERRUN_IDX 24 +#define TFCS_IDX 38 +#define TFRG_IDX 42 +#define TX_UNDERRUN_IDX 43 static void xgene_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) @@ -61,17 +127,21 @@ static int xgene_get_link_ksettings(struct net_device *ndev, struct phy_device *phydev = ndev->phydev; u32 supported; - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { + if (phy_interface_mode_is_rgmii(pdata->phy_mode)) { if (phydev == NULL) return -ENODEV; - return phy_ethtool_ksettings_get(phydev, cmd); + phy_ethtool_ksettings_get(phydev, cmd); + + return 0; } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { if (pdata->mdio_driver) { if (!phydev) return -ENODEV; - return phy_ethtool_ksettings_get(phydev, cmd); + phy_ethtool_ksettings_get(phydev, cmd); + + return 0; } supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | @@ -111,7 +181,7 @@ static int xgene_set_link_ksettings(struct net_device *ndev, struct xgene_enet_pdata *pdata = netdev_priv(ndev); struct phy_device *phydev = ndev->phydev; - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { + if (phy_interface_mode_is_rgmii(pdata->phy_mode)) { if (!phydev) return -ENODEV; @@ -142,6 +212,11 @@ static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data) memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + + for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { + memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } } static int xgene_get_sset_count(struct net_device *ndev, int sset) @@ -149,18 +224,71 @@ static int xgene_get_sset_count(struct net_device *ndev, int sset) if (sset != ETH_SS_STATS) return -EINVAL; - return XGENE_STATS_LEN; + return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN; +} + +static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata) +{ + u32 rx_drop, tx_drop; + u32 mask, tmp; + int i; + + for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) { + tmp = xgene_enet_rd_stat(pdata, gstrings_extd_stats[i].addr); + if (gstrings_extd_stats[i].mask) { + mask = GENMASK(gstrings_extd_stats[i].mask - 1, 0); + pdata->extd_stats[i] += (tmp & mask); + } + } + + if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { + /* Errata 10GE_10 - SW should intepret RALN as 0 */ + pdata->extd_stats[RALN_IDX] = 0; + } else { + /* Errata ENET_15 - Fixes RFCS, RFLR, TFCS counter */ + pdata->extd_stats[RFCS_IDX] -= pdata->extd_stats[RALN_IDX]; + pdata->extd_stats[RFLR_IDX] -= pdata->extd_stats[RUND_IDX]; + pdata->extd_stats[TFCS_IDX] -= pdata->extd_stats[TFRG_IDX]; + } + + pdata->mac_ops->get_drop_cnt(pdata, &rx_drop, &tx_drop); + pdata->extd_stats[RX_OVERRUN_IDX] += rx_drop; + pdata->extd_stats[TX_UNDERRUN_IDX] += tx_drop; + + /* Errata 10GE_8 - Update Frame recovered from Errata 10GE_8/ENET_11 */ + pdata->extd_stats[FALSE_RFLR_IDX] = pdata->false_rflr; + /* Errata ENET_15 - Jabber Frame recov'ed from Errata 10GE_10/ENET_15 */ + pdata->extd_stats[FALSE_RJBR_IDX] = pdata->vlan_rjbr; +} + +int xgene_extd_stats_init(struct xgene_enet_pdata *pdata) +{ + pdata->extd_stats = devm_kmalloc_array(&pdata->pdev->dev, + XGENE_EXTD_STATS_LEN, sizeof(u64), GFP_KERNEL); + if (!pdata->extd_stats) + return -ENOMEM; + + xgene_get_extd_stats(pdata); + memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64)); + + return 0; } static void xgene_get_ethtool_stats(struct net_device *ndev, struct ethtool_stats *dummy, u64 *data) { - void *pdata = netdev_priv(ndev); + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + struct rtnl_link_stats64 stats; int i; + dev_get_stats(ndev, &stats); for (i = 0; i < XGENE_STATS_LEN; i++) - *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); + data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset); + + xgene_get_extd_stats(pdata); + for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) + data[i + XGENE_STATS_LEN] = pdata->extd_stats[i]; } static void xgene_get_pauseparam(struct net_device *ndev, @@ -180,7 +308,7 @@ static int xgene_set_pauseparam(struct net_device *ndev, struct phy_device *phydev = ndev->phydev; u32 oldadv, newadv; - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || + if (phy_interface_mode_is_rgmii(pdata->phy_mode) || pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { if (!phydev) return -EINVAL; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 06e681697c17..e45b587c2994 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -205,30 +205,24 @@ static u32 xgene_enet_ring_len(struct xgene_enet_desc_ring *ring) } void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring, - struct xgene_enet_pdata *pdata, enum xgene_enet_err_code status) { switch (status) { case INGRESS_CRC: ring->rx_crc_errors++; - ring->rx_dropped++; break; case INGRESS_CHECKSUM: case INGRESS_CHECKSUM_COMPUTE: ring->rx_errors++; - ring->rx_dropped++; break; case INGRESS_TRUNC_FRAME: ring->rx_frame_errors++; - ring->rx_dropped++; break; case INGRESS_PKT_LEN: ring->rx_length_errors++; - ring->rx_dropped++; break; case INGRESS_PKT_UNDER: ring->rx_frame_errors++; - ring->rx_dropped++; break; case INGRESS_FIFO_OVERRUN: ring->rx_fifo_errors++; @@ -270,42 +264,39 @@ static void xgene_enet_wr_mcx_csr(struct xgene_enet_pdata *pdata, iowrite32(val, addr); } -static bool xgene_enet_wr_indirect(void __iomem *addr, void __iomem *wr, - void __iomem *cmd, void __iomem *cmd_done, - u32 wr_addr, u32 wr_data) +void xgene_enet_wr_mac(struct xgene_enet_pdata *pdata, u32 wr_addr, u32 wr_data) { - u32 done; + void __iomem *addr, *wr, *cmd, *cmd_done; + struct net_device *ndev = pdata->ndev; u8 wait = 10; + u32 done; + + if (pdata->mdio_driver && ndev->phydev && + phy_interface_mode_is_rgmii(pdata->phy_mode)) { + struct mii_bus *bus = ndev->phydev->mdio.bus; + + return xgene_mdio_wr_mac(bus->priv, wr_addr, wr_data); + } + + addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; + wr = pdata->mcx_mac_addr + MAC_WRITE_REG_OFFSET; + cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; + cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; + spin_lock(&pdata->mac_lock); iowrite32(wr_addr, addr); iowrite32(wr_data, wr); iowrite32(XGENE_ENET_WR_CMD, cmd); - /* wait for write command to complete */ while (!(done = ioread32(cmd_done)) && wait--) udelay(1); if (!done) - return false; + netdev_err(ndev, "mac write failed, addr: %04x data: %08x\n", + wr_addr, wr_data); iowrite32(0, cmd); - - return true; -} - -static void xgene_enet_wr_mcx_mac(struct xgene_enet_pdata *pdata, - u32 wr_addr, u32 wr_data) -{ - void __iomem *addr, *wr, *cmd, *cmd_done; - - addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; - wr = pdata->mcx_mac_addr + MAC_WRITE_REG_OFFSET; - cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; - cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; - - if (!xgene_enet_wr_indirect(addr, wr, cmd, cmd_done, wr_addr, wr_data)) - netdev_err(pdata->ndev, "MCX mac write failed, addr: %04x\n", - wr_addr); + spin_unlock(&pdata->mac_lock); } static void xgene_enet_rd_csr(struct xgene_enet_pdata *pdata, @@ -332,42 +323,69 @@ static void xgene_enet_rd_mcx_csr(struct xgene_enet_pdata *pdata, *val = ioread32(addr); } -static bool xgene_enet_rd_indirect(void __iomem *addr, void __iomem *rd, - void __iomem *cmd, void __iomem *cmd_done, - u32 rd_addr, u32 *rd_data) +u32 xgene_enet_rd_mac(struct xgene_enet_pdata *pdata, u32 rd_addr) { - u32 done; + void __iomem *addr, *rd, *cmd, *cmd_done; + struct net_device *ndev = pdata->ndev; + u32 done, rd_data; u8 wait = 10; + if (pdata->mdio_driver && ndev->phydev && + phy_interface_mode_is_rgmii(pdata->phy_mode)) { + struct mii_bus *bus = ndev->phydev->mdio.bus; + + return xgene_mdio_rd_mac(bus->priv, rd_addr); + } + + addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; + rd = pdata->mcx_mac_addr + MAC_READ_REG_OFFSET; + cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; + cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; + + spin_lock(&pdata->mac_lock); iowrite32(rd_addr, addr); iowrite32(XGENE_ENET_RD_CMD, cmd); - /* wait for read command to complete */ while (!(done = ioread32(cmd_done)) && wait--) udelay(1); if (!done) - return false; + netdev_err(ndev, "mac read failed, addr: %04x\n", rd_addr); - *rd_data = ioread32(rd); + rd_data = ioread32(rd); iowrite32(0, cmd); + spin_unlock(&pdata->mac_lock); - return true; + return rd_data; } -static void xgene_enet_rd_mcx_mac(struct xgene_enet_pdata *pdata, - u32 rd_addr, u32 *rd_data) +u32 xgene_enet_rd_stat(struct xgene_enet_pdata *pdata, u32 rd_addr) { void __iomem *addr, *rd, *cmd, *cmd_done; + u32 done, rd_data; + u8 wait = 10; - addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; - rd = pdata->mcx_mac_addr + MAC_READ_REG_OFFSET; - cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; - cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; + addr = pdata->mcx_stats_addr + STAT_ADDR_REG_OFFSET; + rd = pdata->mcx_stats_addr + STAT_READ_REG_OFFSET; + cmd = pdata->mcx_stats_addr + STAT_COMMAND_REG_OFFSET; + cmd_done = pdata->mcx_stats_addr + STAT_COMMAND_DONE_REG_OFFSET; + + spin_lock(&pdata->stats_lock); + iowrite32(rd_addr, addr); + iowrite32(XGENE_ENET_RD_CMD, cmd); - if (!xgene_enet_rd_indirect(addr, rd, cmd, cmd_done, rd_addr, rd_data)) - netdev_err(pdata->ndev, "MCX mac read failed, addr: %04x\n", + while (!(done = ioread32(cmd_done)) && wait--) + udelay(1); + + if (!done) + netdev_err(pdata->ndev, "mac stats read failed, addr: %04x\n", rd_addr); + + rd_data = ioread32(rd); + iowrite32(0, cmd); + spin_unlock(&pdata->stats_lock); + + return rd_data; } static void xgene_gmac_set_mac_addr(struct xgene_enet_pdata *pdata) @@ -379,8 +397,8 @@ static void xgene_gmac_set_mac_addr(struct xgene_enet_pdata *pdata) (dev_addr[1] << 8) | dev_addr[0]; addr1 = (dev_addr[5] << 24) | (dev_addr[4] << 16); - xgene_enet_wr_mcx_mac(pdata, STATION_ADDR0_ADDR, addr0); - xgene_enet_wr_mcx_mac(pdata, STATION_ADDR1_ADDR, addr1); + xgene_enet_wr_mac(pdata, STATION_ADDR0_ADDR, addr0); + xgene_enet_wr_mac(pdata, STATION_ADDR1_ADDR, addr1); } static int xgene_enet_ecc_init(struct xgene_enet_pdata *pdata) @@ -405,8 +423,8 @@ static int xgene_enet_ecc_init(struct xgene_enet_pdata *pdata) static void xgene_gmac_reset(struct xgene_enet_pdata *pdata) { - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET1); - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, 0); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET1); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, 0); } static void xgene_enet_configure_clock(struct xgene_enet_pdata *pdata) @@ -456,8 +474,8 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) xgene_enet_rd_mcx_csr(pdata, ICM_CONFIG0_REG_0_ADDR, &icm0); xgene_enet_rd_mcx_csr(pdata, ICM_CONFIG2_REG_0_ADDR, &icm2); - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_2_ADDR, &mc2); - xgene_enet_rd_mcx_mac(pdata, INTERFACE_CONTROL_ADDR, &intf_ctl); + mc2 = xgene_enet_rd_mac(pdata, MAC_CONFIG_2_ADDR); + intf_ctl = xgene_enet_rd_mac(pdata, INTERFACE_CONTROL_ADDR); xgene_enet_rd_csr(pdata, RGMII_REG_0_ADDR, &rgmii); switch (pdata->phy_speed) { @@ -494,9 +512,9 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) break; } - mc2 |= FULL_DUPLEX2 | PAD_CRC; - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_2_ADDR, mc2); - xgene_enet_wr_mcx_mac(pdata, INTERFACE_CONTROL_ADDR, intf_ctl); + mc2 |= FULL_DUPLEX2 | PAD_CRC | LENGTH_CHK; + xgene_enet_wr_mac(pdata, MAC_CONFIG_2_ADDR, mc2); + xgene_enet_wr_mac(pdata, INTERFACE_CONTROL_ADDR, intf_ctl); xgene_enet_wr_csr(pdata, RGMII_REG_0_ADDR, rgmii); xgene_enet_configure_clock(pdata); @@ -506,7 +524,7 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) static void xgene_enet_set_frame_size(struct xgene_enet_pdata *pdata, int size) { - xgene_enet_wr_mcx_mac(pdata, MAX_FRAME_LEN_ADDR, size); + xgene_enet_wr_mac(pdata, MAX_FRAME_LEN_ADDR, size); } static void xgene_gmac_enable_tx_pause(struct xgene_enet_pdata *pdata, @@ -528,14 +546,14 @@ static void xgene_gmac_flowctl_tx(struct xgene_enet_pdata *pdata, bool enable) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); if (enable) data |= TX_FLOW_EN; else data &= ~TX_FLOW_EN; - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data); pdata->mac_ops->enable_tx_pause(pdata, enable); } @@ -544,14 +562,14 @@ static void xgene_gmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); if (enable) data |= RX_FLOW_EN; else data &= ~RX_FLOW_EN; - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data); } static void xgene_gmac_init(struct xgene_enet_pdata *pdata) @@ -565,9 +583,9 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata) xgene_gmac_set_mac_addr(pdata); /* Adjust MDC clock frequency */ - xgene_enet_rd_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, &value); + value = xgene_enet_rd_mac(pdata, MII_MGMT_CONFIG_ADDR); MGMT_CLOCK_SEL_SET(&value, 7); - xgene_enet_wr_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, value); + xgene_enet_wr_mac(pdata, MII_MGMT_CONFIG_ADDR, value); /* Enable drop if bufpool not available */ xgene_enet_rd_csr(pdata, RSIF_CONFIG_REG_ADDR, &value); @@ -600,6 +618,18 @@ static void xgene_gmac_init(struct xgene_enet_pdata *pdata) xgene_enet_wr_csr(pdata, CFG_BYPASS_ADDR, RESUME_TX); } +static void xgene_gmac_get_drop_cnt(struct xgene_enet_pdata *pdata, + u32 *rx, u32 *tx) +{ + u32 count; + + xgene_enet_rd_mcx_csr(pdata, ICM_ECM_DROP_COUNT_REG0_ADDR, &count); + *rx = ICM_DROP_COUNT(count); + *tx = ECM_DROP_COUNT(count); + /* Errata: 10GE_4 - Fix ICM_ECM_DROP_COUNT not clear-on-read */ + xgene_enet_rd_mcx_csr(pdata, ECM_CONFIG0_REG_0_ADDR, &count); +} + static void xgene_enet_config_ring_if_assoc(struct xgene_enet_pdata *pdata) { u32 val = 0xffffffff; @@ -623,6 +653,7 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb); cb |= CFG_CLE_BYPASS_EN0; CFG_CLE_IP_PROTOCOL0_SET(&cb, 3); + CFG_CLE_IP_HDR_LEN_SET(&cb, 0); xgene_enet_wr_csr(pdata, CLE_BYPASS_REG0_0_ADDR, cb); xgene_enet_rd_csr(pdata, CLE_BYPASS_REG1_0_ADDR, &cb); @@ -636,32 +667,32 @@ static void xgene_gmac_rx_enable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data | RX_EN); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data | RX_EN); } static void xgene_gmac_tx_enable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data | TX_EN); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data | TX_EN); } static void xgene_gmac_rx_disable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data & ~RX_EN); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data & ~RX_EN); } static void xgene_gmac_tx_disable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mcx_mac(pdata, MAC_CONFIG_1_ADDR, &data); - xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data & ~TX_EN); + data = xgene_enet_rd_mac(pdata, MAC_CONFIG_1_ADDR); + xgene_enet_wr_mac(pdata, MAC_CONFIG_1_ADDR, data & ~TX_EN); } bool xgene_ring_mgr_init(struct xgene_enet_pdata *p) @@ -732,27 +763,6 @@ static void xgene_enet_clear(struct xgene_enet_pdata *pdata, static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata) { struct device *dev = &pdata->pdev->dev; - struct xgene_enet_desc_ring *ring; - u32 pb; - int i; - - pb = 0; - for (i = 0; i < pdata->rxq_cnt; i++) { - ring = pdata->rx_ring[i]->buf_pool; - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - ring = pdata->rx_ring[i]->page_pool; - if (ring) - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - - } - xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); - - pb = 0; - for (i = 0; i < pdata->txq_cnt; i++) { - ring = pdata->tx_ring[i]; - pb |= BIT(xgene_enet_ring_bufnum(ring->id)); - } - xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb); if (dev->of_node) { if (!IS_ERR(pdata->clk)) @@ -1008,6 +1018,7 @@ const struct xgene_mac_ops xgene_gmac_ops = { .tx_enable = xgene_gmac_tx_enable, .rx_disable = xgene_gmac_rx_disable, .tx_disable = xgene_gmac_tx_disable, + .get_drop_cnt = xgene_gmac_get_drop_cnt, .set_speed = xgene_gmac_set_speed, .set_mac_addr = xgene_gmac_set_mac_addr, .set_framesize = xgene_enet_set_frame_size, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index 5f83037bb96b..5d3e18d3c94c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -115,6 +115,7 @@ enum xgene_enet_rm { #define BLOCK_ETH_CLKRST_CSR_OFFSET 0xc000 #define BLOCK_ETH_DIAG_CSR_OFFSET 0xD000 #define BLOCK_ETH_MAC_OFFSET 0x0000 +#define BLOCK_ETH_STATS_OFFSET 0x0000 #define BLOCK_ETH_MAC_CSR_OFFSET 0x2800 #define CLKEN_ADDR 0xc208 @@ -126,6 +127,12 @@ enum xgene_enet_rm { #define MAC_READ_REG_OFFSET 0x0c #define MAC_COMMAND_DONE_REG_OFFSET 0x10 +#define STAT_ADDR_REG_OFFSET 0x14 +#define STAT_COMMAND_REG_OFFSET 0x18 +#define STAT_WRITE_REG_OFFSET 0x1c +#define STAT_READ_REG_OFFSET 0x20 +#define STAT_COMMAND_DONE_REG_OFFSET 0x24 + #define PCS_ADDR_REG_OFFSET 0x00 #define PCS_COMMAND_REG_OFFSET 0x04 #define PCS_WRITE_REG_OFFSET 0x08 @@ -163,6 +170,7 @@ enum xgene_enet_rm { #define CFG_RXCLK_MUXSEL0_SET(dst, val) xgene_set_bits(dst, val, 26, 3) #define CFG_CLE_IP_PROTOCOL0_SET(dst, val) xgene_set_bits(dst, val, 16, 2) +#define CFG_CLE_IP_HDR_LEN_SET(dst, val) xgene_set_bits(dst, val, 8, 5) #define CFG_CLE_DSTQID0_SET(dst, val) xgene_set_bits(dst, val, 0, 12) #define CFG_CLE_FPSEL0_SET(dst, val) xgene_set_bits(dst, val, 16, 4) #define CFG_CLE_NXTFPSEL0_SET(dst, val) xgene_set_bits(dst, val, 20, 4) @@ -184,6 +192,10 @@ enum xgene_enet_rm { #define CFG_CLE_NXTFPSEL0(val) (((val) << 20) & GENMASK(23, 20)) #define ICM_CONFIG0_REG_0_ADDR 0x0400 #define ICM_CONFIG2_REG_0_ADDR 0x0410 +#define ECM_CONFIG0_REG_0_ADDR 0x0500 +#define ECM_CONFIG0_REG_1_ADDR 0x0504 +#define ICM_ECM_DROP_COUNT_REG0_ADDR 0x0508 +#define ICM_ECM_DROP_COUNT_REG1_ADDR 0x050c #define RX_DV_GATE_REG_0_ADDR 0x05fc #define TX_DV_GATE_EN0 BIT(2) #define RX_DV_GATE_EN0 BIT(1) @@ -215,12 +227,54 @@ enum xgene_enet_rm { #define ENET_GHD_MODE BIT(26) #define FULL_DUPLEX2 BIT(0) #define PAD_CRC BIT(2) -#define SCAN_AUTO_INCR BIT(5) -#define TBYT_ADDR 0x38 -#define TPKT_ADDR 0x39 -#define TDRP_ADDR 0x45 -#define TFCS_ADDR 0x47 -#define TUND_ADDR 0x4a +#define LENGTH_CHK BIT(4) + +#define TR64_ADDR 0x20 +#define TR127_ADDR 0x21 +#define TR255_ADDR 0x22 +#define TR511_ADDR 0x23 +#define TR1K_ADDR 0x24 +#define TRMAX_ADDR 0x25 +#define TRMGV_ADDR 0x26 + +#define RFCS_ADDR 0x29 +#define RMCA_ADDR 0x2a +#define RBCA_ADDR 0x2b +#define RXCF_ADDR 0x2c +#define RXPF_ADDR 0x2d +#define RXUO_ADDR 0x2e +#define RALN_ADDR 0x2f +#define RFLR_ADDR 0x30 +#define RCDE_ADDR 0x31 +#define RCSE_ADDR 0x32 +#define RUND_ADDR 0x33 +#define ROVR_ADDR 0x34 +#define RFRG_ADDR 0x35 +#define RJBR_ADDR 0x36 +#define RDRP_ADDR 0x37 + +#define TMCA_ADDR 0x3a +#define TBCA_ADDR 0x3b +#define TXPF_ADDR 0x3c +#define TDFR_ADDR 0x3d +#define TEDF_ADDR 0x3e +#define TSCL_ADDR 0x3f +#define TMCL_ADDR 0x40 +#define TLCL_ADDR 0x41 +#define TXCL_ADDR 0x42 +#define TNCL_ADDR 0x43 +#define TPFH_ADDR 0x44 +#define TDRP_ADDR 0x45 +#define TJBR_ADDR 0x46 +#define TFCS_ADDR 0x47 +#define TXCF_ADDR 0x48 +#define TOVR_ADDR 0x49 +#define TUND_ADDR 0x4a +#define TFRG_ADDR 0x4b +#define DUMP_ADDR 0x27 + +#define ECM_DROP_COUNT(src) xgene_get_bits(src, 0, 15) +#define ICM_DROP_COUNT(src) xgene_get_bits(src, 16, 31) #define TSO_IPPROTO_TCP 1 @@ -378,14 +432,16 @@ static inline u16 xgene_enet_get_numslots(u16 id, u32 size) } void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring, - struct xgene_enet_pdata *pdata, enum xgene_enet_err_code status); - int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata); void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata); bool xgene_ring_mgr_init(struct xgene_enet_pdata *p); int xgene_enet_phy_connect(struct net_device *ndev); void xgene_enet_phy_disconnect(struct xgene_enet_pdata *pdata); +u32 xgene_enet_rd_mac(struct xgene_enet_pdata *pdata, u32 rd_addr); +void xgene_enet_wr_mac(struct xgene_enet_pdata *pdata, u32 wr_addr, + u32 wr_data); +u32 xgene_enet_rd_stat(struct xgene_enet_pdata *pdata, u32 rd_addr); extern const struct xgene_mac_ops xgene_gmac_ops; extern const struct xgene_port_ops xgene_gport_ops; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 523b8eff6d7b..86058a9f3417 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -246,9 +246,9 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, skb_frag_t *frag; dma_addr_t *frag_dma_addr; u16 skb_index; - u8 status; - int i, ret = 0; u8 mss_index; + u8 status; + int i; skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0)); skb = cp_ring->cp_skb[skb_index]; @@ -275,54 +275,45 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, /* Checking for error */ status = GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); if (unlikely(status > 2)) { - xgene_enet_parse_error(cp_ring, netdev_priv(cp_ring->ndev), - status); - ret = -EIO; + cp_ring->tx_dropped++; + cp_ring->tx_errors++; } if (likely(skb)) { dev_kfree_skb_any(skb); } else { netdev_err(cp_ring->ndev, "completion skb is NULL\n"); - ret = -EIO; } - return ret; + return 0; } static int xgene_enet_setup_mss(struct net_device *ndev, u32 mss) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); - bool mss_index_found = false; - int mss_index; + int mss_index = -EBUSY; int i; spin_lock(&pdata->mss_lock); /* Reuse the slot if MSS matches */ - for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { + for (i = 0; mss_index < 0 && i < NUM_MSS_REG; i++) { if (pdata->mss[i] == mss) { pdata->mss_refcnt[i]++; mss_index = i; - mss_index_found = true; } } /* Overwrite the slot with ref_count = 0 */ - for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { + for (i = 0; mss_index < 0 && i < NUM_MSS_REG; i++) { if (!pdata->mss_refcnt[i]) { pdata->mss_refcnt[i]++; pdata->mac_ops->set_mss(pdata, mss, i); pdata->mss[i] = mss; mss_index = i; - mss_index_found = true; } } - /* No slots with ref_count = 0 available, return busy */ - if (!mss_index_found) - mss_index = -EBUSY; - spin_unlock(&pdata->mss_lock); return mss_index; @@ -608,14 +599,24 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static void xgene_enet_skip_csum(struct sk_buff *skb) +static void xgene_enet_rx_csum(struct sk_buff *skb) { + struct net_device *ndev = skb->dev; struct iphdr *iph = ip_hdr(skb); - if (!ip_is_fragment(iph) || - (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } + if (!(ndev->features & NETIF_F_RXCSUM)) + return; + + if (skb->protocol != htons(ETH_P_IP)) + return; + + if (ip_is_fragment(iph)) + return; + + if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) + return; + + skb->ip_summed = CHECKSUM_UNNECESSARY; } static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, @@ -655,12 +656,36 @@ static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, buf_pool->head = head; } +/* Errata 10GE_10 and ENET_15 - Fix duplicated HW statistic counters */ +static bool xgene_enet_errata_10GE_10(struct sk_buff *skb, u32 len, u8 status) +{ + if (status == INGRESS_CRC && + len >= (ETHER_STD_PACKET + 1) && + len <= (ETHER_STD_PACKET + 4) && + skb->protocol == htons(ETH_P_8021Q)) + return true; + + return false; +} + +/* Errata 10GE_8 and ENET_11 - allow packet with length <=64B */ +static bool xgene_enet_errata_10GE_8(struct sk_buff *skb, u32 len, u8 status) +{ + if (status == INGRESS_PKT_LEN && len == ETHER_MIN_PACKET) { + if (ntohs(eth_hdr(skb)->h_proto) < 46) + return true; + } + + return false; +} + static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, struct xgene_enet_raw_desc *raw_desc, struct xgene_enet_raw_desc *exp_desc) { struct xgene_enet_desc_ring *buf_pool, *page_pool; u32 datalen, frag_size, skb_index; + struct xgene_enet_pdata *pdata; struct net_device *ndev; dma_addr_t dma_addr; struct sk_buff *skb; @@ -673,6 +698,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, bool nv; ndev = rx_ring->ndev; + pdata = netdev_priv(ndev); dev = ndev_to_dev(rx_ring->ndev); buf_pool = rx_ring->buf_pool; page_pool = rx_ring->page_pool; @@ -683,30 +709,34 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skb = buf_pool->rx_skb[skb_index]; buf_pool->rx_skb[skb_index] = NULL; + datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); + skb_put(skb, datalen); + prefetch(skb->data - NET_IP_ALIGN); + skb->protocol = eth_type_trans(skb, ndev); + /* checking for error */ - status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) || + status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) | GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); - if (unlikely(status > 2)) { - dev_kfree_skb_any(skb); - xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); - xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev), - status); - ret = -EIO; - goto out; + if (unlikely(status)) { + if (xgene_enet_errata_10GE_8(skb, datalen, status)) { + pdata->false_rflr++; + } else if (xgene_enet_errata_10GE_10(skb, datalen, status)) { + pdata->vlan_rjbr++; + } else { + dev_kfree_skb_any(skb); + xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); + xgene_enet_parse_error(rx_ring, status); + rx_ring->rx_dropped++; + goto out; + } } - /* strip off CRC as HW isn't doing this */ - datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); - nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0)); - if (!nv) + if (!nv) { + /* strip off CRC as HW isn't doing this */ datalen -= 4; - - skb_put(skb, datalen); - prefetch(skb->data - NET_IP_ALIGN); - - if (!nv) goto skip_jumbo; + } slots = page_pool->slots - 1; head = page_pool->head; @@ -735,11 +765,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skip_jumbo: skb_checksum_none_assert(skb); - skb->protocol = eth_type_trans(skb, ndev); - if (likely((ndev->features & NETIF_F_IP_CSUM) && - skb->protocol == htons(ETH_P_IP))) { - xgene_enet_skip_csum(skb); - } + xgene_enet_rx_csum(skb); rx_ring->rx_packets++; rx_ring->rx_bytes += datalen; @@ -840,7 +866,7 @@ static int xgene_enet_napi(struct napi_struct *napi, const int budget) processed = xgene_enet_process_ring(ring, budget); if (processed != budget) { - napi_complete(napi); + napi_complete_done(napi, processed); enable_irq(ring->irq); } @@ -1453,21 +1479,21 @@ err: return ret; } -static struct rtnl_link_stats64 *xgene_enet_get_stats64( +static void xgene_enet_get_stats64( struct net_device *ndev, - struct rtnl_link_stats64 *storage) + struct rtnl_link_stats64 *stats) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); - struct rtnl_link_stats64 *stats = &pdata->stats; struct xgene_enet_desc_ring *ring; int i; - memset(stats, 0, sizeof(struct rtnl_link_stats64)); for (i = 0; i < pdata->txq_cnt; i++) { ring = pdata->tx_ring[i]; if (ring) { stats->tx_packets += ring->tx_packets; stats->tx_bytes += ring->tx_bytes; + stats->tx_dropped += ring->tx_dropped; + stats->tx_errors += ring->tx_errors; } } @@ -1476,16 +1502,18 @@ static struct rtnl_link_stats64 *xgene_enet_get_stats64( if (ring) { stats->rx_packets += ring->rx_packets; stats->rx_bytes += ring->rx_bytes; - stats->rx_errors += ring->rx_length_errors + + stats->rx_dropped += ring->rx_dropped; + stats->rx_errors += ring->rx_errors + + ring->rx_length_errors + ring->rx_crc_errors + ring->rx_frame_errors + ring->rx_fifo_errors; - stats->rx_dropped += ring->rx_dropped; + stats->rx_length_errors += ring->rx_length_errors; + stats->rx_crc_errors += ring->rx_crc_errors; + stats->rx_frame_errors += ring->rx_frame_errors; + stats->rx_fifo_errors += ring->rx_fifo_errors; } } - memcpy(storage, stats, sizeof(struct rtnl_link_stats64)); - - return storage; } static int xgene_enet_set_mac_address(struct net_device *ndev, void *addr) @@ -1606,7 +1634,7 @@ static int xgene_enet_get_irqs(struct xgene_enet_pdata *pdata) struct device *dev = &pdev->dev; int i, ret, max_irqs; - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) + if (phy_interface_mode_is_rgmii(pdata->phy_mode)) max_irqs = 1; else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) max_irqs = 2; @@ -1732,7 +1760,7 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) dev_err(dev, "Unable to get phy-connection-type\n"); return pdata->phy_mode; } - if (pdata->phy_mode != PHY_INTERFACE_MODE_RGMII && + if (!phy_interface_mode_is_rgmii(pdata->phy_mode) && pdata->phy_mode != PHY_INTERFACE_MODE_SGMII && pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) { dev_err(dev, "Incorrect phy-connection-type specified\n"); @@ -1757,10 +1785,18 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) xgene_enet_gpiod_get(pdata); - pdata->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pdata->clk)) { - /* Firmware may have set up the clock already. */ - dev_info(dev, "clocks have been setup already\n"); + if (pdata->phy_mode != PHY_INTERFACE_MODE_SGMII) { + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + /* Abort if the clock is defined but couldn't be + * retrived. Always abort if the clock is missing on + * DT system as the driver can't cope with this case. + */ + if (PTR_ERR(pdata->clk) != -ENOENT || dev->of_node) + return PTR_ERR(pdata->clk); + /* Firmware may have set up the clock already. */ + dev_info(dev, "clocks have been setup already\n"); + } } if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) @@ -1771,15 +1807,18 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) pdata->cle.base = base_addr + BLOCK_ETH_CLE_CSR_OFFSET; pdata->eth_ring_if_addr = base_addr + BLOCK_ETH_RING_IF_OFFSET; pdata->eth_diag_csr_addr = base_addr + BLOCK_ETH_DIAG_CSR_OFFSET; - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII || + if (phy_interface_mode_is_rgmii(pdata->phy_mode) || pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) { pdata->mcx_mac_addr = pdata->base_addr + BLOCK_ETH_MAC_OFFSET; + pdata->mcx_stats_addr = + pdata->base_addr + BLOCK_ETH_STATS_OFFSET; offset = (pdata->enet_id == XGENE_ENET1) ? BLOCK_ETH_MAC_CSR_OFFSET : X2_BLOCK_ETH_MAC_CSR_OFFSET; pdata->mcx_mac_csr_addr = base_addr + offset; } else { pdata->mcx_mac_addr = base_addr + BLOCK_AXG_MAC_OFFSET; + pdata->mcx_stats_addr = base_addr + BLOCK_AXG_STATS_OFFSET; pdata->mcx_mac_csr_addr = base_addr + BLOCK_AXG_MAC_CSR_OFFSET; pdata->pcs_addr = base_addr + BLOCK_PCS_OFFSET; } @@ -1867,6 +1906,9 @@ static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata) { switch (pdata->phy_mode) { case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: pdata->mac_ops = &xgene_gmac_ops; pdata->port_ops = &xgene_gport_ops; pdata->rm = RM3; @@ -1967,6 +2009,30 @@ static void xgene_enet_napi_add(struct xgene_enet_pdata *pdata) } } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_enet_acpi_match[] = { + { "APMC0D05", XGENE_ENET1}, + { "APMC0D30", XGENE_ENET1}, + { "APMC0D31", XGENE_ENET1}, + { "APMC0D3F", XGENE_ENET1}, + { "APMC0D26", XGENE_ENET2}, + { "APMC0D25", XGENE_ENET2}, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); +#endif + +static const struct of_device_id xgene_enet_of_match[] = { + {.compatible = "apm,xgene-enet", .data = (void *)XGENE_ENET1}, + {.compatible = "apm,xgene1-sgenet", .data = (void *)XGENE_ENET1}, + {.compatible = "apm,xgene1-xgenet", .data = (void *)XGENE_ENET1}, + {.compatible = "apm,xgene2-sgenet", .data = (void *)XGENE_ENET2}, + {.compatible = "apm,xgene2-xgenet", .data = (void *)XGENE_ENET2}, + {}, +}; + +MODULE_DEVICE_TABLE(of, xgene_enet_of_match); + static int xgene_enet_probe(struct platform_device *pdev) { struct net_device *ndev; @@ -2017,9 +2083,10 @@ static int xgene_enet_probe(struct platform_device *pdev) goto err; xgene_enet_setup_ops(pdata); + spin_lock_init(&pdata->mac_lock); if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { - ndev->features |= NETIF_F_TSO; + ndev->features |= NETIF_F_TSO | NETIF_F_RXCSUM; spin_lock_init(&pdata->mss_lock); } ndev->hw_features = ndev->features; @@ -2038,7 +2105,7 @@ static int xgene_enet_probe(struct platform_device *pdev) if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { INIT_DELAYED_WORK(&pdata->link_work, link_state); } else if (!pdata->mdio_driver) { - if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) + if (phy_interface_mode_is_rgmii(pdata->phy_mode)) ret = xgene_enet_mdio_config(pdata); else INIT_DELAYED_WORK(&pdata->link_work, link_state); @@ -2047,6 +2114,11 @@ static int xgene_enet_probe(struct platform_device *pdev) goto err1; } + spin_lock_init(&pdata->stats_lock); + ret = xgene_extd_stats_init(pdata); + if (ret) + goto err2; + xgene_enet_napi_add(pdata); ret = register_netdev(ndev); if (ret) { @@ -2064,7 +2136,7 @@ err2: if (pdata->mdio_driver) xgene_enet_phy_disconnect(pdata); - else if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) + else if (phy_interface_mode_is_rgmii(pdata->phy_mode)) xgene_enet_mdio_remove(pdata); err1: xgene_enet_delete_desc_rings(pdata); @@ -2088,12 +2160,12 @@ static int xgene_enet_remove(struct platform_device *pdev) if (pdata->mdio_driver) xgene_enet_phy_disconnect(pdata); - else if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) + else if (phy_interface_mode_is_rgmii(pdata->phy_mode)) xgene_enet_mdio_remove(pdata); unregister_netdev(ndev); - pdata->port_ops->shutdown(pdata); xgene_enet_delete_desc_rings(pdata); + pdata->port_ops->shutdown(pdata); free_netdev(ndev); return 0; @@ -2113,32 +2185,6 @@ static void xgene_enet_shutdown(struct platform_device *pdev) xgene_enet_remove(pdev); } -#ifdef CONFIG_ACPI -static const struct acpi_device_id xgene_enet_acpi_match[] = { - { "APMC0D05", XGENE_ENET1}, - { "APMC0D30", XGENE_ENET1}, - { "APMC0D31", XGENE_ENET1}, - { "APMC0D3F", XGENE_ENET1}, - { "APMC0D26", XGENE_ENET2}, - { "APMC0D25", XGENE_ENET2}, - { } -}; -MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); -#endif - -#ifdef CONFIG_OF -static const struct of_device_id xgene_enet_of_match[] = { - {.compatible = "apm,xgene-enet", .data = (void *)XGENE_ENET1}, - {.compatible = "apm,xgene1-sgenet", .data = (void *)XGENE_ENET1}, - {.compatible = "apm,xgene1-xgenet", .data = (void *)XGENE_ENET1}, - {.compatible = "apm,xgene2-sgenet", .data = (void *)XGENE_ENET2}, - {.compatible = "apm,xgene2-xgenet", .data = (void *)XGENE_ENET2}, - {}, -}; - -MODULE_DEVICE_TABLE(of, xgene_enet_of_match); -#endif - static struct platform_driver xgene_enet_driver = { .driver = { .name = "xgene-enet", diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 52571741da9f..985768596900 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -41,6 +41,8 @@ #include "../../../phy/mdio-xgene.h" #define XGENE_DRV_VERSION "v1.0" +#define ETHER_MIN_PACKET 64 +#define ETHER_STD_PACKET 1518 #define XGENE_ENET_STD_MTU 1536 #define XGENE_ENET_MAX_MTU 9600 #define SKB_BUFFER_SIZE (XGENE_ENET_STD_MTU - NET_IP_ALIGN) @@ -137,6 +139,8 @@ struct xgene_enet_desc_ring { __le64 *exp_bufs; u64 tx_packets; u64 tx_bytes; + u64 tx_dropped; + u64 tx_errors; u64 rx_packets; u64 rx_bytes; u64 rx_dropped; @@ -154,6 +158,7 @@ struct xgene_mac_ops { void (*rx_enable)(struct xgene_enet_pdata *pdata); void (*tx_disable)(struct xgene_enet_pdata *pdata); void (*rx_disable)(struct xgene_enet_pdata *pdata); + void (*get_drop_cnt)(struct xgene_enet_pdata *pdata, u32 *rx, u32 *tx); void (*set_speed)(struct xgene_enet_pdata *pdata); void (*set_mac_addr)(struct xgene_enet_pdata *pdata); void (*set_framesize)(struct xgene_enet_pdata *pdata, int framesize); @@ -211,6 +216,7 @@ struct xgene_enet_pdata { void __iomem *eth_diag_csr_addr; void __iomem *mcx_mac_addr; void __iomem *mcx_mac_csr_addr; + void __iomem *mcx_stats_addr; void __iomem *base_addr; void __iomem *pcs_addr; void __iomem *ring_csr_addr; @@ -218,8 +224,12 @@ struct xgene_enet_pdata { int phy_mode; enum xgene_enet_rm rm; struct xgene_enet_cle cle; - struct rtnl_link_stats64 stats; + u64 *extd_stats; + u64 false_rflr; + u64 vlan_rjbr; + spinlock_t stats_lock; /* statistics lock */ const struct xgene_mac_ops *mac_ops; + spinlock_t mac_lock; /* mac lock */ const struct xgene_port_ops *port_ops; struct xgene_ring_ops *ring_ops; const struct xgene_cle_ops *cle_ops; @@ -262,5 +272,6 @@ static inline u16 xgene_enet_dst_ring_num(struct xgene_enet_desc_ring *ring) } void xgene_enet_set_ethtool_ops(struct net_device *netdev); +int xgene_extd_stats_init(struct xgene_enet_pdata *pdata); #endif /* __XGENE_ENET_MAIN_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index a8e063bdee3b..b1a83fdbefb8 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -54,41 +54,6 @@ static void xgene_enet_wr_mcx_csr(struct xgene_enet_pdata *pdata, iowrite32(val, addr); } -static bool xgene_enet_wr_indirect(struct xgene_indirect_ctl *ctl, - u32 wr_addr, u32 wr_data) -{ - int i; - - iowrite32(wr_addr, ctl->addr); - iowrite32(wr_data, ctl->ctl); - iowrite32(XGENE_ENET_WR_CMD, ctl->cmd); - - /* wait for write command to complete */ - for (i = 0; i < 10; i++) { - if (ioread32(ctl->cmd_done)) { - iowrite32(0, ctl->cmd); - return true; - } - udelay(1); - } - - return false; -} - -static void xgene_enet_wr_mac(struct xgene_enet_pdata *p, - u32 wr_addr, u32 wr_data) -{ - struct xgene_indirect_ctl ctl = { - .addr = p->mcx_mac_addr + MAC_ADDR_REG_OFFSET, - .ctl = p->mcx_mac_addr + MAC_WRITE_REG_OFFSET, - .cmd = p->mcx_mac_addr + MAC_COMMAND_REG_OFFSET, - .cmd_done = p->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET - }; - - if (!xgene_enet_wr_indirect(&ctl, wr_addr, wr_data)) - netdev_err(p->ndev, "mac write failed, addr: %04x\n", wr_addr); -} - static u32 xgene_enet_rd_csr(struct xgene_enet_pdata *p, u32 offset) { return ioread32(p->eth_csr_addr + offset); @@ -104,42 +69,6 @@ static u32 xgene_enet_rd_mcx_csr(struct xgene_enet_pdata *p, u32 offset) return ioread32(p->mcx_mac_csr_addr + offset); } -static u32 xgene_enet_rd_indirect(struct xgene_indirect_ctl *ctl, u32 rd_addr) -{ - u32 rd_data; - int i; - - iowrite32(rd_addr, ctl->addr); - iowrite32(XGENE_ENET_RD_CMD, ctl->cmd); - - /* wait for read command to complete */ - for (i = 0; i < 10; i++) { - if (ioread32(ctl->cmd_done)) { - rd_data = ioread32(ctl->ctl); - iowrite32(0, ctl->cmd); - - return rd_data; - } - udelay(1); - } - - pr_err("%s: mac read failed, addr: %04x\n", __func__, rd_addr); - - return 0; -} - -static u32 xgene_enet_rd_mac(struct xgene_enet_pdata *p, u32 rd_addr) -{ - struct xgene_indirect_ctl ctl = { - .addr = p->mcx_mac_addr + MAC_ADDR_REG_OFFSET, - .ctl = p->mcx_mac_addr + MAC_READ_REG_OFFSET, - .cmd = p->mcx_mac_addr + MAC_COMMAND_REG_OFFSET, - .cmd_done = p->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET - }; - - return xgene_enet_rd_indirect(&ctl, rd_addr); -} - static int xgene_enet_ecc_init(struct xgene_enet_pdata *p) { struct net_device *ndev = p->ndev; @@ -166,6 +95,24 @@ static int xgene_enet_ecc_init(struct xgene_enet_pdata *p) return -ENODEV; } +static void xgene_sgmac_get_drop_cnt(struct xgene_enet_pdata *pdata, + u32 *rx, u32 *tx) +{ + u32 addr, count; + + addr = (pdata->enet_id != XGENE_ENET1) ? + XG_MCX_ICM_ECM_DROP_COUNT_REG0_ADDR : + ICM_ECM_DROP_COUNT_REG0_ADDR + pdata->port_id * OFFSET_4; + count = xgene_enet_rd_mcx_csr(pdata, addr); + *rx = ICM_DROP_COUNT(count); + *tx = ECM_DROP_COUNT(count); + /* Errata: 10GE_4 - ICM_ECM_DROP_COUNT not clear-on-read */ + addr = (pdata->enet_id != XGENE_ENET1) ? + XG_MCX_ECM_CONFIG0_REG_0_ADDR : + ECM_CONFIG0_REG_0_ADDR + pdata->port_id * OFFSET_4; + xgene_enet_rd_mcx_csr(pdata, addr); +} + static void xgene_enet_config_ring_if_assoc(struct xgene_enet_pdata *p) { u32 val; @@ -587,26 +534,6 @@ static void xgene_enet_clear(struct xgene_enet_pdata *pdata, static void xgene_enet_shutdown(struct xgene_enet_pdata *p) { struct device *dev = &p->pdev->dev; - struct xgene_enet_desc_ring *ring; - u32 pb; - int i; - - pb = 0; - for (i = 0; i < p->rxq_cnt; i++) { - ring = p->rx_ring[i]->buf_pool; - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - ring = p->rx_ring[i]->page_pool; - if (ring) - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - } - xgene_enet_wr_ring_if(p, ENET_CFGSSQMIFPRESET_ADDR, pb); - - pb = 0; - for (i = 0; i < p->txq_cnt; i++) { - ring = p->tx_ring[i]; - pb |= BIT(xgene_enet_ring_bufnum(ring->id)); - } - xgene_enet_wr_ring_if(p, ENET_CFGSSQMIWQRESET_ADDR, pb); if (dev->of_node) { if (!IS_ERR(p->clk)) @@ -671,6 +598,7 @@ const struct xgene_mac_ops xgene_sgmac_ops = { .tx_enable = xgene_sgmac_tx_enable, .rx_disable = xgene_sgmac_rx_disable, .tx_disable = xgene_sgmac_tx_disable, + .get_drop_cnt = xgene_sgmac_get_drop_cnt, .set_speed = xgene_sgmac_set_speed, .set_mac_addr = xgene_sgmac_set_mac_addr, .set_framesize = xgene_sgmac_set_frame_size, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index ece19e6d68e3..b7d75d067c7a 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -71,21 +71,6 @@ static bool xgene_enet_wr_indirect(void __iomem *addr, void __iomem *wr, return true; } -static void xgene_enet_wr_mac(struct xgene_enet_pdata *pdata, - u32 wr_addr, u32 wr_data) -{ - void __iomem *addr, *wr, *cmd, *cmd_done; - - addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; - wr = pdata->mcx_mac_addr + MAC_WRITE_REG_OFFSET; - cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; - cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; - - if (!xgene_enet_wr_indirect(addr, wr, cmd, cmd_done, wr_addr, wr_data)) - netdev_err(pdata->ndev, "MCX mac write failed, addr: %04x\n", - wr_addr); -} - static void xgene_enet_wr_pcs(struct xgene_enet_pdata *pdata, u32 wr_addr, u32 wr_data) { @@ -148,21 +133,6 @@ static bool xgene_enet_rd_indirect(void __iomem *addr, void __iomem *rd, return true; } -static void xgene_enet_rd_mac(struct xgene_enet_pdata *pdata, - u32 rd_addr, u32 *rd_data) -{ - void __iomem *addr, *rd, *cmd, *cmd_done; - - addr = pdata->mcx_mac_addr + MAC_ADDR_REG_OFFSET; - rd = pdata->mcx_mac_addr + MAC_READ_REG_OFFSET; - cmd = pdata->mcx_mac_addr + MAC_COMMAND_REG_OFFSET; - cmd_done = pdata->mcx_mac_addr + MAC_COMMAND_DONE_REG_OFFSET; - - if (!xgene_enet_rd_indirect(addr, rd, cmd, cmd_done, rd_addr, rd_data)) - netdev_err(pdata->ndev, "MCX mac read failed, addr: %04x\n", - rd_addr); -} - static bool xgene_enet_rd_pcs(struct xgene_enet_pdata *pdata, u32 rd_addr, u32 *rd_data) { @@ -210,6 +180,18 @@ static int xgene_enet_ecc_init(struct xgene_enet_pdata *pdata) return 0; } +static void xgene_xgmac_get_drop_cnt(struct xgene_enet_pdata *pdata, + u32 *rx, u32 *tx) +{ + u32 count; + + xgene_enet_rd_axg_csr(pdata, XGENET_ICM_ECM_DROP_COUNT_REG0, &count); + *rx = ICM_DROP_COUNT(count); + *tx = ECM_DROP_COUNT(count); + /* Errata: 10GE_4 - ICM_ECM_DROP_COUNT not clear-on-read */ + xgene_enet_rd_axg_csr(pdata, XGENET_ECM_CONFIG0_REG_0, &count); +} + static void xgene_enet_config_ring_if_assoc(struct xgene_enet_pdata *pdata) { xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQASSOC_ADDR, 0); @@ -300,7 +282,7 @@ static void xgene_xgmac_flowctl_tx(struct xgene_enet_pdata *pdata, bool enable) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); if (enable) data |= HSTTCTLEN; @@ -316,7 +298,7 @@ static void xgene_xgmac_flowctl_rx(struct xgene_enet_pdata *pdata, bool enable) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); if (enable) data |= HSTRCTLEN; @@ -332,7 +314,7 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) xgene_xgmac_reset(pdata); - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); data |= HSTPPEN; data &= ~HSTLENCHK; xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data); @@ -341,8 +323,15 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) xgene_enet_rd_csr(pdata, XG_RSIF_CONFIG_REG_ADDR, &data); data |= CFG_RSIF_FPBUFF_TIMEOUT_EN; + /* Errata 10GE_1 - FIFO threshold default value incorrect */ + RSIF_CLE_BUFF_THRESH_SET(&data, XG_RSIF_CLE_BUFF_THRESH); xgene_enet_wr_csr(pdata, XG_RSIF_CONFIG_REG_ADDR, data); + /* Errata 10GE_1 - FIFO threshold default value incorrect */ + xgene_enet_rd_csr(pdata, XG_RSIF_CONFIG1_REG_ADDR, &data); + RSIF_PLC_CLE_BUFF_THRESH_SET(&data, XG_RSIF_PLC_CLE_BUFF_THRESH); + xgene_enet_wr_csr(pdata, XG_RSIF_CONFIG1_REG_ADDR, data); + xgene_enet_rd_csr(pdata, XG_ENET_SPARE_CFG_REG_ADDR, &data); data |= BIT(12); xgene_enet_wr_csr(pdata, XG_ENET_SPARE_CFG_REG_ADDR, data); @@ -372,7 +361,7 @@ static void xgene_xgmac_rx_enable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data | HSTRFEN); } @@ -380,7 +369,7 @@ static void xgene_xgmac_tx_enable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data | HSTTFEN); } @@ -388,7 +377,7 @@ static void xgene_xgmac_rx_disable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data & ~HSTRFEN); } @@ -396,7 +385,7 @@ static void xgene_xgmac_tx_disable(struct xgene_enet_pdata *pdata) { u32 data; - xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1, &data); + data = xgene_enet_rd_mac(pdata, AXGMAC_CONFIG_1); xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data & ~HSTTFEN); } @@ -457,26 +446,6 @@ static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata, static void xgene_enet_shutdown(struct xgene_enet_pdata *pdata) { struct device *dev = &pdata->pdev->dev; - struct xgene_enet_desc_ring *ring; - u32 pb; - int i; - - pb = 0; - for (i = 0; i < pdata->rxq_cnt; i++) { - ring = pdata->rx_ring[i]->buf_pool; - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - ring = pdata->rx_ring[i]->page_pool; - if (ring) - pb |= BIT(xgene_enet_get_fpsel(ring->id)); - } - xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIFPRESET_ADDR, pb); - - pb = 0; - for (i = 0; i < pdata->txq_cnt; i++) { - ring = pdata->tx_ring[i]; - pb |= BIT(xgene_enet_ring_bufnum(ring->id)); - } - xgene_enet_wr_ring_if(pdata, ENET_CFGSSQMIWQRESET_ADDR, pb); if (dev->of_node) { if (!IS_ERR(pdata->clk)) @@ -560,6 +529,7 @@ const struct xgene_mac_ops xgene_xgmac_ops = { .set_mac_addr = xgene_xgmac_set_mac_addr, .set_framesize = xgene_xgmac_set_frame_size, .set_mss = xgene_xgmac_set_mss, + .get_drop_cnt = xgene_xgmac_get_drop_cnt, .link_state = xgene_enet_link_state, .enable_tx_pause = xgene_xgmac_enable_tx_pause, .flowctl_rx = xgene_xgmac_flowctl_rx, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h index 03b847ad8937..a3b45517df45 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h @@ -23,6 +23,7 @@ #define X2_BLOCK_ETH_MAC_CSR_OFFSET 0x3000 #define BLOCK_AXG_MAC_OFFSET 0x0800 +#define BLOCK_AXG_STATS_OFFSET 0x0800 #define BLOCK_AXG_MAC_CSR_OFFSET 0x2000 #define BLOCK_PCS_OFFSET 0x3800 @@ -65,6 +66,13 @@ #define XG_DEF_PAUSE_THRES 0x390 #define XG_DEF_PAUSE_OFF_THRES 0x2c0 #define XG_RSIF_CONFIG_REG_ADDR 0x00a0 +#define XG_RSIF_CLE_BUFF_THRESH 0x3 +#define RSIF_CLE_BUFF_THRESH_SET(dst, val) xgene_set_bits(dst, val, 0, 3) +#define XG_RSIF_CONFIG1_REG_ADDR 0x00b8 +#define XG_RSIF_PLC_CLE_BUFF_THRESH 0x1 +#define RSIF_PLC_CLE_BUFF_THRESH_SET(dst, val) xgene_set_bits(dst, val, 0, 2) +#define XG_MCX_ECM_CONFIG0_REG_0_ADDR 0x0070 +#define XG_MCX_ICM_ECM_DROP_COUNT_REG0_ADDR 0x0124 #define XCLE_BYPASS_REG0_ADDR 0x0160 #define XCLE_BYPASS_REG1_ADDR 0x0164 #define XG_CFG_BYPASS_ADDR 0x0204 @@ -75,6 +83,8 @@ #define XG_ENET_SPARE_CFG_REG_ADDR 0x040c #define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410 #define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804 +#define XGENET_ECM_CONFIG0_REG_0 0x0870 +#define XGENET_ICM_ECM_DROP_COUNT_REG0 0x0924 #define XGENET_CSR_ECM_CFG_0_ADDR 0x0880 #define XGENET_CSR_MULTI_DPF0_ADDR 0x0888 #define XGENET_CSR_MULTI_DPF1_ADDR 0x088c |