diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:18:27 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-04-14 13:19:04 +0200 |
commit | 6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch) | |
tree | 021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/spi/spi.c | |
parent | 682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff) | |
parent | a385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff) |
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree),
to prepare for tooling changes, and also to pick up v3.4 MM
changes that the uprobes code needs to take care of.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r-- | drivers/spi/spi.c | 347 |
1 files changed, 342 insertions, 5 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b2ccdea30cb9..3d8f662e4fe9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -30,6 +30,9 @@ #include <linux/of_spi.h> #include <linux/pm_runtime.h> #include <linux/export.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/kthread.h> static void spidev_release(struct device *dev) { @@ -481,7 +484,7 @@ static void spi_match_master_to_boardinfo(struct spi_master *master, * The board info passed can safely be __initdata ... but be careful of * any embedded pointers (platform_data, etc), they're copied as-is. */ -int __init +int __devinit spi_register_board_info(struct spi_board_info const *info, unsigned n) { struct boardinfo *bi; @@ -507,6 +510,294 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) /*-------------------------------------------------------------------------*/ +/** + * spi_pump_messages - kthread work function which processes spi message queue + * @work: pointer to kthread work struct contained in the master struct + * + * This function checks if there is any spi message in the queue that + * needs processing and if so call out to the driver to initialize hardware + * and transfer each message. + * + */ +static void spi_pump_messages(struct kthread_work *work) +{ + struct spi_master *master = + container_of(work, struct spi_master, pump_messages); + unsigned long flags; + bool was_busy = false; + int ret; + + /* Lock queue and check for queue work */ + spin_lock_irqsave(&master->queue_lock, flags); + if (list_empty(&master->queue) || !master->running) { + if (master->busy) { + ret = master->unprepare_transfer_hardware(master); + if (ret) { + spin_unlock_irqrestore(&master->queue_lock, flags); + dev_err(&master->dev, + "failed to unprepare transfer hardware\n"); + return; + } + } + master->busy = false; + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + + /* Make sure we are not already running a message */ + if (master->cur_msg) { + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + /* Extract head of queue */ + master->cur_msg = + list_entry(master->queue.next, struct spi_message, queue); + + list_del_init(&master->cur_msg->queue); + if (master->busy) + was_busy = true; + else + master->busy = true; + spin_unlock_irqrestore(&master->queue_lock, flags); + + if (!was_busy) { + ret = master->prepare_transfer_hardware(master); + if (ret) { + dev_err(&master->dev, + "failed to prepare transfer hardware\n"); + return; + } + } + + ret = master->transfer_one_message(master, master->cur_msg); + if (ret) { + dev_err(&master->dev, + "failed to transfer one message from queue\n"); + return; + } +} + +static int spi_init_queue(struct spi_master *master) +{ + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + + INIT_LIST_HEAD(&master->queue); + spin_lock_init(&master->queue_lock); + + master->running = false; + master->busy = false; + + init_kthread_worker(&master->kworker); + master->kworker_task = kthread_run(kthread_worker_fn, + &master->kworker, + dev_name(&master->dev)); + if (IS_ERR(master->kworker_task)) { + dev_err(&master->dev, "failed to create message pump task\n"); + return -ENOMEM; + } + init_kthread_work(&master->pump_messages, spi_pump_messages); + + /* + * Master config will indicate if this controller should run the + * message pump with high (realtime) priority to reduce the transfer + * latency on the bus by minimising the delay between a transfer + * request and the scheduling of the message pump thread. Without this + * setting the message pump thread will remain at default priority. + */ + if (master->rt) { + dev_info(&master->dev, + "will run message pump with realtime priority\n"); + sched_setscheduler(master->kworker_task, SCHED_FIFO, ¶m); + } + + return 0; +} + +/** + * spi_get_next_queued_message() - called by driver to check for queued + * messages + * @master: the master to check for queued messages + * + * If there are more messages in the queue, the next message is returned from + * this call. + */ +struct spi_message *spi_get_next_queued_message(struct spi_master *master) +{ + struct spi_message *next; + unsigned long flags; + + /* get a pointer to the next message, if any */ + spin_lock_irqsave(&master->queue_lock, flags); + if (list_empty(&master->queue)) + next = NULL; + else + next = list_entry(master->queue.next, + struct spi_message, queue); + spin_unlock_irqrestore(&master->queue_lock, flags); + + return next; +} +EXPORT_SYMBOL_GPL(spi_get_next_queued_message); + +/** + * spi_finalize_current_message() - the current message is complete + * @master: the master to return the message to + * + * Called by the driver to notify the core that the message in the front of the + * queue is complete and can be removed from the queue. + */ +void spi_finalize_current_message(struct spi_master *master) +{ + struct spi_message *mesg; + unsigned long flags; + + spin_lock_irqsave(&master->queue_lock, flags); + mesg = master->cur_msg; + master->cur_msg = NULL; + + queue_kthread_work(&master->kworker, &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + + mesg->state = NULL; + if (mesg->complete) + mesg->complete(mesg->context); +} +EXPORT_SYMBOL_GPL(spi_finalize_current_message); + +static int spi_start_queue(struct spi_master *master) +{ + unsigned long flags; + + spin_lock_irqsave(&master->queue_lock, flags); + + if (master->running || master->busy) { + spin_unlock_irqrestore(&master->queue_lock, flags); + return -EBUSY; + } + + master->running = true; + master->cur_msg = NULL; + spin_unlock_irqrestore(&master->queue_lock, flags); + + queue_kthread_work(&master->kworker, &master->pump_messages); + + return 0; +} + +static int spi_stop_queue(struct spi_master *master) +{ + unsigned long flags; + unsigned limit = 500; + int ret = 0; + + spin_lock_irqsave(&master->queue_lock, flags); + + /* + * This is a bit lame, but is optimized for the common execution path. + * A wait_queue on the master->busy could be used, but then the common + * execution path (pump_messages) would be required to call wake_up or + * friends on every SPI message. Do this instead. + */ + while ((!list_empty(&master->queue) || master->busy) && limit--) { + spin_unlock_irqrestore(&master->queue_lock, flags); + msleep(10); + spin_lock_irqsave(&master->queue_lock, flags); + } + + if (!list_empty(&master->queue) || master->busy) + ret = -EBUSY; + else + master->running = false; + + spin_unlock_irqrestore(&master->queue_lock, flags); + + if (ret) { + dev_warn(&master->dev, + "could not stop message queue\n"); + return ret; + } + return ret; +} + +static int spi_destroy_queue(struct spi_master *master) +{ + int ret; + + ret = spi_stop_queue(master); + + /* + * flush_kthread_worker will block until all work is done. + * If the reason that stop_queue timed out is that the work will never + * finish, then it does no good to call flush/stop thread, so + * return anyway. + */ + if (ret) { + dev_err(&master->dev, "problem destroying queue\n"); + return ret; + } + + flush_kthread_worker(&master->kworker); + kthread_stop(master->kworker_task); + + return 0; +} + +/** + * spi_queued_transfer - transfer function for queued transfers + * @spi: spi device which is requesting transfer + * @msg: spi message which is to handled is queued to driver queue + */ +static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct spi_master *master = spi->master; + unsigned long flags; + + spin_lock_irqsave(&master->queue_lock, flags); + + if (!master->running) { + spin_unlock_irqrestore(&master->queue_lock, flags); + return -ESHUTDOWN; + } + msg->actual_length = 0; + msg->status = -EINPROGRESS; + + list_add_tail(&msg->queue, &master->queue); + if (master->running && !master->busy) + queue_kthread_work(&master->kworker, &master->pump_messages); + + spin_unlock_irqrestore(&master->queue_lock, flags); + return 0; +} + +static int spi_master_initialize_queue(struct spi_master *master) +{ + int ret; + + master->queued = true; + master->transfer = spi_queued_transfer; + + /* Initialize and start queue */ + ret = spi_init_queue(master); + if (ret) { + dev_err(&master->dev, "problem initializing queue\n"); + goto err_init_queue; + } + ret = spi_start_queue(master); + if (ret) { + dev_err(&master->dev, "problem starting queue\n"); + goto err_start_queue; + } + + return 0; + +err_start_queue: +err_init_queue: + spi_destroy_queue(master); + return ret; +} + +/*-------------------------------------------------------------------------*/ + static void spi_master_release(struct device *dev) { struct spi_master *master; @@ -522,6 +813,7 @@ static struct class spi_master_class = { }; + /** * spi_alloc_master - allocate SPI master controller * @dev: the controller, possibly using the platform_bus @@ -539,7 +831,8 @@ static struct class spi_master_class = { * * The caller is responsible for assigning the bus number and initializing * the master's methods before calling spi_register_master(); and (after errors - * adding the device) calling spi_master_put() to prevent a memory leak. + * adding the device) calling spi_master_put() and kfree() to prevent a memory + * leak. */ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { @@ -621,14 +914,23 @@ int spi_register_master(struct spi_master *master) dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); + /* If we're using a queued driver, start the queue */ + if (master->transfer) + dev_info(dev, "master is unqueued, this is deprecated\n"); + else { + status = spi_master_initialize_queue(master); + if (status) { + device_unregister(&master->dev); + goto done; + } + } + mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); list_for_each_entry(bi, &board_list, list) spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); - status = 0; - /* Register devices from the device tree */ of_register_spi_devices(master); done: @@ -636,7 +938,6 @@ done: } EXPORT_SYMBOL_GPL(spi_register_master); - static int __unregister(struct device *dev, void *null) { spi_unregister_device(to_spi_device(dev)); @@ -657,6 +958,11 @@ void spi_unregister_master(struct spi_master *master) { int dummy; + if (master->queued) { + if (spi_destroy_queue(master)) + dev_err(&master->dev, "queue remove failed\n"); + } + mutex_lock(&board_lock); list_del(&master->list); mutex_unlock(&board_lock); @@ -666,6 +972,37 @@ void spi_unregister_master(struct spi_master *master) } EXPORT_SYMBOL_GPL(spi_unregister_master); +int spi_master_suspend(struct spi_master *master) +{ + int ret; + + /* Basically no-ops for non-queued masters */ + if (!master->queued) + return 0; + + ret = spi_stop_queue(master); + if (ret) + dev_err(&master->dev, "queue stop failed\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_master_suspend); + +int spi_master_resume(struct spi_master *master) +{ + int ret; + + if (!master->queued) + return 0; + + ret = spi_start_queue(master); + if (ret) + dev_err(&master->dev, "queue restart failed\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_master_resume); + static int __spi_master_match(struct device *dev, void *data) { struct spi_master *m; |