linux/drivers/net/mdio/mdio-aspeed.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* Copyright (C) 2019 IBM Corp. */
   3
   4#include <linux/bitfield.h>
   5#include <linux/delay.h>
   6#include <linux/iopoll.h>
   7#include <linux/mdio.h>
   8#include <linux/module.h>
   9#include <linux/of.h>
  10#include <linux/of_mdio.h>
  11#include <linux/phy.h>
  12#include <linux/platform_device.h>
  13
  14#define DRV_NAME "mdio-aspeed"
  15
  16#define ASPEED_MDIO_CTRL                0x0
  17#define   ASPEED_MDIO_CTRL_FIRE         BIT(31)
  18#define   ASPEED_MDIO_CTRL_ST           BIT(28)
  19#define     ASPEED_MDIO_CTRL_ST_C45     0
  20#define     ASPEED_MDIO_CTRL_ST_C22     1
  21#define   ASPEED_MDIO_CTRL_OP           GENMASK(27, 26)
  22#define     MDIO_C22_OP_WRITE           0b01
  23#define     MDIO_C22_OP_READ            0b10
  24#define   ASPEED_MDIO_CTRL_PHYAD        GENMASK(25, 21)
  25#define   ASPEED_MDIO_CTRL_REGAD        GENMASK(20, 16)
  26#define   ASPEED_MDIO_CTRL_MIIWDATA     GENMASK(15, 0)
  27
  28#define ASPEED_MDIO_DATA                0x4
  29#define   ASPEED_MDIO_DATA_MDC_THRES    GENMASK(31, 24)
  30#define   ASPEED_MDIO_DATA_MDIO_EDGE    BIT(23)
  31#define   ASPEED_MDIO_DATA_MDIO_LATCH   GENMASK(22, 20)
  32#define   ASPEED_MDIO_DATA_IDLE         BIT(16)
  33#define   ASPEED_MDIO_DATA_MIIRDATA     GENMASK(15, 0)
  34
  35#define ASPEED_MDIO_INTERVAL_US         100
  36#define ASPEED_MDIO_TIMEOUT_US          (ASPEED_MDIO_INTERVAL_US * 10)
  37
  38struct aspeed_mdio {
  39        void __iomem *base;
  40};
  41
  42static int aspeed_mdio_read(struct mii_bus *bus, int addr, int regnum)
  43{
  44        struct aspeed_mdio *ctx = bus->priv;
  45        u32 ctrl;
  46        u32 data;
  47        int rc;
  48
  49        dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d\n", __func__, addr,
  50                regnum);
  51
  52        /* Just clause 22 for the moment */
  53        if (regnum & MII_ADDR_C45)
  54                return -EOPNOTSUPP;
  55
  56        ctrl = ASPEED_MDIO_CTRL_FIRE
  57                | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22)
  58                | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_READ)
  59                | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr)
  60                | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regnum);
  61
  62        iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL);
  63
  64        rc = readl_poll_timeout(ctx->base + ASPEED_MDIO_DATA, data,
  65                                data & ASPEED_MDIO_DATA_IDLE,
  66                                ASPEED_MDIO_INTERVAL_US,
  67                                ASPEED_MDIO_TIMEOUT_US);
  68        if (rc < 0)
  69                return rc;
  70
  71        return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data);
  72}
  73
  74static int aspeed_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
  75{
  76        struct aspeed_mdio *ctx = bus->priv;
  77        u32 ctrl;
  78
  79        dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d, val: 0x%x\n",
  80                __func__, addr, regnum, val);
  81
  82        /* Just clause 22 for the moment */
  83        if (regnum & MII_ADDR_C45)
  84                return -EOPNOTSUPP;
  85
  86        ctrl = ASPEED_MDIO_CTRL_FIRE
  87                | FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22)
  88                | FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_WRITE)
  89                | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr)
  90                | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regnum)
  91                | FIELD_PREP(ASPEED_MDIO_CTRL_MIIWDATA, val);
  92
  93        iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL);
  94
  95        return readl_poll_timeout(ctx->base + ASPEED_MDIO_CTRL, ctrl,
  96                                  !(ctrl & ASPEED_MDIO_CTRL_FIRE),
  97                                  ASPEED_MDIO_INTERVAL_US,
  98                                  ASPEED_MDIO_TIMEOUT_US);
  99}
 100
 101static int aspeed_mdio_probe(struct platform_device *pdev)
 102{
 103        struct aspeed_mdio *ctx;
 104        struct mii_bus *bus;
 105        int rc;
 106
 107        bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctx));
 108        if (!bus)
 109                return -ENOMEM;
 110
 111        ctx = bus->priv;
 112        ctx->base = devm_platform_ioremap_resource(pdev, 0);
 113        if (IS_ERR(ctx->base))
 114                return PTR_ERR(ctx->base);
 115
 116        bus->name = DRV_NAME;
 117        snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
 118        bus->parent = &pdev->dev;
 119        bus->read = aspeed_mdio_read;
 120        bus->write = aspeed_mdio_write;
 121
 122        rc = of_mdiobus_register(bus, pdev->dev.of_node);
 123        if (rc) {
 124                dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
 125                return rc;
 126        }
 127
 128        platform_set_drvdata(pdev, bus);
 129
 130        return 0;
 131}
 132
 133static int aspeed_mdio_remove(struct platform_device *pdev)
 134{
 135        mdiobus_unregister(platform_get_drvdata(pdev));
 136
 137        return 0;
 138}
 139
 140static const struct of_device_id aspeed_mdio_of_match[] = {
 141        { .compatible = "aspeed,ast2600-mdio", },
 142        { },
 143};
 144
 145static struct platform_driver aspeed_mdio_driver = {
 146        .driver = {
 147                .name = DRV_NAME,
 148                .of_match_table = aspeed_mdio_of_match,
 149        },
 150        .probe = aspeed_mdio_probe,
 151        .remove = aspeed_mdio_remove,
 152};
 153
 154module_platform_driver(aspeed_mdio_driver);
 155
 156MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 157MODULE_LICENSE("GPL");
 158