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 MDIO_C45_OP_ADDR 0b00 25 #define MDIO_C45_OP_WRITE 0b01 26 #define MDIO_C45_OP_PREAD 0b10 27 #define MDIO_C45_OP_READ 0b11 28 #define ASPEED_MDIO_CTRL_PHYAD GENMASK(25, 21) 29 #define ASPEED_MDIO_CTRL_REGAD GENMASK(20, 16) 30 #define ASPEED_MDIO_CTRL_MIIWDATA GENMASK(15, 0) 31 32 #define ASPEED_MDIO_DATA 0x4 33 #define ASPEED_MDIO_DATA_MDC_THRES GENMASK(31, 24) 34 #define ASPEED_MDIO_DATA_MDIO_EDGE BIT(23) 35 #define ASPEED_MDIO_DATA_MDIO_LATCH GENMASK(22, 20) 36 #define ASPEED_MDIO_DATA_IDLE BIT(16) 37 #define ASPEED_MDIO_DATA_MIIRDATA GENMASK(15, 0) 38 39 #define ASPEED_MDIO_INTERVAL_US 100 40 #define ASPEED_MDIO_TIMEOUT_US (ASPEED_MDIO_INTERVAL_US * 10) 41 42 struct aspeed_mdio { 43 void __iomem *base; 44 }; 45 46 static int aspeed_mdio_op(struct mii_bus *bus, u8 st, u8 op, u8 phyad, u8 regad, 47 u16 data) 48 { 49 struct aspeed_mdio *ctx = bus->priv; 50 u32 ctrl; 51 52 dev_dbg(&bus->dev, "%s: st: %u op: %u, phyad: %u, regad: %u, data: %u\n", 53 __func__, st, op, phyad, regad, data); 54 55 ctrl = ASPEED_MDIO_CTRL_FIRE 56 | FIELD_PREP(ASPEED_MDIO_CTRL_ST, st) 57 | FIELD_PREP(ASPEED_MDIO_CTRL_OP, op) 58 | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, phyad) 59 | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regad) 60 | FIELD_PREP(ASPEED_MDIO_DATA_MIIRDATA, data); 61 62 iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL); 63 64 return readl_poll_timeout(ctx->base + ASPEED_MDIO_CTRL, ctrl, 65 !(ctrl & ASPEED_MDIO_CTRL_FIRE), 66 ASPEED_MDIO_INTERVAL_US, 67 ASPEED_MDIO_TIMEOUT_US); 68 } 69 70 static int aspeed_mdio_get_data(struct mii_bus *bus) 71 { 72 struct aspeed_mdio *ctx = bus->priv; 73 u32 data; 74 int rc; 75 76 rc = readl_poll_timeout(ctx->base + ASPEED_MDIO_DATA, data, 77 data & ASPEED_MDIO_DATA_IDLE, 78 ASPEED_MDIO_INTERVAL_US, 79 ASPEED_MDIO_TIMEOUT_US); 80 if (rc < 0) 81 return rc; 82 83 return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data); 84 } 85 86 static int aspeed_mdio_read_c22(struct mii_bus *bus, int addr, int regnum) 87 { 88 int rc; 89 90 rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C22, MDIO_C22_OP_READ, 91 addr, regnum, 0); 92 if (rc < 0) 93 return rc; 94 95 return aspeed_mdio_get_data(bus); 96 } 97 98 static int aspeed_mdio_write_c22(struct mii_bus *bus, int addr, int regnum, 99 u16 val) 100 { 101 return aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C22, MDIO_C22_OP_WRITE, 102 addr, regnum, val); 103 } 104 105 static int aspeed_mdio_read_c45(struct mii_bus *bus, int addr, int regnum) 106 { 107 u8 c45_dev = (regnum >> 16) & 0x1F; 108 u16 c45_addr = regnum & 0xFFFF; 109 int rc; 110 111 rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_ADDR, 112 addr, c45_dev, c45_addr); 113 if (rc < 0) 114 return rc; 115 116 rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_READ, 117 addr, c45_dev, 0); 118 if (rc < 0) 119 return rc; 120 121 return aspeed_mdio_get_data(bus); 122 } 123 124 static int aspeed_mdio_write_c45(struct mii_bus *bus, int addr, int regnum, 125 u16 val) 126 { 127 u8 c45_dev = (regnum >> 16) & 0x1F; 128 u16 c45_addr = regnum & 0xFFFF; 129 int rc; 130 131 rc = aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_ADDR, 132 addr, c45_dev, c45_addr); 133 if (rc < 0) 134 return rc; 135 136 return aspeed_mdio_op(bus, ASPEED_MDIO_CTRL_ST_C45, MDIO_C45_OP_WRITE, 137 addr, c45_dev, val); 138 } 139 140 static int aspeed_mdio_read(struct mii_bus *bus, int addr, int regnum) 141 { 142 dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d\n", __func__, addr, 143 regnum); 144 145 if (regnum & MII_ADDR_C45) 146 return aspeed_mdio_read_c45(bus, addr, regnum); 147 148 return aspeed_mdio_read_c22(bus, addr, regnum); 149 } 150 151 static int aspeed_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) 152 { 153 dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d, val: 0x%x\n", 154 __func__, addr, regnum, val); 155 156 if (regnum & MII_ADDR_C45) 157 return aspeed_mdio_write_c45(bus, addr, regnum, val); 158 159 return aspeed_mdio_write_c22(bus, addr, regnum, val); 160 } 161 162 static int aspeed_mdio_probe(struct platform_device *pdev) 163 { 164 struct aspeed_mdio *ctx; 165 struct mii_bus *bus; 166 int rc; 167 168 bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*ctx)); 169 if (!bus) 170 return -ENOMEM; 171 172 ctx = bus->priv; 173 ctx->base = devm_platform_ioremap_resource(pdev, 0); 174 if (IS_ERR(ctx->base)) 175 return PTR_ERR(ctx->base); 176 177 bus->name = DRV_NAME; 178 snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id); 179 bus->parent = &pdev->dev; 180 bus->read = aspeed_mdio_read; 181 bus->write = aspeed_mdio_write; 182 bus->probe_capabilities = MDIOBUS_C22_C45; 183 184 rc = of_mdiobus_register(bus, pdev->dev.of_node); 185 if (rc) { 186 dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); 187 return rc; 188 } 189 190 platform_set_drvdata(pdev, bus); 191 192 return 0; 193 } 194 195 static int aspeed_mdio_remove(struct platform_device *pdev) 196 { 197 mdiobus_unregister(platform_get_drvdata(pdev)); 198 199 return 0; 200 } 201 202 static const struct of_device_id aspeed_mdio_of_match[] = { 203 { .compatible = "aspeed,ast2600-mdio", }, 204 { }, 205 }; 206 MODULE_DEVICE_TABLE(of, aspeed_mdio_of_match); 207 208 static struct platform_driver aspeed_mdio_driver = { 209 .driver = { 210 .name = DRV_NAME, 211 .of_match_table = aspeed_mdio_of_match, 212 }, 213 .probe = aspeed_mdio_probe, 214 .remove = aspeed_mdio_remove, 215 }; 216 217 module_platform_driver(aspeed_mdio_driver); 218 219 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); 220 MODULE_LICENSE("GPL"); 221