diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 281 |
1 files changed, 218 insertions, 63 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c index 5419d4e478c0..2ac9dfb3462c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c @@ -5,8 +5,13 @@ #include <linux/clk-provider.h> #include <linux/pci.h> #include <linux/dmi.h> +#include "dwmac-intel.h" #include "stmmac.h" +struct intel_priv_data { + int mdio_adhoc_addr; /* mdio address for serdes & etc */ +}; + /* This struct is used to associate PCI Function of MAC controller on a board, * discovered via DMI, with the address of PHY connected to the MAC. The * negative value of the address means that MAC controller is not connected @@ -49,6 +54,152 @@ static int stmmac_pci_find_phy_addr(struct pci_dev *pdev, return -ENODEV; } +static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr, + int phyreg, u32 mask, u32 val) +{ + unsigned int retries = 10; + int val_rd; + + do { + val_rd = mdiobus_read(priv->mii, phyaddr, phyreg); + if ((val_rd & mask) == (val & mask)) + return 0; + udelay(POLL_DELAY_US); + } while (--retries); + + return -ETIMEDOUT; +} + +static int intel_serdes_powerup(struct net_device *ndev, void *priv_data) +{ + struct intel_priv_data *intel_priv = priv_data; + struct stmmac_priv *priv = netdev_priv(ndev); + int serdes_phy_addr = 0; + u32 data = 0; + + if (!intel_priv->mdio_adhoc_addr) + return 0; + + serdes_phy_addr = intel_priv->mdio_adhoc_addr; + + /* assert clk_req */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + data |= SERDES_PLL_CLK; + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* check for clk_ack assertion */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_PLL_CLK, + SERDES_PLL_CLK); + + if (data) { + dev_err(priv->device, "Serdes PLL clk request timeout\n"); + return data; + } + + /* assert lane reset */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + data |= SERDES_RST; + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* check for assert lane reset reflection */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_RST, + SERDES_RST); + + if (data) { + dev_err(priv->device, "Serdes assert lane reset timeout\n"); + return data; + } + + /* move power state to P0 */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + + data &= ~SERDES_PWR_ST_MASK; + data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT; + + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* Check for P0 state */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_PWR_ST_MASK, + SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT); + + if (data) { + dev_err(priv->device, "Serdes power state P0 timeout.\n"); + return data; + } + + return 0; +} + +static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data) +{ + struct intel_priv_data *intel_priv = intel_data; + struct stmmac_priv *priv = netdev_priv(ndev); + int serdes_phy_addr = 0; + u32 data = 0; + + if (!intel_priv->mdio_adhoc_addr) + return; + + serdes_phy_addr = intel_priv->mdio_adhoc_addr; + + /* move power state to P3 */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + + data &= ~SERDES_PWR_ST_MASK; + data |= SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT; + + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* Check for P3 state */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_PWR_ST_MASK, + SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT); + + if (data) { + dev_err(priv->device, "Serdes power state P3 timeout\n"); + return; + } + + /* de-assert clk_req */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + data &= ~SERDES_PLL_CLK; + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* check for clk_ack de-assert */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_PLL_CLK, + (u32)~SERDES_PLL_CLK); + + if (data) { + dev_err(priv->device, "Serdes PLL clk de-assert timeout\n"); + return; + } + + /* de-assert lane reset */ + data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0); + data &= ~SERDES_RST; + mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data); + + /* check for de-assert lane reset reflection */ + data = serdes_status_poll(priv, serdes_phy_addr, + SERDES_GSR0, + SERDES_RST, + (u32)~SERDES_RST); + + if (data) { + dev_err(priv->device, "Serdes de-assert lane reset timeout\n"); + return; + } +} + static void common_default_data(struct plat_stmmacenet_data *plat) { plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ @@ -81,6 +232,7 @@ static void common_default_data(struct plat_stmmacenet_data *plat) static int intel_mgbe_common_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { + int ret; int i; plat->clk_csr = 5; @@ -153,7 +305,12 @@ static int intel_mgbe_common_data(struct pci_dev *pdev, dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); plat->stmmac_clk = NULL; } - clk_prepare_enable(plat->stmmac_clk); + + ret = clk_prepare_enable(plat->stmmac_clk); + if (ret) { + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; + } /* Set default value for multicast hash bins */ plat->multicast_filter_bins = HASH_TABLE_SIZE; @@ -170,16 +327,11 @@ static int intel_mgbe_common_data(struct pci_dev *pdev, static int ehl_common_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { - int ret; - plat->rx_queues_to_use = 8; plat->tx_queues_to_use = 8; plat->clk_ptp_rate = 200000000; - ret = intel_mgbe_common_data(pdev, plat); - if (ret) - return ret; - return 0; + return intel_mgbe_common_data(pdev, plat); } static int ehl_sgmii_data(struct pci_dev *pdev, @@ -189,10 +341,13 @@ static int ehl_sgmii_data(struct pci_dev *pdev, plat->phy_addr = 0; plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->serdes_powerup = intel_serdes_powerup; + plat->serdes_powerdown = intel_serdes_powerdown; + return ehl_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_sgmii1g_pci_info = { +static struct stmmac_pci_info ehl_sgmii1g_info = { .setup = ehl_sgmii_data, }; @@ -206,7 +361,7 @@ static int ehl_rgmii_data(struct pci_dev *pdev, return ehl_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_rgmii1g_pci_info = { +static struct stmmac_pci_info ehl_rgmii1g_info = { .setup = ehl_rgmii_data, }; @@ -225,7 +380,7 @@ static int ehl_pse0_rgmii1g_data(struct pci_dev *pdev, return ehl_pse0_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_pse0_rgmii1g_pci_info = { +static struct stmmac_pci_info ehl_pse0_rgmii1g_info = { .setup = ehl_pse0_rgmii1g_data, }; @@ -233,10 +388,12 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->serdes_powerup = intel_serdes_powerup; + plat->serdes_powerdown = intel_serdes_powerdown; return ehl_pse0_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_pse0_sgmii1g_pci_info = { +static struct stmmac_pci_info ehl_pse0_sgmii1g_info = { .setup = ehl_pse0_sgmii1g_data, }; @@ -255,7 +412,7 @@ static int ehl_pse1_rgmii1g_data(struct pci_dev *pdev, return ehl_pse1_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_pse1_rgmii1g_pci_info = { +static struct stmmac_pci_info ehl_pse1_rgmii1g_info = { .setup = ehl_pse1_rgmii1g_data, }; @@ -263,26 +420,23 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->serdes_powerup = intel_serdes_powerup; + plat->serdes_powerdown = intel_serdes_powerdown; return ehl_pse1_common_data(pdev, plat); } -static struct stmmac_pci_info ehl_pse1_sgmii1g_pci_info = { +static struct stmmac_pci_info ehl_pse1_sgmii1g_info = { .setup = ehl_pse1_sgmii1g_data, }; static int tgl_common_data(struct pci_dev *pdev, struct plat_stmmacenet_data *plat) { - int ret; - plat->rx_queues_to_use = 6; plat->tx_queues_to_use = 4; plat->clk_ptp_rate = 200000000; - ret = intel_mgbe_common_data(pdev, plat); - if (ret) - return ret; - return 0; + return intel_mgbe_common_data(pdev, plat); } static int tgl_sgmii_data(struct pci_dev *pdev, @@ -291,10 +445,12 @@ static int tgl_sgmii_data(struct pci_dev *pdev, plat->bus_id = 1; plat->phy_addr = 0; plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->serdes_powerup = intel_serdes_powerup; + plat->serdes_powerdown = intel_serdes_powerdown; return tgl_common_data(pdev, plat); } -static struct stmmac_pci_info tgl_sgmii1g_pci_info = { +static struct stmmac_pci_info tgl_sgmii1g_info = { .setup = tgl_sgmii_data, }; @@ -397,7 +553,7 @@ static int quark_default_data(struct pci_dev *pdev, return 0; } -static const struct stmmac_pci_info quark_pci_info = { +static const struct stmmac_pci_info quark_info = { .setup = quark_default_data, }; @@ -417,11 +573,15 @@ static int intel_eth_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; + struct intel_priv_data *intel_priv; struct plat_stmmacenet_data *plat; struct stmmac_resources res; - int i; int ret; + intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv), GFP_KERNEL); + if (!intel_priv) + return -ENOMEM; + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); if (!plat) return -ENOMEM; @@ -445,30 +605,36 @@ static int intel_eth_pci_probe(struct pci_dev *pdev, return ret; } - /* Get the base address of device */ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - if (pci_resource_len(pdev, i) == 0) - continue; - ret = pcim_iomap_regions(pdev, BIT(i), pci_name(pdev)); - if (ret) - return ret; - break; - } + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; pci_set_master(pdev); + plat->bsp_priv = intel_priv; + intel_priv->mdio_adhoc_addr = 0x15; + ret = info->setup(pdev, plat); if (ret) return ret; - pci_enable_msi(pdev); + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; memset(&res, 0, sizeof(res)); - res.addr = pcim_iomap_table(pdev)[i]; - res.wol_irq = pdev->irq; - res.irq = pdev->irq; + res.addr = pcim_iomap_table(pdev)[0]; + res.wol_irq = pci_irq_vector(pdev, 0); + res.irq = pci_irq_vector(pdev, 0); + + ret = stmmac_dvr_probe(&pdev->dev, plat, &res); + if (ret) { + pci_free_irq_vectors(pdev); + clk_disable_unprepare(plat->stmmac_clk); + clk_unregister_fixed_rate(plat->stmmac_clk); + } - return stmmac_dvr_probe(&pdev->dev, plat, &res); + return ret; } /** @@ -482,19 +648,15 @@ static void intel_eth_pci_remove(struct pci_dev *pdev) { struct net_device *ndev = dev_get_drvdata(&pdev->dev); struct stmmac_priv *priv = netdev_priv(ndev); - int i; stmmac_dvr_remove(&pdev->dev); - if (priv->plat->stmmac_clk) - clk_unregister_fixed_rate(priv->plat->stmmac_clk); + pci_free_irq_vectors(pdev); - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - if (pci_resource_len(pdev, i) == 0) - continue; - pcim_iounmap_regions(pdev, BIT(i)); - break; - } + clk_disable_unprepare(priv->plat->stmmac_clk); + clk_unregister_fixed_rate(priv->plat->stmmac_clk); + + pcim_iounmap_regions(pdev, BIT(0)); pci_disable_device(pdev); } @@ -553,26 +715,19 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend, #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G_ID 0xa0ac static const struct pci_device_id intel_eth_pci_id_table[] = { - { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_RGMII1G_ID, &ehl_rgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_SGMII1G_ID, &ehl_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_SGMII2G5_ID, &ehl_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE0_RGMII1G_ID, - &ehl_pse0_rgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII1G_ID, - &ehl_pse0_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII2G5_ID, - &ehl_pse0_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE1_RGMII1G_ID, - &ehl_pse1_rgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII1G_ID, - &ehl_pse1_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII2G5_ID, - &ehl_pse1_sgmii1g_pci_info) }, - { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_pci_info) }, + { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_RGMII1G_ID, &ehl_rgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_SGMII1G_ID, &ehl_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_SGMII2G5_ID, &ehl_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE0_RGMII1G_ID, &ehl_pse0_rgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII1G_ID, &ehl_pse0_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII2G5_ID, &ehl_pse0_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE1_RGMII1G_ID, &ehl_pse1_rgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII1G_ID, &ehl_pse1_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII2G5_ID, &ehl_pse1_sgmii1g_info) }, + { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_info) }, {} }; - MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table); static struct pci_driver intel_eth_pci_driver = { |