1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Greybus Bridged-Phy Bus driver |
4 | * |
5 | * Copyright 2014 Google Inc. |
6 | * Copyright 2014 Linaro Ltd. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/types.h> |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/device.h> |
16 | #include <linux/greybus.h> |
17 | |
18 | #include "gbphy.h" |
19 | |
20 | #define GB_GBPHY_AUTOSUSPEND_MS 3000 |
21 | |
22 | struct gbphy_host { |
23 | struct gb_bundle *bundle; |
24 | struct list_head devices; |
25 | }; |
26 | |
27 | static DEFINE_IDA(gbphy_id); |
28 | |
29 | static ssize_t protocol_id_show(struct device *dev, |
30 | struct device_attribute *attr, char *buf) |
31 | { |
32 | struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
33 | |
34 | return sprintf(buf, fmt: "0x%02x\n" , gbphy_dev->cport_desc->protocol_id); |
35 | } |
36 | static DEVICE_ATTR_RO(protocol_id); |
37 | |
38 | static struct attribute *gbphy_dev_attrs[] = { |
39 | &dev_attr_protocol_id.attr, |
40 | NULL, |
41 | }; |
42 | |
43 | ATTRIBUTE_GROUPS(gbphy_dev); |
44 | |
45 | static void gbphy_dev_release(struct device *dev) |
46 | { |
47 | struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
48 | |
49 | ida_free(&gbphy_id, id: gbphy_dev->id); |
50 | kfree(objp: gbphy_dev); |
51 | } |
52 | |
53 | #ifdef CONFIG_PM |
54 | static int gb_gbphy_idle(struct device *dev) |
55 | { |
56 | pm_runtime_mark_last_busy(dev); |
57 | pm_request_autosuspend(dev); |
58 | return 0; |
59 | } |
60 | #endif |
61 | |
62 | static const struct dev_pm_ops gb_gbphy_pm_ops = { |
63 | SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, |
64 | pm_generic_runtime_resume, |
65 | gb_gbphy_idle) |
66 | }; |
67 | |
68 | static const struct device_type greybus_gbphy_dev_type = { |
69 | .name = "gbphy_device" , |
70 | .release = gbphy_dev_release, |
71 | .pm = &gb_gbphy_pm_ops, |
72 | }; |
73 | |
74 | static int gbphy_dev_uevent(const struct device *dev, struct kobj_uevent_env *env) |
75 | { |
76 | const struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
77 | const struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc; |
78 | const struct gb_bundle *bundle = gbphy_dev->bundle; |
79 | const struct gb_interface *intf = bundle->intf; |
80 | const struct gb_module *module = intf->module; |
81 | const struct gb_host_device *hd = intf->hd; |
82 | |
83 | if (add_uevent_var(env, format: "BUS=%u" , hd->bus_id)) |
84 | return -ENOMEM; |
85 | if (add_uevent_var(env, format: "MODULE=%u" , module->module_id)) |
86 | return -ENOMEM; |
87 | if (add_uevent_var(env, format: "INTERFACE=%u" , intf->interface_id)) |
88 | return -ENOMEM; |
89 | if (add_uevent_var(env, format: "GREYBUS_ID=%08x/%08x" , |
90 | intf->vendor_id, intf->product_id)) |
91 | return -ENOMEM; |
92 | if (add_uevent_var(env, format: "BUNDLE=%u" , gbphy_dev->bundle->id)) |
93 | return -ENOMEM; |
94 | if (add_uevent_var(env, format: "BUNDLE_CLASS=%02x" , bundle->class)) |
95 | return -ENOMEM; |
96 | if (add_uevent_var(env, format: "GBPHY=%u" , gbphy_dev->id)) |
97 | return -ENOMEM; |
98 | if (add_uevent_var(env, format: "PROTOCOL_ID=%02x" , cport_desc->protocol_id)) |
99 | return -ENOMEM; |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static const struct gbphy_device_id * |
105 | gbphy_dev_match_id(struct gbphy_device *gbphy_dev, |
106 | struct gbphy_driver *gbphy_drv) |
107 | { |
108 | const struct gbphy_device_id *id = gbphy_drv->id_table; |
109 | |
110 | if (!id) |
111 | return NULL; |
112 | |
113 | for (; id->protocol_id; id++) |
114 | if (id->protocol_id == gbphy_dev->cport_desc->protocol_id) |
115 | return id; |
116 | |
117 | return NULL; |
118 | } |
119 | |
120 | static int gbphy_dev_match(struct device *dev, struct device_driver *drv) |
121 | { |
122 | struct gbphy_driver *gbphy_drv = to_gbphy_driver(drv); |
123 | struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
124 | const struct gbphy_device_id *id; |
125 | |
126 | id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); |
127 | if (id) |
128 | return 1; |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static int gbphy_dev_probe(struct device *dev) |
134 | { |
135 | struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); |
136 | struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
137 | const struct gbphy_device_id *id; |
138 | int ret; |
139 | |
140 | id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); |
141 | if (!id) |
142 | return -ENODEV; |
143 | |
144 | /* for old kernels we need get_sync to resume parent devices */ |
145 | ret = gb_pm_runtime_get_sync(bundle: gbphy_dev->bundle); |
146 | if (ret < 0) |
147 | return ret; |
148 | |
149 | pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS); |
150 | pm_runtime_use_autosuspend(dev); |
151 | pm_runtime_get_noresume(dev); |
152 | pm_runtime_set_active(dev); |
153 | pm_runtime_enable(dev); |
154 | |
155 | /* |
156 | * Drivers should call put on the gbphy dev before returning |
157 | * from probe if they support runtime pm. |
158 | */ |
159 | ret = gbphy_drv->probe(gbphy_dev, id); |
160 | if (ret) { |
161 | pm_runtime_disable(dev); |
162 | pm_runtime_set_suspended(dev); |
163 | pm_runtime_put_noidle(dev); |
164 | pm_runtime_dont_use_autosuspend(dev); |
165 | } |
166 | |
167 | gb_pm_runtime_put_autosuspend(bundle: gbphy_dev->bundle); |
168 | |
169 | return ret; |
170 | } |
171 | |
172 | static void gbphy_dev_remove(struct device *dev) |
173 | { |
174 | struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); |
175 | struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); |
176 | |
177 | gbphy_drv->remove(gbphy_dev); |
178 | |
179 | pm_runtime_disable(dev); |
180 | pm_runtime_set_suspended(dev); |
181 | pm_runtime_put_noidle(dev); |
182 | pm_runtime_dont_use_autosuspend(dev); |
183 | } |
184 | |
185 | static const struct bus_type gbphy_bus_type = { |
186 | .name = "gbphy" , |
187 | .match = gbphy_dev_match, |
188 | .probe = gbphy_dev_probe, |
189 | .remove = gbphy_dev_remove, |
190 | .uevent = gbphy_dev_uevent, |
191 | }; |
192 | |
193 | int gb_gbphy_register_driver(struct gbphy_driver *driver, |
194 | struct module *owner, const char *mod_name) |
195 | { |
196 | int retval; |
197 | |
198 | if (greybus_disabled()) |
199 | return -ENODEV; |
200 | |
201 | driver->driver.bus = &gbphy_bus_type; |
202 | driver->driver.name = driver->name; |
203 | driver->driver.owner = owner; |
204 | driver->driver.mod_name = mod_name; |
205 | |
206 | retval = driver_register(drv: &driver->driver); |
207 | if (retval) |
208 | return retval; |
209 | |
210 | pr_info("registered new driver %s\n" , driver->name); |
211 | return 0; |
212 | } |
213 | EXPORT_SYMBOL_GPL(gb_gbphy_register_driver); |
214 | |
215 | void gb_gbphy_deregister_driver(struct gbphy_driver *driver) |
216 | { |
217 | driver_unregister(drv: &driver->driver); |
218 | } |
219 | EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver); |
220 | |
221 | static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle, |
222 | struct greybus_descriptor_cport *cport_desc) |
223 | { |
224 | struct gbphy_device *gbphy_dev; |
225 | int retval; |
226 | int id; |
227 | |
228 | id = ida_alloc_min(ida: &gbphy_id, min: 1, GFP_KERNEL); |
229 | if (id < 0) |
230 | return ERR_PTR(error: id); |
231 | |
232 | gbphy_dev = kzalloc(size: sizeof(*gbphy_dev), GFP_KERNEL); |
233 | if (!gbphy_dev) { |
234 | ida_free(&gbphy_id, id); |
235 | return ERR_PTR(error: -ENOMEM); |
236 | } |
237 | |
238 | gbphy_dev->id = id; |
239 | gbphy_dev->bundle = bundle; |
240 | gbphy_dev->cport_desc = cport_desc; |
241 | gbphy_dev->dev.parent = &bundle->dev; |
242 | gbphy_dev->dev.bus = &gbphy_bus_type; |
243 | gbphy_dev->dev.type = &greybus_gbphy_dev_type; |
244 | gbphy_dev->dev.groups = gbphy_dev_groups; |
245 | gbphy_dev->dev.dma_mask = bundle->dev.dma_mask; |
246 | dev_set_name(dev: &gbphy_dev->dev, name: "gbphy%d" , id); |
247 | |
248 | retval = device_register(dev: &gbphy_dev->dev); |
249 | if (retval) { |
250 | put_device(dev: &gbphy_dev->dev); |
251 | return ERR_PTR(error: retval); |
252 | } |
253 | |
254 | return gbphy_dev; |
255 | } |
256 | |
257 | static void gb_gbphy_disconnect(struct gb_bundle *bundle) |
258 | { |
259 | struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); |
260 | struct gbphy_device *gbphy_dev, *temp; |
261 | int ret; |
262 | |
263 | ret = gb_pm_runtime_get_sync(bundle); |
264 | if (ret < 0) |
265 | gb_pm_runtime_get_noresume(bundle); |
266 | |
267 | list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { |
268 | list_del(entry: &gbphy_dev->list); |
269 | device_unregister(dev: &gbphy_dev->dev); |
270 | } |
271 | |
272 | kfree(objp: gbphy_host); |
273 | } |
274 | |
275 | static int gb_gbphy_probe(struct gb_bundle *bundle, |
276 | const struct greybus_bundle_id *id) |
277 | { |
278 | struct gbphy_host *gbphy_host; |
279 | struct gbphy_device *gbphy_dev; |
280 | int i; |
281 | |
282 | if (bundle->num_cports == 0) |
283 | return -ENODEV; |
284 | |
285 | gbphy_host = kzalloc(size: sizeof(*gbphy_host), GFP_KERNEL); |
286 | if (!gbphy_host) |
287 | return -ENOMEM; |
288 | |
289 | gbphy_host->bundle = bundle; |
290 | INIT_LIST_HEAD(list: &gbphy_host->devices); |
291 | greybus_set_drvdata(bundle, data: gbphy_host); |
292 | |
293 | /* |
294 | * Create a bunch of children devices, one per cport, and bind the |
295 | * bridged phy drivers to them. |
296 | */ |
297 | for (i = 0; i < bundle->num_cports; ++i) { |
298 | gbphy_dev = gb_gbphy_create_dev(bundle, cport_desc: &bundle->cport_desc[i]); |
299 | if (IS_ERR(ptr: gbphy_dev)) { |
300 | gb_gbphy_disconnect(bundle); |
301 | return PTR_ERR(ptr: gbphy_dev); |
302 | } |
303 | list_add(new: &gbphy_dev->list, head: &gbphy_host->devices); |
304 | } |
305 | |
306 | gb_pm_runtime_put_autosuspend(bundle); |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static const struct greybus_bundle_id gb_gbphy_id_table[] = { |
312 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, |
313 | { }, |
314 | }; |
315 | MODULE_DEVICE_TABLE(greybus, gb_gbphy_id_table); |
316 | |
317 | static struct greybus_driver gb_gbphy_driver = { |
318 | .name = "gbphy" , |
319 | .probe = gb_gbphy_probe, |
320 | .disconnect = gb_gbphy_disconnect, |
321 | .id_table = gb_gbphy_id_table, |
322 | }; |
323 | |
324 | static int __init gbphy_init(void) |
325 | { |
326 | int retval; |
327 | |
328 | retval = bus_register(bus: &gbphy_bus_type); |
329 | if (retval) { |
330 | pr_err("gbphy bus register failed (%d)\n" , retval); |
331 | return retval; |
332 | } |
333 | |
334 | retval = greybus_register(&gb_gbphy_driver); |
335 | if (retval) { |
336 | pr_err("error registering greybus driver\n" ); |
337 | goto error_gbphy; |
338 | } |
339 | |
340 | return 0; |
341 | |
342 | error_gbphy: |
343 | bus_unregister(bus: &gbphy_bus_type); |
344 | ida_destroy(ida: &gbphy_id); |
345 | return retval; |
346 | } |
347 | module_init(gbphy_init); |
348 | |
349 | static void __exit gbphy_exit(void) |
350 | { |
351 | greybus_deregister(&gb_gbphy_driver); |
352 | bus_unregister(bus: &gbphy_bus_type); |
353 | ida_destroy(ida: &gbphy_id); |
354 | } |
355 | module_exit(gbphy_exit); |
356 | |
357 | MODULE_LICENSE("GPL v2" ); |
358 | |