Re: [PATCH v2 1/1] spi: dw: move to SPI core message handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 2015-02-24 at 18:17 +0200, Andy Shevchenko wrote:
> This patch removes a lot of duplicate code since SPI core provides a nice
> message handling.
> 
> Signed-off-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
> ---
> Changelog v2:
> - remove leftovers from spi-dw.h
> - remove unused members of dw_spi, i.e. cur_dev, max_bits_per_word
> - reuse chip->cs_control directly

So, I have few concerns / questions regarding to move to use SPI core
message handling.

1. The message status override (discussion in a separate thread).
Summary: we may and must use message->status to indicate an error, if
any, is happened during transfer, e.g. overrun/underrun interrupt, am I
correct?

2. How to handle timeout error in case of asynchronous transfers, when
transfer_one() sets DMA and returns 1? In transfer_one() I setup DMA
descriptors and in case of bad luck and getting many timeouts in a row
the DMA Engine would come to lack of memory, thus I would like to
terminate any ongoing DMA transfers. Moreover, spi_unmap_msg() does
unmapping before calling unprepare_message(). I'm not sure if it's okay
in DMA case to terminate transfers in unprepare_message(), would it be
already late?

3. This patch perhaps needs one more thing to be incorporated, namely
proper recovering from overrun/underrun error. There is an SPI
controller is disabled and if we keep going with the same settings
(speed, bits_per_word, DMA / PIO, etc.) we never enable controller back.
I didn't check if it is a valid issue for the original code as well.

> 
>  drivers/spi/spi-dw-mid.c |   4 +-
>  drivers/spi/spi-dw.c     | 186 +++++++++++------------------------------------
>  drivers/spi/spi-dw.h     |  26 -------
>  3 files changed, 46 insertions(+), 170 deletions(-)
> 
> diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
> index a0197fd..8f68e82 100644
> --- a/drivers/spi/spi-dw-mid.c
> +++ b/drivers/spi/spi-dw-mid.c
> @@ -110,7 +110,7 @@ static void dw_spi_dma_tx_done(void *arg)
>  
>  	if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY))
>  		return;
> -	dw_spi_xfer_done(dws);
> +	spi_finalize_current_transfer(dws->master);
>  }
>  
>  static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws)
> @@ -155,7 +155,7 @@ static void dw_spi_dma_rx_done(void *arg)
>  
>  	if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY))
>  		return;
> -	dw_spi_xfer_done(dws);
> +	spi_finalize_current_transfer(dws->master);
>  }
>  
>  static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
> diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
> index 281121f..0003ba5 100644
> --- a/drivers/spi/spi-dw.c
> +++ b/drivers/spi/spi-dw.c
> @@ -28,11 +28,6 @@
>  #include <linux/debugfs.h>
>  #endif
>  
> -#define START_STATE	((void *)0)
> -#define RUNNING_STATE	((void *)1)
> -#define DONE_STATE	((void *)2)
> -#define ERROR_STATE	((void *)-1)
> -
>  /* Slave spi_dev related */
>  struct chip_data {
>  	u16 cr0;
> @@ -143,6 +138,19 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
>  }
>  #endif /* CONFIG_DEBUG_FS */
>  
> +static void dw_spi_set_cs(struct spi_device *spi, bool enable)
> +{
> +	struct dw_spi *dws = spi_master_get_devdata(spi->master);
> +	struct chip_data *chip = spi_get_ctldata(spi);
> +
> +	/* Chip select logic is inverted from spi_set_cs() */
> +	if (chip->cs_control)
> +		chip->cs_control(!enable);
> +
> +	if (!enable)
> +		dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
> +}
> +
>  /* Return the max entries we can fill into tx fifo */
>  static inline u32 tx_max(struct dw_spi *dws)
>  {
> @@ -209,93 +217,41 @@ static void dw_reader(struct dw_spi *dws)
>  	}
>  }
>  
> -static void *next_transfer(struct dw_spi *dws)
> -{
> -	struct spi_message *msg = dws->cur_msg;
> -	struct spi_transfer *trans = dws->cur_transfer;
> -
> -	/* Move to next transfer */
> -	if (trans->transfer_list.next != &msg->transfers) {
> -		dws->cur_transfer =
> -			list_entry(trans->transfer_list.next,
> -					struct spi_transfer,
> -					transfer_list);
> -		return RUNNING_STATE;
> -	}
> -
> -	return DONE_STATE;
> -}
> -
>  /*
>   * Note: first step is the protocol driver prepares
>   * a dma-capable memory, and this func just need translate
>   * the virt addr to physical
>   */
> -static int map_dma_buffers(struct dw_spi *dws)
> +static int map_dma_buffers(struct spi_master *master,
> +		struct spi_device *spi, struct spi_transfer *transfer)
>  {
> -	if (!dws->cur_msg->is_dma_mapped
> +	struct dw_spi *dws = spi_master_get_devdata(master);
> +	struct chip_data *chip = spi_get_ctldata(spi);
> +
> +	if (!master->cur_msg->is_dma_mapped
>  		|| !dws->dma_inited
> -		|| !dws->cur_chip->enable_dma
> +		|| !chip->enable_dma
>  		|| !dws->dma_ops)
>  		return 0;
>  
> -	if (dws->cur_transfer->tx_dma)
> -		dws->tx_dma = dws->cur_transfer->tx_dma;
> +	if (transfer->tx_dma)
> +		dws->tx_dma = transfer->tx_dma;
>  
> -	if (dws->cur_transfer->rx_dma)
> -		dws->rx_dma = dws->cur_transfer->rx_dma;
> +	if (transfer->rx_dma)
> +		dws->rx_dma = transfer->rx_dma;
>  
>  	return 1;
>  }
>  
> -/* Caller already set message->status; dma and pio irqs are blocked */
> -static void giveback(struct dw_spi *dws)
> -{
> -	struct spi_transfer *last_transfer;
> -	struct spi_message *msg;
> -
> -	msg = dws->cur_msg;
> -	dws->cur_msg = NULL;
> -	dws->cur_transfer = NULL;
> -	dws->prev_chip = dws->cur_chip;
> -	dws->cur_chip = NULL;
> -	dws->dma_mapped = 0;
> -
> -	last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
> -					transfer_list);
> -
> -	if (!last_transfer->cs_change)
> -		spi_chip_sel(dws, msg->spi, 0);
> -
> -	spi_finalize_current_message(dws->master);
> -}
> -
>  static void int_error_stop(struct dw_spi *dws, const char *msg)
>  {
>  	/* Stop the hw */
>  	spi_enable_chip(dws, 0);
>  
>  	dev_err(&dws->master->dev, "%s\n", msg);
> -	dws->cur_msg->state = ERROR_STATE;
> -	tasklet_schedule(&dws->pump_transfers);
> -}
> -
> -void dw_spi_xfer_done(struct dw_spi *dws)
> -{
> -	/* Update total byte transferred return count actual bytes read */
> -	dws->cur_msg->actual_length += dws->len;
> -
> -	/* Move to next transfer */
> -	dws->cur_msg->state = next_transfer(dws);
> -
> -	/* Handle end of message */
> -	if (dws->cur_msg->state == DONE_STATE) {
> -		dws->cur_msg->status = 0;
> -		giveback(dws);
> -	} else
> -		tasklet_schedule(&dws->pump_transfers);
> +	dws->master->cur_msg->status = -EIO;
> +	spi_finalize_current_transfer(dws->master);
>  }
> -EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
>  
>  static irqreturn_t interrupt_transfer(struct dw_spi *dws)
>  {
> @@ -313,7 +269,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
>  	dw_reader(dws);
>  	if (dws->rx_end == dws->rx) {
>  		spi_mask_intr(dws, SPI_INT_TXEI);
> -		dw_spi_xfer_done(dws);
> +		spi_finalize_current_transfer(dws->master);
>  		return IRQ_HANDLED;
>  	}
>  	if (irq_status & SPI_INT_TXEI) {
> @@ -328,13 +284,14 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
>  
>  static irqreturn_t dw_spi_irq(int irq, void *dev_id)
>  {
> -	struct dw_spi *dws = dev_id;
> +	struct spi_master *master = dev_id;
> +	struct dw_spi *dws = spi_master_get_devdata(master);
>  	u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f;
>  
>  	if (!irq_status)
>  		return IRQ_NONE;
>  
> -	if (!dws->cur_msg) {
> +	if (!master->cur_msg) {
>  		spi_mask_intr(dws, SPI_INT_TXEI);
>  		return IRQ_HANDLED;
>  	}
> @@ -343,7 +300,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
>  }
>  
>  /* Must be called inside pump_transfers() */
> -static void poll_transfer(struct dw_spi *dws)
> +static int poll_transfer(struct dw_spi *dws)
>  {
>  	do {
>  		dw_writer(dws);
> @@ -351,17 +308,14 @@ static void poll_transfer(struct dw_spi *dws)
>  		cpu_relax();
>  	} while (dws->rx_end > dws->rx);
>  
> -	dw_spi_xfer_done(dws);
> +	return 0;
>  }
>  
> -static void pump_transfers(unsigned long data)
> +static int dw_spi_transfer_one(struct spi_master *master,
> +		struct spi_device *spi, struct spi_transfer *transfer)
>  {
> -	struct dw_spi *dws = (struct dw_spi *)data;
> -	struct spi_message *message = NULL;
> -	struct spi_transfer *transfer = NULL;
> -	struct spi_transfer *previous = NULL;
> -	struct spi_device *spi = NULL;
> -	struct chip_data *chip = NULL;
> +	struct dw_spi *dws = spi_master_get_devdata(master);
> +	struct chip_data *chip = spi_get_ctldata(spi);
>  	u8 bits = 0;
>  	u8 imask = 0;
>  	u8 cs_change = 0;
> @@ -370,35 +324,8 @@ static void pump_transfers(unsigned long data)
>  	u32 speed = 0;
>  	u32 cr0 = 0;
>  
> -	/* Get current state information */
> -	message = dws->cur_msg;
> -	transfer = dws->cur_transfer;
> -	chip = dws->cur_chip;
> -	spi = message->spi;
> -
> -	if (message->state == ERROR_STATE) {
> -		message->status = -EIO;
> -		goto early_exit;
> -	}
> -
> -	/* Handle end of message */
> -	if (message->state == DONE_STATE) {
> -		message->status = 0;
> -		goto early_exit;
> -	}
> -
> -	/* Delay if requested at end of transfer */
> -	if (message->state == RUNNING_STATE) {
> -		previous = list_entry(transfer->transfer_list.prev,
> -					struct spi_transfer,
> -					transfer_list);
> -		if (previous->delay_usecs)
> -			udelay(previous->delay_usecs);
> -	}
> -
>  	dws->n_bytes = chip->n_bytes;
>  	dws->dma_width = chip->dma_width;
> -	dws->cs_control = chip->cs_control;
>  
>  	dws->rx_dma = transfer->rx_dma;
>  	dws->tx_dma = transfer->tx_dma;
> @@ -406,7 +333,7 @@ static void pump_transfers(unsigned long data)
>  	dws->tx_end = dws->tx + transfer->len;
>  	dws->rx = transfer->rx_buf;
>  	dws->rx_end = dws->rx + transfer->len;
> -	dws->len = dws->cur_transfer->len;
> +	dws->len = transfer->len;
>  	if (chip != dws->prev_chip)
>  		cs_change = 1;
>  
> @@ -434,13 +361,12 @@ static void pump_transfers(unsigned long data)
>  			| (spi->mode << SPI_MODE_OFFSET)
>  			| (chip->tmode << SPI_TMOD_OFFSET);
>  	}
> -	message->state = RUNNING_STATE;
>  
>  	/*
>  	 * Adjust transfer mode if necessary. Requires platform dependent
>  	 * chipselect mechanism.
>  	 */
> -	if (dws->cs_control) {
> +	if (chip->cs_control) {
>  		if (dws->rx && dws->tx)
>  			chip->tmode = SPI_TMOD_TR;
>  		else if (dws->rx)
> @@ -453,7 +379,7 @@ static void pump_transfers(unsigned long data)
>  	}
>  
>  	/* Check if current transfer is a DMA transaction */
> -	dws->dma_mapped = map_dma_buffers(dws);
> +	dws->dma_mapped = map_dma_buffers(master, spi, transfer);
>  
>  	/*
>  	 * Interrupt mode
> @@ -479,7 +405,6 @@ static void pump_transfers(unsigned long data)
>  		dw_writew(dws, DW_SPI_CTRL0, cr0);
>  
>  		spi_set_clk(dws, chip->clk_div);
> -		spi_chip_sel(dws, spi, 1);
>  
>  		/* Set the interrupt mask, for poll mode just disable all int */
>  		spi_mask_intr(dws, 0xff);
> @@ -498,31 +423,9 @@ static void pump_transfers(unsigned long data)
>  		dws->dma_ops->dma_transfer(dws, cs_change);
>  
>  	if (chip->poll_mode)
> -		poll_transfer(dws);
> -
> -	return;
> +		return poll_transfer(dws);
>  
> -early_exit:
> -	giveback(dws);
> -}
> -
> -static int dw_spi_transfer_one_message(struct spi_master *master,
> -		struct spi_message *msg)
> -{
> -	struct dw_spi *dws = spi_master_get_devdata(master);
> -
> -	dws->cur_msg = msg;
> -	/* Initial message state */
> -	dws->cur_msg->state = START_STATE;
> -	dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
> -						struct spi_transfer,
> -						transfer_list);
> -	dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
> -
> -	/* Launch transfers */
> -	tasklet_schedule(&dws->pump_transfers);
> -
> -	return 0;
> +	return dws->len;
>  }
>  
>  /* This may be called twice for each spi dev */
> @@ -648,7 +551,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
>  	snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
>  
>  	ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
> -			dws->name, dws);
> +			dws->name, master);
>  	if (ret < 0) {
>  		dev_err(&master->dev, "can not get IRQ\n");
>  		goto err_free_master;
> @@ -660,7 +563,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
>  	master->num_chipselect = dws->num_cs;
>  	master->setup = dw_spi_setup;
>  	master->cleanup = dw_spi_cleanup;
> -	master->transfer_one_message = dw_spi_transfer_one_message;
> +	master->set_cs = dw_spi_set_cs;
> +	master->transfer_one = dw_spi_transfer_one;
>  	master->max_speed_hz = dws->max_freq;
>  	master->dev.of_node = dev->of_node;
>  
> @@ -675,8 +579,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
>  		}
>  	}
>  
> -	tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws);
> -
>  	spi_master_set_devdata(master, dws);
>  	ret = devm_spi_register_master(dev, master);
>  	if (ret) {
> diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
> index 3d32be6..70cad09 100644
> --- a/drivers/spi/spi-dw.h
> +++ b/drivers/spi/spi-dw.h
> @@ -96,7 +96,6 @@ struct dw_spi_dma_ops {
>  
>  struct dw_spi {
>  	struct spi_master	*master;
> -	struct spi_device	*cur_dev;
>  	enum dw_ssi_type	type;
>  	char			name[16];
>  
> @@ -109,13 +108,7 @@ struct dw_spi {
>  	u16			bus_num;
>  	u16			num_cs;		/* supported slave numbers */
>  
> -	/* Message Transfer pump */
> -	struct tasklet_struct	pump_transfers;
> -
>  	/* Current message transfer state info */
> -	struct spi_message	*cur_msg;
> -	struct spi_transfer	*cur_transfer;
> -	struct chip_data	*cur_chip;
>  	struct chip_data	*prev_chip;
>  	size_t			len;
>  	void			*tx;
> @@ -128,10 +121,8 @@ struct dw_spi {
>  	size_t			rx_map_len;
>  	size_t			tx_map_len;
>  	u8			n_bytes;	/* current is a 1/2 bytes op */
> -	u8			max_bits_per_word;	/* maxim is 16b */
>  	u32			dma_width;
>  	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
> -	void			(*cs_control)(u32 command);
>  
>  	/* Dma info */
>  	int			dma_inited;
> @@ -182,22 +173,6 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
>  	dw_writel(dws, DW_SPI_BAUDR, div);
>  }
>  
> -static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
> -		int active)
> -{
> -	u16 cs = spi->chip_select;
> -	int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
> -		!(spi->mode & SPI_CS_HIGH);
> -
> -	if (dws->cs_control)
> -		dws->cs_control(active);
> -	if (gpio_is_valid(spi->cs_gpio))
> -		gpio_set_value(spi->cs_gpio, gpio_val);
> -
> -	if (active)
> -		dw_writel(dws, DW_SPI_SER, 1 << cs);
> -}
> -
>  /* Disable IRQ bits */
>  static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
>  {
> @@ -233,7 +208,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
>  extern void dw_spi_remove_host(struct dw_spi *dws);
>  extern int dw_spi_suspend_host(struct dw_spi *dws);
>  extern int dw_spi_resume_host(struct dw_spi *dws);
> -extern void dw_spi_xfer_done(struct dw_spi *dws);
>  
>  /* platform related setup */
>  extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */


-- 
Andy Shevchenko <andriy.shevchenko@xxxxxxxxx>
Intel Finland Oy

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux