linux/drivers/net/phy/mdio_device.c
<<
>>
Prefs
   1/* Framework for MDIO devices, other than PHYs.
   2 *
   3 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
   4 *
   5 * This program is free software; you can redistribute  it and/or modify it
   6 * under  the terms of  the GNU General  Public License as published by the
   7 * Free Software Foundation;  either version 2 of the  License, or (at your
   8 * option) any later version.
   9 *
  10 */
  11
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include <linux/errno.h>
  15#include <linux/gpio.h>
  16#include <linux/gpio/consumer.h>
  17#include <linux/init.h>
  18#include <linux/interrupt.h>
  19#include <linux/kernel.h>
  20#include <linux/mdio.h>
  21#include <linux/mii.h>
  22#include <linux/module.h>
  23#include <linux/phy.h>
  24#include <linux/slab.h>
  25#include <linux/string.h>
  26#include <linux/unistd.h>
  27#include <linux/delay.h>
  28
  29void mdio_device_free(struct mdio_device *mdiodev)
  30{
  31        put_device(&mdiodev->dev);
  32}
  33EXPORT_SYMBOL(mdio_device_free);
  34
  35static void mdio_device_release(struct device *dev)
  36{
  37        kfree(to_mdio_device(dev));
  38}
  39
  40int mdio_device_bus_match(struct device *dev, struct device_driver *drv)
  41{
  42        struct mdio_device *mdiodev = to_mdio_device(dev);
  43        struct mdio_driver *mdiodrv = to_mdio_driver(drv);
  44
  45        if (mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)
  46                return 0;
  47
  48        return strcmp(mdiodev->modalias, drv->name) == 0;
  49}
  50
  51struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
  52{
  53        struct mdio_device *mdiodev;
  54
  55        /* We allocate the device, and initialize the default values */
  56        mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
  57        if (!mdiodev)
  58                return ERR_PTR(-ENOMEM);
  59
  60        mdiodev->dev.release = mdio_device_release;
  61        mdiodev->dev.parent = &bus->dev;
  62        mdiodev->dev.bus = &mdio_bus_type;
  63        mdiodev->device_free = mdio_device_free;
  64        mdiodev->device_remove = mdio_device_remove;
  65        mdiodev->bus = bus;
  66        mdiodev->addr = addr;
  67
  68        dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
  69
  70        device_initialize(&mdiodev->dev);
  71
  72        return mdiodev;
  73}
  74EXPORT_SYMBOL(mdio_device_create);
  75
  76/**
  77 * mdio_device_register - Register the mdio device on the MDIO bus
  78 * @mdiodev: mdio_device structure to be added to the MDIO bus
  79 */
  80int mdio_device_register(struct mdio_device *mdiodev)
  81{
  82        int err;
  83
  84        dev_dbg(&mdiodev->dev, "mdio_device_register\n");
  85
  86        err = mdiobus_register_device(mdiodev);
  87        if (err)
  88                return err;
  89
  90        err = device_add(&mdiodev->dev);
  91        if (err) {
  92                pr_err("MDIO %d failed to add\n", mdiodev->addr);
  93                goto out;
  94        }
  95
  96        return 0;
  97
  98 out:
  99        mdiobus_unregister_device(mdiodev);
 100        return err;
 101}
 102EXPORT_SYMBOL(mdio_device_register);
 103
 104/**
 105 * mdio_device_remove - Remove a previously registered mdio device from the
 106 *                      MDIO bus
 107 * @mdiodev: mdio_device structure to remove
 108 *
 109 * This doesn't free the mdio_device itself, it merely reverses the effects
 110 * of mdio_device_register(). Use mdio_device_free() to free the device
 111 * after calling this function.
 112 */
 113void mdio_device_remove(struct mdio_device *mdiodev)
 114{
 115        device_del(&mdiodev->dev);
 116        mdiobus_unregister_device(mdiodev);
 117}
 118EXPORT_SYMBOL(mdio_device_remove);
 119
 120void mdio_device_reset(struct mdio_device *mdiodev, int value)
 121{
 122        unsigned int d;
 123
 124        if (!mdiodev->reset)
 125                return;
 126
 127        gpiod_set_value(mdiodev->reset, value);
 128
 129        d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
 130        if (d)
 131                usleep_range(d, d + max_t(unsigned int, d / 10, 100));
 132}
 133EXPORT_SYMBOL(mdio_device_reset);
 134
 135/**
 136 * mdio_probe - probe an MDIO device
 137 * @dev: device to probe
 138 *
 139 * Description: Take care of setting up the mdio_device structure
 140 * and calling the driver to probe the device.
 141 */
 142static int mdio_probe(struct device *dev)
 143{
 144        struct mdio_device *mdiodev = to_mdio_device(dev);
 145        struct device_driver *drv = mdiodev->dev.driver;
 146        struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 147        int err = 0;
 148
 149        if (mdiodrv->probe) {
 150                /* Deassert the reset signal */
 151                mdio_device_reset(mdiodev, 0);
 152
 153                err = mdiodrv->probe(mdiodev);
 154                if (err) {
 155                        /* Assert the reset signal */
 156                        mdio_device_reset(mdiodev, 1);
 157                }
 158        }
 159
 160        return err;
 161}
 162
 163static int mdio_remove(struct device *dev)
 164{
 165        struct mdio_device *mdiodev = to_mdio_device(dev);
 166        struct device_driver *drv = mdiodev->dev.driver;
 167        struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 168
 169        if (mdiodrv->remove) {
 170                mdiodrv->remove(mdiodev);
 171
 172                /* Assert the reset signal */
 173                mdio_device_reset(mdiodev, 1);
 174        }
 175
 176        return 0;
 177}
 178
 179/**
 180 * mdio_driver_register - register an mdio_driver with the MDIO layer
 181 * @new_driver: new mdio_driver to register
 182 */
 183int mdio_driver_register(struct mdio_driver *drv)
 184{
 185        struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
 186        int retval;
 187
 188        pr_debug("mdio_driver_register: %s\n", mdiodrv->driver.name);
 189
 190        mdiodrv->driver.bus = &mdio_bus_type;
 191        mdiodrv->driver.probe = mdio_probe;
 192        mdiodrv->driver.remove = mdio_remove;
 193
 194        retval = driver_register(&mdiodrv->driver);
 195        if (retval) {
 196                pr_err("%s: Error %d in registering driver\n",
 197                       mdiodrv->driver.name, retval);
 198
 199                return retval;
 200        }
 201
 202        return 0;
 203}
 204EXPORT_SYMBOL(mdio_driver_register);
 205
 206void mdio_driver_unregister(struct mdio_driver *drv)
 207{
 208        struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
 209
 210        driver_unregister(&mdiodrv->driver);
 211}
 212EXPORT_SYMBOL(mdio_driver_unregister);
 213