1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Applied Micro X-Gene SoC MDIO Driver 3 * 4 * Copyright (c) 2016, Applied Micro Circuits Corporation 5 * Author: Iyappan Subramanian <isubramanian@apm.com> 6 */ 7 8 #include <linux/acpi.h> 9 #include <linux/clk.h> 10 #include <linux/device.h> 11 #include <linux/efi.h> 12 #include <linux/if_vlan.h> 13 #include <linux/io.h> 14 #include <linux/mdio/mdio-xgene.h> 15 #include <linux/module.h> 16 #include <linux/of_platform.h> 17 #include <linux/of_net.h> 18 #include <linux/of_mdio.h> 19 #include <linux/prefetch.h> 20 #include <linux/phy.h> 21 #include <net/ip.h> 22 23 static bool xgene_mdio_status; 24 25 u32 xgene_mdio_rd_mac(struct xgene_mdio_pdata *pdata, u32 rd_addr) 26 { 27 void __iomem *addr, *rd, *cmd, *cmd_done; 28 u32 done, rd_data = BUSY_MASK; 29 u8 wait = 10; 30 31 addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET; 32 rd = pdata->mac_csr_addr + MAC_READ_REG_OFFSET; 33 cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET; 34 cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET; 35 36 spin_lock(&pdata->mac_lock); 37 iowrite32(rd_addr, addr); 38 iowrite32(XGENE_ENET_RD_CMD, cmd); 39 40 while (!(done = ioread32(cmd_done)) && wait--) 41 udelay(1); 42 43 if (done) 44 rd_data = ioread32(rd); 45 46 iowrite32(0, cmd); 47 spin_unlock(&pdata->mac_lock); 48 49 return rd_data; 50 } 51 EXPORT_SYMBOL(xgene_mdio_rd_mac); 52 53 void xgene_mdio_wr_mac(struct xgene_mdio_pdata *pdata, u32 wr_addr, u32 data) 54 { 55 void __iomem *addr, *wr, *cmd, *cmd_done; 56 u8 wait = 10; 57 u32 done; 58 59 addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET; 60 wr = pdata->mac_csr_addr + MAC_WRITE_REG_OFFSET; 61 cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET; 62 cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET; 63 64 spin_lock(&pdata->mac_lock); 65 iowrite32(wr_addr, addr); 66 iowrite32(data, wr); 67 iowrite32(XGENE_ENET_WR_CMD, cmd); 68 69 while (!(done = ioread32(cmd_done)) && wait--) 70 udelay(1); 71 72 if (!done) 73 pr_err("MCX mac write failed, addr: 0x%04x\n", wr_addr); 74 75 iowrite32(0, cmd); 76 spin_unlock(&pdata->mac_lock); 77 } 78 EXPORT_SYMBOL(xgene_mdio_wr_mac); 79 80 int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg) 81 { 82 struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv; 83 u32 data, done; 84 u8 wait = 10; 85 86 data = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg); 87 xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, data); 88 xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK); 89 do { 90 usleep_range(5, 10); 91 done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR); 92 } while ((done & BUSY_MASK) && wait--); 93 94 if (done & BUSY_MASK) { 95 dev_err(&bus->dev, "MII_MGMT read failed\n"); 96 return -EBUSY; 97 } 98 99 data = xgene_mdio_rd_mac(pdata, MII_MGMT_STATUS_ADDR); 100 xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, 0); 101 102 return data; 103 } 104 EXPORT_SYMBOL(xgene_mdio_rgmii_read); 105 106 int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data) 107 { 108 struct xgene_mdio_pdata *pdata = (struct xgene_mdio_pdata *)bus->priv; 109 u32 val, done; 110 u8 wait = 10; 111 112 val = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg); 113 xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, val); 114 115 xgene_mdio_wr_mac(pdata, MII_MGMT_CONTROL_ADDR, data); 116 do { 117 usleep_range(5, 10); 118 done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR); 119 } while ((done & BUSY_MASK) && wait--); 120 121 if (done & BUSY_MASK) { 122 dev_err(&bus->dev, "MII_MGMT write failed\n"); 123 return -EBUSY; 124 } 125 126 return 0; 127 } 128 EXPORT_SYMBOL(xgene_mdio_rgmii_write); 129 130 static u32 xgene_menet_rd_diag_csr(struct xgene_mdio_pdata *pdata, u32 offset) 131 { 132 return ioread32(pdata->diag_csr_addr + offset); 133 } 134 135 static void xgene_menet_wr_diag_csr(struct xgene_mdio_pdata *pdata, 136 u32 offset, u32 val) 137 { 138 iowrite32(val, pdata->diag_csr_addr + offset); 139 } 140 141 static int xgene_enet_ecc_init(struct xgene_mdio_pdata *pdata) 142 { 143 u32 data; 144 u8 wait = 10; 145 146 xgene_menet_wr_diag_csr(pdata, MENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x0); 147 do { 148 usleep_range(100, 110); 149 data = xgene_menet_rd_diag_csr(pdata, MENET_BLOCK_MEM_RDY_ADDR); 150 } while ((data != 0xffffffff) && wait--); 151 152 if (data != 0xffffffff) { 153 dev_err(pdata->dev, "Failed to release memory from shutdown\n"); 154 return -ENODEV; 155 } 156 157 return 0; 158 } 159 160 static void xgene_gmac_reset(struct xgene_mdio_pdata *pdata) 161 { 162 xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET); 163 xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, 0); 164 } 165 166 static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata) 167 { 168 int ret; 169 170 if (pdata->dev->of_node) { 171 clk_prepare_enable(pdata->clk); 172 udelay(5); 173 clk_disable_unprepare(pdata->clk); 174 udelay(5); 175 clk_prepare_enable(pdata->clk); 176 udelay(5); 177 } else { 178 #ifdef CONFIG_ACPI 179 acpi_evaluate_object(ACPI_HANDLE(pdata->dev), 180 "_RST", NULL, NULL); 181 #endif 182 } 183 184 ret = xgene_enet_ecc_init(pdata); 185 if (ret) { 186 if (pdata->dev->of_node) 187 clk_disable_unprepare(pdata->clk); 188 return ret; 189 } 190 xgene_gmac_reset(pdata); 191 192 return 0; 193 } 194 195 static void xgene_enet_rd_mdio_csr(void __iomem *base_addr, 196 u32 offset, u32 *val) 197 { 198 void __iomem *addr = base_addr + offset; 199 200 *val = ioread32(addr); 201 } 202 203 static void xgene_enet_wr_mdio_csr(void __iomem *base_addr, 204 u32 offset, u32 val) 205 { 206 void __iomem *addr = base_addr + offset; 207 208 iowrite32(val, addr); 209 } 210 211 static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id, 212 int reg, u16 data) 213 { 214 void __iomem *addr = (void __iomem *)bus->priv; 215 int timeout = 100; 216 u32 status, val; 217 218 val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) | 219 SET_VAL(HSTMIIMWRDAT, data); 220 xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val); 221 222 val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE); 223 xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val); 224 225 do { 226 usleep_range(5, 10); 227 xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status); 228 } while ((status & BUSY_MASK) && timeout--); 229 230 xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0); 231 232 return 0; 233 } 234 235 static int xgene_xfi_mdio_read(struct mii_bus *bus, int phy_id, int reg) 236 { 237 void __iomem *addr = (void __iomem *)bus->priv; 238 u32 data, status, val; 239 int timeout = 100; 240 241 val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg); 242 xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val); 243 244 val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_READ); 245 xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val); 246 247 do { 248 usleep_range(5, 10); 249 xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status); 250 } while ((status & BUSY_MASK) && timeout--); 251 252 if (status & BUSY_MASK) { 253 pr_err("XGENET_MII_MGMT write failed\n"); 254 return -EBUSY; 255 } 256 257 xgene_enet_rd_mdio_csr(addr, MIIMRD_FIELD_ADDR, &data); 258 xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0); 259 260 return data; 261 } 262 263 struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr) 264 { 265 struct phy_device *phy_dev; 266 267 phy_dev = get_phy_device(bus, phy_addr, false); 268 if (!phy_dev || IS_ERR(phy_dev)) 269 return NULL; 270 271 if (phy_device_register(phy_dev)) 272 phy_device_free(phy_dev); 273 274 return phy_dev; 275 } 276 EXPORT_SYMBOL(xgene_enet_phy_register); 277 278 #ifdef CONFIG_ACPI 279 static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl, 280 void *context, void **ret) 281 { 282 struct mii_bus *mdio = context; 283 struct acpi_device *adev; 284 struct phy_device *phy_dev; 285 const union acpi_object *obj; 286 u32 phy_addr; 287 288 if (acpi_bus_get_device(handle, &adev)) 289 return AE_OK; 290 291 if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj)) 292 return AE_OK; 293 phy_addr = obj->integer.value; 294 295 phy_dev = xgene_enet_phy_register(mdio, phy_addr); 296 adev->driver_data = phy_dev; 297 298 return AE_OK; 299 } 300 #endif 301 302 static const struct of_device_id xgene_mdio_of_match[] = { 303 { 304 .compatible = "apm,xgene-mdio-rgmii", 305 .data = (void *)XGENE_MDIO_RGMII 306 }, 307 { 308 .compatible = "apm,xgene-mdio-xfi", 309 .data = (void *)XGENE_MDIO_XFI 310 }, 311 {}, 312 }; 313 MODULE_DEVICE_TABLE(of, xgene_mdio_of_match); 314 315 #ifdef CONFIG_ACPI 316 static const struct acpi_device_id xgene_mdio_acpi_match[] = { 317 { "APMC0D65", XGENE_MDIO_RGMII }, 318 { "APMC0D66", XGENE_MDIO_XFI }, 319 { } 320 }; 321 322 MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match); 323 #endif 324 325 326 static int xgene_mdio_probe(struct platform_device *pdev) 327 { 328 struct device *dev = &pdev->dev; 329 struct mii_bus *mdio_bus; 330 const struct of_device_id *of_id; 331 struct xgene_mdio_pdata *pdata; 332 void __iomem *csr_base; 333 int mdio_id = 0, ret = 0; 334 335 of_id = of_match_device(xgene_mdio_of_match, &pdev->dev); 336 if (of_id) { 337 mdio_id = (enum xgene_mdio_id)of_id->data; 338 } else { 339 #ifdef CONFIG_ACPI 340 const struct acpi_device_id *acpi_id; 341 342 acpi_id = acpi_match_device(xgene_mdio_acpi_match, &pdev->dev); 343 if (acpi_id) 344 mdio_id = (enum xgene_mdio_id)acpi_id->driver_data; 345 #endif 346 } 347 348 if (!mdio_id) 349 return -ENODEV; 350 351 pdata = devm_kzalloc(dev, sizeof(struct xgene_mdio_pdata), GFP_KERNEL); 352 if (!pdata) 353 return -ENOMEM; 354 pdata->mdio_id = mdio_id; 355 pdata->dev = dev; 356 357 csr_base = devm_platform_ioremap_resource(pdev, 0); 358 if (IS_ERR(csr_base)) 359 return PTR_ERR(csr_base); 360 pdata->mac_csr_addr = csr_base; 361 pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET; 362 pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET; 363 364 if (mdio_id == XGENE_MDIO_RGMII) 365 spin_lock_init(&pdata->mac_lock); 366 367 if (dev->of_node) { 368 pdata->clk = devm_clk_get(dev, NULL); 369 if (IS_ERR(pdata->clk)) { 370 dev_err(dev, "Unable to retrieve clk\n"); 371 return PTR_ERR(pdata->clk); 372 } 373 } 374 375 ret = xgene_mdio_reset(pdata); 376 if (ret) 377 return ret; 378 379 mdio_bus = mdiobus_alloc(); 380 if (!mdio_bus) { 381 ret = -ENOMEM; 382 goto out_clk; 383 } 384 385 mdio_bus->name = "APM X-Gene MDIO bus"; 386 387 if (mdio_id == XGENE_MDIO_RGMII) { 388 mdio_bus->read = xgene_mdio_rgmii_read; 389 mdio_bus->write = xgene_mdio_rgmii_write; 390 mdio_bus->priv = (void __force *)pdata; 391 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s", 392 "xgene-mii-rgmii"); 393 } else { 394 mdio_bus->read = xgene_xfi_mdio_read; 395 mdio_bus->write = xgene_xfi_mdio_write; 396 mdio_bus->priv = (void __force *)pdata->mdio_csr_addr; 397 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s", 398 "xgene-mii-xfi"); 399 } 400 401 mdio_bus->parent = dev; 402 platform_set_drvdata(pdev, pdata); 403 404 if (dev->of_node) { 405 ret = of_mdiobus_register(mdio_bus, dev->of_node); 406 } else { 407 #ifdef CONFIG_ACPI 408 /* Mask out all PHYs from auto probing. */ 409 mdio_bus->phy_mask = ~0; 410 ret = mdiobus_register(mdio_bus); 411 if (ret) 412 goto out_mdiobus; 413 414 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1, 415 acpi_register_phy, NULL, mdio_bus, NULL); 416 #endif 417 } 418 419 if (ret) 420 goto out_mdiobus; 421 422 pdata->mdio_bus = mdio_bus; 423 xgene_mdio_status = true; 424 425 return 0; 426 427 out_mdiobus: 428 mdiobus_free(mdio_bus); 429 430 out_clk: 431 if (dev->of_node) 432 clk_disable_unprepare(pdata->clk); 433 434 return ret; 435 } 436 437 static int xgene_mdio_remove(struct platform_device *pdev) 438 { 439 struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev); 440 struct mii_bus *mdio_bus = pdata->mdio_bus; 441 struct device *dev = &pdev->dev; 442 443 mdiobus_unregister(mdio_bus); 444 mdiobus_free(mdio_bus); 445 446 if (dev->of_node) 447 clk_disable_unprepare(pdata->clk); 448 449 return 0; 450 } 451 452 static struct platform_driver xgene_mdio_driver = { 453 .driver = { 454 .name = "xgene-mdio", 455 .of_match_table = of_match_ptr(xgene_mdio_of_match), 456 .acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match), 457 }, 458 .probe = xgene_mdio_probe, 459 .remove = xgene_mdio_remove, 460 }; 461 462 module_platform_driver(xgene_mdio_driver); 463 464 MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver"); 465 MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>"); 466 MODULE_LICENSE("GPL"); 467