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