mdio-ipq4019.c (7052B)
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 40struct ipq4019_mdio_data { 41 void __iomem *membase; 42 void __iomem *eth_ldo_rdy; 43 struct clk *mdio_clk; 44}; 45 46static 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 56static 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 118static 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 182static 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 ret = clk_prepare_enable(priv->mdio_clk); 204 if (ret == 0) 205 mdelay(10); 206 207 return ret; 208} 209 210static int ipq4019_mdio_probe(struct platform_device *pdev) 211{ 212 struct ipq4019_mdio_data *priv; 213 struct mii_bus *bus; 214 struct resource *res; 215 int ret; 216 217 bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv)); 218 if (!bus) 219 return -ENOMEM; 220 221 priv = bus->priv; 222 223 priv->membase = devm_platform_ioremap_resource(pdev, 0); 224 if (IS_ERR(priv->membase)) 225 return PTR_ERR(priv->membase); 226 227 priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk"); 228 if (IS_ERR(priv->mdio_clk)) 229 return PTR_ERR(priv->mdio_clk); 230 231 /* The platform resource is provided on the chipset IPQ5018 */ 232 /* This resource is optional */ 233 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 234 if (res) 235 priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res); 236 237 bus->name = "ipq4019_mdio"; 238 bus->read = ipq4019_mdio_read; 239 bus->write = ipq4019_mdio_write; 240 bus->reset = ipq_mdio_reset; 241 bus->parent = &pdev->dev; 242 snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id); 243 244 ret = of_mdiobus_register(bus, pdev->dev.of_node); 245 if (ret) { 246 dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); 247 return ret; 248 } 249 250 platform_set_drvdata(pdev, bus); 251 252 return 0; 253} 254 255static int ipq4019_mdio_remove(struct platform_device *pdev) 256{ 257 struct mii_bus *bus = platform_get_drvdata(pdev); 258 259 mdiobus_unregister(bus); 260 261 return 0; 262} 263 264static const struct of_device_id ipq4019_mdio_dt_ids[] = { 265 { .compatible = "qcom,ipq4019-mdio" }, 266 { .compatible = "qcom,ipq5018-mdio" }, 267 { } 268}; 269MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids); 270 271static struct platform_driver ipq4019_mdio_driver = { 272 .probe = ipq4019_mdio_probe, 273 .remove = ipq4019_mdio_remove, 274 .driver = { 275 .name = "ipq4019-mdio", 276 .of_match_table = ipq4019_mdio_dt_ids, 277 }, 278}; 279 280module_platform_driver(ipq4019_mdio_driver); 281 282MODULE_DESCRIPTION("ipq4019 MDIO interface driver"); 283MODULE_AUTHOR("Qualcomm Atheros"); 284MODULE_LICENSE("Dual BSD/GPL");