1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* Copyright (c) 2015, The Linux Foundation. All rights reserved. */ 3 /* Copyright (c) 2020 Sartura Ltd. */ 4 5 #include <linux/delay.h> 6 #include <linux/io.h> 7 #include <linux/iopoll.h> 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/of_address.h> 11 #include <linux/of_mdio.h> 12 #include <linux/phy.h> 13 #include <linux/platform_device.h> 14 #include <linux/clk.h> 15 16 #define MDIO_MODE_REG 0x40 17 #define MDIO_ADDR_REG 0x44 18 #define MDIO_DATA_WRITE_REG 0x48 19 #define MDIO_DATA_READ_REG 0x4c 20 #define MDIO_CMD_REG 0x50 21 #define MDIO_CMD_ACCESS_BUSY BIT(16) 22 #define MDIO_CMD_ACCESS_START BIT(8) 23 #define MDIO_CMD_ACCESS_CODE_READ 0 24 #define MDIO_CMD_ACCESS_CODE_WRITE 1 25 #define MDIO_CMD_ACCESS_CODE_C45_ADDR 0 26 #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1 27 #define MDIO_CMD_ACCESS_CODE_C45_READ 2 28 29 /* 0 = Clause 22, 1 = Clause 45 */ 30 #define MDIO_MODE_C45 BIT(8) 31 32 #define IPQ4019_MDIO_TIMEOUT 10000 33 #define IPQ4019_MDIO_SLEEP 10 34 35 /* MDIO clock source frequency is fixed to 100M */ 36 #define IPQ_MDIO_CLK_RATE 100000000 37 38 #define IPQ_PHY_SET_DELAY_US 100000 39 40 struct ipq4019_mdio_data { 41 void __iomem *membase; 42 void __iomem *eth_ldo_rdy; 43 struct clk *mdio_clk; 44 }; 45 46 static int ipq4019_mdio_wait_busy(struct mii_bus *bus) 47 { 48 struct ipq4019_mdio_data *priv = bus->priv; 49 unsigned int busy; 50 51 return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy, 52 (busy & MDIO_CMD_ACCESS_BUSY) == 0, 53 IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT); 54 } 55 56 static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 57 { 58 struct ipq4019_mdio_data *priv = bus->priv; 59 unsigned int data; 60 unsigned int cmd; 61 62 if (ipq4019_mdio_wait_busy(bus)) 63 return -ETIMEDOUT; 64 65 /* Clause 45 support */ 66 if (regnum & MII_ADDR_C45) { 67 unsigned int mmd = (regnum >> 16) & 0x1F; 68 unsigned int reg = regnum & 0xFFFF; 69 70 /* Enter Clause 45 mode */ 71 data = readl(priv->membase + MDIO_MODE_REG); 72 73 data |= MDIO_MODE_C45; 74 75 writel(data, priv->membase + MDIO_MODE_REG); 76 77 /* issue the phy address and mmd */ 78 writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG); 79 80 /* issue reg */ 81 writel(reg, priv->membase + MDIO_DATA_WRITE_REG); 82 83 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR; 84 } else { 85 /* Enter Clause 22 mode */ 86 data = readl(priv->membase + MDIO_MODE_REG); 87 88 data &= ~MDIO_MODE_C45; 89 90 writel(data, priv->membase + MDIO_MODE_REG); 91 92 /* issue the phy address and reg */ 93 writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG); 94 95 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ; 96 } 97 98 /* issue read command */ 99 writel(cmd, priv->membase + MDIO_CMD_REG); 100 101 /* Wait read complete */ 102 if (ipq4019_mdio_wait_busy(bus)) 103 return -ETIMEDOUT; 104 105 if (regnum & MII_ADDR_C45) { 106 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_READ; 107 108 writel(cmd, priv->membase + MDIO_CMD_REG); 109 110 if (ipq4019_mdio_wait_busy(bus)) 111 return -ETIMEDOUT; 112 } 113 114 /* Read and return data */ 115 return readl(priv->membase + MDIO_DATA_READ_REG); 116 } 117 118 static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 119 u16 value) 120 { 121 struct ipq4019_mdio_data *priv = bus->priv; 122 unsigned int data; 123 unsigned int cmd; 124 125 if (ipq4019_mdio_wait_busy(bus)) 126 return -ETIMEDOUT; 127 128 /* Clause 45 support */ 129 if (regnum & MII_ADDR_C45) { 130 unsigned int mmd = (regnum >> 16) & 0x1F; 131 unsigned int reg = regnum & 0xFFFF; 132 133 /* Enter Clause 45 mode */ 134 data = readl(priv->membase + MDIO_MODE_REG); 135 136 data |= MDIO_MODE_C45; 137 138 writel(data, priv->membase + MDIO_MODE_REG); 139 140 /* issue the phy address and mmd */ 141 writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG); 142 143 /* issue reg */ 144 writel(reg, priv->membase + MDIO_DATA_WRITE_REG); 145 146 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR; 147 148 writel(cmd, priv->membase + MDIO_CMD_REG); 149 150 if (ipq4019_mdio_wait_busy(bus)) 151 return -ETIMEDOUT; 152 } else { 153 /* Enter Clause 22 mode */ 154 data = readl(priv->membase + MDIO_MODE_REG); 155 156 data &= ~MDIO_MODE_C45; 157 158 writel(data, priv->membase + MDIO_MODE_REG); 159 160 /* issue the phy address and reg */ 161 writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG); 162 } 163 164 /* issue write data */ 165 writel(value, priv->membase + MDIO_DATA_WRITE_REG); 166 167 /* issue write command */ 168 if (regnum & MII_ADDR_C45) 169 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_WRITE; 170 else 171 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE; 172 173 writel(cmd, priv->membase + MDIO_CMD_REG); 174 175 /* Wait write complete */ 176 if (ipq4019_mdio_wait_busy(bus)) 177 return -ETIMEDOUT; 178 179 return 0; 180 } 181 182 static int ipq_mdio_reset(struct mii_bus *bus) 183 { 184 struct ipq4019_mdio_data *priv = bus->priv; 185 u32 val; 186 int ret; 187 188 /* To indicate CMN_PLL that ethernet_ldo has been ready if platform resource 1 189 * is specified in the device tree. 190 */ 191 if (priv->eth_ldo_rdy) { 192 val = readl(priv->eth_ldo_rdy); 193 val |= BIT(0); 194 writel(val, priv->eth_ldo_rdy); 195 fsleep(IPQ_PHY_SET_DELAY_US); 196 } 197 198 /* Configure MDIO clock source frequency if clock is specified in the device tree */ 199 ret = clk_set_rate(priv->mdio_clk, IPQ_MDIO_CLK_RATE); 200 if (ret) 201 return ret; 202 203 return clk_prepare_enable(priv->mdio_clk); 204 } 205 206 static int ipq4019_mdio_probe(struct platform_device *pdev) 207 { 208 struct ipq4019_mdio_data *priv; 209 struct mii_bus *bus; 210 int ret; 211 212 bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv)); 213 if (!bus) 214 return -ENOMEM; 215 216 priv = bus->priv; 217 218 priv->membase = devm_platform_ioremap_resource(pdev, 0); 219 if (IS_ERR(priv->membase)) 220 return PTR_ERR(priv->membase); 221 222 priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk"); 223 if (IS_ERR(priv->mdio_clk)) 224 return PTR_ERR(priv->mdio_clk); 225 226 /* The platform resource is provided on the chipset IPQ5018 */ 227 priv->eth_ldo_rdy = devm_platform_ioremap_resource(pdev, 1); 228 229 bus->name = "ipq4019_mdio"; 230 bus->read = ipq4019_mdio_read; 231 bus->write = ipq4019_mdio_write; 232 bus->reset = ipq_mdio_reset; 233 bus->parent = &pdev->dev; 234 snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id); 235 236 ret = of_mdiobus_register(bus, pdev->dev.of_node); 237 if (ret) { 238 dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); 239 return ret; 240 } 241 242 platform_set_drvdata(pdev, bus); 243 244 return 0; 245 } 246 247 static int ipq4019_mdio_remove(struct platform_device *pdev) 248 { 249 struct mii_bus *bus = platform_get_drvdata(pdev); 250 251 mdiobus_unregister(bus); 252 253 return 0; 254 } 255 256 static const struct of_device_id ipq4019_mdio_dt_ids[] = { 257 { .compatible = "qcom,ipq4019-mdio" }, 258 { .compatible = "qcom,ipq5018-mdio" }, 259 { } 260 }; 261 MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids); 262 263 static struct platform_driver ipq4019_mdio_driver = { 264 .probe = ipq4019_mdio_probe, 265 .remove = ipq4019_mdio_remove, 266 .driver = { 267 .name = "ipq4019-mdio", 268 .of_match_table = ipq4019_mdio_dt_ids, 269 }, 270 }; 271 272 module_platform_driver(ipq4019_mdio_driver); 273 274 MODULE_DESCRIPTION("ipq4019 MDIO interface driver"); 275 MODULE_AUTHOR("Qualcomm Atheros"); 276 MODULE_LICENSE("Dual BSD/GPL"); 277