406 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/* 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 0x4017#define MDIO_MODE_MDC_MODE BIT(12)18/* 0 = Clause 22, 1 = Clause 45 */19#define MDIO_MODE_C45 BIT(8)20#define MDIO_MODE_DIV_MASK GENMASK(7, 0)21#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)22#define MDIO_MODE_DIV_1 0x023#define MDIO_MODE_DIV_2 0x124#define MDIO_MODE_DIV_4 0x325#define MDIO_MODE_DIV_8 0x726#define MDIO_MODE_DIV_16 0xf27#define MDIO_MODE_DIV_32 0x1f28#define MDIO_MODE_DIV_64 0x3f29#define MDIO_MODE_DIV_128 0x7f30#define MDIO_MODE_DIV_256 0xff31#define MDIO_ADDR_REG 0x4432#define MDIO_DATA_WRITE_REG 0x4833#define MDIO_DATA_READ_REG 0x4c34#define MDIO_CMD_REG 0x5035#define MDIO_CMD_ACCESS_BUSY BIT(16)36#define MDIO_CMD_ACCESS_START BIT(8)37#define MDIO_CMD_ACCESS_CODE_READ 038#define MDIO_CMD_ACCESS_CODE_WRITE 139#define MDIO_CMD_ACCESS_CODE_C45_ADDR 040#define MDIO_CMD_ACCESS_CODE_C45_WRITE 141#define MDIO_CMD_ACCESS_CODE_C45_READ 242 43#define IPQ4019_MDIO_TIMEOUT 1000044#define IPQ4019_MDIO_SLEEP 1045 46/* MDIO clock source frequency is fixed to 100M */47#define IPQ_MDIO_CLK_RATE 10000000048 49#define IPQ_PHY_SET_DELAY_US 10000050 51struct ipq4019_mdio_data {52 void __iomem *membase;53 void __iomem *eth_ldo_rdy;54 struct clk *mdio_clk;55 unsigned int mdc_rate;56};57 58static int ipq4019_mdio_wait_busy(struct mii_bus *bus)59{60 struct ipq4019_mdio_data *priv = bus->priv;61 unsigned int busy;62 63 return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,64 (busy & MDIO_CMD_ACCESS_BUSY) == 0,65 IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT);66}67 68static int ipq4019_mdio_read_c45(struct mii_bus *bus, int mii_id, int mmd,69 int reg)70{71 struct ipq4019_mdio_data *priv = bus->priv;72 unsigned int data;73 unsigned int cmd;74 75 if (ipq4019_mdio_wait_busy(bus))76 return -ETIMEDOUT;77 78 data = readl(priv->membase + MDIO_MODE_REG);79 80 data |= MDIO_MODE_C45;81 82 writel(data, priv->membase + MDIO_MODE_REG);83 84 /* issue the phy address and mmd */85 writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);86 87 /* issue reg */88 writel(reg, priv->membase + MDIO_DATA_WRITE_REG);89 90 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;91 92 /* issue read command */93 writel(cmd, priv->membase + MDIO_CMD_REG);94 95 /* Wait read complete */96 if (ipq4019_mdio_wait_busy(bus))97 return -ETIMEDOUT;98 99 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_READ;100 101 writel(cmd, priv->membase + MDIO_CMD_REG);102 103 if (ipq4019_mdio_wait_busy(bus))104 return -ETIMEDOUT;105 106 /* Read and return data */107 return readl(priv->membase + MDIO_DATA_READ_REG);108}109 110static int ipq4019_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)111{112 struct ipq4019_mdio_data *priv = bus->priv;113 unsigned int data;114 unsigned int cmd;115 116 if (ipq4019_mdio_wait_busy(bus))117 return -ETIMEDOUT;118 119 data = readl(priv->membase + MDIO_MODE_REG);120 121 data &= ~MDIO_MODE_C45;122 123 writel(data, priv->membase + MDIO_MODE_REG);124 125 /* issue the phy address and reg */126 writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);127 128 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;129 130 /* issue read command */131 writel(cmd, priv->membase + MDIO_CMD_REG);132 133 /* Wait read complete */134 if (ipq4019_mdio_wait_busy(bus))135 return -ETIMEDOUT;136 137 /* Read and return data */138 return readl(priv->membase + MDIO_DATA_READ_REG);139}140 141static int ipq4019_mdio_write_c45(struct mii_bus *bus, int mii_id, int mmd,142 int reg, u16 value)143{144 struct ipq4019_mdio_data *priv = bus->priv;145 unsigned int data;146 unsigned int cmd;147 148 if (ipq4019_mdio_wait_busy(bus))149 return -ETIMEDOUT;150 151 data = readl(priv->membase + MDIO_MODE_REG);152 153 data |= MDIO_MODE_C45;154 155 writel(data, priv->membase + MDIO_MODE_REG);156 157 /* issue the phy address and mmd */158 writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);159 160 /* issue reg */161 writel(reg, priv->membase + MDIO_DATA_WRITE_REG);162 163 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;164 165 writel(cmd, priv->membase + MDIO_CMD_REG);166 167 if (ipq4019_mdio_wait_busy(bus))168 return -ETIMEDOUT;169 170 /* issue write data */171 writel(value, priv->membase + MDIO_DATA_WRITE_REG);172 173 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_WRITE;174 writel(cmd, priv->membase + MDIO_CMD_REG);175 176 /* Wait write complete */177 if (ipq4019_mdio_wait_busy(bus))178 return -ETIMEDOUT;179 180 return 0;181}182 183static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,184 u16 value)185{186 struct ipq4019_mdio_data *priv = bus->priv;187 unsigned int data;188 unsigned int cmd;189 190 if (ipq4019_mdio_wait_busy(bus))191 return -ETIMEDOUT;192 193 /* Enter Clause 22 mode */194 data = readl(priv->membase + MDIO_MODE_REG);195 196 data &= ~MDIO_MODE_C45;197 198 writel(data, priv->membase + MDIO_MODE_REG);199 200 /* issue the phy address and reg */201 writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);202 203 /* issue write data */204 writel(value, priv->membase + MDIO_DATA_WRITE_REG);205 206 /* issue write command */207 cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;208 209 writel(cmd, priv->membase + MDIO_CMD_REG);210 211 /* Wait write complete */212 if (ipq4019_mdio_wait_busy(bus))213 return -ETIMEDOUT;214 215 return 0;216}217 218static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)219{220 unsigned long ahb_rate;221 int div;222 u32 val;223 224 /* If we don't have a clock for AHB use the fixed value */225 ahb_rate = IPQ_MDIO_CLK_RATE;226 if (priv->mdio_clk)227 ahb_rate = clk_get_rate(priv->mdio_clk);228 229 /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)230 * While supported, internal documentation doesn't231 * assure correct functionality of the MDIO bus232 * with divider of 1, 2 or 4.233 */234 for (div = 8; div <= 256; div *= 2) {235 /* The requested rate is supported by the div */236 if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {237 val = readl(priv->membase + MDIO_MODE_REG);238 val &= ~MDIO_MODE_DIV_MASK;239 val |= MDIO_MODE_DIV(div);240 writel(val, priv->membase + MDIO_MODE_REG);241 242 return 0;243 }244 }245 246 /* The requested rate is not supported */247 return -EINVAL;248}249 250static int ipq_mdio_reset(struct mii_bus *bus)251{252 struct ipq4019_mdio_data *priv = bus->priv;253 u32 val;254 int ret;255 256 /* To indicate CMN_PLL that ethernet_ldo has been ready if platform resource 1257 * is specified in the device tree.258 */259 if (priv->eth_ldo_rdy) {260 val = readl(priv->eth_ldo_rdy);261 val |= BIT(0);262 writel(val, priv->eth_ldo_rdy);263 fsleep(IPQ_PHY_SET_DELAY_US);264 }265 266 /* Configure MDIO clock source frequency if clock is specified in the device tree */267 ret = clk_set_rate(priv->mdio_clk, IPQ_MDIO_CLK_RATE);268 if (ret)269 return ret;270 271 ret = clk_prepare_enable(priv->mdio_clk);272 if (ret)273 return ret;274 275 mdelay(10);276 277 /* Restore MDC rate */278 return ipq4019_mdio_set_div(priv);279}280 281static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,282 struct ipq4019_mdio_data *priv)283{284 unsigned long ahb_rate;285 int div;286 u32 val;287 288 /* MDC rate defined in DT, we don't have to decide a default value */289 if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",290 &priv->mdc_rate))291 return;292 293 /* If we don't have a clock for AHB use the fixed value */294 ahb_rate = IPQ_MDIO_CLK_RATE;295 if (priv->mdio_clk)296 ahb_rate = clk_get_rate(priv->mdio_clk);297 298 /* Check what is the current div set */299 val = readl(priv->membase + MDIO_MODE_REG);300 div = FIELD_GET(MDIO_MODE_DIV_MASK, val);301 302 /* div is not set to the default value of /256303 * Probably someone changed that (bootloader, other drivers)304 * Keep this and don't overwrite it.305 */306 if (div != MDIO_MODE_DIV_256) {307 priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);308 return;309 }310 311 /* If div is /256 assume nobody have set this value and312 * try to find one MDC rate that is close the 802.3 spec of313 * 2.5MHz314 */315 for (div = 256; div >= 8; div /= 2) {316 /* Stop as soon as we found a divider that317 * reached the closest value to 2.5MHz318 */319 if (DIV_ROUND_UP(ahb_rate, div) > 2500000)320 break;321 322 priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);323 }324}325 326static int ipq4019_mdio_probe(struct platform_device *pdev)327{328 struct ipq4019_mdio_data *priv;329 struct mii_bus *bus;330 struct resource *res;331 int ret;332 333 bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));334 if (!bus)335 return -ENOMEM;336 337 priv = bus->priv;338 339 priv->membase = devm_platform_ioremap_resource(pdev, 0);340 if (IS_ERR(priv->membase))341 return PTR_ERR(priv->membase);342 343 priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk");344 if (IS_ERR(priv->mdio_clk))345 return PTR_ERR(priv->mdio_clk);346 347 ipq4019_mdio_select_mdc_rate(pdev, priv);348 ret = ipq4019_mdio_set_div(priv);349 if (ret)350 return ret;351 352 /* The platform resource is provided on the chipset IPQ5018 */353 /* This resource is optional */354 res = platform_get_resource(pdev, IORESOURCE_MEM, 1);355 if (res)356 priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res);357 358 bus->name = "ipq4019_mdio";359 bus->read = ipq4019_mdio_read_c22;360 bus->write = ipq4019_mdio_write_c22;361 bus->read_c45 = ipq4019_mdio_read_c45;362 bus->write_c45 = ipq4019_mdio_write_c45;363 bus->reset = ipq_mdio_reset;364 bus->parent = &pdev->dev;365 snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);366 367 ret = of_mdiobus_register(bus, pdev->dev.of_node);368 if (ret) {369 dev_err(&pdev->dev, "Cannot register MDIO bus!\n");370 return ret;371 }372 373 platform_set_drvdata(pdev, bus);374 375 return 0;376}377 378static void ipq4019_mdio_remove(struct platform_device *pdev)379{380 struct mii_bus *bus = platform_get_drvdata(pdev);381 382 mdiobus_unregister(bus);383}384 385static const struct of_device_id ipq4019_mdio_dt_ids[] = {386 { .compatible = "qcom,ipq4019-mdio" },387 { .compatible = "qcom,ipq5018-mdio" },388 { }389};390MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);391 392static struct platform_driver ipq4019_mdio_driver = {393 .probe = ipq4019_mdio_probe,394 .remove_new = ipq4019_mdio_remove,395 .driver = {396 .name = "ipq4019-mdio",397 .of_match_table = ipq4019_mdio_dt_ids,398 },399};400 401module_platform_driver(ipq4019_mdio_driver);402 403MODULE_DESCRIPTION("ipq4019 MDIO interface driver");404MODULE_AUTHOR("Qualcomm Atheros");405MODULE_LICENSE("Dual BSD/GPL");406