linux/drivers/net/phy/mdio-ipq4019.c
<<
>>
Prefs
   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/kernel.h>
   7#include <linux/module.h>
   8#include <linux/io.h>
   9#include <linux/iopoll.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
  15#define MDIO_ADDR_REG                           0x44
  16#define MDIO_DATA_WRITE_REG                     0x48
  17#define MDIO_DATA_READ_REG                      0x4c
  18#define MDIO_CMD_REG                            0x50
  19#define MDIO_CMD_ACCESS_BUSY            BIT(16)
  20#define MDIO_CMD_ACCESS_START           BIT(8)
  21#define MDIO_CMD_ACCESS_CODE_READ       0
  22#define MDIO_CMD_ACCESS_CODE_WRITE      1
  23
  24#define ipq4019_MDIO_TIMEOUT    10000
  25#define ipq4019_MDIO_SLEEP              10
  26
  27struct ipq4019_mdio_data {
  28        void __iomem    *membase;
  29};
  30
  31static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
  32{
  33        struct ipq4019_mdio_data *priv = bus->priv;
  34        unsigned int busy;
  35
  36        return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,
  37                                  (busy & MDIO_CMD_ACCESS_BUSY) == 0,
  38                                  ipq4019_MDIO_SLEEP, ipq4019_MDIO_TIMEOUT);
  39}
  40
  41static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
  42{
  43        struct ipq4019_mdio_data *priv = bus->priv;
  44        unsigned int cmd;
  45
  46        /* Reject clause 45 */
  47        if (regnum & MII_ADDR_C45)
  48                return -EOPNOTSUPP;
  49
  50        if (ipq4019_mdio_wait_busy(bus))
  51                return -ETIMEDOUT;
  52
  53        /* issue the phy address and reg */
  54        writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
  55
  56        cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
  57
  58        /* issue read command */
  59        writel(cmd, priv->membase + MDIO_CMD_REG);
  60
  61        /* Wait read complete */
  62        if (ipq4019_mdio_wait_busy(bus))
  63                return -ETIMEDOUT;
  64
  65        /* Read and return data */
  66        return readl(priv->membase + MDIO_DATA_READ_REG);
  67}
  68
  69static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
  70                                                         u16 value)
  71{
  72        struct ipq4019_mdio_data *priv = bus->priv;
  73        unsigned int cmd;
  74
  75        /* Reject clause 45 */
  76        if (regnum & MII_ADDR_C45)
  77                return -EOPNOTSUPP;
  78
  79        if (ipq4019_mdio_wait_busy(bus))
  80                return -ETIMEDOUT;
  81
  82        /* issue the phy address and reg */
  83        writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
  84
  85        /* issue write data */
  86        writel(value, priv->membase + MDIO_DATA_WRITE_REG);
  87
  88        cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
  89        /* issue write command */
  90        writel(cmd, priv->membase + MDIO_CMD_REG);
  91
  92        /* Wait write complete */
  93        if (ipq4019_mdio_wait_busy(bus))
  94                return -ETIMEDOUT;
  95
  96        return 0;
  97}
  98
  99static int ipq4019_mdio_probe(struct platform_device *pdev)
 100{
 101        struct ipq4019_mdio_data *priv;
 102        struct mii_bus *bus;
 103        int ret;
 104
 105        bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
 106        if (!bus)
 107                return -ENOMEM;
 108
 109        priv = bus->priv;
 110
 111        priv->membase = devm_platform_ioremap_resource(pdev, 0);
 112        if (IS_ERR(priv->membase))
 113                return PTR_ERR(priv->membase);
 114
 115        bus->name = "ipq4019_mdio";
 116        bus->read = ipq4019_mdio_read;
 117        bus->write = ipq4019_mdio_write;
 118        bus->parent = &pdev->dev;
 119        snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
 120
 121        ret = of_mdiobus_register(bus, pdev->dev.of_node);
 122        if (ret) {
 123                dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
 124                return ret;
 125        }
 126
 127        platform_set_drvdata(pdev, bus);
 128
 129        return 0;
 130}
 131
 132static int ipq4019_mdio_remove(struct platform_device *pdev)
 133{
 134        struct mii_bus *bus = platform_get_drvdata(pdev);
 135
 136        mdiobus_unregister(bus);
 137
 138        return 0;
 139}
 140
 141static const struct of_device_id ipq4019_mdio_dt_ids[] = {
 142        { .compatible = "qcom,ipq4019-mdio" },
 143        { }
 144};
 145MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);
 146
 147static struct platform_driver ipq4019_mdio_driver = {
 148        .probe = ipq4019_mdio_probe,
 149        .remove = ipq4019_mdio_remove,
 150        .driver = {
 151                .name = "ipq4019-mdio",
 152                .of_match_table = ipq4019_mdio_dt_ids,
 153        },
 154};
 155
 156module_platform_driver(ipq4019_mdio_driver);
 157
 158MODULE_DESCRIPTION("ipq4019 MDIO interface driver");
 159MODULE_AUTHOR("Qualcomm Atheros");
 160MODULE_LICENSE("Dual BSD/GPL");
 161