mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	* properly format/comment all patches * merge debloat patches * merge Kconfig patches * merge swconfig patches * merge hotplug patches * drop 200-fix_localversion.patch - upstream * drop 222-arm_zimage_none.patch - unused * drop 252-mv_cesa_depends.patch - no longer required * drop 410-mtd-move-forward-declaration-of-struct-mtd_info.patch - unused * drop 661-fq_codel_keep_dropped_stats.patch - outdated * drop 702-phy_add_aneg_done_function.patch - upstream * drop 840-rtc7301.patch - unused * drop 841-rtc_pt7c4338.patch - upstream * drop 921-use_preinit_as_init.patch - unused * drop spio-gpio-old and gpio-mmc - unused Signed-off-by: John Crispin <john@phrozen.org>
		
			
				
	
	
		
			185 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From: Mark Brown <broonie@kernel.org>
 | |
| Date: Tue, 9 Dec 2014 21:38:05 +0000
 | |
| Subject: [PATCH] spi: Pump transfers inside calling context for spi_sync()
 | |
| 
 | |
| If we are using the standard SPI message pump (which all drivers should be
 | |
| transitioning over to) then special case the message enqueue and instead of
 | |
| starting the worker thread to push messages to the hardware do so in the
 | |
| context of the caller if the controller is idle. This avoids a context
 | |
| switch in the common case where the controller has a single user in a
 | |
| single thread, for short PIO transfers there may be no need to context
 | |
| switch away from the calling context to complete the transfer.
 | |
| 
 | |
| The code is a bit more complex than is desirable in part due to the need
 | |
| to handle drivers not using the standard queue and in part due to handling
 | |
| the various combinations of bus locking and asynchronous submission in
 | |
| interrupt context.
 | |
| 
 | |
| It is still suboptimal since it will still wake the message pump for each
 | |
| transfer in order to schedule idling of the hardware and if multiple
 | |
| contexts are using the controller simultaneously a caller may end up
 | |
| pumping a message for some random other thread rather than for itself,
 | |
| and if the thread ends up deferring due to another context idling the
 | |
| hardware then it will just busy wait.  It can, however, have the benefit
 | |
| of aggregating power up and down of the hardware when a caller performs
 | |
| a series of transfers back to back without any need for the use of
 | |
| spi_async().
 | |
| 
 | |
| Signed-off-by: Mark Brown <broonie@kernel.org>
 | |
| ---
 | |
| 
 | |
| --- a/drivers/spi/spi.c
 | |
| +++ b/drivers/spi/spi.c
 | |
| @@ -882,6 +882,9 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_t
 | |
|   * needs processing and if so call out to the driver to initialize hardware
 | |
|   * and transfer each message.
 | |
|   *
 | |
| + * Note that it is called both from the kthread itself and also from
 | |
| + * inside spi_sync(); the queue extraction handling at the top of the
 | |
| + * function should deal with this safely.
 | |
|   */
 | |
|  static void spi_pump_messages(struct kthread_work *work)
 | |
|  {
 | |
| @@ -900,6 +903,13 @@ static void spi_pump_messages(struct kth
 | |
|  		return;
 | |
|  	}
 | |
|  
 | |
| +	/* If another context is idling the device then defer */
 | |
| +	if (master->idling) {
 | |
| +		queue_kthread_work(&master->kworker, &master->pump_messages);
 | |
| +		spin_unlock_irqrestore(&master->queue_lock, flags);
 | |
| +		return;
 | |
| +	}
 | |
| +
 | |
|  	/* Check if the queue is idle */
 | |
|  	if (list_empty(&master->queue) || !master->running) {
 | |
|  		if (!master->busy) {
 | |
| @@ -907,7 +917,9 @@ static void spi_pump_messages(struct kth
 | |
|  			return;
 | |
|  		}
 | |
|  		master->busy = false;
 | |
| +		master->idling = true;
 | |
|  		spin_unlock_irqrestore(&master->queue_lock, flags);
 | |
| +
 | |
|  		kfree(master->dummy_rx);
 | |
|  		master->dummy_rx = NULL;
 | |
|  		kfree(master->dummy_tx);
 | |
| @@ -921,6 +933,10 @@ static void spi_pump_messages(struct kth
 | |
|  			pm_runtime_put_autosuspend(master->dev.parent);
 | |
|  		}
 | |
|  		trace_spi_master_idle(master);
 | |
| +
 | |
| +		spin_lock_irqsave(&master->queue_lock, flags);
 | |
| +		master->idling = false;
 | |
| +		spin_unlock_irqrestore(&master->queue_lock, flags);
 | |
|  		return;
 | |
|  	}
 | |
|  
 | |
| @@ -1166,12 +1182,9 @@ static int spi_destroy_queue(struct spi_
 | |
|  	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)
 | |
| +static int __spi_queued_transfer(struct spi_device *spi,
 | |
| +				 struct spi_message *msg,
 | |
| +				 bool need_pump)
 | |
|  {
 | |
|  	struct spi_master *master = spi->master;
 | |
|  	unsigned long flags;
 | |
| @@ -1186,13 +1199,23 @@ static int spi_queued_transfer(struct sp
 | |
|  	msg->status = -EINPROGRESS;
 | |
|  
 | |
|  	list_add_tail(&msg->queue, &master->queue);
 | |
| -	if (!master->busy)
 | |
| +	if (!master->busy && need_pump)
 | |
|  		queue_kthread_work(&master->kworker, &master->pump_messages);
 | |
|  
 | |
|  	spin_unlock_irqrestore(&master->queue_lock, flags);
 | |
|  	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)
 | |
| +{
 | |
| +	return __spi_queued_transfer(spi, msg, true);
 | |
| +}
 | |
| +
 | |
|  static int spi_master_initialize_queue(struct spi_master *master)
 | |
|  {
 | |
|  	int ret;
 | |
| @@ -2104,19 +2127,46 @@ static int __spi_sync(struct spi_device
 | |
|  	DECLARE_COMPLETION_ONSTACK(done);
 | |
|  	int status;
 | |
|  	struct spi_master *master = spi->master;
 | |
| +	unsigned long flags;
 | |
| +
 | |
| +	status = __spi_validate(spi, message);
 | |
| +	if (status != 0)
 | |
| +		return status;
 | |
|  
 | |
|  	message->complete = spi_complete;
 | |
|  	message->context = &done;
 | |
| +	message->spi = spi;
 | |
|  
 | |
|  	if (!bus_locked)
 | |
|  		mutex_lock(&master->bus_lock_mutex);
 | |
|  
 | |
| -	status = spi_async_locked(spi, message);
 | |
| +	/* If we're not using the legacy transfer method then we will
 | |
| +	 * try to transfer in the calling context so special case.
 | |
| +	 * This code would be less tricky if we could remove the
 | |
| +	 * support for driver implemented message queues.
 | |
| +	 */
 | |
| +	if (master->transfer == spi_queued_transfer) {
 | |
| +		spin_lock_irqsave(&master->bus_lock_spinlock, flags);
 | |
| +
 | |
| +		trace_spi_message_submit(message);
 | |
| +
 | |
| +		status = __spi_queued_transfer(spi, message, false);
 | |
| +
 | |
| +		spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
 | |
| +	} else {
 | |
| +		status = spi_async_locked(spi, message);
 | |
| +	}
 | |
|  
 | |
|  	if (!bus_locked)
 | |
|  		mutex_unlock(&master->bus_lock_mutex);
 | |
|  
 | |
|  	if (status == 0) {
 | |
| +		/* Push out the messages in the calling context if we
 | |
| +		 * can.
 | |
| +		 */
 | |
| +		if (master->transfer == spi_queued_transfer)
 | |
| +			spi_pump_messages(&master->pump_messages);
 | |
| +
 | |
|  		wait_for_completion(&done);
 | |
|  		status = message->status;
 | |
|  	}
 | |
| --- a/include/linux/spi/spi.h
 | |
| +++ b/include/linux/spi/spi.h
 | |
| @@ -260,6 +260,7 @@ static inline void spi_unregister_driver
 | |
|   * @pump_messages: work struct for scheduling work to the message pump
 | |
|   * @queue_lock: spinlock to syncronise access to message queue
 | |
|   * @queue: message queue
 | |
| + * @idling: the device is entering idle state
 | |
|   * @cur_msg: the currently in-flight message
 | |
|   * @cur_msg_prepared: spi_prepare_message was called for the currently
 | |
|   *                    in-flight message
 | |
| @@ -425,6 +426,7 @@ struct spi_master {
 | |
|  	spinlock_t			queue_lock;
 | |
|  	struct list_head		queue;
 | |
|  	struct spi_message		*cur_msg;
 | |
| +	bool				idling;
 | |
|  	bool				busy;
 | |
|  	bool				running;
 | |
|  	bool				rt;
 |