diff options
| -rw-r--r-- | drivers/hsi/hsi.c | 101 | ||||
| -rw-r--r-- | include/linux/hsi/hsi.h | 25 |
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 | ||
| 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 | ||
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 | */ |
| 127 | struct hsi_client { | 128 | struct 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 | ||
| 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); | ||
| 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 | */ |
| 220 | struct hsi_port { | 223 | struct 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 | ||
| 241 | void hsi_event(struct hsi_port *port, unsigned int event); | 244 | int hsi_event(struct hsi_port *port, unsigned long event); |
| 242 | int hsi_claim_port(struct hsi_client *cl, unsigned int share); | 245 | int hsi_claim_port(struct hsi_client *cl, unsigned int share); |
| 243 | void hsi_release_port(struct hsi_client *cl); | 246 | void hsi_release_port(struct hsi_client *cl); |
| 244 | 247 | ||
