xref: /openbmc/linux/drivers/staging/greybus/gbphy.c (revision e54b106d)
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 
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 
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 
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 
53e54b106dSSandeep Patil static struct device_type greybus_gbphy_dev_type = {
54e54b106dSSandeep Patil 	.name	 =	"gbphy_device",
55e54b106dSSandeep Patil 	.release =	gbphy_dev_release,
56e54b106dSSandeep Patil };
57e54b106dSSandeep Patil 
58e54b106dSSandeep Patil static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
59e54b106dSSandeep Patil {
60e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
61e54b106dSSandeep Patil 	struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc;
62e54b106dSSandeep Patil 	struct gb_bundle *bundle = gbphy_dev->bundle;
63e54b106dSSandeep Patil 	struct gb_interface *intf = bundle->intf;
64e54b106dSSandeep Patil 	struct gb_module *module = intf->module;
65e54b106dSSandeep Patil 	struct gb_host_device *hd = intf->hd;
66e54b106dSSandeep Patil 
67e54b106dSSandeep Patil 	if (add_uevent_var(env, "BUS=%u", hd->bus_id))
68e54b106dSSandeep Patil 		return -ENOMEM;
69e54b106dSSandeep Patil 	if (add_uevent_var(env, "MODULE=%u", module->module_id))
70e54b106dSSandeep Patil 		return -ENOMEM;
71e54b106dSSandeep Patil 	if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
72e54b106dSSandeep Patil 		return -ENOMEM;
73e54b106dSSandeep Patil 	if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x",
74e54b106dSSandeep Patil 			   intf->vendor_id, intf->product_id))
75e54b106dSSandeep Patil 		return -ENOMEM;
76e54b106dSSandeep Patil 	if (add_uevent_var(env, "BUNDLE=%u", gbphy_dev->bundle->id))
77e54b106dSSandeep Patil 		return -ENOMEM;
78e54b106dSSandeep Patil 	if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
79e54b106dSSandeep Patil 		return -ENOMEM;
80e54b106dSSandeep Patil 	if (add_uevent_var(env, "GBPHY=%u", gbphy_dev->id))
81e54b106dSSandeep Patil 		return -ENOMEM;
82e54b106dSSandeep Patil 	if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id))
83e54b106dSSandeep Patil 		return -ENOMEM;
84e54b106dSSandeep Patil 
85e54b106dSSandeep Patil 	return 0;
86e54b106dSSandeep Patil }
87e54b106dSSandeep Patil 
88e54b106dSSandeep Patil static const struct gbphy_device_id *
89e54b106dSSandeep Patil gbphy_dev_match_id(struct gbphy_device *gbphy_dev, struct gbphy_driver *gbphy_drv)
90e54b106dSSandeep Patil {
91e54b106dSSandeep Patil 	const struct gbphy_device_id *id = gbphy_drv->id_table;
92e54b106dSSandeep Patil 
93e54b106dSSandeep Patil 	if (!id)
94e54b106dSSandeep Patil 		return NULL;
95e54b106dSSandeep Patil 
96e54b106dSSandeep Patil 	for (; id->protocol_id; id++)
97e54b106dSSandeep Patil 		if (id->protocol_id == gbphy_dev->cport_desc->protocol_id)
98e54b106dSSandeep Patil 			return id;
99e54b106dSSandeep Patil 
100e54b106dSSandeep Patil 	return NULL;
101e54b106dSSandeep Patil }
102e54b106dSSandeep Patil 
103e54b106dSSandeep Patil static int gbphy_dev_match(struct device *dev, struct device_driver *drv)
104e54b106dSSandeep Patil {
105e54b106dSSandeep Patil 	struct gbphy_driver *gbphy_drv = to_gbphy_driver(drv);
106e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
107e54b106dSSandeep Patil 	const struct gbphy_device_id *id;
108e54b106dSSandeep Patil 
109e54b106dSSandeep Patil 	id = gbphy_dev_match_id(gbphy_dev, gbphy_drv);
110e54b106dSSandeep Patil 	if (id)
111e54b106dSSandeep Patil 		return 1;
112e54b106dSSandeep Patil 
113e54b106dSSandeep Patil 	return 0;
114e54b106dSSandeep Patil }
115e54b106dSSandeep Patil 
116e54b106dSSandeep Patil static int gbphy_dev_probe(struct device *dev)
117e54b106dSSandeep Patil {
118e54b106dSSandeep Patil 	struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver);
119e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
120e54b106dSSandeep Patil 	const struct gbphy_device_id *id;
121e54b106dSSandeep Patil 
122e54b106dSSandeep Patil 	id = gbphy_dev_match_id(gbphy_dev, gbphy_drv);
123e54b106dSSandeep Patil 	if (!id)
124e54b106dSSandeep Patil 		return -ENODEV;
125e54b106dSSandeep Patil 
126e54b106dSSandeep Patil 	return gbphy_drv->probe(gbphy_dev, id);
127e54b106dSSandeep Patil }
128e54b106dSSandeep Patil 
129e54b106dSSandeep Patil static int gbphy_dev_remove(struct device *dev)
130e54b106dSSandeep Patil {
131e54b106dSSandeep Patil 	struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver);
132e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev = to_gbphy_dev(dev);
133e54b106dSSandeep Patil 
134e54b106dSSandeep Patil 	gbphy_drv->remove(gbphy_dev);
135e54b106dSSandeep Patil 	return 0;
136e54b106dSSandeep Patil }
137e54b106dSSandeep Patil 
138e54b106dSSandeep Patil static struct bus_type gbphy_bus_type = {
139e54b106dSSandeep Patil 	.name =		"gbphy",
140e54b106dSSandeep Patil 	.match =	gbphy_dev_match,
141e54b106dSSandeep Patil 	.probe =	gbphy_dev_probe,
142e54b106dSSandeep Patil 	.remove =	gbphy_dev_remove,
143e54b106dSSandeep Patil 	.uevent =	gbphy_dev_uevent,
144e54b106dSSandeep Patil };
145e54b106dSSandeep Patil 
146e54b106dSSandeep Patil int gb_gbphy_register_driver(struct gbphy_driver *driver,
147e54b106dSSandeep Patil 			     struct module *owner, const char *mod_name)
148e54b106dSSandeep Patil {
149e54b106dSSandeep Patil 	int retval;
150e54b106dSSandeep Patil 
151e54b106dSSandeep Patil 	if (greybus_disabled())
152e54b106dSSandeep Patil 		return -ENODEV;
153e54b106dSSandeep Patil 
154e54b106dSSandeep Patil 	driver->driver.bus = &gbphy_bus_type;
155e54b106dSSandeep Patil 	driver->driver.name = driver->name;
156e54b106dSSandeep Patil 	driver->driver.owner = owner;
157e54b106dSSandeep Patil 	driver->driver.mod_name = mod_name;
158e54b106dSSandeep Patil 
159e54b106dSSandeep Patil 	retval = driver_register(&driver->driver);
160e54b106dSSandeep Patil 	if (retval)
161e54b106dSSandeep Patil 		return retval;
162e54b106dSSandeep Patil 
163e54b106dSSandeep Patil 	pr_info("registered new driver %s\n", driver->name);
164e54b106dSSandeep Patil 	return 0;
165e54b106dSSandeep Patil }
166e54b106dSSandeep Patil EXPORT_SYMBOL_GPL(gb_gbphy_register_driver);
167e54b106dSSandeep Patil 
168e54b106dSSandeep Patil void gb_gbphy_deregister_driver(struct gbphy_driver *driver)
169e54b106dSSandeep Patil {
170e54b106dSSandeep Patil 	driver_unregister(&driver->driver);
171e54b106dSSandeep Patil }
172e54b106dSSandeep Patil EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver);
173e54b106dSSandeep Patil 
174e54b106dSSandeep Patil int gb_gbphy_get_version(struct gb_connection *connection)
175e54b106dSSandeep Patil {
176e54b106dSSandeep Patil 	struct gb_protocol_version_request request;
177e54b106dSSandeep Patil 	struct gb_protocol_version_response response;
178e54b106dSSandeep Patil 	int retval;
179e54b106dSSandeep Patil 
180e54b106dSSandeep Patil 	request.major = 1;
181e54b106dSSandeep Patil 	request.minor = 0;
182e54b106dSSandeep Patil 
183e54b106dSSandeep Patil 	retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION,
184e54b106dSSandeep Patil 				   &request, sizeof(request), &response,
185e54b106dSSandeep Patil 				   sizeof(response));
186e54b106dSSandeep Patil 	if (retval)
187e54b106dSSandeep Patil 		return retval;
188e54b106dSSandeep Patil 
189e54b106dSSandeep Patil 	/* FIXME - do proper version negotiation here someday... */
190e54b106dSSandeep Patil 
191e54b106dSSandeep Patil 	connection->module_major = response.major;
192e54b106dSSandeep Patil 	connection->module_minor = response.minor;
193e54b106dSSandeep Patil 
194e54b106dSSandeep Patil 	dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name,
195e54b106dSSandeep Patil 		response.major, response.minor);
196e54b106dSSandeep Patil 
197e54b106dSSandeep Patil 	return 0;
198e54b106dSSandeep Patil }
199e54b106dSSandeep Patil EXPORT_SYMBOL_GPL(gb_gbphy_get_version);
200e54b106dSSandeep Patil 
201e54b106dSSandeep Patil static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle,
202e54b106dSSandeep Patil 				struct greybus_descriptor_cport *cport_desc)
203e54b106dSSandeep Patil {
204e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev;
205e54b106dSSandeep Patil 	int retval;
206e54b106dSSandeep Patil 	int id;
207e54b106dSSandeep Patil 
208e54b106dSSandeep Patil 	id = ida_simple_get(&gbphy_id, 1, 0, GFP_KERNEL);
209e54b106dSSandeep Patil 	if (id < 0)
210e54b106dSSandeep Patil 		return ERR_PTR(id);
211e54b106dSSandeep Patil 
212e54b106dSSandeep Patil 	gbphy_dev = kzalloc(sizeof(*gbphy_dev), GFP_KERNEL);
213e54b106dSSandeep Patil 	if (!gbphy_dev) {
214e54b106dSSandeep Patil 		ida_simple_remove(&gbphy_id, id);
215e54b106dSSandeep Patil 		return ERR_PTR(-ENOMEM);
216e54b106dSSandeep Patil 	}
217e54b106dSSandeep Patil 
218e54b106dSSandeep Patil 	gbphy_dev->id = id;
219e54b106dSSandeep Patil 	gbphy_dev->bundle = bundle;
220e54b106dSSandeep Patil 	gbphy_dev->cport_desc = cport_desc;
221e54b106dSSandeep Patil 	gbphy_dev->dev.parent = &bundle->dev;
222e54b106dSSandeep Patil 	gbphy_dev->dev.bus = &gbphy_bus_type;
223e54b106dSSandeep Patil 	gbphy_dev->dev.type = &greybus_gbphy_dev_type;
224e54b106dSSandeep Patil 	gbphy_dev->dev.groups = gbphy_dev_groups;
225e54b106dSSandeep Patil 	gbphy_dev->dev.dma_mask = bundle->dev.dma_mask;
226e54b106dSSandeep Patil 	dev_set_name(&gbphy_dev->dev, "gbphy%d", id);
227e54b106dSSandeep Patil 
228e54b106dSSandeep Patil 	retval = device_register(&gbphy_dev->dev);
229e54b106dSSandeep Patil 	if (retval) {
230e54b106dSSandeep Patil 		put_device(&gbphy_dev->dev);
231e54b106dSSandeep Patil 		return ERR_PTR(retval);
232e54b106dSSandeep Patil 	}
233e54b106dSSandeep Patil 
234e54b106dSSandeep Patil 	return gbphy_dev;
235e54b106dSSandeep Patil }
236e54b106dSSandeep Patil 
237e54b106dSSandeep Patil static void gb_gbphy_disconnect(struct gb_bundle *bundle)
238e54b106dSSandeep Patil {
239e54b106dSSandeep Patil 	struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle);
240e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev, *temp;
241e54b106dSSandeep Patil 
242e54b106dSSandeep Patil 	list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) {
243e54b106dSSandeep Patil 		list_del(&gbphy_dev->list);
244e54b106dSSandeep Patil 		device_unregister(&gbphy_dev->dev);
245e54b106dSSandeep Patil 	}
246e54b106dSSandeep Patil 
247e54b106dSSandeep Patil 	kfree(gbphy_host);
248e54b106dSSandeep Patil }
249e54b106dSSandeep Patil 
250e54b106dSSandeep Patil static int gb_gbphy_probe(struct gb_bundle *bundle,
251e54b106dSSandeep Patil 			  const struct greybus_bundle_id *id)
252e54b106dSSandeep Patil {
253e54b106dSSandeep Patil 	struct gbphy_host *gbphy_host;
254e54b106dSSandeep Patil 	struct gbphy_device *gbphy_dev;
255e54b106dSSandeep Patil 	int i;
256e54b106dSSandeep Patil 
257e54b106dSSandeep Patil 	if (bundle->num_cports == 0)
258e54b106dSSandeep Patil 		return -ENODEV;
259e54b106dSSandeep Patil 
260e54b106dSSandeep Patil 	gbphy_host = kzalloc(sizeof(*gbphy_host), GFP_KERNEL);
261e54b106dSSandeep Patil 	if (!gbphy_host)
262e54b106dSSandeep Patil 		return -ENOMEM;
263e54b106dSSandeep Patil 
264e54b106dSSandeep Patil 	gbphy_host->bundle = bundle;
265e54b106dSSandeep Patil 	INIT_LIST_HEAD(&gbphy_host->devices);
266e54b106dSSandeep Patil 	greybus_set_drvdata(bundle, gbphy_host);
267e54b106dSSandeep Patil 
268e54b106dSSandeep Patil 	/*
269e54b106dSSandeep Patil 	 * Create a bunch of children devices, one per cport, and bind the
270e54b106dSSandeep Patil 	 * bridged phy drivers to them.
271e54b106dSSandeep Patil 	 */
272e54b106dSSandeep Patil 	for (i = 0; i < bundle->num_cports; ++i) {
273e54b106dSSandeep Patil 		gbphy_dev = gb_gbphy_create_dev(bundle, &bundle->cport_desc[i]);
274e54b106dSSandeep Patil 		if (IS_ERR(gbphy_dev)) {
275e54b106dSSandeep Patil 			gb_gbphy_disconnect(bundle);
276e54b106dSSandeep Patil 			return PTR_ERR(gbphy_dev);
277e54b106dSSandeep Patil 		}
278e54b106dSSandeep Patil 		list_add(&gbphy_dev->list, &gbphy_host->devices);
279e54b106dSSandeep Patil 	}
280e54b106dSSandeep Patil 
281e54b106dSSandeep Patil 	return 0;
282e54b106dSSandeep Patil }
283e54b106dSSandeep Patil 
284e54b106dSSandeep Patil static const struct greybus_bundle_id gb_gbphy_id_table[] = {
285e54b106dSSandeep Patil 	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) },
286e54b106dSSandeep Patil 	{ },
287e54b106dSSandeep Patil };
288e54b106dSSandeep Patil MODULE_DEVICE_TABLE(greybus, gb_gbphy_id_table);
289e54b106dSSandeep Patil 
290e54b106dSSandeep Patil static struct greybus_driver gb_gbphy_driver = {
291e54b106dSSandeep Patil 	.name		= "gbphy",
292e54b106dSSandeep Patil 	.probe		= gb_gbphy_probe,
293e54b106dSSandeep Patil 	.disconnect	= gb_gbphy_disconnect,
294e54b106dSSandeep Patil 	.id_table	= gb_gbphy_id_table,
295e54b106dSSandeep Patil };
296e54b106dSSandeep Patil 
297e54b106dSSandeep Patil static int __init gbphy_init(void)
298e54b106dSSandeep Patil {
299e54b106dSSandeep Patil 	int retval;
300e54b106dSSandeep Patil 
301e54b106dSSandeep Patil 	retval = bus_register(&gbphy_bus_type);
302e54b106dSSandeep Patil 	if (retval) {
303e54b106dSSandeep Patil 		pr_err("gbphy bus register failed (%d)\n", retval);
304e54b106dSSandeep Patil 		return retval;
305e54b106dSSandeep Patil 	}
306e54b106dSSandeep Patil 
307e54b106dSSandeep Patil 	retval = greybus_register(&gb_gbphy_driver);
308e54b106dSSandeep Patil 	if (retval) {
309e54b106dSSandeep Patil 		pr_err("error registering greybus driver\n");
310e54b106dSSandeep Patil 		goto error_gbphy;
311e54b106dSSandeep Patil 	}
312e54b106dSSandeep Patil 
313e54b106dSSandeep Patil 	return 0;
314e54b106dSSandeep Patil 
315e54b106dSSandeep Patil error_gbphy:
316e54b106dSSandeep Patil 	bus_unregister(&gbphy_bus_type);
317e54b106dSSandeep Patil 	ida_destroy(&gbphy_id);
318e54b106dSSandeep Patil 	return retval;
319e54b106dSSandeep Patil }
320e54b106dSSandeep Patil module_init(gbphy_init);
321e54b106dSSandeep Patil 
322e54b106dSSandeep Patil static void __exit gbphy_exit(void)
323e54b106dSSandeep Patil {
324e54b106dSSandeep Patil 	greybus_deregister(&gb_gbphy_driver);
325e54b106dSSandeep Patil 	bus_unregister(&gbphy_bus_type);
326e54b106dSSandeep Patil 	ida_destroy(&gbphy_id);
327e54b106dSSandeep Patil }
328e54b106dSSandeep Patil module_exit(gbphy_exit);
329e54b106dSSandeep Patil 
330e54b106dSSandeep Patil MODULE_LICENSE("GPL v2");
331