xref: /openbmc/linux/drivers/greybus/hd.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
1*8465def4SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2*8465def4SGreg Kroah-Hartman /*
3*8465def4SGreg Kroah-Hartman  * Greybus Host Device
4*8465def4SGreg Kroah-Hartman  *
5*8465def4SGreg Kroah-Hartman  * Copyright 2014-2015 Google Inc.
6*8465def4SGreg Kroah-Hartman  * Copyright 2014-2015 Linaro Ltd.
7*8465def4SGreg Kroah-Hartman  */
8*8465def4SGreg Kroah-Hartman 
9*8465def4SGreg Kroah-Hartman #include <linux/kernel.h>
10*8465def4SGreg Kroah-Hartman #include <linux/slab.h>
11*8465def4SGreg Kroah-Hartman #include <linux/greybus.h>
12*8465def4SGreg Kroah-Hartman 
13*8465def4SGreg Kroah-Hartman #include "greybus_trace.h"
14*8465def4SGreg Kroah-Hartman 
15*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create);
16*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release);
17*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add);
18*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del);
19*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in);
20*8465def4SGreg Kroah-Hartman EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit);
21*8465def4SGreg Kroah-Hartman 
22*8465def4SGreg Kroah-Hartman static struct ida gb_hd_bus_id_map;
23*8465def4SGreg Kroah-Hartman 
gb_hd_output(struct gb_host_device * hd,void * req,u16 size,u8 cmd,bool async)24*8465def4SGreg Kroah-Hartman int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
25*8465def4SGreg Kroah-Hartman 		 bool async)
26*8465def4SGreg Kroah-Hartman {
27*8465def4SGreg Kroah-Hartman 	if (!hd || !hd->driver || !hd->driver->output)
28*8465def4SGreg Kroah-Hartman 		return -EINVAL;
29*8465def4SGreg Kroah-Hartman 	return hd->driver->output(hd, req, size, cmd, async);
30*8465def4SGreg Kroah-Hartman }
31*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_output);
32*8465def4SGreg Kroah-Hartman 
bus_id_show(struct device * dev,struct device_attribute * attr,char * buf)33*8465def4SGreg Kroah-Hartman static ssize_t bus_id_show(struct device *dev,
34*8465def4SGreg Kroah-Hartman 			   struct device_attribute *attr, char *buf)
35*8465def4SGreg Kroah-Hartman {
36*8465def4SGreg Kroah-Hartman 	struct gb_host_device *hd = to_gb_host_device(dev);
37*8465def4SGreg Kroah-Hartman 
38*8465def4SGreg Kroah-Hartman 	return sprintf(buf, "%d\n", hd->bus_id);
39*8465def4SGreg Kroah-Hartman }
40*8465def4SGreg Kroah-Hartman static DEVICE_ATTR_RO(bus_id);
41*8465def4SGreg Kroah-Hartman 
42*8465def4SGreg Kroah-Hartman static struct attribute *bus_attrs[] = {
43*8465def4SGreg Kroah-Hartman 	&dev_attr_bus_id.attr,
44*8465def4SGreg Kroah-Hartman 	NULL
45*8465def4SGreg Kroah-Hartman };
46*8465def4SGreg Kroah-Hartman ATTRIBUTE_GROUPS(bus);
47*8465def4SGreg Kroah-Hartman 
gb_hd_cport_reserve(struct gb_host_device * hd,u16 cport_id)48*8465def4SGreg Kroah-Hartman int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
49*8465def4SGreg Kroah-Hartman {
50*8465def4SGreg Kroah-Hartman 	struct ida *id_map = &hd->cport_id_map;
51*8465def4SGreg Kroah-Hartman 	int ret;
52*8465def4SGreg Kroah-Hartman 
53*8465def4SGreg Kroah-Hartman 	ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
54*8465def4SGreg Kroah-Hartman 	if (ret < 0) {
55*8465def4SGreg Kroah-Hartman 		dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
56*8465def4SGreg Kroah-Hartman 		return ret;
57*8465def4SGreg Kroah-Hartman 	}
58*8465def4SGreg Kroah-Hartman 
59*8465def4SGreg Kroah-Hartman 	return 0;
60*8465def4SGreg Kroah-Hartman }
61*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_cport_reserve);
62*8465def4SGreg Kroah-Hartman 
gb_hd_cport_release_reserved(struct gb_host_device * hd,u16 cport_id)63*8465def4SGreg Kroah-Hartman void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id)
64*8465def4SGreg Kroah-Hartman {
65*8465def4SGreg Kroah-Hartman 	struct ida *id_map = &hd->cport_id_map;
66*8465def4SGreg Kroah-Hartman 
67*8465def4SGreg Kroah-Hartman 	ida_simple_remove(id_map, cport_id);
68*8465def4SGreg Kroah-Hartman }
69*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
70*8465def4SGreg Kroah-Hartman 
71*8465def4SGreg Kroah-Hartman /* Locking: Caller guarantees serialisation */
gb_hd_cport_allocate(struct gb_host_device * hd,int cport_id,unsigned long flags)72*8465def4SGreg Kroah-Hartman int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
73*8465def4SGreg Kroah-Hartman 			 unsigned long flags)
74*8465def4SGreg Kroah-Hartman {
75*8465def4SGreg Kroah-Hartman 	struct ida *id_map = &hd->cport_id_map;
76*8465def4SGreg Kroah-Hartman 	int ida_start, ida_end;
77*8465def4SGreg Kroah-Hartman 
78*8465def4SGreg Kroah-Hartman 	if (hd->driver->cport_allocate)
79*8465def4SGreg Kroah-Hartman 		return hd->driver->cport_allocate(hd, cport_id, flags);
80*8465def4SGreg Kroah-Hartman 
81*8465def4SGreg Kroah-Hartman 	if (cport_id < 0) {
82*8465def4SGreg Kroah-Hartman 		ida_start = 0;
83*8465def4SGreg Kroah-Hartman 		ida_end = hd->num_cports;
84*8465def4SGreg Kroah-Hartman 	} else if (cport_id < hd->num_cports) {
85*8465def4SGreg Kroah-Hartman 		ida_start = cport_id;
86*8465def4SGreg Kroah-Hartman 		ida_end = cport_id + 1;
87*8465def4SGreg Kroah-Hartman 	} else {
88*8465def4SGreg Kroah-Hartman 		dev_err(&hd->dev, "cport %d not available\n", cport_id);
89*8465def4SGreg Kroah-Hartman 		return -EINVAL;
90*8465def4SGreg Kroah-Hartman 	}
91*8465def4SGreg Kroah-Hartman 
92*8465def4SGreg Kroah-Hartman 	return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
93*8465def4SGreg Kroah-Hartman }
94*8465def4SGreg Kroah-Hartman 
95*8465def4SGreg Kroah-Hartman /* Locking: Caller guarantees serialisation */
gb_hd_cport_release(struct gb_host_device * hd,u16 cport_id)96*8465def4SGreg Kroah-Hartman void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
97*8465def4SGreg Kroah-Hartman {
98*8465def4SGreg Kroah-Hartman 	if (hd->driver->cport_release) {
99*8465def4SGreg Kroah-Hartman 		hd->driver->cport_release(hd, cport_id);
100*8465def4SGreg Kroah-Hartman 		return;
101*8465def4SGreg Kroah-Hartman 	}
102*8465def4SGreg Kroah-Hartman 
103*8465def4SGreg Kroah-Hartman 	ida_simple_remove(&hd->cport_id_map, cport_id);
104*8465def4SGreg Kroah-Hartman }
105*8465def4SGreg Kroah-Hartman 
gb_hd_release(struct device * dev)106*8465def4SGreg Kroah-Hartman static void gb_hd_release(struct device *dev)
107*8465def4SGreg Kroah-Hartman {
108*8465def4SGreg Kroah-Hartman 	struct gb_host_device *hd = to_gb_host_device(dev);
109*8465def4SGreg Kroah-Hartman 
110*8465def4SGreg Kroah-Hartman 	trace_gb_hd_release(hd);
111*8465def4SGreg Kroah-Hartman 
112*8465def4SGreg Kroah-Hartman 	if (hd->svc)
113*8465def4SGreg Kroah-Hartman 		gb_svc_put(hd->svc);
114*8465def4SGreg Kroah-Hartman 	ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
115*8465def4SGreg Kroah-Hartman 	ida_destroy(&hd->cport_id_map);
116*8465def4SGreg Kroah-Hartman 	kfree(hd);
117*8465def4SGreg Kroah-Hartman }
118*8465def4SGreg Kroah-Hartman 
119*8465def4SGreg Kroah-Hartman struct device_type greybus_hd_type = {
120*8465def4SGreg Kroah-Hartman 	.name		= "greybus_host_device",
121*8465def4SGreg Kroah-Hartman 	.release	= gb_hd_release,
122*8465def4SGreg Kroah-Hartman };
123*8465def4SGreg Kroah-Hartman 
gb_hd_create(struct gb_hd_driver * driver,struct device * parent,size_t buffer_size_max,size_t num_cports)124*8465def4SGreg Kroah-Hartman struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
125*8465def4SGreg Kroah-Hartman 				    struct device *parent,
126*8465def4SGreg Kroah-Hartman 				    size_t buffer_size_max,
127*8465def4SGreg Kroah-Hartman 				    size_t num_cports)
128*8465def4SGreg Kroah-Hartman {
129*8465def4SGreg Kroah-Hartman 	struct gb_host_device *hd;
130*8465def4SGreg Kroah-Hartman 	int ret;
131*8465def4SGreg Kroah-Hartman 
132*8465def4SGreg Kroah-Hartman 	/*
133*8465def4SGreg Kroah-Hartman 	 * Validate that the driver implements all of the callbacks
134*8465def4SGreg Kroah-Hartman 	 * so that we don't have to every time we make them.
135*8465def4SGreg Kroah-Hartman 	 */
136*8465def4SGreg Kroah-Hartman 	if ((!driver->message_send) || (!driver->message_cancel)) {
137*8465def4SGreg Kroah-Hartman 		dev_err(parent, "mandatory hd-callbacks missing\n");
138*8465def4SGreg Kroah-Hartman 		return ERR_PTR(-EINVAL);
139*8465def4SGreg Kroah-Hartman 	}
140*8465def4SGreg Kroah-Hartman 
141*8465def4SGreg Kroah-Hartman 	if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
142*8465def4SGreg Kroah-Hartman 		dev_err(parent, "greybus host-device buffers too small\n");
143*8465def4SGreg Kroah-Hartman 		return ERR_PTR(-EINVAL);
144*8465def4SGreg Kroah-Hartman 	}
145*8465def4SGreg Kroah-Hartman 
146*8465def4SGreg Kroah-Hartman 	if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) {
147*8465def4SGreg Kroah-Hartman 		dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
148*8465def4SGreg Kroah-Hartman 		return ERR_PTR(-EINVAL);
149*8465def4SGreg Kroah-Hartman 	}
150*8465def4SGreg Kroah-Hartman 
151*8465def4SGreg Kroah-Hartman 	/*
152*8465def4SGreg Kroah-Hartman 	 * Make sure to never allocate messages larger than what the Greybus
153*8465def4SGreg Kroah-Hartman 	 * protocol supports.
154*8465def4SGreg Kroah-Hartman 	 */
155*8465def4SGreg Kroah-Hartman 	if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
156*8465def4SGreg Kroah-Hartman 		dev_warn(parent, "limiting buffer size to %u\n",
157*8465def4SGreg Kroah-Hartman 			 GB_OPERATION_MESSAGE_SIZE_MAX);
158*8465def4SGreg Kroah-Hartman 		buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
159*8465def4SGreg Kroah-Hartman 	}
160*8465def4SGreg Kroah-Hartman 
161*8465def4SGreg Kroah-Hartman 	hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
162*8465def4SGreg Kroah-Hartman 	if (!hd)
163*8465def4SGreg Kroah-Hartman 		return ERR_PTR(-ENOMEM);
164*8465def4SGreg Kroah-Hartman 
165*8465def4SGreg Kroah-Hartman 	ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
166*8465def4SGreg Kroah-Hartman 	if (ret < 0) {
167*8465def4SGreg Kroah-Hartman 		kfree(hd);
168*8465def4SGreg Kroah-Hartman 		return ERR_PTR(ret);
169*8465def4SGreg Kroah-Hartman 	}
170*8465def4SGreg Kroah-Hartman 	hd->bus_id = ret;
171*8465def4SGreg Kroah-Hartman 
172*8465def4SGreg Kroah-Hartman 	hd->driver = driver;
173*8465def4SGreg Kroah-Hartman 	INIT_LIST_HEAD(&hd->modules);
174*8465def4SGreg Kroah-Hartman 	INIT_LIST_HEAD(&hd->connections);
175*8465def4SGreg Kroah-Hartman 	ida_init(&hd->cport_id_map);
176*8465def4SGreg Kroah-Hartman 	hd->buffer_size_max = buffer_size_max;
177*8465def4SGreg Kroah-Hartman 	hd->num_cports = num_cports;
178*8465def4SGreg Kroah-Hartman 
179*8465def4SGreg Kroah-Hartman 	hd->dev.parent = parent;
180*8465def4SGreg Kroah-Hartman 	hd->dev.bus = &greybus_bus_type;
181*8465def4SGreg Kroah-Hartman 	hd->dev.type = &greybus_hd_type;
182*8465def4SGreg Kroah-Hartman 	hd->dev.groups = bus_groups;
183*8465def4SGreg Kroah-Hartman 	hd->dev.dma_mask = hd->dev.parent->dma_mask;
184*8465def4SGreg Kroah-Hartman 	device_initialize(&hd->dev);
185*8465def4SGreg Kroah-Hartman 	dev_set_name(&hd->dev, "greybus%d", hd->bus_id);
186*8465def4SGreg Kroah-Hartman 
187*8465def4SGreg Kroah-Hartman 	trace_gb_hd_create(hd);
188*8465def4SGreg Kroah-Hartman 
189*8465def4SGreg Kroah-Hartman 	hd->svc = gb_svc_create(hd);
190*8465def4SGreg Kroah-Hartman 	if (!hd->svc) {
191*8465def4SGreg Kroah-Hartman 		dev_err(&hd->dev, "failed to create svc\n");
192*8465def4SGreg Kroah-Hartman 		put_device(&hd->dev);
193*8465def4SGreg Kroah-Hartman 		return ERR_PTR(-ENOMEM);
194*8465def4SGreg Kroah-Hartman 	}
195*8465def4SGreg Kroah-Hartman 
196*8465def4SGreg Kroah-Hartman 	return hd;
197*8465def4SGreg Kroah-Hartman }
198*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_create);
199*8465def4SGreg Kroah-Hartman 
gb_hd_add(struct gb_host_device * hd)200*8465def4SGreg Kroah-Hartman int gb_hd_add(struct gb_host_device *hd)
201*8465def4SGreg Kroah-Hartman {
202*8465def4SGreg Kroah-Hartman 	int ret;
203*8465def4SGreg Kroah-Hartman 
204*8465def4SGreg Kroah-Hartman 	ret = device_add(&hd->dev);
205*8465def4SGreg Kroah-Hartman 	if (ret)
206*8465def4SGreg Kroah-Hartman 		return ret;
207*8465def4SGreg Kroah-Hartman 
208*8465def4SGreg Kroah-Hartman 	ret = gb_svc_add(hd->svc);
209*8465def4SGreg Kroah-Hartman 	if (ret) {
210*8465def4SGreg Kroah-Hartman 		device_del(&hd->dev);
211*8465def4SGreg Kroah-Hartman 		return ret;
212*8465def4SGreg Kroah-Hartman 	}
213*8465def4SGreg Kroah-Hartman 
214*8465def4SGreg Kroah-Hartman 	trace_gb_hd_add(hd);
215*8465def4SGreg Kroah-Hartman 
216*8465def4SGreg Kroah-Hartman 	return 0;
217*8465def4SGreg Kroah-Hartman }
218*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_add);
219*8465def4SGreg Kroah-Hartman 
gb_hd_del(struct gb_host_device * hd)220*8465def4SGreg Kroah-Hartman void gb_hd_del(struct gb_host_device *hd)
221*8465def4SGreg Kroah-Hartman {
222*8465def4SGreg Kroah-Hartman 	trace_gb_hd_del(hd);
223*8465def4SGreg Kroah-Hartman 
224*8465def4SGreg Kroah-Hartman 	/*
225*8465def4SGreg Kroah-Hartman 	 * Tear down the svc and flush any on-going hotplug processing before
226*8465def4SGreg Kroah-Hartman 	 * removing the remaining interfaces.
227*8465def4SGreg Kroah-Hartman 	 */
228*8465def4SGreg Kroah-Hartman 	gb_svc_del(hd->svc);
229*8465def4SGreg Kroah-Hartman 
230*8465def4SGreg Kroah-Hartman 	device_del(&hd->dev);
231*8465def4SGreg Kroah-Hartman }
232*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_del);
233*8465def4SGreg Kroah-Hartman 
gb_hd_shutdown(struct gb_host_device * hd)234*8465def4SGreg Kroah-Hartman void gb_hd_shutdown(struct gb_host_device *hd)
235*8465def4SGreg Kroah-Hartman {
236*8465def4SGreg Kroah-Hartman 	gb_svc_del(hd->svc);
237*8465def4SGreg Kroah-Hartman }
238*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_shutdown);
239*8465def4SGreg Kroah-Hartman 
gb_hd_put(struct gb_host_device * hd)240*8465def4SGreg Kroah-Hartman void gb_hd_put(struct gb_host_device *hd)
241*8465def4SGreg Kroah-Hartman {
242*8465def4SGreg Kroah-Hartman 	put_device(&hd->dev);
243*8465def4SGreg Kroah-Hartman }
244*8465def4SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(gb_hd_put);
245*8465def4SGreg Kroah-Hartman 
gb_hd_init(void)246*8465def4SGreg Kroah-Hartman int __init gb_hd_init(void)
247*8465def4SGreg Kroah-Hartman {
248*8465def4SGreg Kroah-Hartman 	ida_init(&gb_hd_bus_id_map);
249*8465def4SGreg Kroah-Hartman 
250*8465def4SGreg Kroah-Hartman 	return 0;
251*8465def4SGreg Kroah-Hartman }
252*8465def4SGreg Kroah-Hartman 
gb_hd_exit(void)253*8465def4SGreg Kroah-Hartman void gb_hd_exit(void)
254*8465def4SGreg Kroah-Hartman {
255*8465def4SGreg Kroah-Hartman 	ida_destroy(&gb_hd_bus_id_map);
256*8465def4SGreg Kroah-Hartman }
257