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