Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+68 -58
+54 -47
drivers/hsi/hsi.c
··· 21 21 */ 22 22 #include <linux/hsi/hsi.h> 23 23 #include <linux/compiler.h> 24 - #include <linux/rwsem.h> 25 24 #include <linux/list.h> 26 - #include <linux/spinlock.h> 27 25 #include <linux/kobject.h> 28 26 #include <linux/slab.h> 29 27 #include <linux/string.h> 28 + #include <linux/notifier.h> 30 29 #include "hsi_core.h" 31 30 32 31 static ssize_t modalias_show(struct device *dev, ··· 66 67 static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) 67 68 { 68 69 struct hsi_client *cl; 69 - unsigned long flags; 70 70 71 71 cl = kzalloc(sizeof(*cl), GFP_KERNEL); 72 72 if (!cl) ··· 77 79 cl->device.release = hsi_client_release; 78 80 dev_set_name(&cl->device, info->name); 79 81 cl->device.platform_data = info->platform_data; 80 - spin_lock_irqsave(&port->clock, flags); 81 - list_add_tail(&cl->link, &port->clients); 82 - spin_unlock_irqrestore(&port->clock, flags); 83 82 if (info->archdata) 84 83 cl->device.archdata = *info->archdata; 85 84 if (device_register(&cl->device) < 0) { ··· 101 106 102 107 static int hsi_remove_client(struct device *dev, void *data __maybe_unused) 103 108 { 104 - struct hsi_client *cl = to_hsi_client(dev); 105 - struct hsi_port *port = to_hsi_port(dev->parent); 106 - unsigned long flags; 107 - 108 - spin_lock_irqsave(&port->clock, flags); 109 - list_del(&cl->link); 110 - spin_unlock_irqrestore(&port->clock, flags); 111 109 device_unregister(dev); 112 110 113 111 return 0; ··· 259 271 port[i]->stop_tx = hsi_dummy_cl; 260 272 port[i]->release = hsi_dummy_cl; 261 273 mutex_init(&port[i]->lock); 262 - INIT_LIST_HEAD(&hsi->port[i]->clients); 263 - spin_lock_init(&hsi->port[i]->clock); 274 + ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head); 264 275 dev_set_name(&port[i]->device, "port%d", i); 265 276 hsi->port[i]->device.release = hsi_port_release; 266 277 device_initialize(&hsi->port[i]->device); ··· 407 420 } 408 421 EXPORT_SYMBOL_GPL(hsi_release_port); 409 422 410 - static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused) 423 + static int hsi_event_notifier_call(struct notifier_block *nb, 424 + unsigned long event, void *data __maybe_unused) 411 425 { 412 - if (cl->hsi_start_rx) 413 - (*cl->hsi_start_rx)(cl); 426 + struct hsi_client *cl = container_of(nb, struct hsi_client, nb); 427 + 428 + (*cl->ehandler)(cl, event); 414 429 415 430 return 0; 416 431 } 417 432 418 - static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused) 433 + /** 434 + * hsi_register_port_event - Register a client to receive port events 435 + * @cl: HSI client that wants to receive port events 436 + * @cb: Event handler callback 437 + * 438 + * Clients should register a callback to be able to receive 439 + * events from the ports. Registration should happen after 440 + * claiming the port. 441 + * The handler can be called in interrupt context. 442 + * 443 + * Returns -errno on error, or 0 on success. 444 + */ 445 + int hsi_register_port_event(struct hsi_client *cl, 446 + void (*handler)(struct hsi_client *, unsigned long)) 419 447 { 420 - if (cl->hsi_stop_rx) 421 - (*cl->hsi_stop_rx)(cl); 448 + struct hsi_port *port = hsi_get_port(cl); 422 449 423 - return 0; 450 + if (!handler || cl->ehandler) 451 + return -EINVAL; 452 + if (!hsi_port_claimed(cl)) 453 + return -EACCES; 454 + cl->ehandler = handler; 455 + cl->nb.notifier_call = hsi_event_notifier_call; 456 + 457 + return atomic_notifier_chain_register(&port->n_head, &cl->nb); 424 458 } 459 + EXPORT_SYMBOL_GPL(hsi_register_port_event); 425 460 426 - static int hsi_port_for_each_client(struct hsi_port *port, void *data, 427 - int (*fn)(struct hsi_client *cl, void *data)) 461 + /** 462 + * hsi_unregister_port_event - Stop receiving port events for a client 463 + * @cl: HSI client that wants to stop receiving port events 464 + * 465 + * Clients should call this function before releasing their associated 466 + * port. 467 + * 468 + * Returns -errno on error, or 0 on success. 469 + */ 470 + int hsi_unregister_port_event(struct hsi_client *cl) 428 471 { 429 - struct hsi_client *cl; 472 + struct hsi_port *port = hsi_get_port(cl); 473 + int err; 430 474 431 - spin_lock(&port->clock); 432 - list_for_each_entry(cl, &port->clients, link) { 433 - spin_unlock(&port->clock); 434 - (*fn)(cl, data); 435 - spin_lock(&port->clock); 436 - } 437 - spin_unlock(&port->clock); 475 + WARN_ON(!hsi_port_claimed(cl)); 438 476 439 - return 0; 477 + err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb); 478 + if (!err) 479 + cl->ehandler = NULL; 480 + 481 + return err; 440 482 } 483 + EXPORT_SYMBOL_GPL(hsi_unregister_port_event); 441 484 442 485 /** 443 486 * hsi_event -Notifies clients about port events ··· 481 464 * Events: 482 465 * HSI_EVENT_START_RX - Incoming wake line high 483 466 * HSI_EVENT_STOP_RX - Incoming wake line down 467 + * 468 + * Returns -errno on error, or 0 on success. 484 469 */ 485 - void hsi_event(struct hsi_port *port, unsigned int event) 470 + int hsi_event(struct hsi_port *port, unsigned long event) 486 471 { 487 - int (*fn)(struct hsi_client *cl, void *data); 488 - 489 - switch (event) { 490 - case HSI_EVENT_START_RX: 491 - fn = hsi_start_rx; 492 - break; 493 - case HSI_EVENT_STOP_RX: 494 - fn = hsi_stop_rx; 495 - break; 496 - default: 497 - return; 498 - } 499 - hsi_port_for_each_client(port, NULL, fn); 472 + return atomic_notifier_call_chain(&port->n_head, event, NULL); 500 473 } 501 474 EXPORT_SYMBOL_GPL(hsi_event); 502 475
+14 -11
include/linux/hsi/hsi.h
··· 26 26 #include <linux/device.h> 27 27 #include <linux/mutex.h> 28 28 #include <linux/scatterlist.h> 29 - #include <linux/spinlock.h> 30 29 #include <linux/list.h> 31 30 #include <linux/module.h> 31 + #include <linux/notifier.h> 32 32 33 33 /* HSI message ttype */ 34 34 #define HSI_MSG_READ 0 ··· 121 121 * @device: Driver model representation of the device 122 122 * @tx_cfg: HSI TX configuration 123 123 * @rx_cfg: HSI RX configuration 124 - * @hsi_start_rx: Called after incoming wake line goes high 125 - * @hsi_stop_rx: Called after incoming wake line goes low 124 + * @e_handler: Callback for handling port events (RX Wake High/Low) 125 + * @pclaimed: Keeps tracks if the clients claimed its associated HSI port 126 + * @nb: Notifier block for port events 126 127 */ 127 128 struct hsi_client { 128 129 struct device device; 129 130 struct hsi_config tx_cfg; 130 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 132 /* private: */ 133 + void (*ehandler)(struct hsi_client *, unsigned long); 134 134 unsigned int pclaimed:1; 135 - struct list_head link; 135 + struct notifier_block nb; 136 136 }; 137 137 138 138 #define to_hsi_client(dev) container_of(dev, struct hsi_client, device) ··· 146 146 { 147 147 return dev_get_drvdata(&cl->device); 148 148 } 149 + 150 + int hsi_register_port_event(struct hsi_client *cl, 151 + void (*handler)(struct hsi_client *, unsigned long)); 152 + int hsi_unregister_port_event(struct hsi_client *cl); 149 153 150 154 /** 151 155 * struct hsi_client_driver - Driver associated to an HSI client ··· 218 214 * @start_tx: Callback to inform that a client wants to TX data 219 215 * @stop_tx: Callback to inform that a client no longer wishes to TX data 220 216 * @release: Callback to inform that a client no longer uses the port 221 - * @clients: List of hsi_clients using the port. 222 - * @clock: Lock to serialize access to the clients list. 217 + * @n_head: Notifier chain for signaling port events to the clients. 223 218 */ 224 219 struct hsi_port { 225 220 struct device device; ··· 234 231 int (*start_tx)(struct hsi_client *cl); 235 232 int (*stop_tx)(struct hsi_client *cl); 236 233 int (*release)(struct hsi_client *cl); 237 - struct list_head clients; 238 - spinlock_t clock; 234 + /* private */ 235 + struct atomic_notifier_head n_head; 239 236 }; 240 237 241 238 #define to_hsi_port(dev) container_of(dev, struct hsi_port, device) 242 239 #define hsi_get_port(cl) to_hsi_port((cl)->device.parent) 243 240 244 - void hsi_event(struct hsi_port *port, unsigned int event); 241 + int hsi_event(struct hsi_port *port, unsigned long event); 245 242 int hsi_claim_port(struct hsi_client *cl, unsigned int share); 246 243 void hsi_release_port(struct hsi_client *cl); 247 244