|
5 | 5 | #include <linux/clk-provider.h>
|
6 | 6 | #include <linux/pci.h>
|
7 | 7 | #include <linux/dmi.h>
|
| 8 | +#include "dwmac-intel.h" |
8 | 9 | #include "stmmac.h"
|
9 | 10 |
|
| 11 | +struct intel_priv_data { |
| 12 | + int mdio_adhoc_addr; /* mdio address for serdes & etc */ |
| 13 | +}; |
| 14 | + |
10 | 15 | /* This struct is used to associate PCI Function of MAC controller on a board,
|
11 | 16 | * discovered via DMI, with the address of PHY connected to the MAC. The
|
12 | 17 | * negative value of the address means that MAC controller is not connected
|
@@ -49,6 +54,172 @@ static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
|
49 | 54 | return -ENODEV;
|
50 | 55 | }
|
51 | 56 |
|
| 57 | +static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr, |
| 58 | + int phyreg, u32 mask, u32 val) |
| 59 | +{ |
| 60 | + unsigned int retries = 10; |
| 61 | + int val_rd; |
| 62 | + |
| 63 | + do { |
| 64 | + val_rd = mdiobus_read(priv->mii, phyaddr, phyreg); |
| 65 | + if ((val_rd & mask) == (val & mask)) |
| 66 | + return 0; |
| 67 | + udelay(POLL_DELAY_US); |
| 68 | + } while (--retries); |
| 69 | + |
| 70 | + return -ETIMEDOUT; |
| 71 | +} |
| 72 | + |
| 73 | +static int intel_serdes_powerup(struct net_device *ndev, void *priv_data) |
| 74 | +{ |
| 75 | + struct intel_priv_data *intel_priv = priv_data; |
| 76 | + struct stmmac_priv *priv = netdev_priv(ndev); |
| 77 | + int serdes_phy_addr = 0; |
| 78 | + u32 data = 0; |
| 79 | + |
| 80 | + if (!intel_priv->mdio_adhoc_addr) |
| 81 | + return 0; |
| 82 | + |
| 83 | + serdes_phy_addr = intel_priv->mdio_adhoc_addr; |
| 84 | + |
| 85 | + /* assert clk_req */ |
| 86 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 87 | + SERDES_GCR0); |
| 88 | + |
| 89 | + data |= SERDES_PLL_CLK; |
| 90 | + |
| 91 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 92 | + SERDES_GCR0, data); |
| 93 | + |
| 94 | + /* check for clk_ack assertion */ |
| 95 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 96 | + SERDES_GSR0, |
| 97 | + SERDES_PLL_CLK, |
| 98 | + SERDES_PLL_CLK); |
| 99 | + |
| 100 | + if (data) { |
| 101 | + dev_err(priv->device, "Serdes PLL clk request timeout\n"); |
| 102 | + return data; |
| 103 | + } |
| 104 | + |
| 105 | + /* assert lane reset */ |
| 106 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 107 | + SERDES_GCR0); |
| 108 | + |
| 109 | + data |= SERDES_RST; |
| 110 | + |
| 111 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 112 | + SERDES_GCR0, data); |
| 113 | + |
| 114 | + /* check for assert lane reset reflection */ |
| 115 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 116 | + SERDES_GSR0, |
| 117 | + SERDES_RST, |
| 118 | + SERDES_RST); |
| 119 | + |
| 120 | + if (data) { |
| 121 | + dev_err(priv->device, "Serdes assert lane reset timeout\n"); |
| 122 | + return data; |
| 123 | + } |
| 124 | + |
| 125 | + /* move power state to P0 */ |
| 126 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 127 | + SERDES_GCR0); |
| 128 | + |
| 129 | + data &= ~SERDES_PWR_ST_MASK; |
| 130 | + data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT; |
| 131 | + |
| 132 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 133 | + SERDES_GCR0, data); |
| 134 | + |
| 135 | + /* Check for P0 state */ |
| 136 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 137 | + SERDES_GSR0, |
| 138 | + SERDES_PWR_ST_MASK, |
| 139 | + SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT); |
| 140 | + |
| 141 | + if (data) { |
| 142 | + dev_err(priv->device, "Serdes power state P0 timeout.\n"); |
| 143 | + return data; |
| 144 | + } |
| 145 | + |
| 146 | + return 0; |
| 147 | +} |
| 148 | + |
| 149 | +static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data) |
| 150 | +{ |
| 151 | + struct intel_priv_data *intel_priv = intel_data; |
| 152 | + struct stmmac_priv *priv = netdev_priv(ndev); |
| 153 | + int serdes_phy_addr = 0; |
| 154 | + u32 data = 0; |
| 155 | + |
| 156 | + if (!intel_priv->mdio_adhoc_addr) |
| 157 | + return; |
| 158 | + |
| 159 | + serdes_phy_addr = intel_priv->mdio_adhoc_addr; |
| 160 | + |
| 161 | + /* move power state to P3 */ |
| 162 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 163 | + SERDES_GCR0); |
| 164 | + |
| 165 | + data &= ~SERDES_PWR_ST_MASK; |
| 166 | + data |= SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT; |
| 167 | + |
| 168 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 169 | + SERDES_GCR0, data); |
| 170 | + |
| 171 | + /* Check for P3 state */ |
| 172 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 173 | + SERDES_GSR0, |
| 174 | + SERDES_PWR_ST_MASK, |
| 175 | + SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT); |
| 176 | + |
| 177 | + if (data) { |
| 178 | + dev_err(priv->device, "Serdes power state P3 timeout\n"); |
| 179 | + return; |
| 180 | + } |
| 181 | + |
| 182 | + /* de-assert clk_req */ |
| 183 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 184 | + SERDES_GCR0); |
| 185 | + |
| 186 | + data &= ~SERDES_PLL_CLK; |
| 187 | + |
| 188 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 189 | + SERDES_GCR0, data); |
| 190 | + |
| 191 | + /* check for clk_ack de-assert */ |
| 192 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 193 | + SERDES_GSR0, |
| 194 | + SERDES_PLL_CLK, |
| 195 | + (u32)~SERDES_PLL_CLK); |
| 196 | + |
| 197 | + if (data) { |
| 198 | + dev_err(priv->device, "Serdes PLL clk de-assert timeout\n"); |
| 199 | + return; |
| 200 | + } |
| 201 | + |
| 202 | + /* de-assert lane reset */ |
| 203 | + data = mdiobus_read(priv->mii, serdes_phy_addr, |
| 204 | + SERDES_GCR0); |
| 205 | + |
| 206 | + data &= ~SERDES_RST; |
| 207 | + |
| 208 | + mdiobus_write(priv->mii, serdes_phy_addr, |
| 209 | + SERDES_GCR0, data); |
| 210 | + |
| 211 | + /* check for de-assert lane reset reflection */ |
| 212 | + data = serdes_status_poll(priv, serdes_phy_addr, |
| 213 | + SERDES_GSR0, |
| 214 | + SERDES_RST, |
| 215 | + (u32)~SERDES_RST); |
| 216 | + |
| 217 | + if (data) { |
| 218 | + dev_err(priv->device, "Serdes de-assert lane reset timeout\n"); |
| 219 | + return; |
| 220 | + } |
| 221 | +} |
| 222 | + |
52 | 223 | static void common_default_data(struct plat_stmmacenet_data *plat)
|
53 | 224 | {
|
54 | 225 | plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
|
@@ -189,6 +360,9 @@ static int ehl_sgmii_data(struct pci_dev *pdev,
|
189 | 360 | plat->phy_addr = 0;
|
190 | 361 | plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
|
191 | 362 |
|
| 363 | + plat->serdes_powerup = intel_serdes_powerup; |
| 364 | + plat->serdes_powerdown = intel_serdes_powerdown; |
| 365 | + |
192 | 366 | return ehl_common_data(pdev, plat);
|
193 | 367 | }
|
194 | 368 |
|
@@ -233,6 +407,8 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
|
233 | 407 | struct plat_stmmacenet_data *plat)
|
234 | 408 | {
|
235 | 409 | plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
|
| 410 | + plat->serdes_powerup = intel_serdes_powerup; |
| 411 | + plat->serdes_powerdown = intel_serdes_powerdown; |
236 | 412 | return ehl_pse0_common_data(pdev, plat);
|
237 | 413 | }
|
238 | 414 |
|
@@ -263,6 +439,8 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
|
263 | 439 | struct plat_stmmacenet_data *plat)
|
264 | 440 | {
|
265 | 441 | plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
|
| 442 | + plat->serdes_powerup = intel_serdes_powerup; |
| 443 | + plat->serdes_powerdown = intel_serdes_powerdown; |
266 | 444 | return ehl_pse1_common_data(pdev, plat);
|
267 | 445 | }
|
268 | 446 |
|
@@ -291,6 +469,8 @@ static int tgl_sgmii_data(struct pci_dev *pdev,
|
291 | 469 | plat->bus_id = 1;
|
292 | 470 | plat->phy_addr = 0;
|
293 | 471 | plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
|
| 472 | + plat->serdes_powerup = intel_serdes_powerup; |
| 473 | + plat->serdes_powerdown = intel_serdes_powerdown; |
294 | 474 | return tgl_common_data(pdev, plat);
|
295 | 475 | }
|
296 | 476 |
|
@@ -417,11 +597,17 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
|
417 | 597 | const struct pci_device_id *id)
|
418 | 598 | {
|
419 | 599 | struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;
|
| 600 | + struct intel_priv_data *intel_priv; |
420 | 601 | struct plat_stmmacenet_data *plat;
|
421 | 602 | struct stmmac_resources res;
|
422 | 603 | int i;
|
423 | 604 | int ret;
|
424 | 605 |
|
| 606 | + intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv), |
| 607 | + GFP_KERNEL); |
| 608 | + if (!intel_priv) |
| 609 | + return -ENOMEM; |
| 610 | + |
425 | 611 | plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
|
426 | 612 | if (!plat)
|
427 | 613 | return -ENOMEM;
|
@@ -457,6 +643,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
|
457 | 643 |
|
458 | 644 | pci_set_master(pdev);
|
459 | 645 |
|
| 646 | + plat->bsp_priv = intel_priv; |
| 647 | + intel_priv->mdio_adhoc_addr = 0x15; |
| 648 | + |
460 | 649 | ret = info->setup(pdev, plat);
|
461 | 650 | if (ret)
|
462 | 651 | return ret;
|
|
0 commit comments