Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* Copyright (C) 2019 IBM Corp. */
0003 
0004 #include <linux/bitfield.h>
0005 #include <linux/delay.h>
0006 #include <linux/reset.h>
0007 #include <linux/iopoll.h>
0008 #include <linux/mdio.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/of_mdio.h>
0012 #include <linux/phy.h>
0013 #include <linux/platform_device.h>
0014 
0015 #define DRV_NAME "mdio-aspeed"
0016 
0017 #define ASPEED_MDIO_CTRL        0x0
0018 #define   ASPEED_MDIO_CTRL_FIRE     BIT(31)
0019 #define   ASPEED_MDIO_CTRL_ST       BIT(28)
0020 #define     ASPEED_MDIO_CTRL_ST_C45 0
0021 #define     ASPEED_MDIO_CTRL_ST_C22 1
0022 #define   ASPEED_MDIO_CTRL_OP       GENMASK(27, 26)
0023 #define     MDIO_C22_OP_WRITE       0b01
0024 #define     MDIO_C22_OP_READ        0b10
0025 #define     MDIO_C45_OP_ADDR        0b00
0026 #define     MDIO_C45_OP_WRITE       0b01
0027 #define     MDIO_C45_OP_PREAD       0b10
0028 #define     MDIO_C45_OP_READ        0b11
0029 #define   ASPEED_MDIO_CTRL_PHYAD    GENMASK(25, 21)
0030 #define   ASPEED_MDIO_CTRL_REGAD    GENMASK(20, 16)
0031 #define   ASPEED_MDIO_CTRL_MIIWDATA GENMASK(15, 0)
0032 
0033 #define ASPEED_MDIO_DATA        0x4
0034 #define   ASPEED_MDIO_DATA_MDC_THRES    GENMASK(31, 24)
0035 #define   ASPEED_MDIO_DATA_MDIO_EDGE    BIT(23)
0036 #define   ASPEED_MDIO_DATA_MDIO_LATCH   GENMASK(22, 20)
0037 #define   ASPEED_MDIO_DATA_IDLE     BIT(16)
0038 #define   ASPEED_MDIO_DATA_MIIRDATA GENMASK(15, 0)
0039 
0040 #define ASPEED_MDIO_INTERVAL_US     100
0041 #define ASPEED_MDIO_TIMEOUT_US      (ASPEED_MDIO_INTERVAL_US * 10)
0042 
0043 struct aspeed_mdio {
0044     void __iomem *base;
0045     struct reset_control *reset;
0046 };
0047 
0048 static int aspeed_mdio_op(struct mii_bus *bus, u8 st, u8 op, u8 phyad, u8 regad,
0049               u16 data)
0050 {
0051     struct aspeed_mdio *ctx = bus->priv;
0052     u32 ctrl;
0053 
0054     dev_dbg(&bus->dev, "%s: st: %u op: %u, phyad: %u, regad: %u, data: %u\n",
0055         __func__, st, op, phyad, regad, data);
0056 
0057     ctrl = ASPEED_MDIO_CTRL_FIRE
0058         | FIELD_PREP(ASPEED_MDIO_CTRL_ST, st)
0059         | FIELD_PREP(ASPEED_MDIO_CTRL_OP, op)
0060         | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, phyad)
0061         | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regad)
0062         | FIELD_PREP(ASPEED_MDIO_DATA_MIIRDATA, data);
0063 
0064     iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL);
0065 
0066     return readl_poll_timeout(ctx->base + ASPEED_MDIO_CTRL, ctrl,
0067                 !(ctrl & ASPEED_MDIO_CTRL_FIRE),
0068                 ASPEED_MDIO_INTERVAL_US,
0069                 ASPEED_MDIO_TIMEOUT_US);
0070 }
0071 
0072 static int aspeed_mdio_get_data(struct mii_bus *bus)
0073 {
0074     struct aspeed_mdio *ctx = bus->priv;
0075     u32 data;
0076     int rc;
0077 
0078     rc = readl_poll_timeout(ctx->base + ASPEED_MDIO_DATA, data,
0079                 data & ASPEED_MDIO_DATA_IDLE,
0080                 ASPEED_MDIO_INTERVAL_US,
0081                 ASPEED_MDIO_TIMEOUT_US);
0082     if (rc < 0)
0083         return rc;
0084 
0085     return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data);
0086 }
0087 
0088 static int aspeed_mdio_read_c22(struct mii_bus *bus, int addr, int regnum)
0089 {
0090     int rc;
0091 
0092     rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C22, MDIO_C22_OP_READ,
0093                 addr, regnum, 0);
0094     if (rc < 0)
0095         return rc;
0096 
0097     return aspeed_mdio_get_data(bus);
0098 }
0099 
0100 static int aspeed_mdio_write_c22(struct mii_bus *bus, int addr, int regnum,
0101                  u16 val)
0102 {
0103     return aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C22, MDIO_C22_OP_WRITE,
0104                   addr, regnum, val);
0105 }
0106 
0107 static int aspeed_mdio_read_c45(struct mii_bus *bus, int addr, int regnum)
0108 {
0109     u8 c45_dev = (regnum >> 16) & 0x1F;
0110     u16 c45_addr = regnum & 0xFFFF;
0111     int rc;
0112 
0113     rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_ADDR,
0114                 addr, c45_dev, c45_addr);
0115     if (rc < 0)
0116         return rc;
0117 
0118     rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_READ,
0119                 addr, c45_dev, 0);
0120     if (rc < 0)
0121         return rc;
0122 
0123     return aspeed_mdio_get_data(bus);
0124 }
0125 
0126 static int aspeed_mdio_write_c45(struct mii_bus *bus, int addr, int regnum,
0127                  u16 val)
0128 {
0129     u8 c45_dev = (regnum >> 16) & 0x1F;
0130     u16 c45_addr = regnum & 0xFFFF;
0131     int rc;
0132 
0133     rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_ADDR,
0134                 addr, c45_dev, c45_addr);
0135     if (rc < 0)
0136         return rc;
0137 
0138     return aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_WRITE,
0139                   addr, c45_dev, val);
0140 }
0141 
0142 static int aspeed_mdio_read(struct mii_bus *bus, int addr, int regnum)
0143 {
0144     dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d\n", __func__, addr,
0145         regnum);
0146 
0147     if (regnum & MII_ADDR_C45)
0148         return aspeed_mdio_read_c45(bus, addr, regnum);
0149 
0150     return aspeed_mdio_read_c22(bus, addr, regnum);
0151 }
0152 
0153 static int aspeed_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
0154 {
0155     dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d, val: 0x%x\n",
0156         __func__, addr, regnum, val);
0157 
0158     if (regnum & MII_ADDR_C45)
0159         return aspeed_mdio_write_c45(bus, addr, regnum, val);
0160 
0161     return aspeed_mdio_write_c22(bus, addr, regnum, val);
0162 }
0163 
0164 static int aspeed_mdio_probe(struct platform_device *pdev)
0165 {
0166     struct aspeed_mdio *ctx;
0167     struct mii_bus *bus;
0168     int rc;
0169 
0170     bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctx));
0171     if (!bus)
0172         return -ENOMEM;
0173 
0174     ctx = bus->priv;
0175     ctx->base = devm_platform_ioremap_resource(pdev, 0);
0176     if (IS_ERR(ctx->base))
0177         return PTR_ERR(ctx->base);
0178 
0179     ctx->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
0180     if (IS_ERR(ctx->reset))
0181         return PTR_ERR(ctx->reset);
0182 
0183     reset_control_deassert(ctx->reset);
0184 
0185     bus->name = DRV_NAME;
0186     snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
0187     bus->parent = &pdev->dev;
0188     bus->read = aspeed_mdio_read;
0189     bus->write = aspeed_mdio_write;
0190     bus->probe_capabilities = MDIOBUS_C22_C45;
0191 
0192     rc = of_mdiobus_register(bus, pdev->dev.of_node);
0193     if (rc) {
0194         dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
0195         reset_control_assert(ctx->reset);
0196         return rc;
0197     }
0198 
0199     platform_set_drvdata(pdev, bus);
0200 
0201     return 0;
0202 }
0203 
0204 static int aspeed_mdio_remove(struct platform_device *pdev)
0205 {
0206     struct mii_bus *bus = (struct mii_bus *)platform_get_drvdata(pdev);
0207     struct aspeed_mdio *ctx = bus->priv;
0208 
0209     reset_control_assert(ctx->reset);
0210     mdiobus_unregister(bus);
0211 
0212     return 0;
0213 }
0214 
0215 static const struct of_device_id aspeed_mdio_of_match[] = {
0216     { .compatible = "aspeed,ast2600-mdio", },
0217     { },
0218 };
0219 MODULE_DEVICE_TABLE(of, aspeed_mdio_of_match);
0220 
0221 static struct platform_driver aspeed_mdio_driver = {
0222     .driver = {
0223         .name = DRV_NAME,
0224         .of_match_table = aspeed_mdio_of_match,
0225     },
0226     .probe = aspeed_mdio_probe,
0227     .remove = aspeed_mdio_remove,
0228 };
0229 
0230 module_platform_driver(aspeed_mdio_driver);
0231 
0232 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
0233 MODULE_LICENSE("GPL");