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>
24cb376176SZeng 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 {
34cb376176SZeng Heng fwnode_handle_put(dev->fwnode);
35a9049e0cSAndrew Lunn kfree(to_mdio_device(dev));
36a9049e0cSAndrew Lunn }
37a9049e0cSAndrew Lunn
mdio_device_bus_match(struct device * dev,const struct device_driver * drv)38*d69d8048SGreg Kroah-Hartman int mdio_device_bus_match(struct device *dev, const struct device_driver *drv)
39648ea013SFlorian Fainelli {
40648ea013SFlorian Fainelli struct mdio_device *mdiodev = to_mdio_device(dev);
41*d69d8048SGreg Kroah-Hartman const 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;
65df16c1c5SAndrew Halaney mdiodev->reset_state = -1;
66a9049e0cSAndrew Lunn
67a9049e0cSAndrew Lunn dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
68a9049e0cSAndrew Lunn
69a9049e0cSAndrew Lunn device_initialize(&mdiodev->dev);
70a9049e0cSAndrew Lunn
71a9049e0cSAndrew Lunn return mdiodev;
72a9049e0cSAndrew Lunn }
73a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_create);
74a9049e0cSAndrew Lunn
75a9049e0cSAndrew Lunn /**
76a9049e0cSAndrew Lunn * mdio_device_register - Register the mdio device on the MDIO bus
77a9049e0cSAndrew Lunn * @mdiodev: mdio_device structure to be added to the MDIO bus
78a9049e0cSAndrew Lunn */
mdio_device_register(struct mdio_device * mdiodev)79a9049e0cSAndrew Lunn int mdio_device_register(struct mdio_device *mdiodev)
80a9049e0cSAndrew Lunn {
81a9049e0cSAndrew Lunn int err;
82a9049e0cSAndrew Lunn
83450bf1f0SWenpeng Liang dev_dbg(&mdiodev->dev, "%s\n", __func__);
84a9049e0cSAndrew Lunn
85a9049e0cSAndrew Lunn err = mdiobus_register_device(mdiodev);
86a9049e0cSAndrew Lunn if (err)
87a9049e0cSAndrew Lunn return err;
88a9049e0cSAndrew Lunn
89a9049e0cSAndrew Lunn err = device_add(&mdiodev->dev);
90a9049e0cSAndrew Lunn if (err) {
91a9049e0cSAndrew Lunn pr_err("MDIO %d failed to add\n", mdiodev->addr);
92a9049e0cSAndrew Lunn goto out;
93a9049e0cSAndrew Lunn }
94a9049e0cSAndrew Lunn
95a9049e0cSAndrew Lunn return 0;
96a9049e0cSAndrew Lunn
97a9049e0cSAndrew Lunn out:
98a9049e0cSAndrew Lunn mdiobus_unregister_device(mdiodev);
99a9049e0cSAndrew Lunn return err;
100a9049e0cSAndrew Lunn }
101a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_register);
102a9049e0cSAndrew Lunn
103a9049e0cSAndrew Lunn /**
104a9049e0cSAndrew Lunn * mdio_device_remove - Remove a previously registered mdio device from the
105a9049e0cSAndrew Lunn * MDIO bus
106a9049e0cSAndrew Lunn * @mdiodev: mdio_device structure to remove
107a9049e0cSAndrew Lunn *
108a9049e0cSAndrew Lunn * This doesn't free the mdio_device itself, it merely reverses the effects
109a9049e0cSAndrew Lunn * of mdio_device_register(). Use mdio_device_free() to free the device
110a9049e0cSAndrew Lunn * after calling this function.
111a9049e0cSAndrew Lunn */
mdio_device_remove(struct mdio_device * mdiodev)112a9049e0cSAndrew Lunn void mdio_device_remove(struct mdio_device *mdiodev)
113a9049e0cSAndrew Lunn {
114a9049e0cSAndrew Lunn device_del(&mdiodev->dev);
115a9049e0cSAndrew Lunn mdiobus_unregister_device(mdiodev);
116a9049e0cSAndrew Lunn }
117a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_remove);
118a9049e0cSAndrew Lunn
mdio_device_reset(struct mdio_device * mdiodev,int value)119bafbdd52SSergei Shtylyov void mdio_device_reset(struct mdio_device *mdiodev, int value)
120bafbdd52SSergei Shtylyov {
1213a30ae6eSRichard Leitner unsigned int d;
1223a30ae6eSRichard Leitner
1236110ed2dSDavid Bauer if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)
1243a30ae6eSRichard Leitner return;
1253a30ae6eSRichard Leitner
126df16c1c5SAndrew Halaney if (mdiodev->reset_state == value)
127df16c1c5SAndrew Halaney return;
128df16c1c5SAndrew Halaney
1296110ed2dSDavid Bauer if (mdiodev->reset_gpio)
130ea977d19SAndrea Merello gpiod_set_value_cansleep(mdiodev->reset_gpio, value);
1313a30ae6eSRichard Leitner
13271dd6c0dSDavid Bauer if (mdiodev->reset_ctrl) {
13371dd6c0dSDavid Bauer if (value)
13471dd6c0dSDavid Bauer reset_control_assert(mdiodev->reset_ctrl);
13571dd6c0dSDavid Bauer else
13671dd6c0dSDavid Bauer reset_control_deassert(mdiodev->reset_ctrl);
13771dd6c0dSDavid Bauer }
13871dd6c0dSDavid Bauer
13904f629f7SRichard Leitner d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
1403a30ae6eSRichard Leitner if (d)
141e4d5efddSBruno Thomsen fsleep(d);
142df16c1c5SAndrew Halaney
143df16c1c5SAndrew Halaney mdiodev->reset_state = value;
144bafbdd52SSergei Shtylyov }
145bafbdd52SSergei Shtylyov EXPORT_SYMBOL(mdio_device_reset);
146bafbdd52SSergei Shtylyov
147a9049e0cSAndrew Lunn /**
148a9049e0cSAndrew Lunn * mdio_probe - probe an MDIO device
149a9049e0cSAndrew Lunn * @dev: device to probe
150a9049e0cSAndrew Lunn *
151a9049e0cSAndrew Lunn * Description: Take care of setting up the mdio_device structure
152a9049e0cSAndrew Lunn * and calling the driver to probe the device.
153a9049e0cSAndrew Lunn */
mdio_probe(struct device * dev)154a9049e0cSAndrew Lunn static int mdio_probe(struct device *dev)
155a9049e0cSAndrew Lunn {
156a9049e0cSAndrew Lunn struct mdio_device *mdiodev = to_mdio_device(dev);
157a9049e0cSAndrew Lunn struct device_driver *drv = mdiodev->dev.driver;
158a9049e0cSAndrew Lunn struct mdio_driver *mdiodrv = to_mdio_driver(drv);
159a9049e0cSAndrew Lunn int err = 0;
160a9049e0cSAndrew Lunn
161bafbdd52SSergei Shtylyov /* Deassert the reset signal */
162bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 0);
163bafbdd52SSergei Shtylyov
16496e26359SBartosz Golaszewski if (mdiodrv->probe) {
165a9049e0cSAndrew Lunn err = mdiodrv->probe(mdiodev);
166bafbdd52SSergei Shtylyov if (err) {
167bafbdd52SSergei Shtylyov /* Assert the reset signal */
168bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 1);
169bafbdd52SSergei Shtylyov }
170bafbdd52SSergei Shtylyov }
171a9049e0cSAndrew Lunn
172a9049e0cSAndrew Lunn return err;
173a9049e0cSAndrew Lunn }
174a9049e0cSAndrew Lunn
mdio_remove(struct device * dev)175a9049e0cSAndrew Lunn static int mdio_remove(struct device *dev)
176a9049e0cSAndrew Lunn {
177a9049e0cSAndrew Lunn struct mdio_device *mdiodev = to_mdio_device(dev);
178a9049e0cSAndrew Lunn struct device_driver *drv = mdiodev->dev.driver;
179a9049e0cSAndrew Lunn struct mdio_driver *mdiodrv = to_mdio_driver(drv);
180a9049e0cSAndrew Lunn
18196e26359SBartosz Golaszewski if (mdiodrv->remove)
182a9049e0cSAndrew Lunn mdiodrv->remove(mdiodev);
183a9049e0cSAndrew Lunn
184bafbdd52SSergei Shtylyov /* Assert the reset signal */
185bafbdd52SSergei Shtylyov mdio_device_reset(mdiodev, 1);
186bafbdd52SSergei Shtylyov
187a9049e0cSAndrew Lunn return 0;
188a9049e0cSAndrew Lunn }
189a9049e0cSAndrew Lunn
mdio_shutdown(struct device * dev)190cf957997SVladimir Oltean static void mdio_shutdown(struct device *dev)
191cf957997SVladimir Oltean {
192cf957997SVladimir Oltean struct mdio_device *mdiodev = to_mdio_device(dev);
193cf957997SVladimir Oltean struct device_driver *drv = mdiodev->dev.driver;
194cf957997SVladimir Oltean struct mdio_driver *mdiodrv = to_mdio_driver(drv);
195cf957997SVladimir Oltean
196cf957997SVladimir Oltean if (mdiodrv->shutdown)
197cf957997SVladimir Oltean mdiodrv->shutdown(mdiodev);
198cf957997SVladimir Oltean }
199cf957997SVladimir Oltean
200a9049e0cSAndrew Lunn /**
201a9049e0cSAndrew Lunn * mdio_driver_register - register an mdio_driver with the MDIO layer
20219c5a5feSAndrew Lunn * @drv: new mdio_driver to register
203a9049e0cSAndrew Lunn */
mdio_driver_register(struct mdio_driver * drv)204a9049e0cSAndrew Lunn int mdio_driver_register(struct mdio_driver *drv)
205a9049e0cSAndrew Lunn {
206a9049e0cSAndrew Lunn struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
207a9049e0cSAndrew Lunn int retval;
208a9049e0cSAndrew Lunn
209450bf1f0SWenpeng Liang pr_debug("%s: %s\n", __func__, mdiodrv->driver.name);
210a9049e0cSAndrew Lunn
211a9049e0cSAndrew Lunn mdiodrv->driver.bus = &mdio_bus_type;
212a9049e0cSAndrew Lunn mdiodrv->driver.probe = mdio_probe;
213a9049e0cSAndrew Lunn mdiodrv->driver.remove = mdio_remove;
214cf957997SVladimir Oltean mdiodrv->driver.shutdown = mdio_shutdown;
215a9049e0cSAndrew Lunn
216a9049e0cSAndrew Lunn retval = driver_register(&mdiodrv->driver);
217a9049e0cSAndrew Lunn if (retval) {
218a9049e0cSAndrew Lunn pr_err("%s: Error %d in registering driver\n",
219a9049e0cSAndrew Lunn mdiodrv->driver.name, retval);
220a9049e0cSAndrew Lunn
221a9049e0cSAndrew Lunn return retval;
222a9049e0cSAndrew Lunn }
223a9049e0cSAndrew Lunn
224a9049e0cSAndrew Lunn return 0;
225a9049e0cSAndrew Lunn }
226a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_register);
227a9049e0cSAndrew Lunn
mdio_driver_unregister(struct mdio_driver * drv)228a9049e0cSAndrew Lunn void mdio_driver_unregister(struct mdio_driver *drv)
229a9049e0cSAndrew Lunn {
230a9049e0cSAndrew Lunn struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
231a9049e0cSAndrew Lunn
232a9049e0cSAndrew Lunn driver_unregister(&mdiodrv->driver);
233a9049e0cSAndrew Lunn }
234a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_unregister);
235