From b28cb9414db9f8e42ac18c9e360e4e99cda42489 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Thu, 7 May 2015 18:36:53 -0500 Subject: [PATCH 1/7] spi: omap2-mcspi: Switch driver to use transfer_one Switches from transfer_one_message to transfer_one to prepare driver for use of GPIO chip selects. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 268 ++++++++++++++++------------------ 1 file changed, 122 insertions(+), 146 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d1a5b9fc3eba..3ac06ade23d0 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1052,7 +1052,8 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) } } -static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) +static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, + struct spi_device *spi, struct spi_transfer *t) { /* We only enable one channel at a time -- the one whose message is @@ -1062,8 +1063,6 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) * chipselect with the FORCE bit ... CS != channel enable. */ - struct spi_device *spi; - struct spi_transfer *t = NULL; struct spi_master *master; struct omap2_mcspi_dma *mcspi_dma; int cs_active = 0; @@ -1073,7 +1072,6 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) int status = 0; u32 chconf; - spi = m->spi; master = spi->master; mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; @@ -1090,94 +1088,89 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) par_override = 1; omap2_mcspi_set_enable(spi, 0); - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - status = -EINVAL; - break; - } - if (par_override || - (t->speed_hz != spi->max_speed_hz) || - (t->bits_per_word != spi->bits_per_word)) { - par_override = 1; - status = omap2_mcspi_setup_transfer(spi, t); - if (status < 0) - break; - if (t->speed_hz == spi->max_speed_hz && - t->bits_per_word == spi->bits_per_word) - par_override = 0; - } - if (cd && cd->cs_per_word) { - chconf = mcspi->ctx.modulctrl; - chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; - mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); - mcspi->ctx.modulctrl = - mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); - } - - if (!cs_active) { - omap2_mcspi_force_cs(spi, 1); - cs_active = 1; - } - - chconf = mcspi_cached_chconf0(spi); - chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; - chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; - - if (t->tx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; - else if (t->rx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; - - if (cd && cd->turbo_mode && t->tx_buf == NULL) { - /* Turbo mode is for more than one word */ - if (t->len > ((cs->word_len + 7) >> 3)) - chconf |= OMAP2_MCSPI_CHCONF_TURBO; - } - - mcspi_write_chconf0(spi, chconf); - - if (t->len) { - unsigned count; - - if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) - omap2_mcspi_set_fifo(spi, t, 1); - - omap2_mcspi_set_enable(spi, 1); - - /* RX_ONLY mode needs dummy data in TX reg */ - if (t->tx_buf == NULL) - writel_relaxed(0, cs->base - + OMAP2_MCSPI_TX0); - - if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) - count = omap2_mcspi_txrx_dma(spi, t); - else - count = omap2_mcspi_txrx_pio(spi, t); - m->actual_length += count; - - if (count != t->len) { - status = -EIO; - break; - } - } - - if (t->delay_usecs) - udelay(t->delay_usecs); - - /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap2_mcspi_force_cs(spi, 0); - cs_active = 0; - } - - omap2_mcspi_set_enable(spi, 0); - - if (mcspi->fifo_depth > 0) - omap2_mcspi_set_fifo(spi, t, 0); + if (par_override || + (t->speed_hz != spi->max_speed_hz) || + (t->bits_per_word != spi->bits_per_word)) { + par_override = 1; + status = omap2_mcspi_setup_transfer(spi, t); + if (status < 0) + goto out; + if (t->speed_hz == spi->max_speed_hz && + t->bits_per_word == spi->bits_per_word) + par_override = 0; } + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); + } + + if (!cs_active) { + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + } + + chconf = mcspi_cached_chconf0(spi); + chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; + + if (t->tx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; + else if (t->rx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; + + if (cd && cd->turbo_mode && t->tx_buf == NULL) { + /* Turbo mode is for more than one word */ + if (t->len > ((cs->word_len + 7) >> 3)) + chconf |= OMAP2_MCSPI_CHCONF_TURBO; + } + + mcspi_write_chconf0(spi, chconf); + + if (t->len) { + unsigned count; + + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (t->len >= DMA_MIN_BYTES)) + omap2_mcspi_set_fifo(spi, t, 1); + + omap2_mcspi_set_enable(spi, 1); + + /* RX_ONLY mode needs dummy data in TX reg */ + if (t->tx_buf == NULL) + writel_relaxed(0, cs->base + + OMAP2_MCSPI_TX0); + + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (t->len >= DMA_MIN_BYTES)) + count = omap2_mcspi_txrx_dma(spi, t); + else + count = omap2_mcspi_txrx_pio(spi, t); + + if (count != t->len) { + status = -EIO; + goto out; + } + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + + /* ignore the "leave it on after last xfer" hint */ + if (t->cs_change) { + omap2_mcspi_force_cs(spi, 0); + cs_active = 0; + } + + omap2_mcspi_set_enable(spi, 0); + + if (mcspi->fifo_depth > 0) + omap2_mcspi_set_fifo(spi, t, 0); + +out: /* Restore defaults if they were overriden */ if (par_override) { par_override = 0; @@ -1200,75 +1193,58 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) if (mcspi->fifo_depth > 0 && t) omap2_mcspi_set_fifo(spi, t, 0); - m->status = status; + return status; } -static int omap2_mcspi_transfer_one_message(struct spi_master *master, - struct spi_message *m) +static int omap2_mcspi_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) { - struct spi_device *spi; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - struct spi_transfer *t; - int status; + const void *tx_buf = t->tx_buf; + void *rx_buf = t->rx_buf; + unsigned len = t->len; - spi = m->spi; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; - m->actual_length = 0; - m->status = 0; - list_for_each_entry(t, &m->transfers, transfer_list) { - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; + if ((len && !(rx_buf || tx_buf))) { + dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", + t->speed_hz, + len, + tx_buf ? "tx" : "", + rx_buf ? "rx" : "", + t->bits_per_word); + return -EINVAL; + } - if ((len && !(rx_buf || tx_buf))) { - dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - status = -EINVAL; - goto out; + if (len < DMA_MIN_BYTES) + goto skip_dma_map; + + if (mcspi_dma->dma_tx && tx_buf != NULL) { + t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, + len, DMA_TO_DEVICE); + if (dma_mapping_error(mcspi->dev, t->tx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", + 'T', len); + return -EINVAL; } - - if (m->is_dma_mapped || len < DMA_MIN_BYTES) - continue; - - if (mcspi_dma->dma_tx && tx_buf != NULL) { - t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mcspi->dev, t->tx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'T', len); - status = -EINVAL; - goto out; - } - } - if (mcspi_dma->dma_rx && rx_buf != NULL) { - t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(mcspi->dev, t->rx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(mcspi->dev, t->tx_dma, - len, DMA_TO_DEVICE); - status = -EINVAL; - goto out; - } + } + if (mcspi_dma->dma_rx && rx_buf != NULL) { + t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(mcspi->dev, t->rx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", + 'R', len); + if (tx_buf != NULL) + dma_unmap_single(mcspi->dev, t->tx_dma, + len, DMA_TO_DEVICE); + return -EINVAL; } } - omap2_mcspi_work(mcspi, m); - /* spi_finalize_current_message() changes the status inside the - * spi_message, save the status here. */ - status = m->status; -out: - spi_finalize_current_message(master); - return status; +skip_dma_map: + return omap2_mcspi_work_one(mcspi, spi, t); } static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) @@ -1347,7 +1323,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; - master->transfer_one_message = omap2_mcspi_transfer_one_message; + master->transfer_one = omap2_mcspi_transfer_one; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; From bc7f9bbc80bcc77745b3f54ec4e7103e3e142bb9 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Fri, 8 May 2015 13:31:01 -0500 Subject: [PATCH 2/7] spi: omap2-mcspi: Add gpio_request and init CS If GPIO chip select is specified, request the GPIO in the setup function and release it in the cleanup function. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3ac06ade23d0..90cf7e775f15 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -35,6 +35,7 @@ #include #include +#include #include @@ -1011,6 +1012,12 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } + if (gpio_is_valid(spi->cs_gpio)) { + if (gpio_request(spi->cs_gpio, dev_name(&spi->dev)) == 0) + gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + } + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; @@ -1050,6 +1057,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) mcspi_dma->dma_tx = NULL; } } + + if (gpio_is_valid(spi->cs_gpio)) + gpio_free(spi->cs_gpio); } static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, From ddcad7e9068ebc6526728df1f34f1dde4b7dbbab Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Tue, 12 May 2015 12:38:57 -0500 Subject: [PATCH 3/7] spi: omap2-mcspi: Fix native cs with new set_cs GPIO chip select patch series appears to have broken the native chip select support. This patch pulls the manual native chip select toggling out of the transfer_one routine and adds a set_cs routine. Tested natively on AM3354 with SPI serial flash on spi0cs0. Reported-by: Nishanth Menon Signed-off-by: Michael Welling Tested-by: Nishanth Menon Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 90cf7e775f15..a7d85c5ab2fa 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -243,17 +243,20 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } -static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) +static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) { u32 l; - l = mcspi_cached_chconf0(spi); - if (cs_active) - l |= OMAP2_MCSPI_CHCONF_FORCE; - else - l &= ~OMAP2_MCSPI_CHCONF_FORCE; + if (spi->controller_state) { + l = mcspi_cached_chconf0(spi); - mcspi_write_chconf0(spi, l); + if (enable) + l &= ~OMAP2_MCSPI_CHCONF_FORCE; + else + l |= OMAP2_MCSPI_CHCONF_FORCE; + + mcspi_write_chconf0(spi, l); + } } static void omap2_mcspi_set_master_mode(struct spi_master *master) @@ -1075,7 +1078,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, struct spi_master *master; struct omap2_mcspi_dma *mcspi_dma; - int cs_active = 0; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; int par_override = 0; @@ -1118,11 +1120,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); } - if (!cs_active) { - omap2_mcspi_force_cs(spi, 1); - cs_active = 1; - } - chconf = mcspi_cached_chconf0(spi); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; @@ -1169,12 +1166,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, if (t->delay_usecs) udelay(t->delay_usecs); - /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap2_mcspi_force_cs(spi, 0); - cs_active = 0; - } - omap2_mcspi_set_enable(spi, 0); if (mcspi->fifo_depth > 0) @@ -1187,9 +1178,6 @@ out: status = omap2_mcspi_setup_transfer(spi, NULL); } - if (cs_active) - omap2_mcspi_force_cs(spi, 0); - if (cd && cd->cs_per_word) { chconf = mcspi->ctx.modulctrl; chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE; @@ -1334,6 +1322,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; master->transfer_one = omap2_mcspi_transfer_one; + master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; From be632f658462f5c80817a383c6d9b9790c0e7d1f Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:42 -0500 Subject: [PATCH 4/7] spi: omap2-mcspi: Remove unnecessary delay The core spi driver handles the delay between transactions. This is a remanant from the transfer_one conversion. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index a7d85c5ab2fa..304b427db95d 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1163,9 +1163,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, } } - if (t->delay_usecs) - udelay(t->delay_usecs); - omap2_mcspi_set_enable(spi, 0); if (mcspi->fifo_depth > 0) From 4373f8b6dab0de16e5ecf527626d958d20a45b3b Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:43 -0500 Subject: [PATCH 5/7] spi: omap2-mcspi: Fix set_cs function for active high The core spi driver swaps the polarity of the enable based on SPI_CS_HIGH. The omap2 controller has an internal configuration register bit called OMAP2_MCSPI_CHCONF_EPOL to handle active high chip selects as well. So we have to revert swap the polarity back for the correct setting of the OMAP2_MCSPI_CHCONF_FORCE bit in omap2_mcspi_set_cs. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 304b427db95d..502db29fd0fe 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -247,6 +247,13 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) { u32 l; + /* The controller handles the inverted chip selects + * using the OMAP2_MCSPI_CHCONF_EPOL bit so revert + * the inversion from the core spi_set_cs function. + */ + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + if (spi->controller_state) { l = mcspi_cached_chconf0(spi); From a06b430fd82c816bf76fb6f6f63f1ae1ced3b897 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:44 -0500 Subject: [PATCH 6/7] spi: omap2-mcspi: Fix GPIO chip select support The OMAP2_MCSPI_CHCONF_FORCE must be toggled even when using GPIO chip selects. This patch conditionally calls the omap2_mcspi_set_cs function to do so when using GPIO chip selects. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 502db29fd0fe..c4e21adb9bf7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1108,6 +1108,9 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, omap2_mcspi_set_enable(spi, 0); + if (gpio_is_valid(spi->cs_gpio)) + omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH); + if (par_override || (t->speed_hz != spi->max_speed_hz) || (t->bits_per_word != spi->bits_per_word)) { @@ -1192,6 +1195,9 @@ out: omap2_mcspi_set_enable(spi, 0); + if (gpio_is_valid(spi->cs_gpio)) + omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH)); + if (mcspi->fifo_depth > 0 && t) omap2_mcspi_set_fifo(spi, t, 0); From c4339ac775b0558373bb6882b8355cf6e85d5709 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:45 -0500 Subject: [PATCH 7/7] spi: omap2-mcspi: Handle error on gpio_request If a valid GPIO is specified but cannot be requested by the driver, print a message and error out of omap2_mcspi_setup. Signed-off-by: Michael Welling Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index c4e21adb9bf7..58673841286c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1023,9 +1023,12 @@ static int omap2_mcspi_setup(struct spi_device *spi) } if (gpio_is_valid(spi->cs_gpio)) { - if (gpio_request(spi->cs_gpio, dev_name(&spi->dev)) == 0) - gpio_direction_output(spi->cs_gpio, - !(spi->mode & SPI_CS_HIGH)); + ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "failed to request gpio\n"); + return ret; + } + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); } ret = pm_runtime_get_sync(mcspi->dev);