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