| 1 | |
| 2 | |
| 3 | *
|
| 4 | * Copyright (c) 2016 Andrew Lunn <[email protected]>
|
| 5 |
|
| 6 |
|
| 7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 8 |
|
| 9 | #include <linux/delay.h>
|
| 10 | #include <linux/errno.h>
|
| 11 | #include <linux/gpio/consumer.h>
|
| 12 | #include <linux/init.h>
|
| 13 | #include <linux/interrupt.h>
|
| 14 | #include <linux/kernel.h>
|
| 15 | #include <linux/mdio.h>
|
| 16 | #include <linux/mii.h>
|
| 17 | #include <linux/module.h>
|
| 18 | #include <linux/phy.h>
|
| 19 | #include <linux/reset.h>
|
| 20 | #include <linux/slab.h>
|
| 21 | #include <linux/string.h>
|
| 22 | #include <linux/unistd.h>
|
| 23 | #include <linux/property.h>
|
| 24 | #include "phylib-internal.h"
|
| 25 |
|
| 26 | |
| 27 | * mdio_device_register_reset - Read and initialize the reset properties of
|
| 28 | * an mdio device
|
| 29 | * @mdiodev: mdio_device structure
|
| 30 | *
|
| 31 | * Return: Zero if successful, negative error code on failure
|
| 32 |
|
| 33 | static int mdio_device_register_reset(struct mdio_device *mdiodev)
|
| 34 | {
|
| 35 | struct reset_control *reset;
|
| 36 |
|
| 37 |
|
| 38 | mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,
|
| 39 | "reset", GPIOD_OUT_LOW);
|
| 40 | if (IS_ERR(mdiodev->reset_gpio))
|
| 41 | return PTR_ERR(mdiodev->reset_gpio);
|
| 42 |
|
| 43 | if (mdiodev->reset_gpio)
|
| 44 | gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset");
|
| 45 |
|
| 46 | reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy");
|
| 47 | if (IS_ERR(reset)) {
|
| 48 | gpiod_put(mdiodev->reset_gpio);
|
| 49 | mdiodev->reset_gpio = NULL;
|
| 50 | return PTR_ERR(reset);
|
| 51 | }
|
| 52 |
|
| 53 | mdiodev->reset_ctrl = reset;
|
| 54 |
|
| 55 |
|
| 56 | device_property_read_u32(&mdiodev->dev, "reset-assert-us",
|
| 57 | &mdiodev->reset_assert_delay);
|
| 58 | device_property_read_u32(&mdiodev->dev, "reset-deassert-us",
|
| 59 | &mdiodev->reset_deassert_delay);
|
| 60 |
|
| 61 | return 0;
|
| 62 | }
|
| 63 |
|
| 64 | |
| 65 | * mdio_device_unregister_reset - uninitialize the reset properties of
|
| 66 | * an mdio device
|
| 67 | * @mdiodev: mdio_device structure
|
| 68 |
|
| 69 | static void mdio_device_unregister_reset(struct mdio_device *mdiodev)
|
| 70 | {
|
| 71 | gpiod_put(mdiodev->reset_gpio);
|
| 72 | mdiodev->reset_gpio = NULL;
|
| 73 | reset_control_put(mdiodev->reset_ctrl);
|
| 74 | mdiodev->reset_ctrl = NULL;
|
| 75 | mdiodev->reset_assert_delay = 0;
|
| 76 | mdiodev->reset_deassert_delay = 0;
|
| 77 | }
|
| 78 |
|
| 79 | void mdio_device_reset(struct mdio_device *mdiodev, int value)
|
| 80 | {
|
| 81 | unsigned int d;
|
| 82 |
|
| 83 | if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)
|
| 84 | return;
|
| 85 |
|
| 86 | if (mdiodev->reset_state == value)
|
| 87 | return;
|
| 88 |
|
| 89 | if (mdiodev->reset_gpio)
|
| 90 | gpiod_set_value_cansleep(mdiodev->reset_gpio, value);
|
| 91 |
|
| 92 | if (mdiodev->reset_ctrl) {
|
| 93 | if (value)
|
| 94 | reset_control_assert(mdiodev->reset_ctrl);
|
| 95 | else
|
| 96 | reset_control_deassert(mdiodev->reset_ctrl);
|
| 97 | }
|
| 98 |
|
| 99 | d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
|
| 100 | if (d)
|
| 101 | fsleep(d);
|
| 102 |
|
| 103 | mdiodev->reset_state = value;
|
| 104 | }
|
| 105 | EXPORT_SYMBOL(mdio_device_reset);
|
| 106 |
|
| 107 | void mdio_device_free(struct mdio_device *mdiodev)
|
| 108 | {
|
| 109 | put_device(&mdiodev->dev);
|
| 110 | }
|
| 111 | EXPORT_SYMBOL(mdio_device_free);
|
| 112 |
|
| 113 | static void mdio_device_release(struct device *dev)
|
| 114 | {
|
| 115 | fwnode_handle_put(dev->fwnode);
|
| 116 | kfree(to_mdio_device(dev));
|
| 117 | }
|
| 118 |
|
| 119 | struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
|
| 120 | {
|
| 121 | struct mdio_device *mdiodev;
|
| 122 |
|
| 123 |
|
| 124 | mdiodev = kzalloc_obj(*mdiodev);
|
| 125 | if (!mdiodev)
|
| 126 | return ERR_PTR(-ENOMEM);
|
| 127 |
|
| 128 | mdiodev->dev.release = mdio_device_release;
|
| 129 | mdiodev->dev.parent = &bus->dev;
|
| 130 | mdiodev->dev.bus = &mdio_bus_type;
|
| 131 | mdiodev->device_free = mdio_device_free;
|
| 132 | mdiodev->device_remove = mdio_device_remove;
|
| 133 | mdiodev->bus = bus;
|
| 134 | mdiodev->addr = addr;
|
| 135 | mdiodev->reset_state = -1;
|
| 136 |
|
| 137 | dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
|
| 138 |
|
| 139 | device_initialize(&mdiodev->dev);
|
| 140 |
|
| 141 | return mdiodev;
|
| 142 | }
|
| 143 | EXPORT_SYMBOL(mdio_device_create);
|
| 144 |
|
| 145 | |
| 146 | * mdio_device_register - Register the mdio device on the MDIO bus
|
| 147 | * @mdiodev: mdio_device structure to be added to the MDIO bus
|
| 148 | *
|
| 149 | * Return: Zero if successful, negative error code on failure
|
| 150 |
|
| 151 | int mdio_device_register(struct mdio_device *mdiodev)
|
| 152 | {
|
| 153 | int err;
|
| 154 |
|
| 155 | dev_dbg(&mdiodev->dev, "%s\n", __func__);
|
| 156 |
|
| 157 | err = mdiobus_register_device(mdiodev);
|
| 158 | if (err)
|
| 159 | return err;
|
| 160 |
|
| 161 | err = device_add(&mdiodev->dev);
|
| 162 | if (err) {
|
| 163 | pr_err("MDIO %d failed to add\n", mdiodev->addr);
|
| 164 | goto out;
|
| 165 | }
|
| 166 |
|
| 167 | return 0;
|
| 168 |
|
| 169 | out:
|
| 170 | mdiobus_unregister_device(mdiodev);
|
| 171 | return err;
|
| 172 | }
|
| 173 | EXPORT_SYMBOL(mdio_device_register);
|
| 174 |
|
| 175 | |
| 176 | * mdio_device_remove - Remove a previously registered mdio device from the
|
| 177 | * MDIO bus
|
| 178 | * @mdiodev: mdio_device structure to remove
|
| 179 | *
|
| 180 | * This doesn't free the mdio_device itself, it merely reverses the effects
|
| 181 | * of mdio_device_register(). Use mdio_device_free() to free the device
|
| 182 | * after calling this function.
|
| 183 |
|
| 184 | void mdio_device_remove(struct mdio_device *mdiodev)
|
| 185 | {
|
| 186 | device_del(&mdiodev->dev);
|
| 187 | mdiobus_unregister_device(mdiodev);
|
| 188 | }
|
| 189 | EXPORT_SYMBOL(mdio_device_remove);
|
| 190 |
|
| 191 | int mdiobus_register_device(struct mdio_device *mdiodev)
|
| 192 | {
|
| 193 | int err;
|
| 194 |
|
| 195 | if (mdiodev->bus->mdio_map[mdiodev->addr])
|
| 196 | return -EBUSY;
|
| 197 |
|
| 198 | if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
|
| 199 | err = mdio_device_register_reset(mdiodev);
|
| 200 | if (err)
|
| 201 | return err;
|
| 202 |
|
| 203 |
|
| 204 | mdio_device_reset(mdiodev, 1);
|
| 205 | }
|
| 206 |
|
| 207 | mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
|
| 208 |
|
| 209 | return 0;
|
| 210 | }
|
| 211 |
|
| 212 | int mdiobus_unregister_device(struct mdio_device *mdiodev)
|
| 213 | {
|
| 214 | if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
|
| 215 | return -EINVAL;
|
| 216 |
|
| 217 | mdio_device_unregister_reset(mdiodev);
|
| 218 |
|
| 219 | mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
|
| 220 |
|
| 221 | return 0;
|
| 222 | }
|
| 223 |
|
| 224 | |
| 225 | * mdio_probe - probe an MDIO device
|
| 226 | * @dev: device to probe
|
| 227 | *
|
| 228 | * Description: Take care of setting up the mdio_device structure
|
| 229 | * and calling the driver to probe the device.
|
| 230 | *
|
| 231 | * Return: Zero if successful, negative error code on failure
|
| 232 |
|
| 233 | static int mdio_probe(struct device *dev)
|
| 234 | {
|
| 235 | struct mdio_device *mdiodev = to_mdio_device(dev);
|
| 236 | struct device_driver *drv = mdiodev->dev.driver;
|
| 237 | struct mdio_driver *mdiodrv = to_mdio_driver(drv);
|
| 238 | int err = 0;
|
| 239 |
|
| 240 |
|
| 241 | mdio_device_reset(mdiodev, 0);
|
| 242 |
|
| 243 | if (mdiodrv->probe) {
|
| 244 | err = mdiodrv->probe(mdiodev);
|
| 245 | if (err) {
|
| 246 |
|
| 247 | mdio_device_reset(mdiodev, 1);
|
| 248 | }
|
| 249 | }
|
| 250 |
|
| 251 | return err;
|
| 252 | }
|
| 253 |
|
| 254 | static int mdio_remove(struct device *dev)
|
| 255 | {
|
| 256 | struct mdio_device *mdiodev = to_mdio_device(dev);
|
| 257 | struct device_driver *drv = mdiodev->dev.driver;
|
| 258 | struct mdio_driver *mdiodrv = to_mdio_driver(drv);
|
| 259 |
|
| 260 | if (mdiodrv->remove)
|
| 261 | mdiodrv->remove(mdiodev);
|
| 262 |
|
| 263 |
|
| 264 | mdio_device_reset(mdiodev, 1);
|
| 265 |
|
| 266 | return 0;
|
| 267 | }
|
| 268 |
|
| 269 | static void mdio_shutdown(struct device *dev)
|
| 270 | {
|
| 271 | struct mdio_device *mdiodev = to_mdio_device(dev);
|
| 272 | struct device_driver *drv = mdiodev->dev.driver;
|
| 273 | struct mdio_driver *mdiodrv = to_mdio_driver(drv);
|
| 274 |
|
| 275 | if (mdiodrv->shutdown)
|
| 276 | mdiodrv->shutdown(mdiodev);
|
| 277 | }
|
| 278 |
|
| 279 | |
| 280 | * mdio_driver_register - register an mdio_driver with the MDIO layer
|
| 281 | * @drv: new mdio_driver to register
|
| 282 | *
|
| 283 | * Return: Zero if successful, negative error code on failure
|
| 284 |
|
| 285 | int mdio_driver_register(struct mdio_driver *drv)
|
| 286 | {
|
| 287 | struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
|
| 288 | int retval;
|
| 289 |
|
| 290 | pr_debug("%s: %s\n", __func__, mdiodrv->driver.name);
|
| 291 |
|
| 292 | mdiodrv->driver.bus = &mdio_bus_type;
|
| 293 | mdiodrv->driver.probe = mdio_probe;
|
| 294 | mdiodrv->driver.remove = mdio_remove;
|
| 295 | mdiodrv->driver.shutdown = mdio_shutdown;
|
| 296 |
|
| 297 | retval = driver_register(&mdiodrv->driver);
|
| 298 | if (retval) {
|
| 299 | pr_err("%s: Error %d in registering driver\n",
|
| 300 | mdiodrv->driver.name, retval);
|
| 301 |
|
| 302 | return retval;
|
| 303 | }
|
| 304 |
|
| 305 | return 0;
|
| 306 | }
|
| 307 | EXPORT_SYMBOL(mdio_driver_register);
|
| 308 |
|
| 309 | void mdio_driver_unregister(struct mdio_driver *drv)
|
| 310 | {
|
| 311 | struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
|
| 312 |
|
| 313 | driver_unregister(&mdiodrv->driver);
|
| 314 | }
|
| 315 | EXPORT_SYMBOL(mdio_driver_unregister);
|