0001
0002
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");