Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /* Applied Micro X-Gene SoC MDIO Driver
0003  *
0004  * Copyright (c) 2016, Applied Micro Circuits Corporation
0005  * Author: Iyappan Subramanian <isubramanian@apm.com>
0006  */
0007 
0008 #include <linux/acpi.h>
0009 #include <linux/clk.h>
0010 #include <linux/device.h>
0011 #include <linux/efi.h>
0012 #include <linux/if_vlan.h>
0013 #include <linux/io.h>
0014 #include <linux/mdio/mdio-xgene.h>
0015 #include <linux/module.h>
0016 #include <linux/of_mdio.h>
0017 #include <linux/of_net.h>
0018 #include <linux/of_platform.h>
0019 #include <linux/phy.h>
0020 #include <linux/prefetch.h>
0021 #include <net/ip.h>
0022 
0023 static bool xgene_mdio_status;
0024 
0025 u32 xgene_mdio_rd_mac(struct xgene_mdio_pdata *pdata, u32 rd_addr)
0026 {
0027     void __iomem *addr, *rd, *cmd, *cmd_done;
0028     u32 done, rd_data = BUSY_MASK;
0029     u8 wait = 10;
0030 
0031     addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
0032     rd = pdata->mac_csr_addr + MAC_READ_REG_OFFSET;
0033     cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
0034     cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
0035 
0036     spin_lock(&pdata->mac_lock);
0037     iowrite32(rd_addr, addr);
0038     iowrite32(XGENE_ENET_RD_CMD, cmd);
0039 
0040     while (!(done = ioread32(cmd_done)) && wait--)
0041         udelay(1);
0042 
0043     if (done)
0044         rd_data = ioread32(rd);
0045 
0046     iowrite32(0, cmd);
0047     spin_unlock(&pdata->mac_lock);
0048 
0049     return rd_data;
0050 }
0051 EXPORT_SYMBOL(xgene_mdio_rd_mac);
0052 
0053 void xgene_mdio_wr_mac(struct xgene_mdio_pdata *pdata, u32 wr_addr, u32 data)
0054 {
0055     void __iomem *addr, *wr, *cmd, *cmd_done;
0056     u8 wait = 10;
0057     u32 done;
0058 
0059     addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
0060     wr = pdata->mac_csr_addr + MAC_WRITE_REG_OFFSET;
0061     cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
0062     cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
0063 
0064     spin_lock(&pdata->mac_lock);
0065     iowrite32(wr_addr, addr);
0066     iowrite32(data, wr);
0067     iowrite32(XGENE_ENET_WR_CMD, cmd);
0068 
0069     while (!(done = ioread32(cmd_done)) && wait--)
0070         udelay(1);
0071 
0072     if (!done)
0073         pr_err("MCX mac write failed, addr: 0x%04x\n", wr_addr);
0074 
0075     iowrite32(0, cmd);
0076     spin_unlock(&pdata->mac_lock);
0077 }
0078 EXPORT_SYMBOL(xgene_mdio_wr_mac);
0079 
0080 int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg)
0081 {
0082     struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv;
0083     u32 data, done;
0084     u8 wait = 10;
0085 
0086     data = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
0087     xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, data);
0088     xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
0089     do {
0090         usleep_range(5, 10);
0091         done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
0092     } while ((done & BUSY_MASK) && wait--);
0093 
0094     if (done & BUSY_MASK) {
0095         dev_err(&bus->dev, "MII_MGMT read failed\n");
0096         return -EBUSY;
0097     }
0098 
0099     data = xgene_mdio_rd_mac(pdata, MII_MGMT_STATUS_ADDR);
0100     xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, 0);
0101 
0102     return data;
0103 }
0104 EXPORT_SYMBOL(xgene_mdio_rgmii_read);
0105 
0106 int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
0107 {
0108     struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv;
0109     u32 val, done;
0110     u8 wait = 10;
0111 
0112     val = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
0113     xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, val);
0114 
0115     xgene_mdio_wr_mac(pdata, MII_MGMT_CONTROL_ADDR, data);
0116     do {
0117         usleep_range(5, 10);
0118         done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
0119     } while ((done & BUSY_MASK) && wait--);
0120 
0121     if (done & BUSY_MASK) {
0122         dev_err(&bus->dev, "MII_MGMT write failed\n");
0123         return -EBUSY;
0124     }
0125 
0126     return 0;
0127 }
0128 EXPORT_SYMBOL(xgene_mdio_rgmii_write);
0129 
0130 static u32 xgene_menet_rd_diag_csr(struct xgene_mdio_pdata *pdata, u32 offset)
0131 {
0132     return ioread32(pdata->diag_csr_addr + offset);
0133 }
0134 
0135 static void xgene_menet_wr_diag_csr(struct xgene_mdio_pdata *pdata,
0136                     u32 offset, u32 val)
0137 {
0138     iowrite32(val, pdata->diag_csr_addr + offset);
0139 }
0140 
0141 static int xgene_enet_ecc_init(struct xgene_mdio_pdata *pdata)
0142 {
0143     u32 data;
0144     u8 wait = 10;
0145 
0146     xgene_menet_wr_diag_csr(pdata, MENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x0);
0147     do {
0148         usleep_range(100, 110);
0149         data = xgene_menet_rd_diag_csr(pdata, MENET_BLOCK_MEM_RDY_ADDR);
0150     } while ((data != 0xffffffff) && wait--);
0151 
0152     if (data != 0xffffffff) {
0153         dev_err(pdata->dev, "Failed to release memory from shutdown\n");
0154         return -ENODEV;
0155     }
0156 
0157     return 0;
0158 }
0159 
0160 static void xgene_gmac_reset(struct xgene_mdio_pdata *pdata)
0161 {
0162     xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET);
0163     xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, 0);
0164 }
0165 
0166 static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata)
0167 {
0168     int ret;
0169 
0170     if (pdata->dev->of_node) {
0171         clk_prepare_enable(pdata->clk);
0172         udelay(5);
0173         clk_disable_unprepare(pdata->clk);
0174         udelay(5);
0175         clk_prepare_enable(pdata->clk);
0176         udelay(5);
0177     } else {
0178 #ifdef CONFIG_ACPI
0179         acpi_evaluate_object(ACPI_HANDLE(pdata->dev),
0180                      "_RST", NULL, NULL);
0181 #endif
0182     }
0183 
0184     ret = xgene_enet_ecc_init(pdata);
0185     if (ret) {
0186         if (pdata->dev->of_node)
0187             clk_disable_unprepare(pdata->clk);
0188         return ret;
0189     }
0190     xgene_gmac_reset(pdata);
0191 
0192     return 0;
0193 }
0194 
0195 static void xgene_enet_rd_mdio_csr(void __iomem *base_addr,
0196                    u32 offset, u32 *val)
0197 {
0198     void __iomem *addr = base_addr  + offset;
0199 
0200     *val = ioread32(addr);
0201 }
0202 
0203 static void xgene_enet_wr_mdio_csr(void __iomem *base_addr,
0204                    u32 offset, u32 val)
0205 {
0206     void __iomem *addr = base_addr  + offset;
0207 
0208     iowrite32(val, addr);
0209 }
0210 
0211 static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id,
0212                 int reg, u16 data)
0213 {
0214     void __iomem *addr = (void __iomem *)bus->priv;
0215     int timeout = 100;
0216     u32 status, val;
0217 
0218     val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) |
0219           SET_VAL(HSTMIIMWRDAT, data);
0220     xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
0221 
0222     val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE);
0223     xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
0224 
0225     do {
0226         usleep_range(5, 10);
0227         xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
0228     } while ((status & BUSY_MASK) && timeout--);
0229 
0230     xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
0231 
0232     return 0;
0233 }
0234 
0235 static int xgene_xfi_mdio_read(struct mii_bus *bus, int phy_id, int reg)
0236 {
0237     void __iomem *addr = (void __iomem *)bus->priv;
0238     u32 data, status, val;
0239     int timeout = 100;
0240 
0241     val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg);
0242     xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
0243 
0244     val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_READ);
0245     xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
0246 
0247     do {
0248         usleep_range(5, 10);
0249         xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
0250     } while ((status & BUSY_MASK) && timeout--);
0251 
0252     if (status & BUSY_MASK) {
0253         pr_err("XGENET_MII_MGMT write failed\n");
0254         return -EBUSY;
0255     }
0256 
0257     xgene_enet_rd_mdio_csr(addr, MIIMRD_FIELD_ADDR, &data);
0258     xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
0259 
0260     return data;
0261 }
0262 
0263 struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr)
0264 {
0265     struct phy_device *phy_dev;
0266 
0267     phy_dev = get_phy_device(bus, phy_addr, false);
0268     if (!phy_dev || IS_ERR(phy_dev))
0269         return NULL;
0270 
0271     if (phy_device_register(phy_dev))
0272         phy_device_free(phy_dev);
0273 
0274     return phy_dev;
0275 }
0276 EXPORT_SYMBOL(xgene_enet_phy_register);
0277 
0278 #ifdef CONFIG_ACPI
0279 static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl,
0280                      void *context, void **ret)
0281 {
0282     struct mii_bus *mdio = context;
0283     struct acpi_device *adev;
0284     struct phy_device *phy_dev;
0285     const union acpi_object *obj;
0286     u32 phy_addr;
0287 
0288     adev = acpi_fetch_acpi_dev(handle);
0289     if (!adev)
0290         return AE_OK;
0291 
0292     if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj))
0293         return AE_OK;
0294     phy_addr = obj->integer.value;
0295 
0296     phy_dev = xgene_enet_phy_register(mdio, phy_addr);
0297     adev->driver_data = phy_dev;
0298 
0299     return AE_OK;
0300 }
0301 #endif
0302 
0303 static const struct of_device_id xgene_mdio_of_match[] = {
0304     {
0305         .compatible = "apm,xgene-mdio-rgmii",
0306         .data = (void *)XGENE_MDIO_RGMII
0307     },
0308     {
0309         .compatible = "apm,xgene-mdio-xfi",
0310         .data = (void *)XGENE_MDIO_XFI
0311     },
0312     {},
0313 };
0314 MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
0315 
0316 #ifdef CONFIG_ACPI
0317 static const struct acpi_device_id xgene_mdio_acpi_match[] = {
0318     { "APMC0D65", XGENE_MDIO_RGMII },
0319     { "APMC0D66", XGENE_MDIO_XFI },
0320     { }
0321 };
0322 
0323 MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
0324 #endif
0325 
0326 
0327 static int xgene_mdio_probe(struct platform_device *pdev)
0328 {
0329     struct device *dev = &pdev->dev;
0330     struct mii_bus *mdio_bus;
0331     const struct of_device_id *of_id;
0332     struct xgene_mdio_pdata *pdata;
0333     void __iomem *csr_base;
0334     int mdio_id = 0, ret = 0;
0335 
0336     of_id = of_match_device(xgene_mdio_of_match, &pdev->dev);
0337     if (of_id) {
0338         mdio_id = (enum xgene_mdio_id)of_id->data;
0339     } else {
0340 #ifdef CONFIG_ACPI
0341         const struct acpi_device_id *acpi_id;
0342 
0343         acpi_id = acpi_match_device(xgene_mdio_acpi_match, &pdev->dev);
0344         if (acpi_id)
0345             mdio_id = (enum xgene_mdio_id)acpi_id->driver_data;
0346 #endif
0347     }
0348 
0349     if (!mdio_id)
0350         return -ENODEV;
0351 
0352     pdata = devm_kzalloc(dev, sizeof(struct xgene_mdio_pdata), GFP_KERNEL);
0353     if (!pdata)
0354         return -ENOMEM;
0355     pdata->mdio_id = mdio_id;
0356     pdata->dev = dev;
0357 
0358     csr_base = devm_platform_ioremap_resource(pdev, 0);
0359     if (IS_ERR(csr_base))
0360         return PTR_ERR(csr_base);
0361     pdata->mac_csr_addr = csr_base;
0362     pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET;
0363     pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET;
0364 
0365     if (mdio_id == XGENE_MDIO_RGMII)
0366         spin_lock_init(&pdata->mac_lock);
0367 
0368     if (dev->of_node) {
0369         pdata->clk = devm_clk_get(dev, NULL);
0370         if (IS_ERR(pdata->clk)) {
0371             dev_err(dev, "Unable to retrieve clk\n");
0372             return PTR_ERR(pdata->clk);
0373         }
0374     }
0375 
0376     ret = xgene_mdio_reset(pdata);
0377     if (ret)
0378         return ret;
0379 
0380     mdio_bus = mdiobus_alloc();
0381     if (!mdio_bus) {
0382         ret = -ENOMEM;
0383         goto out_clk;
0384     }
0385 
0386     mdio_bus->name = "APM X-Gene MDIO bus";
0387 
0388     if (mdio_id == XGENE_MDIO_RGMII) {
0389         mdio_bus->read = xgene_mdio_rgmii_read;
0390         mdio_bus->write = xgene_mdio_rgmii_write;
0391         mdio_bus->priv = (void __force *)pdata;
0392         snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
0393              "xgene-mii-rgmii");
0394     } else {
0395         mdio_bus->read = xgene_xfi_mdio_read;
0396         mdio_bus->write = xgene_xfi_mdio_write;
0397         mdio_bus->priv = (void __force *)pdata->mdio_csr_addr;
0398         snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
0399              "xgene-mii-xfi");
0400     }
0401 
0402     mdio_bus->parent = dev;
0403     platform_set_drvdata(pdev, pdata);
0404 
0405     if (dev->of_node) {
0406         ret = of_mdiobus_register(mdio_bus, dev->of_node);
0407     } else {
0408 #ifdef CONFIG_ACPI
0409         /* Mask out all PHYs from auto probing. */
0410         mdio_bus->phy_mask = ~0;
0411         ret = mdiobus_register(mdio_bus);
0412         if (ret)
0413             goto out_mdiobus;
0414 
0415         acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
0416                     acpi_register_phy, NULL, mdio_bus, NULL);
0417 #endif
0418     }
0419 
0420     if (ret)
0421         goto out_mdiobus;
0422 
0423     pdata->mdio_bus = mdio_bus;
0424     xgene_mdio_status = true;
0425 
0426     return 0;
0427 
0428 out_mdiobus:
0429     mdiobus_free(mdio_bus);
0430 
0431 out_clk:
0432     if (dev->of_node)
0433         clk_disable_unprepare(pdata->clk);
0434 
0435     return ret;
0436 }
0437 
0438 static int xgene_mdio_remove(struct platform_device *pdev)
0439 {
0440     struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev);
0441     struct mii_bus *mdio_bus = pdata->mdio_bus;
0442     struct device *dev = &pdev->dev;
0443 
0444     mdiobus_unregister(mdio_bus);
0445     mdiobus_free(mdio_bus);
0446 
0447     if (dev->of_node)
0448         clk_disable_unprepare(pdata->clk);
0449 
0450     return 0;
0451 }
0452 
0453 static struct platform_driver xgene_mdio_driver = {
0454     .driver = {
0455         .name = "xgene-mdio",
0456         .of_match_table = of_match_ptr(xgene_mdio_of_match),
0457         .acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match),
0458     },
0459     .probe = xgene_mdio_probe,
0460     .remove = xgene_mdio_remove,
0461 };
0462 
0463 module_platform_driver(xgene_mdio_driver);
0464 
0465 MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver");
0466 MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>");
0467 MODULE_LICENSE("GPL");