1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Framework for MDIO devices, other than PHYs. |
3 | * |
4 | * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> |
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.h> |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/mdio.h> |
17 | #include <linux/mii.h> |
18 | #include <linux/module.h> |
19 | #include <linux/phy.h> |
20 | #include <linux/reset.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/string.h> |
23 | #include <linux/unistd.h> |
24 | #include <linux/property.h> |
25 | |
26 | void mdio_device_free(struct mdio_device *mdiodev) |
27 | { |
28 | put_device(dev: &mdiodev->dev); |
29 | } |
30 | EXPORT_SYMBOL(mdio_device_free); |
31 | |
32 | static void mdio_device_release(struct device *dev) |
33 | { |
34 | fwnode_handle_put(fwnode: dev->fwnode); |
35 | kfree(objp: to_mdio_device(dev)); |
36 | } |
37 | |
38 | int mdio_device_bus_match(struct device *dev, struct device_driver *drv) |
39 | { |
40 | struct mdio_device *mdiodev = to_mdio_device(dev); |
41 | struct mdio_driver *mdiodrv = to_mdio_driver(driver: drv); |
42 | |
43 | if (mdiodrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY) |
44 | return 0; |
45 | |
46 | return strcmp(mdiodev->modalias, drv->name) == 0; |
47 | } |
48 | |
49 | struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr) |
50 | { |
51 | struct mdio_device *mdiodev; |
52 | |
53 | /* We allocate the device, and initialize the default values */ |
54 | mdiodev = kzalloc(size: sizeof(*mdiodev), GFP_KERNEL); |
55 | if (!mdiodev) |
56 | return ERR_PTR(error: -ENOMEM); |
57 | |
58 | mdiodev->dev.release = mdio_device_release; |
59 | mdiodev->dev.parent = &bus->dev; |
60 | mdiodev->dev.bus = &mdio_bus_type; |
61 | mdiodev->device_free = mdio_device_free; |
62 | mdiodev->device_remove = mdio_device_remove; |
63 | mdiodev->bus = bus; |
64 | mdiodev->addr = addr; |
65 | |
66 | dev_set_name(dev: &mdiodev->dev, PHY_ID_FMT, bus->id, addr); |
67 | |
68 | device_initialize(dev: &mdiodev->dev); |
69 | |
70 | return mdiodev; |
71 | } |
72 | EXPORT_SYMBOL(mdio_device_create); |
73 | |
74 | /** |
75 | * mdio_device_register - Register the mdio device on the MDIO bus |
76 | * @mdiodev: mdio_device structure to be added to the MDIO bus |
77 | */ |
78 | int mdio_device_register(struct mdio_device *mdiodev) |
79 | { |
80 | int err; |
81 | |
82 | dev_dbg(&mdiodev->dev, "%s\n" , __func__); |
83 | |
84 | err = mdiobus_register_device(mdiodev); |
85 | if (err) |
86 | return err; |
87 | |
88 | err = device_add(dev: &mdiodev->dev); |
89 | if (err) { |
90 | pr_err("MDIO %d failed to add\n" , mdiodev->addr); |
91 | goto out; |
92 | } |
93 | |
94 | return 0; |
95 | |
96 | out: |
97 | mdiobus_unregister_device(mdiodev); |
98 | return err; |
99 | } |
100 | EXPORT_SYMBOL(mdio_device_register); |
101 | |
102 | /** |
103 | * mdio_device_remove - Remove a previously registered mdio device from the |
104 | * MDIO bus |
105 | * @mdiodev: mdio_device structure to remove |
106 | * |
107 | * This doesn't free the mdio_device itself, it merely reverses the effects |
108 | * of mdio_device_register(). Use mdio_device_free() to free the device |
109 | * after calling this function. |
110 | */ |
111 | void mdio_device_remove(struct mdio_device *mdiodev) |
112 | { |
113 | device_del(dev: &mdiodev->dev); |
114 | mdiobus_unregister_device(mdiodev); |
115 | } |
116 | EXPORT_SYMBOL(mdio_device_remove); |
117 | |
118 | void mdio_device_reset(struct mdio_device *mdiodev, int value) |
119 | { |
120 | unsigned int d; |
121 | |
122 | if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl) |
123 | return; |
124 | |
125 | if (mdiodev->reset_gpio) |
126 | gpiod_set_value_cansleep(desc: mdiodev->reset_gpio, value); |
127 | |
128 | if (mdiodev->reset_ctrl) { |
129 | if (value) |
130 | reset_control_assert(rstc: mdiodev->reset_ctrl); |
131 | else |
132 | reset_control_deassert(rstc: mdiodev->reset_ctrl); |
133 | } |
134 | |
135 | d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay; |
136 | if (d) |
137 | fsleep(usecs: d); |
138 | } |
139 | EXPORT_SYMBOL(mdio_device_reset); |
140 | |
141 | /** |
142 | * mdio_probe - probe an MDIO device |
143 | * @dev: device to probe |
144 | * |
145 | * Description: Take care of setting up the mdio_device structure |
146 | * and calling the driver to probe the device. |
147 | */ |
148 | static int mdio_probe(struct device *dev) |
149 | { |
150 | struct mdio_device *mdiodev = to_mdio_device(dev); |
151 | struct device_driver *drv = mdiodev->dev.driver; |
152 | struct mdio_driver *mdiodrv = to_mdio_driver(driver: drv); |
153 | int err = 0; |
154 | |
155 | /* Deassert the reset signal */ |
156 | mdio_device_reset(mdiodev, 0); |
157 | |
158 | if (mdiodrv->probe) { |
159 | err = mdiodrv->probe(mdiodev); |
160 | if (err) { |
161 | /* Assert the reset signal */ |
162 | mdio_device_reset(mdiodev, 1); |
163 | } |
164 | } |
165 | |
166 | return err; |
167 | } |
168 | |
169 | static int mdio_remove(struct device *dev) |
170 | { |
171 | struct mdio_device *mdiodev = to_mdio_device(dev); |
172 | struct device_driver *drv = mdiodev->dev.driver; |
173 | struct mdio_driver *mdiodrv = to_mdio_driver(driver: drv); |
174 | |
175 | if (mdiodrv->remove) |
176 | mdiodrv->remove(mdiodev); |
177 | |
178 | /* Assert the reset signal */ |
179 | mdio_device_reset(mdiodev, 1); |
180 | |
181 | return 0; |
182 | } |
183 | |
184 | static void mdio_shutdown(struct device *dev) |
185 | { |
186 | struct mdio_device *mdiodev = to_mdio_device(dev); |
187 | struct device_driver *drv = mdiodev->dev.driver; |
188 | struct mdio_driver *mdiodrv = to_mdio_driver(driver: drv); |
189 | |
190 | if (mdiodrv->shutdown) |
191 | mdiodrv->shutdown(mdiodev); |
192 | } |
193 | |
194 | /** |
195 | * mdio_driver_register - register an mdio_driver with the MDIO layer |
196 | * @drv: new mdio_driver to register |
197 | */ |
198 | int mdio_driver_register(struct mdio_driver *drv) |
199 | { |
200 | struct mdio_driver_common *mdiodrv = &drv->mdiodrv; |
201 | int retval; |
202 | |
203 | pr_debug("%s: %s\n" , __func__, mdiodrv->driver.name); |
204 | |
205 | mdiodrv->driver.bus = &mdio_bus_type; |
206 | mdiodrv->driver.probe = mdio_probe; |
207 | mdiodrv->driver.remove = mdio_remove; |
208 | mdiodrv->driver.shutdown = mdio_shutdown; |
209 | |
210 | retval = driver_register(drv: &mdiodrv->driver); |
211 | if (retval) { |
212 | pr_err("%s: Error %d in registering driver\n" , |
213 | mdiodrv->driver.name, retval); |
214 | |
215 | return retval; |
216 | } |
217 | |
218 | return 0; |
219 | } |
220 | EXPORT_SYMBOL(mdio_driver_register); |
221 | |
222 | void mdio_driver_unregister(struct mdio_driver *drv) |
223 | { |
224 | struct mdio_driver_common *mdiodrv = &drv->mdiodrv; |
225 | |
226 | driver_unregister(drv: &mdiodrv->driver); |
227 | } |
228 | EXPORT_SYMBOL(mdio_driver_unregister); |
229 | |