aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hsi/hsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hsi/hsi.c')
-rw-r--r--drivers/hsi/hsi.c101
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
32static ssize_t modalias_show(struct device *dev, 31static ssize_t modalias_show(struct device *dev,
@@ -67,7 +66,6 @@ static void hsi_client_release(struct device *dev)
67static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) 66static 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
107static int hsi_remove_client(struct device *dev, void *data __maybe_unused) 102static 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}
421EXPORT_SYMBOL_GPL(hsi_release_port); 408EXPORT_SYMBOL_GPL(hsi_release_port);
422 409
423static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused) 410static 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
431static 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 */
432int 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}
446EXPORT_SYMBOL_GPL(hsi_register_port_event);
438 447
439static 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 */
457int 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}
470EXPORT_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 */
468void hsi_event(struct hsi_port *port, unsigned int event) 487int 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}
484EXPORT_SYMBOL_GPL(hsi_event); 491EXPORT_SYMBOL_GPL(hsi_event);
485 492