1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2a9049e0cSAndrew Lunn /* Framework for MDIO devices, other than PHYs.
3a9049e0cSAndrew Lunn *
4a9049e0cSAndrew Lunn * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
5a9049e0cSAndrew Lunn */
6a9049e0cSAndrew Lunn
7a9049e0cSAndrew Lunn #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8a9049e0cSAndrew Lunn
91d0018a4SBartosz Golaszewski #include <linux/delay.h>
10a9049e0cSAndrew Lunn #include <linux/errno.h>
11bafbdd52SSergei Shtylyov #include <linux/gpio.h>
12bafbdd52SSergei Shtylyov #include <linux/gpio/consumer.h>
13a9049e0cSAndrew Lunn #include <linux/init.h>
14a9049e0cSAndrew Lunn #include <linux/interrupt.h>
15a9049e0cSAndrew Lunn #include <linux/kernel.h>
16a9049e0cSAndrew Lunn #include <linux/mdio.h>
17a9049e0cSAndrew Lunn #include <linux/mii.h>
18a9049e0cSAndrew Lunn #include <linux/module.h>
19a9049e0cSAndrew Lunn #include <linux/phy.h>
2071dd6c0dSDavid Bauer #include <linux/reset.h>
21a9049e0cSAndrew Lunn #include <linux/slab.h>
22a9049e0cSAndrew Lunn #include <linux/string.h>
23a9049e0cSAndrew Lunn #include <linux/unistd.h>
24*cb376176SZeng Heng #include <linux/property.h>
25a9049e0cSAndrew Lunn
mdio_device_free(struct mdio_device * mdiodev)26a9049e0cSAndrew Lunn void mdio_device_free(struct mdio_device *mdiodev)
27a9049e0cSAndrew Lunn {
28a9049e0cSAndrew Lunn put_device(&mdiodev->dev);
29a9049e0cSAndrew Lunn }
30a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_free);
31a9049e0cSAndrew Lunn
mdio_device_release(struct device * dev)32a9049e0cSAndrew Lunn static void mdio_device_release(struct device *dev)
33a9049e0cSAndrew Lunn {
34*cb376176SZeng Heng fwnode_handle_put(dev->fwnode);
35a9049e0cSAndrew Lunn kfree(to_mdio_device(dev));
36a9049e0cSAndrew Lunn }
37a9049e0cSAndrew Lunn
mdio_device_bus_match(struct device * dev,struct device_driver * drv)38648ea013SFlorian Fainelli int mdio_device_bus_match(struct device *dev, struct device_driver *drv)
39648ea013SFlorian Fainelli {
40648ea013SFlorian Fainelli struct mdio_device *mdiodev = to_mdio_device(dev);
41648ea013SFlorian Fainelli struct mdio_driver *mdiodrv = to_mdio_driver(drv);
42648ea013SFlorian Fainelli
43648ea013SFlorian Fainelli if (mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)
44648ea013SFlorian Fainelli return 0;
45648ea013SFlorian Fainelli
46648ea013SFlorian Fainelli return strcmp(mdiodev->modalias, drv->name) == 0;
47648ea013SFlorian Fainelli }
48648ea013SFlorian Fainelli
mdio_device_create(struct mii_bus * bus,int addr)49a9049e0cSAndrew Lunn struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
50a9049e0cSAndrew Lunn {
51a9049e0cSAndrew Lunn struct mdio_device *mdiodev;
52a9049e0cSAndrew Lunn
53a9049e0cSAndrew Lunn /* We allocate the device, and initialize the default values */
54a9049e0cSAndrew Lunn mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
55a9049e0cSAndrew Lunn if (!mdiodev)
56a9049e0cSAndrew Lunn return ERR_PTR(-ENOMEM);
57a9049e0cSAndrew Lunn
58a9049e0cSAndrew Lunn mdiodev->dev.release = mdio_device_release;
59a9049e0cSAndrew Lunn mdiodev->dev.parent = &bus->dev;
60a9049e0cSAndrew Lunn mdiodev->dev.bus = &mdio_bus_type;
61711fdba3SAndrew Lunn mdiodev->device_free = mdio_device_free;
62711fdba3SAndrew Lunn mdiodev->device_remove = mdio_device_remove;
63a9049e0cSAndrew Lunn mdiodev->bus = bus;
64a9049e0cSAndrew Lunn mdiodev->addr = addr;
65a9049e0cSAndrew Lunn
66a9049e0cSAndrew Lunn dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
67a9049e0cSAndrew Lunn
68a9049e0cSAndrew Lunn device_initialize(&mdiodev->dev);
69a9049e0cSAndrew Lunn
70a9049e0cSAndrew Lunn return mdiodev;
71a9049e0cSAndrew Lunn }
72a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_create);
73a9049e0cSAndrew Lunn
74a9049e0cSAndrew Lunn /**
75a9049e0cSAndrew Lunn * mdio_device_register - Register the mdio device on the MDIO bus
76a9049e0cSAndrew Lunn * @mdiodev: mdio_device structure to be added to the MDIO bus
77a9049e0cSAndrew Lunn */
mdio_device_register(struct mdio_device * mdiodev)78a9049e0cSAndrew Lunn int mdio_device_register(struct mdio_device *mdiodev)
79a9049e0cSAndrew Lunn {
80a9049e0cSAndrew Lunn int err;
81a9049e0cSAndrew Lunn
82450bf1f0SWenpeng Liang dev_dbg(&mdiodev->dev, "%s\n", __func__);
83a9049e0cSAndrew Lunn
84a9049e0cSAndrew Lunn err = mdiobus_register_device(mdiodev);
85a9049e0cSAndrew Lunn if (err)
86a9049e0cSAndrew Lunn return err;
87a9049e0cSAndrew Lunn
88a9049e0cSAndrew Lunn err = device_add(&mdiodev->dev);
89a9049e0cSAndrew Lunn if (err) {
90a9049e0cSAndrew Lunn pr_err("MDIO %d failed to add\n", mdiodev->addr);
91a9049e0cSAndrew Lunn goto out;
92a9049e0cSAndrew Lunn }
93a9049e0cSAndrew Lunn
94a9049e0cSAndrew Lunn return 0;
95a9049e0cSAndrew Lunn
96a9049e0cSAndrew Lunn out:
97a9049e0cSAndrew Lunn mdiobus_unregister_device(mdiodev);
98a9049e0cSAndrew Lunn return err;
99a9049e0cSAndrew Lunn }
100a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_register);
101a9049e0cSAndrew Lunn
102a9049e0cSAndrew Lunn /**
103a9049e0cSAndrew Lunn * mdio_device_remove - Remove a previously registered mdio device from the
104a9049e0cSAndrew Lunn * MDIO bus
105a9049e0cSAndrew Lunn * @mdiodev: mdio_device structure to remove
106a9049e0cSAndrew Lunn *
107a9049e0cSAndrew Lunn * This doesn't free the mdio_device itself, it merely reverses the effects
108a9049e0cSAndrew Lunn * of mdio_device_register(). Use mdio_device_free() to free the device
109a9049e0cSAndrew Lunn * after calling this function.
110a9049e0cSAndrew Lunn */
mdio_device_remove(struct mdio_device * mdiodev)111a9049e0cSAndrew Lunn void mdio_device_remove(struct mdio_device *mdiodev)
112a9049e0cSAndrew Lunn {
113a9049e0cSAndrew Lunn device_del(&mdiodev->dev);
114a9049e0cSAndrew Lunn mdiobus_unregister_device(mdiodev);
115a9049e0cSAndrew Lunn }
116a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_remove);
117a9049e0cSAndrew Lunn
mdio_device_reset(struct mdio_device * mdiodev,int value)118bafbdd52SSergei Shtylyov void mdio_device_reset(struct mdio_device *mdiodev, int value)
119bafbdd52SSergei Shtylyov {
1203a30ae6eSRichard Leitner unsigned int d;
1213a30ae6eSRichard Leitner
1226110ed2dSDavid Bauer if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)
1233a30ae6eSRichard Leitner return;
1243a30ae6eSRichard Leitner
1256110ed2dSDavid Bauer if (mdiodev->reset_gpio)
126ea977d19SAndrea Merello gpiod_set_value_cansleep(mdiodev->reset_gpio, value);
1273a30ae6eSRichard Leitner
12871dd6c0dSDavid Bauer if (mdiodev->reset_ctrl) {
12971dd6c0dSDavid Bauer if (value)
13071dd6c0dSDavid Bauer reset_control_assert(mdiodev->reset_ctrl);
13171dd6c0dSDavid Bauer else
13271dd6c0dSDavid Bauer reset_control_deassert(mdiodev->reset_ctrl);
13371dd6c0dSDavid Bauer }
13471dd6c0dSDavid Bauer
13504f629f7SRichard Leitner d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
1363a30ae6eSRichard Leitner if (d)
137e4d5efddSBruno Thomsen fsleep(d);
138bafbdd52SSergei Shtylyov }
139bafbdd52SSergei Shtylyov EXPORT_SYMBOL(mdio_device_reset);
140bafbdd52SSergei Shtylyov
141a9049e0cSAndrew Lunn /**
142a9049e0cSAndrew Lunn * mdio_probe - probe an MDIO device
143a9049e0cSAndrew Lunn * @dev: device to probe
144a9049e0cSAndrew Lunn *
145a9049e0cSAndrew Lunn * Description: Take care of setting up the mdio_device structure
146a9049e0cSAndrew Lunn * and calling the driver to probe the device.
147a9049e0cSAndrew Lunn */
mdio_probe(struct device * dev)148a9049e0cSAndrew Lunn static int mdio_probe(struct device *dev)
149a9049e0cSAndrew Lunn {
150a9049e0cSAndrew Lunn struct mdio_device *mdiodev = to_mdio_device(dev);
151a9049e0cSAndrew Lunn struct device_driver *drv = mdiodev->dev.driver;
152a9049e0cSAndrew Lunn struct mdio_driver *mdiodrv = to_mdio_driver(drv);
153a9049e0cSAndrew Lunn int err = 0;
154a9049e0cSAndrew Lunn
155bafbdd52SSergei Shtylyov /* Deassert the reset signal */
156bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 0);
157bafbdd52SSergei Shtylyov
15896e26359SBartosz Golaszewski if (mdiodrv->probe) {
159a9049e0cSAndrew Lunn err = mdiodrv->probe(mdiodev);
160bafbdd52SSergei Shtylyov if (err) {
161bafbdd52SSergei Shtylyov /* Assert the reset signal */
162bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 1);
163bafbdd52SSergei Shtylyov }
164bafbdd52SSergei Shtylyov }
165a9049e0cSAndrew Lunn
166a9049e0cSAndrew Lunn return err;
167a9049e0cSAndrew Lunn }
168a9049e0cSAndrew Lunn
mdio_remove(struct device * dev)169a9049e0cSAndrew Lunn static int mdio_remove(struct device *dev)
170a9049e0cSAndrew Lunn {
171a9049e0cSAndrew Lunn struct mdio_device *mdiodev = to_mdio_device(dev);
172a9049e0cSAndrew Lunn struct device_driver *drv = mdiodev->dev.driver;
173a9049e0cSAndrew Lunn struct mdio_driver *mdiodrv = to_mdio_driver(drv);
174a9049e0cSAndrew Lunn
17596e26359SBartosz Golaszewski if (mdiodrv->remove)
176a9049e0cSAndrew Lunn mdiodrv->remove(mdiodev);
177a9049e0cSAndrew Lunn
178bafbdd52SSergei Shtylyov /* Assert the reset signal */
179bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 1);
180bafbdd52SSergei Shtylyov
181a9049e0cSAndrew Lunn return 0;
182a9049e0cSAndrew Lunn }
183a9049e0cSAndrew Lunn
mdio_shutdown(struct device * dev)184cf957997SVladimir Oltean static void mdio_shutdown(struct device *dev)
185cf957997SVladimir Oltean {
186cf957997SVladimir Oltean struct mdio_device *mdiodev = to_mdio_device(dev);
187cf957997SVladimir Oltean struct device_driver *drv = mdiodev->dev.driver;
188cf957997SVladimir Oltean struct mdio_driver *mdiodrv = to_mdio_driver(drv);
189cf957997SVladimir Oltean
190cf957997SVladimir Oltean if (mdiodrv->shutdown)
191cf957997SVladimir Oltean mdiodrv->shutdown(mdiodev);
192cf957997SVladimir Oltean }
193cf957997SVladimir Oltean
194a9049e0cSAndrew Lunn /**
195a9049e0cSAndrew Lunn * mdio_driver_register - register an mdio_driver with the MDIO layer
19619c5a5feSAndrew Lunn * @drv: new mdio_driver to register
197a9049e0cSAndrew Lunn */
mdio_driver_register(struct mdio_driver * drv)198a9049e0cSAndrew Lunn int mdio_driver_register(struct mdio_driver *drv)
199a9049e0cSAndrew Lunn {
200a9049e0cSAndrew Lunn struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
201a9049e0cSAndrew Lunn int retval;
202a9049e0cSAndrew Lunn
203450bf1f0SWenpeng Liang pr_debug("%s: %s\n", __func__, mdiodrv->driver.name);
204a9049e0cSAndrew Lunn
205a9049e0cSAndrew Lunn mdiodrv->driver.bus = &mdio_bus_type;
206a9049e0cSAndrew Lunn mdiodrv->driver.probe = mdio_probe;
207a9049e0cSAndrew Lunn mdiodrv->driver.remove = mdio_remove;
208cf957997SVladimir Oltean mdiodrv->driver.shutdown = mdio_shutdown;
209a9049e0cSAndrew Lunn
210a9049e0cSAndrew Lunn retval = driver_register(&mdiodrv->driver);
211a9049e0cSAndrew Lunn if (retval) {
212a9049e0cSAndrew Lunn pr_err("%s: Error %d in registering driver\n",
213a9049e0cSAndrew Lunn mdiodrv->driver.name, retval);
214a9049e0cSAndrew Lunn
215a9049e0cSAndrew Lunn return retval;
216a9049e0cSAndrew Lunn }
217a9049e0cSAndrew Lunn
218a9049e0cSAndrew Lunn return 0;
219a9049e0cSAndrew Lunn }
220a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_register);
221a9049e0cSAndrew Lunn
mdio_driver_unregister(struct mdio_driver * drv)222a9049e0cSAndrew Lunn void mdio_driver_unregister(struct mdio_driver *drv)
223a9049e0cSAndrew Lunn {
224a9049e0cSAndrew Lunn struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
225a9049e0cSAndrew Lunn
226a9049e0cSAndrew Lunn driver_unregister(&mdiodrv->driver);
227a9049e0cSAndrew Lunn }
228a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_unregister);
229