diff options
Diffstat (limited to 'drivers/hsi/hsi.c')
-rw-r--r-- | drivers/hsi/hsi.c | 101 |
1 files changed, 54 insertions, 47 deletions
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index cec1f0c04557..2d58f939d27f 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c | |||
@@ -21,12 +21,11 @@ | |||
21 | */ | 21 | */ |
22 | #include <linux/hsi/hsi.h> | 22 | #include <linux/hsi/hsi.h> |
23 | #include <linux/compiler.h> | 23 | #include <linux/compiler.h> |
24 | #include <linux/rwsem.h> | ||
25 | #include <linux/list.h> | 24 | #include <linux/list.h> |
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/kobject.h> | 25 | #include <linux/kobject.h> |
28 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
29 | #include <linux/string.h> | 27 | #include <linux/string.h> |
28 | #include <linux/notifier.h> | ||
30 | #include "hsi_core.h" | 29 | #include "hsi_core.h" |
31 | 30 | ||
32 | static ssize_t modalias_show(struct device *dev, | 31 | static ssize_t modalias_show(struct device *dev, |
@@ -67,7 +66,6 @@ static void hsi_client_release(struct device *dev) | |||
67 | static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) | 66 | static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) |
68 | { | 67 | { |
69 | struct hsi_client *cl; | 68 | struct hsi_client *cl; |
70 | unsigned long flags; | ||
71 | 69 | ||
72 | cl = kzalloc(sizeof(*cl), GFP_KERNEL); | 70 | cl = kzalloc(sizeof(*cl), GFP_KERNEL); |
73 | if (!cl) | 71 | if (!cl) |
@@ -79,9 +77,6 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) | |||
79 | cl->device.release = hsi_client_release; | 77 | cl->device.release = hsi_client_release; |
80 | dev_set_name(&cl->device, info->name); | 78 | dev_set_name(&cl->device, info->name); |
81 | cl->device.platform_data = info->platform_data; | 79 | cl->device.platform_data = info->platform_data; |
82 | spin_lock_irqsave(&port->clock, flags); | ||
83 | list_add_tail(&cl->link, &port->clients); | ||
84 | spin_unlock_irqrestore(&port->clock, flags); | ||
85 | if (info->archdata) | 80 | if (info->archdata) |
86 | cl->device.archdata = *info->archdata; | 81 | cl->device.archdata = *info->archdata; |
87 | if (device_register(&cl->device) < 0) { | 82 | if (device_register(&cl->device) < 0) { |
@@ -106,13 +101,6 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) | |||
106 | 101 | ||
107 | static int hsi_remove_client(struct device *dev, void *data __maybe_unused) | 102 | static int hsi_remove_client(struct device *dev, void *data __maybe_unused) |
108 | { | 103 | { |
109 | struct hsi_client *cl = to_hsi_client(dev); | ||
110 | struct hsi_port *port = to_hsi_port(dev->parent); | ||
111 | unsigned long flags; | ||
112 | |||
113 | spin_lock_irqsave(&port->clock, flags); | ||
114 | list_del(&cl->link); | ||
115 | spin_unlock_irqrestore(&port->clock, flags); | ||
116 | device_unregister(dev); | 104 | device_unregister(dev); |
117 | 105 | ||
118 | return 0; | 106 | return 0; |
@@ -271,8 +259,7 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) | |||
271 | port[i]->stop_tx = hsi_dummy_cl; | 259 | port[i]->stop_tx = hsi_dummy_cl; |
272 | port[i]->release = hsi_dummy_cl; | 260 | port[i]->release = hsi_dummy_cl; |
273 | mutex_init(&port[i]->lock); | 261 | mutex_init(&port[i]->lock); |
274 | INIT_LIST_HEAD(&hsi->port[i]->clients); | 262 | ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head); |
275 | spin_lock_init(&hsi->port[i]->clock); | ||
276 | dev_set_name(&port[i]->device, "port%d", i); | 263 | dev_set_name(&port[i]->device, "port%d", i); |
277 | hsi->port[i]->device.release = hsi_port_release; | 264 | hsi->port[i]->device.release = hsi_port_release; |
278 | device_initialize(&hsi->port[i]->device); | 265 | device_initialize(&hsi->port[i]->device); |
@@ -420,37 +407,67 @@ void hsi_release_port(struct hsi_client *cl) | |||
420 | } | 407 | } |
421 | EXPORT_SYMBOL_GPL(hsi_release_port); | 408 | EXPORT_SYMBOL_GPL(hsi_release_port); |
422 | 409 | ||
423 | static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused) | 410 | static int hsi_event_notifier_call(struct notifier_block *nb, |
411 | unsigned long event, void *data __maybe_unused) | ||
424 | { | 412 | { |
425 | if (cl->hsi_start_rx) | 413 | struct hsi_client *cl = container_of(nb, struct hsi_client, nb); |
426 | (*cl->hsi_start_rx)(cl); | 414 | |
415 | (*cl->ehandler)(cl, event); | ||
427 | 416 | ||
428 | return 0; | 417 | return 0; |
429 | } | 418 | } |
430 | 419 | ||
431 | static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused) | 420 | /** |
421 | * hsi_register_port_event - Register a client to receive port events | ||
422 | * @cl: HSI client that wants to receive port events | ||
423 | * @cb: Event handler callback | ||
424 | * | ||
425 | * Clients should register a callback to be able to receive | ||
426 | * events from the ports. Registration should happen after | ||
427 | * claiming the port. | ||
428 | * The handler can be called in interrupt context. | ||
429 | * | ||
430 | * Returns -errno on error, or 0 on success. | ||
431 | */ | ||
432 | int hsi_register_port_event(struct hsi_client *cl, | ||
433 | void (*handler)(struct hsi_client *, unsigned long)) | ||
432 | { | 434 | { |
433 | if (cl->hsi_stop_rx) | 435 | struct hsi_port *port = hsi_get_port(cl); |
434 | (*cl->hsi_stop_rx)(cl); | ||
435 | 436 | ||
436 | return 0; | 437 | if (!handler || cl->ehandler) |
438 | return -EINVAL; | ||
439 | if (!hsi_port_claimed(cl)) | ||
440 | return -EACCES; | ||
441 | cl->ehandler = handler; | ||
442 | cl->nb.notifier_call = hsi_event_notifier_call; | ||
443 | |||
444 | return atomic_notifier_chain_register(&port->n_head, &cl->nb); | ||
437 | } | 445 | } |
446 | EXPORT_SYMBOL_GPL(hsi_register_port_event); | ||
438 | 447 | ||
439 | static int hsi_port_for_each_client(struct hsi_port *port, void *data, | 448 | /** |
440 | int (*fn)(struct hsi_client *cl, void *data)) | 449 | * hsi_unregister_port_event - Stop receiving port events for a client |
450 | * @cl: HSI client that wants to stop receiving port events | ||
451 | * | ||
452 | * Clients should call this function before releasing their associated | ||
453 | * port. | ||
454 | * | ||
455 | * Returns -errno on error, or 0 on success. | ||
456 | */ | ||
457 | int hsi_unregister_port_event(struct hsi_client *cl) | ||
441 | { | 458 | { |
442 | struct hsi_client *cl; | 459 | struct hsi_port *port = hsi_get_port(cl); |
460 | int err; | ||
443 | 461 | ||
444 | spin_lock(&port->clock); | 462 | WARN_ON(!hsi_port_claimed(cl)); |
445 | list_for_each_entry(cl, &port->clients, link) { | ||
446 | spin_unlock(&port->clock); | ||
447 | (*fn)(cl, data); | ||
448 | spin_lock(&port->clock); | ||
449 | } | ||
450 | spin_unlock(&port->clock); | ||
451 | 463 | ||
452 | return 0; | 464 | err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb); |
465 | if (!err) | ||
466 | cl->ehandler = NULL; | ||
467 | |||
468 | return err; | ||
453 | } | 469 | } |
470 | EXPORT_SYMBOL_GPL(hsi_unregister_port_event); | ||
454 | 471 | ||
455 | /** | 472 | /** |
456 | * hsi_event -Notifies clients about port events | 473 | * hsi_event -Notifies clients about port events |
@@ -464,22 +481,12 @@ static int hsi_port_for_each_client(struct hsi_port *port, void *data, | |||
464 | * Events: | 481 | * Events: |
465 | * HSI_EVENT_START_RX - Incoming wake line high | 482 | * HSI_EVENT_START_RX - Incoming wake line high |
466 | * HSI_EVENT_STOP_RX - Incoming wake line down | 483 | * HSI_EVENT_STOP_RX - Incoming wake line down |
484 | * | ||
485 | * Returns -errno on error, or 0 on success. | ||
467 | */ | 486 | */ |
468 | void hsi_event(struct hsi_port *port, unsigned int event) | 487 | int hsi_event(struct hsi_port *port, unsigned long event) |
469 | { | 488 | { |
470 | int (*fn)(struct hsi_client *cl, void *data); | 489 | return atomic_notifier_call_chain(&port->n_head, event, NULL); |
471 | |||
472 | switch (event) { | ||
473 | case HSI_EVENT_START_RX: | ||
474 | fn = hsi_start_rx; | ||
475 | break; | ||
476 | case HSI_EVENT_STOP_RX: | ||
477 | fn = hsi_stop_rx; | ||
478 | break; | ||
479 | default: | ||
480 | return; | ||
481 | } | ||
482 | hsi_port_for_each_client(port, NULL, fn); | ||
483 | } | 490 | } |
484 | EXPORT_SYMBOL_GPL(hsi_event); | 491 | EXPORT_SYMBOL_GPL(hsi_event); |
485 | 492 | ||