aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hsi/hsi.c101
-rw-r--r--include/linux/hsi/hsi.h25
2 files changed, 68 insertions, 58 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
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 7f3b7262a2b6..56fae865e272 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -26,9 +26,9 @@
26#include <linux/device.h> 26#include <linux/device.h>
27#include <linux/mutex.h> 27#include <linux/mutex.h>
28#include <linux/scatterlist.h> 28#include <linux/scatterlist.h>
29#include <linux/spinlock.h>
30#include <linux/list.h> 29#include <linux/list.h>
31#include <linux/module.h> 30#include <linux/module.h>
31#include <linux/notifier.h>
32 32
33/* HSI message ttype */ 33/* HSI message ttype */
34#define HSI_MSG_READ 0 34#define HSI_MSG_READ 0
@@ -121,18 +121,18 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info,
121 * @device: Driver model representation of the device 121 * @device: Driver model representation of the device
122 * @tx_cfg: HSI TX configuration 122 * @tx_cfg: HSI TX configuration
123 * @rx_cfg: HSI RX configuration 123 * @rx_cfg: HSI RX configuration
124 * @hsi_start_rx: Called after incoming wake line goes high 124 * @e_handler: Callback for handling port events (RX Wake High/Low)
125 * @hsi_stop_rx: Called after incoming wake line goes low 125 * @pclaimed: Keeps tracks if the clients claimed its associated HSI port
126 * @nb: Notifier block for port events
126 */ 127 */
127struct hsi_client { 128struct hsi_client {
128 struct device device; 129 struct device device;
129 struct hsi_config tx_cfg; 130 struct hsi_config tx_cfg;
130 struct hsi_config rx_cfg; 131 struct hsi_config rx_cfg;
131 void (*hsi_start_rx)(struct hsi_client *cl);
132 void (*hsi_stop_rx)(struct hsi_client *cl);
133 /* private: */ 132 /* private: */
133 void (*ehandler)(struct hsi_client *, unsigned long);
134 unsigned int pclaimed:1; 134 unsigned int pclaimed:1;
135 struct list_head link; 135 struct notifier_block nb;
136}; 136};
137 137
138#define to_hsi_client(dev) container_of(dev, struct hsi_client, device) 138#define to_hsi_client(dev) container_of(dev, struct hsi_client, device)
@@ -147,6 +147,10 @@ static inline void *hsi_client_drvdata(struct hsi_client *cl)
147 return dev_get_drvdata(&cl->device); 147 return dev_get_drvdata(&cl->device);
148} 148}
149 149
150int hsi_register_port_event(struct hsi_client *cl,
151 void (*handler)(struct hsi_client *, unsigned long));
152int hsi_unregister_port_event(struct hsi_client *cl);
153
150/** 154/**
151 * struct hsi_client_driver - Driver associated to an HSI client 155 * struct hsi_client_driver - Driver associated to an HSI client
152 * @driver: Driver model representation of the driver 156 * @driver: Driver model representation of the driver
@@ -214,8 +218,7 @@ void hsi_free_msg(struct hsi_msg *msg);
214 * @start_tx: Callback to inform that a client wants to TX data 218 * @start_tx: Callback to inform that a client wants to TX data
215 * @stop_tx: Callback to inform that a client no longer wishes to TX data 219 * @stop_tx: Callback to inform that a client no longer wishes to TX data
216 * @release: Callback to inform that a client no longer uses the port 220 * @release: Callback to inform that a client no longer uses the port
217 * @clients: List of hsi_clients using the port. 221 * @n_head: Notifier chain for signaling port events to the clients.
218 * @clock: Lock to serialize access to the clients list.
219 */ 222 */
220struct hsi_port { 223struct hsi_port {
221 struct device device; 224 struct device device;
@@ -231,14 +234,14 @@ struct hsi_port {
231 int (*start_tx)(struct hsi_client *cl); 234 int (*start_tx)(struct hsi_client *cl);
232 int (*stop_tx)(struct hsi_client *cl); 235 int (*stop_tx)(struct hsi_client *cl);
233 int (*release)(struct hsi_client *cl); 236 int (*release)(struct hsi_client *cl);
234 struct list_head clients; 237 /* private */
235 spinlock_t clock; 238 struct atomic_notifier_head n_head;
236}; 239};
237 240
238#define to_hsi_port(dev) container_of(dev, struct hsi_port, device) 241#define to_hsi_port(dev) container_of(dev, struct hsi_port, device)
239#define hsi_get_port(cl) to_hsi_port((cl)->device.parent) 242#define hsi_get_port(cl) to_hsi_port((cl)->device.parent)
240 243
241void hsi_event(struct hsi_port *port, unsigned int event); 244int hsi_event(struct hsi_port *port, unsigned long event);
242int hsi_claim_port(struct hsi_client *cl, unsigned int share); 245int hsi_claim_port(struct hsi_client *cl, unsigned int share);
243void hsi_release_port(struct hsi_client *cl); 246void hsi_release_port(struct hsi_client *cl);
244 247