mdio-xgene.c (11230B)
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_mdio.h> 17#include <linux/of_net.h> 18#include <linux/of_platform.h> 19#include <linux/phy.h> 20#include <linux/prefetch.h> 21#include <net/ip.h> 22 23static bool xgene_mdio_status; 24 25u32 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} 51EXPORT_SYMBOL(xgene_mdio_rd_mac); 52 53void 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} 78EXPORT_SYMBOL(xgene_mdio_wr_mac); 79 80int 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} 104EXPORT_SYMBOL(xgene_mdio_rgmii_read); 105 106int 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} 128EXPORT_SYMBOL(xgene_mdio_rgmii_write); 129 130static u32 xgene_menet_rd_diag_csr(struct xgene_mdio_pdata *pdata, u32 offset) 131{ 132 return ioread32(pdata->diag_csr_addr + offset); 133} 134 135static 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 141static 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 160static 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 166static 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 195static 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 203static 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 211static 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 235static 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 263struct 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} 276EXPORT_SYMBOL(xgene_enet_phy_register); 277 278#ifdef CONFIG_ACPI 279static 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 adev = acpi_fetch_acpi_dev(handle); 289 if (!adev) 290 return AE_OK; 291 292 if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj)) 293 return AE_OK; 294 phy_addr = obj->integer.value; 295 296 phy_dev = xgene_enet_phy_register(mdio, phy_addr); 297 adev->driver_data = phy_dev; 298 299 return AE_OK; 300} 301#endif 302 303static const struct of_device_id xgene_mdio_of_match[] = { 304 { 305 .compatible = "apm,xgene-mdio-rgmii", 306 .data = (void *)XGENE_MDIO_RGMII 307 }, 308 { 309 .compatible = "apm,xgene-mdio-xfi", 310 .data = (void *)XGENE_MDIO_XFI 311 }, 312 {}, 313}; 314MODULE_DEVICE_TABLE(of, xgene_mdio_of_match); 315 316#ifdef CONFIG_ACPI 317static const struct acpi_device_id xgene_mdio_acpi_match[] = { 318 { "APMC0D65", XGENE_MDIO_RGMII }, 319 { "APMC0D66", XGENE_MDIO_XFI }, 320 { } 321}; 322 323MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match); 324#endif 325 326 327static int xgene_mdio_probe(struct platform_device *pdev) 328{ 329 struct device *dev = &pdev->dev; 330 struct mii_bus *mdio_bus; 331 const struct of_device_id *of_id; 332 struct xgene_mdio_pdata *pdata; 333 void __iomem *csr_base; 334 int mdio_id = 0, ret = 0; 335 336 of_id = of_match_device(xgene_mdio_of_match, &pdev->dev); 337 if (of_id) { 338 mdio_id = (enum xgene_mdio_id)of_id->data; 339 } else { 340#ifdef CONFIG_ACPI 341 const struct acpi_device_id *acpi_id; 342 343 acpi_id = acpi_match_device(xgene_mdio_acpi_match, &pdev->dev); 344 if (acpi_id) 345 mdio_id = (enum xgene_mdio_id)acpi_id->driver_data; 346#endif 347 } 348 349 if (!mdio_id) 350 return -ENODEV; 351 352 pdata = devm_kzalloc(dev, sizeof(struct xgene_mdio_pdata), GFP_KERNEL); 353 if (!pdata) 354 return -ENOMEM; 355 pdata->mdio_id = mdio_id; 356 pdata->dev = dev; 357 358 csr_base = devm_platform_ioremap_resource(pdev, 0); 359 if (IS_ERR(csr_base)) 360 return PTR_ERR(csr_base); 361 pdata->mac_csr_addr = csr_base; 362 pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET; 363 pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET; 364 365 if (mdio_id == XGENE_MDIO_RGMII) 366 spin_lock_init(&pdata->mac_lock); 367 368 if (dev->of_node) { 369 pdata->clk = devm_clk_get(dev, NULL); 370 if (IS_ERR(pdata->clk)) { 371 dev_err(dev, "Unable to retrieve clk\n"); 372 return PTR_ERR(pdata->clk); 373 } 374 } 375 376 ret = xgene_mdio_reset(pdata); 377 if (ret) 378 return ret; 379 380 mdio_bus = mdiobus_alloc(); 381 if (!mdio_bus) { 382 ret = -ENOMEM; 383 goto out_clk; 384 } 385 386 mdio_bus->name = "APM X-Gene MDIO bus"; 387 388 if (mdio_id == XGENE_MDIO_RGMII) { 389 mdio_bus->read = xgene_mdio_rgmii_read; 390 mdio_bus->write = xgene_mdio_rgmii_write; 391 mdio_bus->priv = (void __force *)pdata; 392 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s", 393 "xgene-mii-rgmii"); 394 } else { 395 mdio_bus->read = xgene_xfi_mdio_read; 396 mdio_bus->write = xgene_xfi_mdio_write; 397 mdio_bus->priv = (void __force *)pdata->mdio_csr_addr; 398 snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s", 399 "xgene-mii-xfi"); 400 } 401 402 mdio_bus->parent = dev; 403 platform_set_drvdata(pdev, pdata); 404 405 if (dev->of_node) { 406 ret = of_mdiobus_register(mdio_bus, dev->of_node); 407 } else { 408#ifdef CONFIG_ACPI 409 /* Mask out all PHYs from auto probing. */ 410 mdio_bus->phy_mask = ~0; 411 ret = mdiobus_register(mdio_bus); 412 if (ret) 413 goto out_mdiobus; 414 415 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1, 416 acpi_register_phy, NULL, mdio_bus, NULL); 417#endif 418 } 419 420 if (ret) 421 goto out_mdiobus; 422 423 pdata->mdio_bus = mdio_bus; 424 xgene_mdio_status = true; 425 426 return 0; 427 428out_mdiobus: 429 mdiobus_free(mdio_bus); 430 431out_clk: 432 if (dev->of_node) 433 clk_disable_unprepare(pdata->clk); 434 435 return ret; 436} 437 438static int xgene_mdio_remove(struct platform_device *pdev) 439{ 440 struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev); 441 struct mii_bus *mdio_bus = pdata->mdio_bus; 442 struct device *dev = &pdev->dev; 443 444 mdiobus_unregister(mdio_bus); 445 mdiobus_free(mdio_bus); 446 447 if (dev->of_node) 448 clk_disable_unprepare(pdata->clk); 449 450 return 0; 451} 452 453static struct platform_driver xgene_mdio_driver = { 454 .driver = { 455 .name = "xgene-mdio", 456 .of_match_table = of_match_ptr(xgene_mdio_of_match), 457 .acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match), 458 }, 459 .probe = xgene_mdio_probe, 460 .remove = xgene_mdio_remove, 461}; 462 463module_platform_driver(xgene_mdio_driver); 464 465MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver"); 466MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>"); 467MODULE_LICENSE("GPL");