From 39bc03bfecab38e7532449d54aeef3db817084af Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 24 Feb 2015 13:32:10 +0200 Subject: spi: dw: move piece of code out of condition There is no sense to keep a member assignment in the internal structure inside the condition which reprograms HW. It makes code readability better if kept outside of the condition. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 5a97a62b298a..29157f00f45a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -494,10 +494,11 @@ static void pump_transfers(unsigned long data) dw_writew(dws, DW_SPI_TXFLTR, txint_level); spi_enable_chip(dws, 1); - if (cs_change) - dws->prev_chip = chip; } + if (cs_change) + dws->prev_chip = chip; + if (dws->dma_mapped) dws->dma_ops->dma_transfer(dws, cs_change); -- cgit v1.2.3 From ea11370fffdfedbd0cca0fce17907d2c993246bc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 24 Feb 2015 13:32:11 +0200 Subject: spi: dw: get TX level without an additional variable There is no need to have an additional variable to get a TX level. The patch refactors this piece of code. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 29157f00f45a..0febb97ae32f 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -365,7 +365,7 @@ static void pump_transfers(unsigned long data) u8 bits = 0; u8 imask = 0; u8 cs_change = 0; - u16 txint_level = 0; + u16 txlevel = 0; u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; @@ -461,10 +461,7 @@ static void pump_transfers(unsigned long data) * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ if (!dws->dma_mapped && !chip->poll_mode) { - int templen = dws->len / dws->n_bytes; - - txint_level = dws->fifo_len / 2; - txint_level = (templen > txint_level) ? txint_level : templen; + txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI; @@ -490,8 +487,8 @@ static void pump_transfers(unsigned long data) spi_mask_intr(dws, 0xff); if (imask) spi_umask_intr(dws, imask); - if (txint_level) - dw_writew(dws, DW_SPI_TXFLTR, txint_level); + if (txlevel) + dw_writew(dws, DW_SPI_TXFLTR, txlevel); spi_enable_chip(dws, 1); } -- cgit v1.2.3 From 341c7dc7c074e80d7344e0d75e2b8918ffc982fb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 24 Feb 2015 13:32:12 +0200 Subject: spi: dw: refactor code that handles clk_div This patch does the following changes: a) the calculation of clk_div is simplified to oneliner; b) chip->clk_div is updated if clk_div is not zero, therefore the condition is simplified by using chip->clk_div in both cases; c) while here, the redundant parentheses are removed. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0febb97ae32f..0e0c48b37b83 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -416,12 +416,11 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) { speed = chip->speed_hz; - if ((transfer->speed_hz != speed) || (!chip->clk_div)) { + if ((transfer->speed_hz != speed) || !chip->clk_div) { speed = transfer->speed_hz; /* clk_div doesn't support odd number */ - clk_div = dws->max_freq / speed; - clk_div = (clk_div + 1) & 0xfffe; + clk_div = (dws->max_freq / speed + 1) & 0xfffe; chip->speed_hz = speed; chip->clk_div = clk_div; @@ -480,7 +479,7 @@ static void pump_transfers(unsigned long data) if (dw_readw(dws, DW_SPI_CTRL0) != cr0) dw_writew(dws, DW_SPI_CTRL0, cr0); - spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); + spi_set_clk(dws, chip->clk_div); spi_chip_sel(dws, spi, 1); /* Set the interrupt mask, for poll mode just disable all int */ -- cgit v1.2.3 From 1a18f9f753209977450c94dcd354dd4fa5370966 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 24 Feb 2015 13:32:13 +0200 Subject: spi: dw: always reprogram CTRL0 Instead of an additional reading from the register let's update it even if the value is kept the same. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0e0c48b37b83..281121f00138 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -476,8 +476,7 @@ static void pump_transfers(unsigned long data) if (dw_readw(dws, DW_SPI_CTRL0) != cr0 || cs_change || clk_div || imask) { spi_enable_chip(dws, 0); - if (dw_readw(dws, DW_SPI_CTRL0) != cr0) - dw_writew(dws, DW_SPI_CTRL0, cr0); + dw_writew(dws, DW_SPI_CTRL0, cr0); spi_set_clk(dws, chip->clk_div); spi_chip_sel(dws, spi, 1); -- cgit v1.2.3 From 45746e82cf89f432f9c986a52137d8a64b78aba9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2015 14:58:55 +0200 Subject: spi: dw: make sure SPI controller is enabled The error handling is partially broken since the controller is disabled on error and is not re-enabled until condition occurs, i.e. mode (poll, PIO/DMA), chip (cs_change), or speed (clk_div) is changed. In the result of these changes we will have a predictable state of the SPi controller independently on how successfull was a previous transfer. The patch disables interrupts and re-enables the SPI controller wherever it needs to be done. Thus most of the time the SPI controller is kept enabled. The runtime PM, when it will be implemented, must take care of the controller disabling and re-enabling. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 281121f00138..321965607fc0 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -272,8 +272,7 @@ static void giveback(struct dw_spi *dws) static void int_error_stop(struct dw_spi *dws, const char *msg) { - /* Stop the hw */ - spi_enable_chip(dws, 0); + spi_reset_chip(dws); dev_err(&dws->master->dev, "%s\n", msg); dws->cur_msg->state = ERROR_STATE; @@ -606,9 +605,7 @@ static void dw_spi_cleanup(struct spi_device *spi) /* Restart the controller, disable all interrupts, clean rx fifo */ static void spi_hw_init(struct device *dev, struct dw_spi *dws) { - spi_enable_chip(dws, 0); - spi_mask_intr(dws, 0xff); - spi_enable_chip(dws, 1); + spi_reset_chip(dws); /* * Try to detect the FIFO depth if not set by interface driver, -- cgit v1.2.3 From 0b2e8915ead06b21d8f2360bfc28e747c4c0df8c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2015 14:58:56 +0200 Subject: spi: dw: program registers as soon as possible This patch refactors the code in pump_transfers() to reprogram the registers immediately when we have a new configuration data. The behaviour is slightly modified: - chip is always disabled and reenabled - CTRL0 is always reprogrammed This change allows to do a further refactoring and simplier conversion to use SPI core DMA routines in the future. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 321965607fc0..9a855bb00694 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -409,6 +409,8 @@ static void pump_transfers(unsigned long data) if (chip != dws->prev_chip) cs_change = 1; + spi_enable_chip(dws, 0); + cr0 = chip->cr0; /* Handle per transfer options for bpw and speed */ @@ -423,6 +425,8 @@ static void pump_transfers(unsigned long data) chip->speed_hz = speed; chip->clk_div = clk_div; + + spi_set_clk(dws, chip->clk_div); } } if (transfer->bits_per_word) { @@ -451,44 +455,32 @@ static void pump_transfers(unsigned long data) cr0 |= (chip->tmode << SPI_TMOD_OFFSET); } + dw_writew(dws, DW_SPI_CTRL0, cr0); + spi_chip_sel(dws, spi, 1); + /* Check if current transfer is a DMA transaction */ dws->dma_mapped = map_dma_buffers(dws); + /* For poll mode just disable all interrupts */ + spi_mask_intr(dws, 0xff); + /* * Interrupt mode * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ if (!dws->dma_mapped && !chip->poll_mode) { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); + dw_writew(dws, DW_SPI_TXFLTR, txlevel); + /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI; + spi_umask_intr(dws, imask); + dws->transfer_handler = interrupt_transfer; } - /* - * Reprogram registers only if - * 1. chip select changes - * 2. clk_div is changed - * 3. control value changes - */ - if (dw_readw(dws, DW_SPI_CTRL0) != cr0 || cs_change || clk_div || imask) { - spi_enable_chip(dws, 0); - - 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); - if (imask) - spi_umask_intr(dws, imask); - if (txlevel) - dw_writew(dws, DW_SPI_TXFLTR, txlevel); - - spi_enable_chip(dws, 1); - } + spi_enable_chip(dws, 1); if (cs_change) dws->prev_chip = chip; -- cgit v1.2.3 From c22c62db3f7388422257918c9d2582ef20d2e12d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2015 14:58:57 +0200 Subject: spi: dw: move to SPI core message handling This patch removes a lot of duplicate code since SPI core provides a nice message handling. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 183 +++++++++++++-------------------------------------- 1 file changed, 47 insertions(+), 136 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 9a855bb00694..7d3ee82e10be 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -28,11 +28,6 @@ #include #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) { spi_reset_chip(dws); dev_err(&dws->master->dev, "%s\n", msg); - dws->cur_msg->state = ERROR_STATE; - tasklet_schedule(&dws->pump_transfers); + dws->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(dws->master); } -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); -} -EXPORT_SYMBOL_GPL(dw_spi_xfer_done); - static irqreturn_t interrupt_transfer(struct dw_spi *dws) { u16 irq_status = dw_readw(dws, DW_SPI_ISR); @@ -312,7 +268,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) { @@ -327,13 +283,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; } @@ -342,7 +299,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); @@ -350,17 +307,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; @@ -369,35 +323,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; @@ -405,7 +332,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; @@ -437,13 +364,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) @@ -456,10 +382,9 @@ static void pump_transfers(unsigned long data) } dw_writew(dws, DW_SPI_CTRL0, cr0); - spi_chip_sel(dws, spi, 1); /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = map_dma_buffers(dws); + dws->dma_mapped = map_dma_buffers(master, spi, transfer); /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); @@ -489,31 +414,17 @@ static void pump_transfers(unsigned long data) dws->dma_ops->dma_transfer(dws, cs_change); if (chip->poll_mode) - poll_transfer(dws); + return poll_transfer(dws); - return; - -early_exit: - giveback(dws); + return 1; } -static int dw_spi_transfer_one_message(struct spi_master *master, +static void dw_spi_handle_err(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; + spi_reset_chip(dws); } /* This may be called twice for each spi dev */ @@ -637,7 +548,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; @@ -649,7 +560,9 @@ 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->handle_err = dw_spi_handle_err; master->max_speed_hz = dws->max_freq; master->dev.of_node = dev->of_node; @@ -664,8 +577,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) { -- cgit v1.2.3 From e31abce778bc05b94d406e8cbebd9953d12e84b8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:45 +0200 Subject: spi: dw-mid: convert value of dma_width to enum dma_slave_buswidth DMAEngine has a specific type to be used for bus width. This patch converts the code to use the values of the specific type when configure DMA transfer. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 950bc50361b3..f3e4092cd8dc 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -315,7 +315,6 @@ static int dw_spi_transfer_one(struct spi_master *master, { 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; u16 txlevel = 0; @@ -357,9 +356,14 @@ static int dw_spi_transfer_one(struct spi_master *master, } } if (transfer->bits_per_word) { - bits = transfer->bits_per_word; - dws->n_bytes = dws->dma_width = bits >> 3; - cr0 = (bits - 1) + if (transfer->bits_per_word == 8) { + dws->n_bytes = 1; + dws->dma_width = 1; + } else if (transfer->bits_per_word == 16) { + dws->n_bytes = 2; + dws->dma_width = 2; + } + cr0 = (transfer->bits_per_word - 1) | (chip->type << SPI_FRF_OFFSET) | (spi->mode << SPI_MODE_OFFSET) | (chip->tmode << SPI_TMOD_OFFSET); -- cgit v1.2.3 From 9f14538ecd1a210eff244a0a2281f6744fe4a59d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:46 +0200 Subject: spi: dw-mid: split dma_setup() from dma_transfer() The patch splits DMA preparatory code to dma_setup() callback. The change also converts transfer_one() to program DMA whenever the transfer is DMA mapped. The change is a follow up of the converion to use SPI core transfer_one_message(). Since the DMA mapped transfers can be interleaved with PIO ones the DMA related configuration should respect that. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f3e4092cd8dc..c7c2fcc2b58e 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -316,11 +316,11 @@ static int dw_spi_transfer_one(struct spi_master *master, struct dw_spi *dws = spi_master_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; - u8 cs_change = 0; u16 txlevel = 0; u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; + int ret; dws->n_bytes = chip->n_bytes; dws->dma_width = chip->dma_width; @@ -332,8 +332,6 @@ static int dw_spi_transfer_one(struct spi_master *master, dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; dws->len = transfer->len; - if (chip != dws->prev_chip) - cs_change = 1; spi_enable_chip(dws, 0); @@ -397,7 +395,13 @@ static int dw_spi_transfer_one(struct spi_master *master, * Interrupt mode * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ - if (!dws->dma_mapped && !chip->poll_mode) { + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_setup(dws); + if (ret < 0) { + spi_enable_chip(dws, 1); + return ret; + } + } else if (!chip->poll_mode) { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); dw_writew(dws, DW_SPI_TXFLTR, txlevel); @@ -411,11 +415,11 @@ static int dw_spi_transfer_one(struct spi_master *master, spi_enable_chip(dws, 1); - if (cs_change) - dws->prev_chip = chip; - - if (dws->dma_mapped) - dws->dma_ops->dma_transfer(dws, cs_change); + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_transfer(dws); + if (ret < 0) + return ret; + } if (chip->poll_mode) return poll_transfer(dws); @@ -546,7 +550,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->type = SSI_MOTO_SPI; - dws->prev_chip = NULL; dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60); snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num); -- cgit v1.2.3 From 4d5ac1edfdd79aea31983333cb53dd5db29559f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:48 +0200 Subject: spi: dw-mid: clear ongoing DMA transfers on timeout This patch shuts up any ongoing DMA transfer in case of error. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c7c2fcc2b58e..d53cffe7ff22 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -432,6 +432,9 @@ static void dw_spi_handle_err(struct spi_master *master, { struct dw_spi *dws = spi_master_get_devdata(master); + if (dws->dma_mapped) + dws->dma_ops->dma_stop(dws); + spi_reset_chip(dws); } -- cgit v1.2.3 From f89a6d8f43ebe9508bb5492c846ad997ad50eafe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:49 +0200 Subject: spi: dw-mid: move to use core SPI DMA mappings SPI core has a comprehensive function set to map and unmap a message when it's needed. This patch converts driver to use that advantage. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index d53cffe7ff22..2437bfcbf2f8 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -217,32 +217,6 @@ static void dw_reader(struct dw_spi *dws) } } -/* - * 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 spi_master *master, - struct spi_device *spi, struct spi_transfer *transfer) -{ - 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 - || !chip->enable_dma - || !dws->dma_ops) - return 0; - - if (transfer->tx_dma) - dws->tx_dma = transfer->tx_dma; - - if (transfer->rx_dma) - dws->rx_dma = transfer->rx_dma; - - return 1; -} - static void int_error_stop(struct dw_spi *dws, const char *msg) { spi_reset_chip(dws); @@ -322,11 +296,10 @@ static int dw_spi_transfer_one(struct spi_master *master, u32 cr0 = 0; int ret; + dws->dma_mapped = 0; dws->n_bytes = chip->n_bytes; dws->dma_width = chip->dma_width; - dws->rx_dma = transfer->rx_dma; - dws->tx_dma = transfer->tx_dma; dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; @@ -386,7 +359,8 @@ static int dw_spi_transfer_one(struct spi_master *master, dw_writew(dws, DW_SPI_CTRL0, cr0); /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = map_dma_buffers(master, spi, transfer); + if (master->can_dma && master->can_dma(master, spi, transfer)) + dws->dma_mapped = master->cur_msg_mapped; /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); @@ -396,7 +370,7 @@ static int dw_spi_transfer_one(struct spi_master *master, * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ if (dws->dma_mapped) { - ret = dws->dma_ops->dma_setup(dws); + ret = dws->dma_ops->dma_setup(dws, transfer); if (ret < 0) { spi_enable_chip(dws, 1); return ret; @@ -416,7 +390,7 @@ static int dw_spi_transfer_one(struct spi_master *master, spi_enable_chip(dws, 1); if (dws->dma_mapped) { - ret = dws->dma_ops->dma_transfer(dws); + ret = dws->dma_ops->dma_transfer(dws, transfer); if (ret < 0) return ret; } @@ -470,8 +444,6 @@ static int dw_spi_setup(struct spi_device *spi) chip->rx_threshold = 0; chip->tx_threshold = 0; - - chip->enable_dma = chip_info->enable_dma; } if (spi->bits_per_word == 8) { @@ -584,6 +556,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (ret) { dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; + } else { + master->can_dma = dws->dma_ops->can_dma; } } -- cgit v1.2.3 From 2306509605d3cb45b8480089af2d282600650e9e Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Fri, 6 Mar 2015 17:46:32 -0600 Subject: spi: dw-spi: Single Register read to clear IRQs Instead of clearing the RxU, RxO, and TxO IRQs individually with 3 register reads, a single read of the ICR register will do the same thing. Signed-off-by: Thor Thayer Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 2437bfcbf2f8..0f0106933b52 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -232,9 +232,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readw(dws, DW_SPI_TXOICR); - dw_readw(dws, DW_SPI_RXOICR); - dw_readw(dws, DW_SPI_RXUICR); + dw_readw(dws, DW_SPI_ICR); int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); return IRQ_HANDLED; } -- cgit v1.2.3 From dd11444327ce0fd549cce552b04aa441688ef2d3 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 12 Mar 2015 14:19:31 -0500 Subject: spi: dw-spi: Convert 16bit accesses to 32bit accesses Altera's Arria10 SoC interconnect requires a 32-bit write for APB peripherals. The current spi-dw driver uses 16-bit accesses in some locations. This patch converts all the 16-bit reads and writes to 32-bit reads and writes. Additional Documentation to Support this Change: The DW_apb_ssi databook states: "All registers in the DW_apb_ssi are addressed at 32-bit boundaries to remain consistent with the AHB bus. Where the physical size of any register is less than 32-bits wide, the upper unused bits of the 32-bit boundary are reserved. Writing to these bits has no effect; reading from these bits returns 0." [1] [1] Section 6.1 of dw_apb_ssi.pdf (version 3.22a) Request for test with platforms using the DesignWare SPI IP. Tested On: Altera CycloneV development kit Altera Arria10 development kit Compile tested for build errors on x86_64 (allyesconfigs) Signed-off-by: Thor Thayer Reviewed-and-tested-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0f0106933b52..cafac221fbf6 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -157,7 +157,7 @@ static inline u32 tx_max(struct dw_spi *dws) u32 tx_left, tx_room, rxtx_gap; tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; - tx_room = dws->fifo_len - dw_readw(dws, DW_SPI_TXFLR); + tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); /* * Another concern is about the tx/rx mismatch, we @@ -178,7 +178,7 @@ static inline u32 rx_max(struct dw_spi *dws) { u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; - return min_t(u32, rx_left, dw_readw(dws, DW_SPI_RXFLR)); + return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); } static void dw_writer(struct dw_spi *dws) @@ -194,7 +194,7 @@ static void dw_writer(struct dw_spi *dws) else txw = *(u16 *)(dws->tx); } - dw_writew(dws, DW_SPI_DR, txw); + dw_writel(dws, DW_SPI_DR, txw); dws->tx += dws->n_bytes; } } @@ -205,7 +205,7 @@ static void dw_reader(struct dw_spi *dws) u16 rxw; while (max--) { - rxw = dw_readw(dws, DW_SPI_DR); + rxw = dw_readl(dws, DW_SPI_DR); /* Care rx only if the transfer's original "rx" is not null */ if (dws->rx_end - dws->len) { if (dws->n_bytes == 1) @@ -228,11 +228,11 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) static irqreturn_t interrupt_transfer(struct dw_spi *dws) { - u16 irq_status = dw_readw(dws, DW_SPI_ISR); + u16 irq_status = dw_readl(dws, DW_SPI_ISR); /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readw(dws, DW_SPI_ICR); + dw_readl(dws, DW_SPI_ICR); int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); return IRQ_HANDLED; } @@ -257,7 +257,7 @@ static irqreturn_t dw_spi_irq(int irq, void *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; + u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f; if (!irq_status) return IRQ_NONE; @@ -354,7 +354,7 @@ static int dw_spi_transfer_one(struct spi_master *master, cr0 |= (chip->tmode << SPI_TMOD_OFFSET); } - dw_writew(dws, DW_SPI_CTRL0, cr0); + dw_writel(dws, DW_SPI_CTRL0, cr0); /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) @@ -375,7 +375,7 @@ static int dw_spi_transfer_one(struct spi_master *master, } } else if (!chip->poll_mode) { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); - dw_writew(dws, DW_SPI_TXFLTR, txlevel); + dw_writel(dws, DW_SPI_TXFLTR, txlevel); /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | @@ -499,11 +499,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writew(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) + dw_writel(dws, DW_SPI_TXFLTR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFLTR)) break; } - dw_writew(dws, DW_SPI_TXFLTR, 0); + dw_writel(dws, DW_SPI_TXFLTR, 0); dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); -- cgit v1.2.3 From 207cda93f0446c2da5461a37252c7468ddc1c2bc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 25 Mar 2015 20:26:26 +0200 Subject: spi: dw: fix crash on setup stage The commit 1a7b7ee72c21 (spi: Ensure that CS line is in non-active state after spi_setup()) introduces an unconditional call of spi_set_cs() before ->setup(). The dw_spi_set_cs() relies on that fact that ->setup() is already called, but it doesn't now. This patch fixes the crash by adding an additional check to dw_spi_set_cs(). Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi/spi-dw.c') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index cafac221fbf6..8d67d03c71eb 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -144,7 +144,7 @@ static void dw_spi_set_cs(struct spi_device *spi, bool enable) struct chip_data *chip = spi_get_ctldata(spi); /* Chip select logic is inverted from spi_set_cs() */ - if (chip->cs_control) + if (chip && chip->cs_control) chip->cs_control(!enable); if (!enable) -- cgit v1.2.3