linux/drivers/net/phy/mdio-xgene.c
<<
>>
Prefs
   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/module.h>
  15#include <linux/of_platform.h>
  16#include <linux/of_net.h>
  17#include <linux/of_mdio.h>
  18#include <linux/prefetch.h>
  19#include <linux/phy.h>
  20#include <net/ip.h>
  21#include "mdio-xgene.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        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
 302static 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};
 313MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
 314
 315#ifdef CONFIG_ACPI
 316static const struct acpi_device_id xgene_mdio_acpi_match[] = {
 317        { "APMC0D65", XGENE_MDIO_RGMII },
 318        { "APMC0D66", XGENE_MDIO_XFI },
 319        { }
 320};
 321
 322MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
 323#endif
 324
 325
 326static 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 resource *res;
 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        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 359        csr_base = devm_ioremap_resource(dev, res);
 360        if (IS_ERR(csr_base))
 361                return PTR_ERR(csr_base);
 362        pdata->mac_csr_addr = csr_base;
 363        pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET;
 364        pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET;
 365
 366        if (mdio_id == XGENE_MDIO_RGMII)
 367                spin_lock_init(&pdata->mac_lock);
 368
 369        if (dev->of_node) {
 370                pdata->clk = devm_clk_get(dev, NULL);
 371                if (IS_ERR(pdata->clk)) {
 372                        dev_err(dev, "Unable to retrieve clk\n");
 373                        return PTR_ERR(pdata->clk);
 374                }
 375        }
 376
 377        ret = xgene_mdio_reset(pdata);
 378        if (ret)
 379                return ret;
 380
 381        mdio_bus = mdiobus_alloc();
 382        if (!mdio_bus) {
 383                ret = -ENOMEM;
 384                goto out_clk;
 385        }
 386
 387        mdio_bus->name = "APM X-Gene MDIO bus";
 388
 389        if (mdio_id == XGENE_MDIO_RGMII) {
 390                mdio_bus->read = xgene_mdio_rgmii_read;
 391                mdio_bus->write = xgene_mdio_rgmii_write;
 392                mdio_bus->priv = (void __force *)pdata;
 393                snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
 394                         "xgene-mii-rgmii");
 395        } else {
 396                mdio_bus->read = xgene_xfi_mdio_read;
 397                mdio_bus->write = xgene_xfi_mdio_write;
 398                mdio_bus->priv = (void __force *)pdata->mdio_csr_addr;
 399                snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
 400                         "xgene-mii-xfi");
 401        }
 402
 403        mdio_bus->parent = dev;
 404        platform_set_drvdata(pdev, pdata);
 405
 406        if (dev->of_node) {
 407                ret = of_mdiobus_register(mdio_bus, dev->of_node);
 408        } else {
 409#ifdef CONFIG_ACPI
 410                /* Mask out all PHYs from auto probing. */
 411                mdio_bus->phy_mask = ~0;
 412                ret = mdiobus_register(mdio_bus);
 413                if (ret)
 414                        goto out_mdiobus;
 415
 416                acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
 417                                    acpi_register_phy, NULL, mdio_bus, NULL);
 418#endif
 419        }
 420
 421        if (ret)
 422                goto out_mdiobus;
 423
 424        pdata->mdio_bus = mdio_bus;
 425        xgene_mdio_status = true;
 426
 427        return 0;
 428
 429out_mdiobus:
 430        mdiobus_free(mdio_bus);
 431
 432out_clk:
 433        if (dev->of_node)
 434                clk_disable_unprepare(pdata->clk);
 435
 436        return ret;
 437}
 438
 439static int xgene_mdio_remove(struct platform_device *pdev)
 440{
 441        struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev);
 442        struct mii_bus *mdio_bus = pdata->mdio_bus;
 443        struct device *dev = &pdev->dev;
 444
 445        mdiobus_unregister(mdio_bus);
 446        mdiobus_free(mdio_bus);
 447
 448        if (dev->of_node)
 449                clk_disable_unprepare(pdata->clk);
 450
 451        return 0;
 452}
 453
 454static struct platform_driver xgene_mdio_driver = {
 455        .driver = {
 456                .name = "xgene-mdio",
 457                .of_match_table = of_match_ptr(xgene_mdio_of_match),
 458                .acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match),
 459        },
 460        .probe = xgene_mdio_probe,
 461        .remove = xgene_mdio_remove,
 462};
 463
 464module_platform_driver(xgene_mdio_driver);
 465
 466MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver");
 467MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>");
 468MODULE_LICENSE("GPL");
 469