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