diff options
author | David S. Miller <davem@davemloft.net> | 2017-03-22 19:17:15 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-22 19:17:15 -0700 |
commit | b5bacf86b63e850e185dfd9d0e6f1bff51e8919e (patch) | |
tree | 650bf7ddbc178cf26c54217103cca27014bc4314 | |
parent | 9bdf64d511c5c23e39b7a58ff4056d839060c026 (diff) | |
parent | 1ffa8a7aa516473b3575ee8c30f4e0b1bb29a7d8 (diff) | |
download | linux-b5bacf86b63e850e185dfd9d0e6f1bff51e8919e.tar.bz2 |
Merge branch 'xgene-v2-mdio-and-ethtool'
Iyappan Subramanian says:
====================
drivers: net: xgene-v2: Add MDIO and ethtool support
This patch set,
- adds phy management and ethtool support
- fixes ethernet reset
- addresses review comments from previous patch set
v2: Address review comments from v1
- removed mdio_lock, since there is a top level lock in mdio_bus.c
v1:
- Initial version
====================
Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/enet.c | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/enet.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/ethtool.c | 121 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/mac.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/mac.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/main.c | 67 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/main.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/apm/xgene-v2/mdio.c | 167 |
9 files changed, 351 insertions, 41 deletions
diff --git a/drivers/net/ethernet/apm/xgene-v2/Makefile b/drivers/net/ethernet/apm/xgene-v2/Makefile index 735309c0b8b1..f16a2b3dde8b 100644 --- a/drivers/net/ethernet/apm/xgene-v2/Makefile +++ b/drivers/net/ethernet/apm/xgene-v2/Makefile @@ -2,5 +2,5 @@ # Makefile for APM X-Gene Ethernet v2 driver # -xgene-enet-v2-objs := main.o mac.o enet.o ring.o +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 index b49edeeb6275..5998da014923 100644 --- a/drivers/net/ethernet/apm/xgene-v2/enet.c +++ b/drivers/net/ethernet/apm/xgene-v2/enet.c @@ -38,10 +38,24 @@ u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset) 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_SRST, 0x3); - xge_wr_csr(pdata, ENET_SRST, 0x2); - xge_wr_csr(pdata, ENET_SRST, 0x0); + 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); @@ -59,13 +73,11 @@ static void xge_traffic_resume(struct net_device *ndev) xge_wr_csr(pdata, RX_DV_GATE_REG, 1); } -int xge_port_init(struct net_device *ndev) +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); - - return 0; } diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.h b/drivers/net/ethernet/apm/xgene-v2/enet.h index 40371cfcfce4..3fd36dc66a23 100644 --- a/drivers/net/ethernet/apm/xgene-v2/enet.h +++ b/drivers/net/ethernet/apm/xgene-v2/enet.h @@ -28,6 +28,7 @@ #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) @@ -39,5 +40,6 @@ 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..0c426f55ffdb --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.c @@ -0,0 +1,121 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <isubramanian@apm.com> + * Keyur Chudgar <kchudgar@apm.com> + * + * 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" + +struct xge_gstrings_stats { + char name[ETH_GSTRING_LEN]; + int offset; +}; + +#define XGE_STAT(m) { #m, offsetof(struct xge_pdata, stats.m) } + +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) +}; + +#define XGE_STATS_LEN ARRAY_SIZE(gstrings_stats) + +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; + } +} + +static int xge_get_sset_count(struct net_device *ndev, int sset) +{ + if (sset != ETH_SS_STATS) + return -EINVAL; + + return XGE_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); +} + +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; + + return phy_ethtool_ksettings_get(phydev, cmd); +} + +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/mac.c b/drivers/net/ethernet/apm/xgene-v2/mac.c index c3189de3df55..ee431e397e57 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.c +++ b/drivers/net/ethernet/apm/xgene-v2/mac.c @@ -27,7 +27,7 @@ void xge_mac_reset(struct xge_pdata *pdata) xge_wr_csr(pdata, MAC_CONFIG_1, 0); } -static void xge_mac_set_speed(struct xge_pdata *pdata) +void xge_mac_set_speed(struct xge_pdata *pdata) { u32 icm0, icm2, ecm0, mc2; u32 intf_ctrl, rgmii; diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h index 0fce6ae15ce0..18a9c9d8a5e5 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.h +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -101,10 +101,10 @@ static inline u32 xgene_get_reg_bits(u32 var, int pos, int len) 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); -int xge_port_init(struct net_device *ndev); 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 index ae76977d10b4..0f2ad50f3bd7 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -66,9 +66,8 @@ static int xge_get_resources(struct xge_pdata *pdata) } ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(dev, "Unable to get ENET IRQ\n"); - ret = ret ? : -ENXIO; + if (ret < 0) { + dev_err(dev, "Unable to get irq\n"); return ret; } pdata->resources.irq = ret; @@ -156,13 +155,12 @@ static irqreturn_t xge_irq(const int irq, void *data) static int xge_request_irq(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - struct device *dev = &pdata->pdev->dev; int ret; snprintf(pdata->irq_name, IRQ_ID_SIZE, "%s", ndev->name); - ret = devm_request_irq(dev, pdata->resources.irq, xge_irq, - 0, pdata->irq_name, pdata); + 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); @@ -172,9 +170,8 @@ static int xge_request_irq(struct net_device *ndev) static void xge_free_irq(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - struct device *dev = &pdata->pdev->dev; - devm_free_irq(dev, pdata->resources.irq, pdata); + free_irq(pdata->resources.irq, pdata); } static bool is_tx_slot_available(struct xge_raw_desc *raw_desc) @@ -424,7 +421,7 @@ static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) struct xge_desc_ring *ring; u16 size; - ring = kzalloc(sizeof(struct xge_desc_ring), GFP_KERNEL); + ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) return NULL; @@ -436,7 +433,7 @@ static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) if (!ring->desc_addr) goto err; - ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(struct pkt_info), + ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(*ring->pkt_info), GFP_KERNEL); if (!ring->pkt_info) goto err; @@ -500,9 +497,10 @@ static int xge_open(struct net_device *ndev) xge_intr_enable(pdata); xge_wr_csr(pdata, DMARXCTRL, 1); + + phy_start(ndev->phydev); xge_mac_enable(pdata); netif_start_queue(ndev); - netif_carrier_on(ndev); return 0; } @@ -511,9 +509,9 @@ static int xge_close(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - netif_carrier_off(ndev); netif_stop_queue(ndev); xge_mac_disable(pdata); + phy_stop(ndev->phydev); xge_intr_disable(pdata); xge_free_irq(ndev); @@ -597,28 +595,28 @@ static void xge_timeout(struct net_device *ndev) rtnl_lock(); - if (netif_running(ndev)) { - netif_carrier_off(ndev); - netif_stop_queue(ndev); - xge_intr_disable(pdata); - napi_disable(&pdata->napi); + if (!netif_running(ndev)) + goto out; - xge_wr_csr(pdata, DMATXCTRL, 0); - xge_txc_poll(ndev); - xge_free_pending_skb(ndev); - xge_wr_csr(pdata, DMATXSTATUS, ~0U); + netif_stop_queue(ndev); + xge_intr_disable(pdata); + napi_disable(&pdata->napi); - xge_setup_desc(pdata->tx_ring); - xge_update_tx_desc_addr(pdata); - xge_mac_init(pdata); + xge_wr_csr(pdata, DMATXCTRL, 0); + xge_txc_poll(ndev); + xge_free_pending_skb(ndev); + xge_wr_csr(pdata, DMATXSTATUS, ~0U); - napi_enable(&pdata->napi); - xge_intr_enable(pdata); - xge_mac_enable(pdata); - netif_start_queue(ndev); - netif_carrier_on(ndev); - } + 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(); } @@ -652,7 +650,7 @@ static int xge_probe(struct platform_device *pdev) struct xge_pdata *pdata; int ret; - ndev = alloc_etherdev(sizeof(struct xge_pdata)); + ndev = alloc_etherdev(sizeof(*pdata)); if (!ndev) return -ENOMEM; @@ -672,6 +670,7 @@ static int xge_probe(struct platform_device *pdev) 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) { @@ -683,9 +682,12 @@ static int xge_probe(struct platform_device *pdev) if (ret) goto err; + ret = xge_mdio_config(ndev); + if (ret) + goto err; + netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); - netif_carrier_off(ndev); ret = register_netdev(ndev); if (ret) { netdev_err(ndev, "Failed to register netdev\n"); @@ -713,6 +715,7 @@ static int xge_remove(struct platform_device *pdev) dev_close(ndev); rtnl_unlock(); + xge_mdio_remove(ndev); unregister_netdev(ndev); free_netdev(ndev); diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h index ada7b0e82586..db1178e82a0a 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.h +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -65,6 +65,7 @@ struct xge_pdata { 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; @@ -72,4 +73,8 @@ struct xge_pdata { u8 nbufs; }; +int xge_mdio_config(struct net_device *ndev); +void xge_mdio_remove(struct net_device *ndev); +void xge_set_ethtool_ops(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..a583c6a9a5ea --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mdio.c @@ -0,0 +1,167 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian <isubramanian@apm.com> + * Keyur Chudgar <kchudgar@apm.com> + * + * 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"); + 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; +} |