xref: /linux/drivers/net/phy/mdio_device.c (revision eb2ca35f1814dad3ca547261eedfbbd0d65a0efc)
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 
37a9049e0cSAndrew Lunn struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
38a9049e0cSAndrew Lunn {
39a9049e0cSAndrew Lunn 	struct mdio_device *mdiodev;
40a9049e0cSAndrew Lunn 
41a9049e0cSAndrew Lunn 	/* We allocate the device, and initialize the default values */
42a9049e0cSAndrew Lunn 	mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
43a9049e0cSAndrew Lunn 	if (!mdiodev)
44a9049e0cSAndrew Lunn 		return ERR_PTR(-ENOMEM);
45a9049e0cSAndrew Lunn 
46a9049e0cSAndrew Lunn 	mdiodev->dev.release = mdio_device_release;
47a9049e0cSAndrew Lunn 	mdiodev->dev.parent = &bus->dev;
48a9049e0cSAndrew Lunn 	mdiodev->dev.bus = &mdio_bus_type;
49711fdba3SAndrew Lunn 	mdiodev->device_free = mdio_device_free;
50711fdba3SAndrew Lunn 	mdiodev->device_remove = mdio_device_remove;
51a9049e0cSAndrew Lunn 	mdiodev->bus = bus;
52a9049e0cSAndrew Lunn 	mdiodev->addr = addr;
53a9049e0cSAndrew Lunn 
54a9049e0cSAndrew Lunn 	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
55a9049e0cSAndrew Lunn 
56a9049e0cSAndrew Lunn 	device_initialize(&mdiodev->dev);
57a9049e0cSAndrew Lunn 
58a9049e0cSAndrew Lunn 	return mdiodev;
59a9049e0cSAndrew Lunn }
60a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_create);
61a9049e0cSAndrew Lunn 
62a9049e0cSAndrew Lunn /**
63a9049e0cSAndrew Lunn  * mdio_device_register - Register the mdio device on the MDIO bus
64a9049e0cSAndrew Lunn  * @mdiodev: mdio_device structure to be added to the MDIO bus
65a9049e0cSAndrew Lunn  */
66a9049e0cSAndrew Lunn int mdio_device_register(struct mdio_device *mdiodev)
67a9049e0cSAndrew Lunn {
68a9049e0cSAndrew Lunn 	int err;
69a9049e0cSAndrew Lunn 
70a9049e0cSAndrew Lunn 	dev_info(&mdiodev->dev, "mdio_device_register\n");
71a9049e0cSAndrew Lunn 
72a9049e0cSAndrew Lunn 	err = mdiobus_register_device(mdiodev);
73a9049e0cSAndrew Lunn 	if (err)
74a9049e0cSAndrew Lunn 		return err;
75a9049e0cSAndrew Lunn 
76a9049e0cSAndrew Lunn 	err = device_add(&mdiodev->dev);
77a9049e0cSAndrew Lunn 	if (err) {
78a9049e0cSAndrew Lunn 		pr_err("MDIO %d failed to add\n", mdiodev->addr);
79a9049e0cSAndrew Lunn 		goto out;
80a9049e0cSAndrew Lunn 	}
81a9049e0cSAndrew Lunn 
82a9049e0cSAndrew Lunn 	return 0;
83a9049e0cSAndrew Lunn 
84a9049e0cSAndrew Lunn  out:
85a9049e0cSAndrew Lunn 	mdiobus_unregister_device(mdiodev);
86a9049e0cSAndrew Lunn 	return err;
87a9049e0cSAndrew Lunn }
88a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_register);
89a9049e0cSAndrew Lunn 
90a9049e0cSAndrew Lunn /**
91a9049e0cSAndrew Lunn  * mdio_device_remove - Remove a previously registered mdio device from the
92a9049e0cSAndrew Lunn  *			MDIO bus
93a9049e0cSAndrew Lunn  * @mdiodev: mdio_device structure to remove
94a9049e0cSAndrew Lunn  *
95a9049e0cSAndrew Lunn  * This doesn't free the mdio_device itself, it merely reverses the effects
96a9049e0cSAndrew Lunn  * of mdio_device_register(). Use mdio_device_free() to free the device
97a9049e0cSAndrew Lunn  * after calling this function.
98a9049e0cSAndrew Lunn  */
99a9049e0cSAndrew Lunn void mdio_device_remove(struct mdio_device *mdiodev)
100a9049e0cSAndrew Lunn {
101a9049e0cSAndrew Lunn 	device_del(&mdiodev->dev);
102a9049e0cSAndrew Lunn 	mdiobus_unregister_device(mdiodev);
103a9049e0cSAndrew Lunn }
104a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_device_remove);
105a9049e0cSAndrew Lunn 
106a9049e0cSAndrew Lunn /**
107a9049e0cSAndrew Lunn  * mdio_probe - probe an MDIO device
108a9049e0cSAndrew Lunn  * @dev: device to probe
109a9049e0cSAndrew Lunn  *
110a9049e0cSAndrew Lunn  * Description: Take care of setting up the mdio_device structure
111a9049e0cSAndrew Lunn  * and calling the driver to probe the device.
112a9049e0cSAndrew Lunn  */
113a9049e0cSAndrew Lunn static int mdio_probe(struct device *dev)
114a9049e0cSAndrew Lunn {
115a9049e0cSAndrew Lunn 	struct mdio_device *mdiodev = to_mdio_device(dev);
116a9049e0cSAndrew Lunn 	struct device_driver *drv = mdiodev->dev.driver;
117a9049e0cSAndrew Lunn 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
118a9049e0cSAndrew Lunn 	int err = 0;
119a9049e0cSAndrew Lunn 
120a9049e0cSAndrew Lunn 	if (mdiodrv->probe)
121a9049e0cSAndrew Lunn 		err = mdiodrv->probe(mdiodev);
122a9049e0cSAndrew Lunn 
123a9049e0cSAndrew Lunn 	return err;
124a9049e0cSAndrew Lunn }
125a9049e0cSAndrew Lunn 
126a9049e0cSAndrew Lunn static int mdio_remove(struct device *dev)
127a9049e0cSAndrew Lunn {
128a9049e0cSAndrew Lunn 	struct mdio_device *mdiodev = to_mdio_device(dev);
129a9049e0cSAndrew Lunn 	struct device_driver *drv = mdiodev->dev.driver;
130a9049e0cSAndrew Lunn 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
131a9049e0cSAndrew Lunn 
132a9049e0cSAndrew Lunn 	if (mdiodrv->remove)
133a9049e0cSAndrew Lunn 		mdiodrv->remove(mdiodev);
134a9049e0cSAndrew Lunn 
135a9049e0cSAndrew Lunn 	return 0;
136a9049e0cSAndrew Lunn }
137a9049e0cSAndrew Lunn 
138a9049e0cSAndrew Lunn /**
139a9049e0cSAndrew Lunn  * mdio_driver_register - register an mdio_driver with the MDIO layer
140a9049e0cSAndrew Lunn  * @new_driver: new mdio_driver to register
141a9049e0cSAndrew Lunn  */
142a9049e0cSAndrew Lunn int mdio_driver_register(struct mdio_driver *drv)
143a9049e0cSAndrew Lunn {
144a9049e0cSAndrew Lunn 	struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
145a9049e0cSAndrew Lunn 	int retval;
146a9049e0cSAndrew Lunn 
147*eb2ca35fSFlorian Fainelli 	pr_debug("mdio_driver_register: %s\n", mdiodrv->driver.name);
148a9049e0cSAndrew Lunn 
149a9049e0cSAndrew Lunn 	mdiodrv->driver.bus = &mdio_bus_type;
150a9049e0cSAndrew Lunn 	mdiodrv->driver.probe = mdio_probe;
151a9049e0cSAndrew Lunn 	mdiodrv->driver.remove = mdio_remove;
152a9049e0cSAndrew Lunn 
153a9049e0cSAndrew Lunn 	retval = driver_register(&mdiodrv->driver);
154a9049e0cSAndrew Lunn 	if (retval) {
155a9049e0cSAndrew Lunn 		pr_err("%s: Error %d in registering driver\n",
156a9049e0cSAndrew Lunn 		       mdiodrv->driver.name, retval);
157a9049e0cSAndrew Lunn 
158a9049e0cSAndrew Lunn 		return retval;
159a9049e0cSAndrew Lunn 	}
160a9049e0cSAndrew Lunn 
161a9049e0cSAndrew Lunn 	return 0;
162a9049e0cSAndrew Lunn }
163a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_register);
164a9049e0cSAndrew Lunn 
165a9049e0cSAndrew Lunn void mdio_driver_unregister(struct mdio_driver *drv)
166a9049e0cSAndrew Lunn {
167a9049e0cSAndrew Lunn 	struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
168a9049e0cSAndrew Lunn 
169a9049e0cSAndrew Lunn 	driver_unregister(&mdiodrv->driver);
170a9049e0cSAndrew Lunn }
171a9049e0cSAndrew Lunn EXPORT_SYMBOL(mdio_driver_unregister);
172