1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HSI core. |
4 | * |
5 | * Copyright (C) 2010 Nokia Corporation. All rights reserved. |
6 | * |
7 | * Contact: Carlos Chinea <carlos.chinea@nokia.com> |
8 | */ |
9 | #include <linux/hsi/hsi.h> |
10 | #include <linux/compiler.h> |
11 | #include <linux/list.h> |
12 | #include <linux/kobject.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/string.h> |
15 | #include <linux/notifier.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_device.h> |
18 | #include "hsi_core.h" |
19 | |
20 | static ssize_t modalias_show(struct device *dev, |
21 | struct device_attribute *a __maybe_unused, char *buf) |
22 | { |
23 | return sprintf(buf, fmt: "hsi:%s\n" , dev_name(dev)); |
24 | } |
25 | static DEVICE_ATTR_RO(modalias); |
26 | |
27 | static struct attribute *hsi_bus_dev_attrs[] = { |
28 | &dev_attr_modalias.attr, |
29 | NULL, |
30 | }; |
31 | ATTRIBUTE_GROUPS(hsi_bus_dev); |
32 | |
33 | static int hsi_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) |
34 | { |
35 | add_uevent_var(env, format: "MODALIAS=hsi:%s" , dev_name(dev)); |
36 | |
37 | return 0; |
38 | } |
39 | |
40 | static int hsi_bus_match(struct device *dev, struct device_driver *driver) |
41 | { |
42 | if (of_driver_match_device(dev, drv: driver)) |
43 | return true; |
44 | |
45 | if (strcmp(dev_name(dev), driver->name) == 0) |
46 | return true; |
47 | |
48 | return false; |
49 | } |
50 | |
51 | static struct bus_type hsi_bus_type = { |
52 | .name = "hsi" , |
53 | .dev_groups = hsi_bus_dev_groups, |
54 | .match = hsi_bus_match, |
55 | .uevent = hsi_bus_uevent, |
56 | }; |
57 | |
58 | static void hsi_client_release(struct device *dev) |
59 | { |
60 | struct hsi_client *cl = to_hsi_client(dev); |
61 | |
62 | kfree(objp: cl->tx_cfg.channels); |
63 | kfree(objp: cl->rx_cfg.channels); |
64 | kfree(objp: cl); |
65 | } |
66 | |
67 | struct hsi_client *hsi_new_client(struct hsi_port *port, |
68 | struct hsi_board_info *info) |
69 | { |
70 | struct hsi_client *cl; |
71 | size_t size; |
72 | |
73 | cl = kzalloc(size: sizeof(*cl), GFP_KERNEL); |
74 | if (!cl) |
75 | goto err; |
76 | |
77 | cl->tx_cfg = info->tx_cfg; |
78 | if (cl->tx_cfg.channels) { |
79 | size = cl->tx_cfg.num_channels * sizeof(*cl->tx_cfg.channels); |
80 | cl->tx_cfg.channels = kmemdup(p: info->tx_cfg.channels, size, |
81 | GFP_KERNEL); |
82 | if (!cl->tx_cfg.channels) |
83 | goto err_tx; |
84 | } |
85 | |
86 | cl->rx_cfg = info->rx_cfg; |
87 | if (cl->rx_cfg.channels) { |
88 | size = cl->rx_cfg.num_channels * sizeof(*cl->rx_cfg.channels); |
89 | cl->rx_cfg.channels = kmemdup(p: info->rx_cfg.channels, size, |
90 | GFP_KERNEL); |
91 | if (!cl->rx_cfg.channels) |
92 | goto err_rx; |
93 | } |
94 | |
95 | cl->device.bus = &hsi_bus_type; |
96 | cl->device.parent = &port->device; |
97 | cl->device.release = hsi_client_release; |
98 | dev_set_name(dev: &cl->device, name: "%s" , info->name); |
99 | cl->device.platform_data = info->platform_data; |
100 | if (info->archdata) |
101 | cl->device.archdata = *info->archdata; |
102 | if (device_register(dev: &cl->device) < 0) { |
103 | pr_err("hsi: failed to register client: %s\n" , info->name); |
104 | put_device(dev: &cl->device); |
105 | goto err; |
106 | } |
107 | |
108 | return cl; |
109 | err_rx: |
110 | kfree(objp: cl->tx_cfg.channels); |
111 | err_tx: |
112 | kfree(objp: cl); |
113 | err: |
114 | return NULL; |
115 | } |
116 | EXPORT_SYMBOL_GPL(hsi_new_client); |
117 | |
118 | static void hsi_scan_board_info(struct hsi_controller *hsi) |
119 | { |
120 | struct hsi_cl_info *cl_info; |
121 | struct hsi_port *p; |
122 | |
123 | list_for_each_entry(cl_info, &hsi_board_list, list) |
124 | if (cl_info->info.hsi_id == hsi->id) { |
125 | p = hsi_find_port_num(hsi, num: cl_info->info.port); |
126 | if (!p) |
127 | continue; |
128 | hsi_new_client(p, &cl_info->info); |
129 | } |
130 | } |
131 | |
132 | #ifdef CONFIG_OF |
133 | static struct hsi_board_info hsi_char_dev_info = { |
134 | .name = "hsi_char" , |
135 | }; |
136 | |
137 | static int hsi_of_property_parse_mode(struct device_node *client, char *name, |
138 | unsigned int *result) |
139 | { |
140 | const char *mode; |
141 | int err; |
142 | |
143 | err = of_property_read_string(np: client, propname: name, out_string: &mode); |
144 | if (err < 0) |
145 | return err; |
146 | |
147 | if (strcmp(mode, "stream" ) == 0) |
148 | *result = HSI_MODE_STREAM; |
149 | else if (strcmp(mode, "frame" ) == 0) |
150 | *result = HSI_MODE_FRAME; |
151 | else |
152 | return -EINVAL; |
153 | |
154 | return 0; |
155 | } |
156 | |
157 | static int hsi_of_property_parse_flow(struct device_node *client, char *name, |
158 | unsigned int *result) |
159 | { |
160 | const char *flow; |
161 | int err; |
162 | |
163 | err = of_property_read_string(np: client, propname: name, out_string: &flow); |
164 | if (err < 0) |
165 | return err; |
166 | |
167 | if (strcmp(flow, "synchronized" ) == 0) |
168 | *result = HSI_FLOW_SYNC; |
169 | else if (strcmp(flow, "pipeline" ) == 0) |
170 | *result = HSI_FLOW_PIPE; |
171 | else |
172 | return -EINVAL; |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | static int hsi_of_property_parse_arb_mode(struct device_node *client, |
178 | char *name, unsigned int *result) |
179 | { |
180 | const char *arb_mode; |
181 | int err; |
182 | |
183 | err = of_property_read_string(np: client, propname: name, out_string: &arb_mode); |
184 | if (err < 0) |
185 | return err; |
186 | |
187 | if (strcmp(arb_mode, "round-robin" ) == 0) |
188 | *result = HSI_ARB_RR; |
189 | else if (strcmp(arb_mode, "priority" ) == 0) |
190 | *result = HSI_ARB_PRIO; |
191 | else |
192 | return -EINVAL; |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static void hsi_add_client_from_dt(struct hsi_port *port, |
198 | struct device_node *client) |
199 | { |
200 | struct hsi_client *cl; |
201 | struct hsi_channel channel; |
202 | struct property *prop; |
203 | char name[32]; |
204 | int length, cells, err, i, max_chan, mode; |
205 | |
206 | cl = kzalloc(size: sizeof(*cl), GFP_KERNEL); |
207 | if (!cl) |
208 | return; |
209 | |
210 | err = of_alias_from_compatible(node: client, alias: name, len: sizeof(name)); |
211 | if (err) |
212 | goto err; |
213 | |
214 | err = hsi_of_property_parse_mode(client, name: "hsi-mode" , result: &mode); |
215 | if (err) { |
216 | err = hsi_of_property_parse_mode(client, name: "hsi-rx-mode" , |
217 | result: &cl->rx_cfg.mode); |
218 | if (err) |
219 | goto err; |
220 | |
221 | err = hsi_of_property_parse_mode(client, name: "hsi-tx-mode" , |
222 | result: &cl->tx_cfg.mode); |
223 | if (err) |
224 | goto err; |
225 | } else { |
226 | cl->rx_cfg.mode = mode; |
227 | cl->tx_cfg.mode = mode; |
228 | } |
229 | |
230 | err = of_property_read_u32(np: client, propname: "hsi-speed-kbps" , |
231 | out_value: &cl->tx_cfg.speed); |
232 | if (err) |
233 | goto err; |
234 | cl->rx_cfg.speed = cl->tx_cfg.speed; |
235 | |
236 | err = hsi_of_property_parse_flow(client, name: "hsi-flow" , |
237 | result: &cl->rx_cfg.flow); |
238 | if (err) |
239 | goto err; |
240 | |
241 | err = hsi_of_property_parse_arb_mode(client, name: "hsi-arb-mode" , |
242 | result: &cl->rx_cfg.arb_mode); |
243 | if (err) |
244 | goto err; |
245 | |
246 | prop = of_find_property(np: client, name: "hsi-channel-ids" , lenp: &length); |
247 | if (!prop) { |
248 | err = -EINVAL; |
249 | goto err; |
250 | } |
251 | |
252 | cells = length / sizeof(u32); |
253 | |
254 | cl->rx_cfg.num_channels = cells; |
255 | cl->tx_cfg.num_channels = cells; |
256 | cl->rx_cfg.channels = kcalloc(n: cells, size: sizeof(channel), GFP_KERNEL); |
257 | if (!cl->rx_cfg.channels) { |
258 | err = -ENOMEM; |
259 | goto err; |
260 | } |
261 | |
262 | cl->tx_cfg.channels = kcalloc(n: cells, size: sizeof(channel), GFP_KERNEL); |
263 | if (!cl->tx_cfg.channels) { |
264 | err = -ENOMEM; |
265 | goto err2; |
266 | } |
267 | |
268 | max_chan = 0; |
269 | for (i = 0; i < cells; i++) { |
270 | err = of_property_read_u32_index(np: client, propname: "hsi-channel-ids" , index: i, |
271 | out_value: &channel.id); |
272 | if (err) |
273 | goto err3; |
274 | |
275 | err = of_property_read_string_index(np: client, propname: "hsi-channel-names" , |
276 | index: i, output: &channel.name); |
277 | if (err) |
278 | channel.name = NULL; |
279 | |
280 | if (channel.id > max_chan) |
281 | max_chan = channel.id; |
282 | |
283 | cl->rx_cfg.channels[i] = channel; |
284 | cl->tx_cfg.channels[i] = channel; |
285 | } |
286 | |
287 | cl->rx_cfg.num_hw_channels = max_chan + 1; |
288 | cl->tx_cfg.num_hw_channels = max_chan + 1; |
289 | |
290 | cl->device.bus = &hsi_bus_type; |
291 | cl->device.parent = &port->device; |
292 | cl->device.release = hsi_client_release; |
293 | cl->device.of_node = client; |
294 | |
295 | dev_set_name(dev: &cl->device, name: "%s" , name); |
296 | if (device_register(dev: &cl->device) < 0) { |
297 | pr_err("hsi: failed to register client: %s\n" , name); |
298 | put_device(dev: &cl->device); |
299 | } |
300 | |
301 | return; |
302 | |
303 | err3: |
304 | kfree(objp: cl->tx_cfg.channels); |
305 | err2: |
306 | kfree(objp: cl->rx_cfg.channels); |
307 | err: |
308 | kfree(objp: cl); |
309 | pr_err("hsi client: missing or incorrect of property: err=%d\n" , err); |
310 | } |
311 | |
312 | void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients) |
313 | { |
314 | struct device_node *child; |
315 | |
316 | /* register hsi-char device */ |
317 | hsi_new_client(port, &hsi_char_dev_info); |
318 | |
319 | for_each_available_child_of_node(clients, child) |
320 | hsi_add_client_from_dt(port, client: child); |
321 | } |
322 | EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt); |
323 | #endif |
324 | |
325 | int hsi_remove_client(struct device *dev, void *data __maybe_unused) |
326 | { |
327 | device_unregister(dev); |
328 | |
329 | return 0; |
330 | } |
331 | EXPORT_SYMBOL_GPL(hsi_remove_client); |
332 | |
333 | static int hsi_remove_port(struct device *dev, void *data __maybe_unused) |
334 | { |
335 | device_for_each_child(dev, NULL, fn: hsi_remove_client); |
336 | device_unregister(dev); |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static void hsi_controller_release(struct device *dev) |
342 | { |
343 | struct hsi_controller *hsi = to_hsi_controller(dev); |
344 | |
345 | kfree(objp: hsi->port); |
346 | kfree(objp: hsi); |
347 | } |
348 | |
349 | static void hsi_port_release(struct device *dev) |
350 | { |
351 | kfree(to_hsi_port(dev)); |
352 | } |
353 | |
354 | /** |
355 | * hsi_port_unregister_clients - Unregister an HSI port |
356 | * @port: The HSI port to unregister |
357 | */ |
358 | void hsi_port_unregister_clients(struct hsi_port *port) |
359 | { |
360 | device_for_each_child(dev: &port->device, NULL, fn: hsi_remove_client); |
361 | } |
362 | EXPORT_SYMBOL_GPL(hsi_port_unregister_clients); |
363 | |
364 | /** |
365 | * hsi_unregister_controller - Unregister an HSI controller |
366 | * @hsi: The HSI controller to register |
367 | */ |
368 | void hsi_unregister_controller(struct hsi_controller *hsi) |
369 | { |
370 | device_for_each_child(dev: &hsi->device, NULL, fn: hsi_remove_port); |
371 | device_unregister(dev: &hsi->device); |
372 | } |
373 | EXPORT_SYMBOL_GPL(hsi_unregister_controller); |
374 | |
375 | /** |
376 | * hsi_register_controller - Register an HSI controller and its ports |
377 | * @hsi: The HSI controller to register |
378 | * |
379 | * Returns -errno on failure, 0 on success. |
380 | */ |
381 | int hsi_register_controller(struct hsi_controller *hsi) |
382 | { |
383 | unsigned int i; |
384 | int err; |
385 | |
386 | err = device_add(dev: &hsi->device); |
387 | if (err < 0) |
388 | return err; |
389 | for (i = 0; i < hsi->num_ports; i++) { |
390 | hsi->port[i]->device.parent = &hsi->device; |
391 | err = device_add(dev: &hsi->port[i]->device); |
392 | if (err < 0) |
393 | goto out; |
394 | } |
395 | /* Populate HSI bus with HSI clients */ |
396 | hsi_scan_board_info(hsi); |
397 | |
398 | return 0; |
399 | out: |
400 | while (i-- > 0) |
401 | device_del(dev: &hsi->port[i]->device); |
402 | device_del(dev: &hsi->device); |
403 | |
404 | return err; |
405 | } |
406 | EXPORT_SYMBOL_GPL(hsi_register_controller); |
407 | |
408 | /** |
409 | * hsi_register_client_driver - Register an HSI client to the HSI bus |
410 | * @drv: HSI client driver to register |
411 | * |
412 | * Returns -errno on failure, 0 on success. |
413 | */ |
414 | int hsi_register_client_driver(struct hsi_client_driver *drv) |
415 | { |
416 | drv->driver.bus = &hsi_bus_type; |
417 | |
418 | return driver_register(drv: &drv->driver); |
419 | } |
420 | EXPORT_SYMBOL_GPL(hsi_register_client_driver); |
421 | |
422 | static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused) |
423 | { |
424 | return 0; |
425 | } |
426 | |
427 | static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) |
428 | { |
429 | return 0; |
430 | } |
431 | |
432 | /** |
433 | * hsi_put_controller - Free an HSI controller |
434 | * |
435 | * @hsi: Pointer to the HSI controller to freed |
436 | * |
437 | * HSI controller drivers should only use this function if they need |
438 | * to free their allocated hsi_controller structures before a successful |
439 | * call to hsi_register_controller. Other use is not allowed. |
440 | */ |
441 | void hsi_put_controller(struct hsi_controller *hsi) |
442 | { |
443 | unsigned int i; |
444 | |
445 | if (!hsi) |
446 | return; |
447 | |
448 | for (i = 0; i < hsi->num_ports; i++) |
449 | if (hsi->port && hsi->port[i]) |
450 | put_device(dev: &hsi->port[i]->device); |
451 | put_device(dev: &hsi->device); |
452 | } |
453 | EXPORT_SYMBOL_GPL(hsi_put_controller); |
454 | |
455 | /** |
456 | * hsi_alloc_controller - Allocate an HSI controller and its ports |
457 | * @n_ports: Number of ports on the HSI controller |
458 | * @flags: Kernel allocation flags |
459 | * |
460 | * Return NULL on failure or a pointer to an hsi_controller on success. |
461 | */ |
462 | struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) |
463 | { |
464 | struct hsi_controller *hsi; |
465 | struct hsi_port **port; |
466 | unsigned int i; |
467 | |
468 | if (!n_ports) |
469 | return NULL; |
470 | |
471 | hsi = kzalloc(size: sizeof(*hsi), flags); |
472 | if (!hsi) |
473 | return NULL; |
474 | port = kcalloc(n: n_ports, size: sizeof(*port), flags); |
475 | if (!port) { |
476 | kfree(objp: hsi); |
477 | return NULL; |
478 | } |
479 | hsi->num_ports = n_ports; |
480 | hsi->port = port; |
481 | hsi->device.release = hsi_controller_release; |
482 | device_initialize(dev: &hsi->device); |
483 | |
484 | for (i = 0; i < n_ports; i++) { |
485 | port[i] = kzalloc(size: sizeof(**port), flags); |
486 | if (port[i] == NULL) |
487 | goto out; |
488 | port[i]->num = i; |
489 | port[i]->async = hsi_dummy_msg; |
490 | port[i]->setup = hsi_dummy_cl; |
491 | port[i]->flush = hsi_dummy_cl; |
492 | port[i]->start_tx = hsi_dummy_cl; |
493 | port[i]->stop_tx = hsi_dummy_cl; |
494 | port[i]->release = hsi_dummy_cl; |
495 | mutex_init(&port[i]->lock); |
496 | BLOCKING_INIT_NOTIFIER_HEAD(&port[i]->n_head); |
497 | dev_set_name(dev: &port[i]->device, name: "port%d" , i); |
498 | hsi->port[i]->device.release = hsi_port_release; |
499 | device_initialize(dev: &hsi->port[i]->device); |
500 | } |
501 | |
502 | return hsi; |
503 | out: |
504 | hsi_put_controller(hsi); |
505 | |
506 | return NULL; |
507 | } |
508 | EXPORT_SYMBOL_GPL(hsi_alloc_controller); |
509 | |
510 | /** |
511 | * hsi_free_msg - Free an HSI message |
512 | * @msg: Pointer to the HSI message |
513 | * |
514 | * Client is responsible to free the buffers pointed by the scatterlists. |
515 | */ |
516 | void hsi_free_msg(struct hsi_msg *msg) |
517 | { |
518 | if (!msg) |
519 | return; |
520 | sg_free_table(&msg->sgt); |
521 | kfree(objp: msg); |
522 | } |
523 | EXPORT_SYMBOL_GPL(hsi_free_msg); |
524 | |
525 | /** |
526 | * hsi_alloc_msg - Allocate an HSI message |
527 | * @nents: Number of memory entries |
528 | * @flags: Kernel allocation flags |
529 | * |
530 | * nents can be 0. This mainly makes sense for read transfer. |
531 | * In that case, HSI drivers will call the complete callback when |
532 | * there is data to be read without consuming it. |
533 | * |
534 | * Return NULL on failure or a pointer to an hsi_msg on success. |
535 | */ |
536 | struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags) |
537 | { |
538 | struct hsi_msg *msg; |
539 | int err; |
540 | |
541 | msg = kzalloc(size: sizeof(*msg), flags); |
542 | if (!msg) |
543 | return NULL; |
544 | |
545 | if (!nents) |
546 | return msg; |
547 | |
548 | err = sg_alloc_table(&msg->sgt, nents, flags); |
549 | if (unlikely(err)) { |
550 | kfree(objp: msg); |
551 | msg = NULL; |
552 | } |
553 | |
554 | return msg; |
555 | } |
556 | EXPORT_SYMBOL_GPL(hsi_alloc_msg); |
557 | |
558 | /** |
559 | * hsi_async - Submit an HSI transfer to the controller |
560 | * @cl: HSI client sending the transfer |
561 | * @msg: The HSI transfer passed to controller |
562 | * |
563 | * The HSI message must have the channel, ttype, complete and destructor |
564 | * fields set beforehand. If nents > 0 then the client has to initialize |
565 | * also the scatterlists to point to the buffers to write to or read from. |
566 | * |
567 | * HSI controllers relay on pre-allocated buffers from their clients and they |
568 | * do not allocate buffers on their own. |
569 | * |
570 | * Once the HSI message transfer finishes, the HSI controller calls the |
571 | * complete callback with the status and actual_len fields of the HSI message |
572 | * updated. The complete callback can be called before returning from |
573 | * hsi_async. |
574 | * |
575 | * Returns -errno on failure or 0 on success |
576 | */ |
577 | int hsi_async(struct hsi_client *cl, struct hsi_msg *msg) |
578 | { |
579 | struct hsi_port *port = hsi_get_port(cl); |
580 | |
581 | if (!hsi_port_claimed(cl)) |
582 | return -EACCES; |
583 | |
584 | WARN_ON_ONCE(!msg->destructor || !msg->complete); |
585 | msg->cl = cl; |
586 | |
587 | return port->async(msg); |
588 | } |
589 | EXPORT_SYMBOL_GPL(hsi_async); |
590 | |
591 | /** |
592 | * hsi_claim_port - Claim the HSI client's port |
593 | * @cl: HSI client that wants to claim its port |
594 | * @share: Flag to indicate if the client wants to share the port or not. |
595 | * |
596 | * Returns -errno on failure, 0 on success. |
597 | */ |
598 | int hsi_claim_port(struct hsi_client *cl, unsigned int share) |
599 | { |
600 | struct hsi_port *port = hsi_get_port(cl); |
601 | int err = 0; |
602 | |
603 | mutex_lock(&port->lock); |
604 | if ((port->claimed) && (!port->shared || !share)) { |
605 | err = -EBUSY; |
606 | goto out; |
607 | } |
608 | if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) { |
609 | err = -ENODEV; |
610 | goto out; |
611 | } |
612 | port->claimed++; |
613 | port->shared = !!share; |
614 | cl->pclaimed = 1; |
615 | out: |
616 | mutex_unlock(lock: &port->lock); |
617 | |
618 | return err; |
619 | } |
620 | EXPORT_SYMBOL_GPL(hsi_claim_port); |
621 | |
622 | /** |
623 | * hsi_release_port - Release the HSI client's port |
624 | * @cl: HSI client which previously claimed its port |
625 | */ |
626 | void hsi_release_port(struct hsi_client *cl) |
627 | { |
628 | struct hsi_port *port = hsi_get_port(cl); |
629 | |
630 | mutex_lock(&port->lock); |
631 | /* Allow HW driver to do some cleanup */ |
632 | port->release(cl); |
633 | if (cl->pclaimed) |
634 | port->claimed--; |
635 | BUG_ON(port->claimed < 0); |
636 | cl->pclaimed = 0; |
637 | if (!port->claimed) |
638 | port->shared = 0; |
639 | module_put(to_hsi_controller(port->device.parent)->owner); |
640 | mutex_unlock(lock: &port->lock); |
641 | } |
642 | EXPORT_SYMBOL_GPL(hsi_release_port); |
643 | |
644 | static int hsi_event_notifier_call(struct notifier_block *nb, |
645 | unsigned long event, void *data __maybe_unused) |
646 | { |
647 | struct hsi_client *cl = container_of(nb, struct hsi_client, nb); |
648 | |
649 | (*cl->ehandler)(cl, event); |
650 | |
651 | return 0; |
652 | } |
653 | |
654 | /** |
655 | * hsi_register_port_event - Register a client to receive port events |
656 | * @cl: HSI client that wants to receive port events |
657 | * @handler: Event handler callback |
658 | * |
659 | * Clients should register a callback to be able to receive |
660 | * events from the ports. Registration should happen after |
661 | * claiming the port. |
662 | * The handler can be called in interrupt context. |
663 | * |
664 | * Returns -errno on error, or 0 on success. |
665 | */ |
666 | int hsi_register_port_event(struct hsi_client *cl, |
667 | void (*handler)(struct hsi_client *, unsigned long)) |
668 | { |
669 | struct hsi_port *port = hsi_get_port(cl); |
670 | |
671 | if (!handler || cl->ehandler) |
672 | return -EINVAL; |
673 | if (!hsi_port_claimed(cl)) |
674 | return -EACCES; |
675 | cl->ehandler = handler; |
676 | cl->nb.notifier_call = hsi_event_notifier_call; |
677 | |
678 | return blocking_notifier_chain_register(nh: &port->n_head, nb: &cl->nb); |
679 | } |
680 | EXPORT_SYMBOL_GPL(hsi_register_port_event); |
681 | |
682 | /** |
683 | * hsi_unregister_port_event - Stop receiving port events for a client |
684 | * @cl: HSI client that wants to stop receiving port events |
685 | * |
686 | * Clients should call this function before releasing their associated |
687 | * port. |
688 | * |
689 | * Returns -errno on error, or 0 on success. |
690 | */ |
691 | int hsi_unregister_port_event(struct hsi_client *cl) |
692 | { |
693 | struct hsi_port *port = hsi_get_port(cl); |
694 | int err; |
695 | |
696 | WARN_ON(!hsi_port_claimed(cl)); |
697 | |
698 | err = blocking_notifier_chain_unregister(nh: &port->n_head, nb: &cl->nb); |
699 | if (!err) |
700 | cl->ehandler = NULL; |
701 | |
702 | return err; |
703 | } |
704 | EXPORT_SYMBOL_GPL(hsi_unregister_port_event); |
705 | |
706 | /** |
707 | * hsi_event - Notifies clients about port events |
708 | * @port: Port where the event occurred |
709 | * @event: The event type |
710 | * |
711 | * Clients should not be concerned about wake line behavior. However, due |
712 | * to a race condition in HSI HW protocol, clients need to be notified |
713 | * about wake line changes, so they can implement a workaround for it. |
714 | * |
715 | * Events: |
716 | * HSI_EVENT_START_RX - Incoming wake line high |
717 | * HSI_EVENT_STOP_RX - Incoming wake line down |
718 | * |
719 | * Returns -errno on error, or 0 on success. |
720 | */ |
721 | int hsi_event(struct hsi_port *port, unsigned long event) |
722 | { |
723 | return blocking_notifier_call_chain(nh: &port->n_head, val: event, NULL); |
724 | } |
725 | EXPORT_SYMBOL_GPL(hsi_event); |
726 | |
727 | /** |
728 | * hsi_get_channel_id_by_name - acquire channel id by channel name |
729 | * @cl: HSI client, which uses the channel |
730 | * @name: name the channel is known under |
731 | * |
732 | * Clients can call this function to get the hsi channel ids similar to |
733 | * requesting IRQs or GPIOs by name. This function assumes the same |
734 | * channel configuration is used for RX and TX. |
735 | * |
736 | * Returns -errno on error or channel id on success. |
737 | */ |
738 | int hsi_get_channel_id_by_name(struct hsi_client *cl, char *name) |
739 | { |
740 | int i; |
741 | |
742 | if (!cl->rx_cfg.channels) |
743 | return -ENOENT; |
744 | |
745 | for (i = 0; i < cl->rx_cfg.num_channels; i++) |
746 | if (!strcmp(cl->rx_cfg.channels[i].name, name)) |
747 | return cl->rx_cfg.channels[i].id; |
748 | |
749 | return -ENXIO; |
750 | } |
751 | EXPORT_SYMBOL_GPL(hsi_get_channel_id_by_name); |
752 | |
753 | static int __init hsi_init(void) |
754 | { |
755 | return bus_register(bus: &hsi_bus_type); |
756 | } |
757 | postcore_initcall(hsi_init); |
758 | |
759 | static void __exit hsi_exit(void) |
760 | { |
761 | bus_unregister(bus: &hsi_bus_type); |
762 | } |
763 | module_exit(hsi_exit); |
764 | |
765 | MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>" ); |
766 | MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework" ); |
767 | MODULE_LICENSE("GPL v2" ); |
768 | |