root/drivers/net/phy/mdio-sun4i.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sun4i_mdio_read
  2. sun4i_mdio_write
  3. sun4i_mdio_probe
  4. sun4i_mdio_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Allwinner EMAC MDIO interface driver
   4  *
   5  * Copyright 2012-2013 Stefan Roese <sr@denx.de>
   6  * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
   7  *
   8  * Based on the Linux driver provided by Allwinner:
   9  * Copyright (C) 1997  Sten Wang
  10  */
  11 
  12 #include <linux/delay.h>
  13 #include <linux/kernel.h>
  14 #include <linux/module.h>
  15 #include <linux/mutex.h>
  16 #include <linux/of_address.h>
  17 #include <linux/of_mdio.h>
  18 #include <linux/phy.h>
  19 #include <linux/platform_device.h>
  20 #include <linux/regulator/consumer.h>
  21 
  22 #define EMAC_MAC_MCMD_REG       (0x00)
  23 #define EMAC_MAC_MADR_REG       (0x04)
  24 #define EMAC_MAC_MWTD_REG       (0x08)
  25 #define EMAC_MAC_MRDD_REG       (0x0c)
  26 #define EMAC_MAC_MIND_REG       (0x10)
  27 #define EMAC_MAC_SSRR_REG       (0x14)
  28 
  29 #define MDIO_TIMEOUT            (msecs_to_jiffies(100))
  30 
  31 struct sun4i_mdio_data {
  32         void __iomem            *membase;
  33         struct regulator        *regulator;
  34 };
  35 
  36 static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
  37 {
  38         struct sun4i_mdio_data *data = bus->priv;
  39         unsigned long timeout_jiffies;
  40         int value;
  41 
  42         /* issue the phy address and reg */
  43         writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
  44         /* pull up the phy io line */
  45         writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
  46 
  47         /* Wait read complete */
  48         timeout_jiffies = jiffies + MDIO_TIMEOUT;
  49         while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
  50                 if (time_is_before_jiffies(timeout_jiffies))
  51                         return -ETIMEDOUT;
  52                 msleep(1);
  53         }
  54 
  55         /* push down the phy io line */
  56         writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
  57         /* and read data */
  58         value = readl(data->membase + EMAC_MAC_MRDD_REG);
  59 
  60         return value;
  61 }
  62 
  63 static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
  64                             u16 value)
  65 {
  66         struct sun4i_mdio_data *data = bus->priv;
  67         unsigned long timeout_jiffies;
  68 
  69         /* issue the phy address and reg */
  70         writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
  71         /* pull up the phy io line */
  72         writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
  73 
  74         /* Wait read complete */
  75         timeout_jiffies = jiffies + MDIO_TIMEOUT;
  76         while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
  77                 if (time_is_before_jiffies(timeout_jiffies))
  78                         return -ETIMEDOUT;
  79                 msleep(1);
  80         }
  81 
  82         /* push down the phy io line */
  83         writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
  84         /* and write data */
  85         writel(value, data->membase + EMAC_MAC_MWTD_REG);
  86 
  87         return 0;
  88 }
  89 
  90 static int sun4i_mdio_probe(struct platform_device *pdev)
  91 {
  92         struct device_node *np = pdev->dev.of_node;
  93         struct mii_bus *bus;
  94         struct sun4i_mdio_data *data;
  95         int ret;
  96 
  97         bus = mdiobus_alloc_size(sizeof(*data));
  98         if (!bus)
  99                 return -ENOMEM;
 100 
 101         bus->name = "sun4i_mii_bus";
 102         bus->read = &sun4i_mdio_read;
 103         bus->write = &sun4i_mdio_write;
 104         snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
 105         bus->parent = &pdev->dev;
 106 
 107         data = bus->priv;
 108         data->membase = devm_platform_ioremap_resource(pdev, 0);
 109         if (IS_ERR(data->membase)) {
 110                 ret = PTR_ERR(data->membase);
 111                 goto err_out_free_mdiobus;
 112         }
 113 
 114         data->regulator = devm_regulator_get(&pdev->dev, "phy");
 115         if (IS_ERR(data->regulator)) {
 116                 if (PTR_ERR(data->regulator) == -EPROBE_DEFER) {
 117                         ret = -EPROBE_DEFER;
 118                         goto err_out_free_mdiobus;
 119                 }
 120 
 121                 dev_info(&pdev->dev, "no regulator found\n");
 122                 data->regulator = NULL;
 123         } else {
 124                 ret = regulator_enable(data->regulator);
 125                 if (ret)
 126                         goto err_out_free_mdiobus;
 127         }
 128 
 129         ret = of_mdiobus_register(bus, np);
 130         if (ret < 0)
 131                 goto err_out_disable_regulator;
 132 
 133         platform_set_drvdata(pdev, bus);
 134 
 135         return 0;
 136 
 137 err_out_disable_regulator:
 138         if (data->regulator)
 139                 regulator_disable(data->regulator);
 140 err_out_free_mdiobus:
 141         mdiobus_free(bus);
 142         return ret;
 143 }
 144 
 145 static int sun4i_mdio_remove(struct platform_device *pdev)
 146 {
 147         struct mii_bus *bus = platform_get_drvdata(pdev);
 148         struct sun4i_mdio_data *data = bus->priv;
 149 
 150         mdiobus_unregister(bus);
 151         if (data->regulator)
 152                 regulator_disable(data->regulator);
 153         mdiobus_free(bus);
 154 
 155         return 0;
 156 }
 157 
 158 static const struct of_device_id sun4i_mdio_dt_ids[] = {
 159         { .compatible = "allwinner,sun4i-a10-mdio" },
 160 
 161         /* Deprecated */
 162         { .compatible = "allwinner,sun4i-mdio" },
 163         { }
 164 };
 165 MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids);
 166 
 167 static struct platform_driver sun4i_mdio_driver = {
 168         .probe = sun4i_mdio_probe,
 169         .remove = sun4i_mdio_remove,
 170         .driver = {
 171                 .name = "sun4i-mdio",
 172                 .of_match_table = sun4i_mdio_dt_ids,
 173         },
 174 };
 175 
 176 module_platform_driver(sun4i_mdio_driver);
 177 
 178 MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver");
 179 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 180 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */