aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hsi
diff options
context:
space:
mode:
authorCarlos Chinea <carlos.chinea@nokia.com>2012-04-11 03:55:53 -0400
committerCarlos Chinea <carlos.chinea@nokia.com>2012-04-23 07:23:32 -0400
commitec1c56ff813a198d656d4aa42e5de03e45751bf8 (patch)
treeb066f0a692454be661907d6ab91b4194f09c0fca /drivers/hsi
parent6f02b9e9b44a3bfc0046da3ff2707dae0b5e2f30 (diff)
HSI: hsi: Rework hsi_event interface
Remove custom hack and make use of the notifier chain interfaces for delivering events from the ports to their associated clients. Clients that want to receive port events need to register their callbacks using hsi_register_port_event(). The callbacks can be called in interrupt context. Use hsi_unregestier_port_event() to undo the registration. Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/hsi')
-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