summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig14
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-ath79.c34
-rw-r--r--drivers/spi/spi-atmel.c292
-rw-r--r--drivers/spi/spi-bcm2835.c393
-rw-r--r--drivers/spi/spi-bitbang.c17
-rw-r--r--drivers/spi/spi-davinci.c2
-rw-r--r--drivers/spi/spi-fsl-cpm.c40
-rw-r--r--drivers/spi/spi-fsl-dspi.c307
-rw-r--r--drivers/spi/spi-fsl-espi.c51
-rw-r--r--drivers/spi/spi-imx.c2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c270
-rw-r--r--drivers/spi/spi-orion.c70
-rw-r--r--drivers/spi/spi-rb4xx.c210
-rw-r--r--drivers/spi/spi-rspi.c23
-rw-r--r--drivers/spi/spi-s3c64xx.c2
-rw-r--r--drivers/spi/spi-sh-msiof.c2
-rw-r--r--drivers/spi/spi.c36
18 files changed, 1397 insertions, 369 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c82a3d1..ec40a27 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,7 +77,9 @@ config SPI_ATMEL
config SPI_BCM2835
tristate "BCM2835 SPI controller"
+ depends on GPIOLIB
depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on GPIOLIB
help
This selects a driver for the Broadcom BCM2835 SPI master.
@@ -220,7 +222,7 @@ config SPI_FALCON
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG
help
This simple GPIO bitbanging SPI master uses the arch-neutral GPIO
@@ -302,7 +304,7 @@ config SPI_FSL_SPI
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
- depends on SOC_VF610 || COMPILE_TEST
+ depends on SOC_VF610 || SOC_LS1021A || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
mode. VF610 platform uses the controller.
@@ -326,7 +328,7 @@ config SPI_MESON_SPIFC
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select SPI_BITBANG
help
This is the driver for OpenCores tiny SPI master controller.
@@ -421,6 +423,12 @@ config SPI_ROCKCHIP
The main usecase of this controller is to use spi flash as boot
device.
+config SPI_RB4XX
+ tristate "Mikrotik RB4XX SPI master"
+ depends on SPI_MASTER && ATH79
+ help
+ SPI controller driver for the Mikrotik RB4xx series boards.
+
config SPI_RSPI
tristate "Renesas RSPI/QSPI controller"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 11df160..2e7089f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
+obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index b02eb4a..bf1f9b3 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
}
if (spi->chip_select) {
- struct ath79_spi_controller_data *cdata = spi->controller_data;
-
/* SPI is normally active-low */
- gpio_set_value(cdata->gpio, cs_high);
+ gpio_set_value(spi->cs_gpio, cs_high);
} else {
if (cs_high)
sp->ioc_base |= AR71XX_SPI_IOC_CS0;
@@ -117,11 +115,10 @@ static void ath79_spi_disable(struct ath79_spi *sp)
static int ath79_spi_setup_cs(struct spi_device *spi)
{
- struct ath79_spi_controller_data *cdata;
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
int status;
- cdata = spi->controller_data;
- if (spi->chip_select && !cdata)
+ if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
return -EINVAL;
status = 0;
@@ -134,8 +131,15 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
else
flags |= GPIOF_INIT_HIGH;
- status = gpio_request_one(cdata->gpio, flags,
+ status = gpio_request_one(spi->cs_gpio, flags,
dev_name(&spi->dev));
+ } else {
+ if (spi->mode & SPI_CS_HIGH)
+ sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+ else
+ sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+
+ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
}
return status;
@@ -144,8 +148,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
static void ath79_spi_cleanup_cs(struct spi_device *spi)
{
if (spi->chip_select) {
- struct ath79_spi_controller_data *cdata = spi->controller_data;
- gpio_free(cdata->gpio);
+ gpio_free(spi->cs_gpio);
}
}
@@ -217,6 +220,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
}
sp = spi_master_get_devdata(master);
+ master->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, sp);
pdata = dev_get_platdata(&pdev->dev);
@@ -253,7 +257,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
goto err_put_master;
}
- ret = clk_enable(sp->clk);
+ ret = clk_prepare_enable(sp->clk);
if (ret)
goto err_put_master;
@@ -277,7 +281,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
err_disable:
ath79_spi_disable(sp);
err_clk_disable:
- clk_disable(sp->clk);
+ clk_disable_unprepare(sp->clk);
err_put_master:
spi_master_put(sp->bitbang.master);
@@ -290,7 +294,7 @@ static int ath79_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&sp->bitbang);
ath79_spi_disable(sp);
- clk_disable(sp->clk);
+ clk_disable_unprepare(sp->clk);
spi_master_put(sp->bitbang.master);
return 0;
@@ -301,12 +305,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev)
ath79_spi_remove(pdev);
}
+static const struct of_device_id ath79_spi_of_match[] = {
+ { .compatible = "qca,ar7100-spi", },
+ { },
+};
+
static struct platform_driver ath79_spi_driver = {
.probe = ath79_spi_probe,
.remove = ath79_spi_remove,
.shutdown = ath79_spi_shutdown,
.driver = {
.name = DRV_NAME,
+ .of_match_table = ath79_spi_of_match,
},
};
module_platform_driver(ath79_spi_driver);
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c
index a2f40b1..c9eca34 100644
--- a/drivers/spi/spi-atmel.c
+++ b/drivers/spi/spi-atmel.c
@@ -41,6 +41,8 @@
#define SPI_CSR1 0x0034
#define SPI_CSR2 0x0038
#define SPI_CSR3 0x003c
+#define SPI_FMR 0x0040
+#define SPI_FLR 0x0044
#define SPI_VERSION 0x00fc
#define SPI_RPR 0x0100
#define SPI_RCR 0x0104
@@ -62,6 +64,14 @@
#define SPI_SWRST_SIZE 1
#define SPI_LASTXFER_OFFSET 24
#define SPI_LASTXFER_SIZE 1
+#define SPI_TXFCLR_OFFSET 16
+#define SPI_TXFCLR_SIZE 1
+#define SPI_RXFCLR_OFFSET 17
+#define SPI_RXFCLR_SIZE 1
+#define SPI_FIFOEN_OFFSET 30
+#define SPI_FIFOEN_SIZE 1
+#define SPI_FIFODIS_OFFSET 31
+#define SPI_FIFODIS_SIZE 1
/* Bitfields in MR */
#define SPI_MSTR_OFFSET 0
@@ -114,6 +124,22 @@
#define SPI_TXEMPTY_SIZE 1
#define SPI_SPIENS_OFFSET 16
#define SPI_SPIENS_SIZE 1
+#define SPI_TXFEF_OFFSET 24
+#define SPI_TXFEF_SIZE 1
+#define SPI_TXFFF_OFFSET 25
+#define SPI_TXFFF_SIZE 1
+#define SPI_TXFTHF_OFFSET 26
+#define SPI_TXFTHF_SIZE 1
+#define SPI_RXFEF_OFFSET 27
+#define SPI_RXFEF_SIZE 1
+#define SPI_RXFFF_OFFSET 28
+#define SPI_RXFFF_SIZE 1
+#define SPI_RXFTHF_OFFSET 29
+#define SPI_RXFTHF_SIZE 1
+#define SPI_TXFPTEF_OFFSET 30
+#define SPI_TXFPTEF_SIZE 1
+#define SPI_RXFPTEF_OFFSET 31
+#define SPI_RXFPTEF_SIZE 1
/* Bitfields in CSR0 */
#define SPI_CPOL_OFFSET 0
@@ -157,6 +183,22 @@
#define SPI_TXTDIS_OFFSET 9
#define SPI_TXTDIS_SIZE 1
+/* Bitfields in FMR */
+#define SPI_TXRDYM_OFFSET 0
+#define SPI_TXRDYM_SIZE 2
+#define SPI_RXRDYM_OFFSET 4
+#define SPI_RXRDYM_SIZE 2
+#define SPI_TXFTHRES_OFFSET 16
+#define SPI_TXFTHRES_SIZE 6
+#define SPI_RXFTHRES_OFFSET 24
+#define SPI_RXFTHRES_SIZE 6
+
+/* Bitfields in FLR */
+#define SPI_TXFL_OFFSET 0
+#define SPI_TXFL_SIZE 6
+#define SPI_RXFL_OFFSET 16
+#define SPI_RXFL_SIZE 6
+
/* Constants for BITS */
#define SPI_BITS_8_BPT 0
#define SPI_BITS_9_BPT 1
@@ -167,6 +209,9 @@
#define SPI_BITS_14_BPT 6
#define SPI_BITS_15_BPT 7
#define SPI_BITS_16_BPT 8
+#define SPI_ONE_DATA 0
+#define SPI_TWO_DATA 1
+#define SPI_FOUR_DATA 2
/* Bit manipulation macros */
#define SPI_BIT(name) \
@@ -185,11 +230,31 @@
__raw_readl((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \
__raw_writel((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+ __raw_readw((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+ __raw_writew((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+ __raw_readb((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+ __raw_writeb((value), (port)->regs + SPI_##reg)
#else
#define spi_readl(port, reg) \
readl_relaxed((port)->regs + SPI_##reg)
#define spi_writel(port, reg, value) \
writel_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+ readw_relaxed((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+ writew_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+ readb_relaxed((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+ writeb_relaxed((value), (port)->regs + SPI_##reg)
#endif
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
* cache operations; better heuristics consider wordsize and bitrate.
@@ -246,11 +311,14 @@ struct atmel_spi {
bool use_dma;
bool use_pdc;
+ bool use_cs_gpios;
/* dmaengine data */
struct atmel_spi_dma dma;
bool keep_cs;
bool cs_active;
+
+ u32 fifo_size;
};
/* Controller-specific per-slave state */
@@ -321,7 +389,8 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
}
mr = spi_readl(as, MR);
- gpio_set_value(asd->npcs_pin, active);
+ if (as->use_cs_gpios)
+ gpio_set_value(asd->npcs_pin, active);
} else {
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
int i;
@@ -337,7 +406,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
mr = spi_readl(as, MR);
mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
- if (spi->chip_select != 0)
+ if (as->use_cs_gpios && spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, active);
spi_writel(as, MR, mr);
}
@@ -366,7 +435,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
asd->npcs_pin, active ? " (low)" : "",
mr);
- if (atmel_spi_is_v2(as) || spi->chip_select != 0)
+ if (!as->use_cs_gpios)
+ spi_writel(as, CR, SPI_BIT(LASTXFER));
+ else if (atmel_spi_is_v2(as) || spi->chip_select != 0)
gpio_set_value(asd->npcs_pin, !active);
}
@@ -406,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
slave_config->dst_maxburst = 1;
slave_config->device_fc = false;
+ /*
+ * This driver uses fixed peripheral select mode (PS bit set to '0' in
+ * the Mode Register).
+ * So according to the datasheet, when FIFOs are available (and
+ * enabled), the Transmit FIFO operates in Multiple Data Mode.
+ * In this mode, up to 2 data, not 4, can be written into the Transmit
+ * Data Register in a single access.
+ * However, the first data has to be written into the lowest 16 bits and
+ * the second data into the highest 16 bits of the Transmit
+ * Data Register. For 8bit data (the most frequent case), it would
+ * require to rework tx_buf so each data would actualy fit 16 bits.
+ * So we'd rather write only one data at the time. Hence the transmit
+ * path works the same whether FIFOs are available (and enabled) or not.
+ */
slave_config->direction = DMA_MEM_TO_DEV;
if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
dev_err(&as->pdev->dev,
@@ -413,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
err = -EINVAL;
}
+ /*
+ * This driver configures the spi controller for master mode (MSTR bit
+ * set to '1' in the Mode Register).
+ * So according to the datasheet, when FIFOs are available (and
+ * enabled), the Receive FIFO operates in Single Data Mode.
+ * So the receive path works the same whether FIFOs are available (and
+ * enabled) or not.
+ */
slave_config->direction = DMA_DEV_TO_MEM;
if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
dev_err(&as->pdev->dev,
@@ -502,10 +595,10 @@ static void dma_callback(void *data)
}
/*
- * Next transfer using PIO.
+ * Next transfer using PIO without FIFO.
*/
-static void atmel_spi_next_xfer_pio(struct spi_master *master,
- struct spi_transfer *xfer)
+static void atmel_spi_next_xfer_single(struct spi_master *master,
+ struct spi_transfer *xfer)
{
struct atmel_spi *as = spi_master_get_devdata(master);
unsigned long xfer_pos = xfer->len - as->current_remaining_bytes;
@@ -538,6 +631,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
}
/*
+ * Next transfer using PIO with FIFO.
+ */
+static void atmel_spi_next_xfer_fifo(struct spi_master *master,
+ struct spi_transfer *xfer)
+{
+ struct atmel_spi *as = spi_master_get_devdata(master);
+ u32 current_remaining_data, num_data;
+ u32 offset = xfer->len - as->current_remaining_bytes;
+ const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset);
+ const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset);
+ u16 td0, td1;
+ u32 fifomr;
+
+ dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n");
+
+ /* Compute the number of data to transfer in the current iteration */
+ current_remaining_data = ((xfer->bits_per_word > 8) ?
+ ((u32)as->current_remaining_bytes >> 1) :
+ (u32)as->current_remaining_bytes);
+ num_data = min(current_remaining_data, as->fifo_size);
+
+ /* Flush RX and TX FIFOs */
+ spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR));
+ while (spi_readl(as, FLR))
+ cpu_relax();
+
+ /* Set RX FIFO Threshold to the number of data to transfer */
+ fifomr = spi_readl(as, FMR);
+ spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr));
+
+ /* Clear FIFO flags in the Status Register, especially RXFTHF */
+ (void)spi_readl(as, SR);
+
+ /* Fill TX FIFO */
+ while (num_data >= 2) {
+ if (xfer->tx_buf) {
+ if (xfer->bits_per_word > 8) {
+ td0 = *words++;
+ td1 = *words++;
+ } else {
+ td0 = *bytes++;
+ td1 = *bytes++;
+ }
+ } else {
+ td0 = 0;
+ td1 = 0;
+ }
+
+ spi_writel(as, TDR, (td1 << 16) | td0);
+ num_data -= 2;
+ }
+
+ if (num_data) {
+ if (xfer->tx_buf) {
+ if (xfer->bits_per_word > 8)
+ td0 = *words++;
+ else
+ td0 = *bytes++;
+ } else {
+ td0 = 0;
+ }
+
+ spi_writew(as, TDR, td0);
+ num_data--;
+ }
+
+ dev_dbg(master->dev.parent,
+ " start fifo xfer %p: len %u tx %p rx %p bitpw %d\n",
+ xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
+ xfer->bits_per_word);
+
+ /*
+ * Enable RX FIFO Threshold Flag interrupt to be notified about
+ * transfer completion.
+ */
+ spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES));
+}
+
+/*
+ * Next transfer using PIO.
+ */
+static void atmel_spi_next_xfer_pio(struct spi_master *master,
+ struct spi_transfer *xfer)
+{
+ struct atmel_spi *as = spi_master_get_devdata(master);
+
+ if (as->fifo_size)
+ atmel_spi_next_xfer_fifo(master, xfer);
+ else
+ atmel_spi_next_xfer_single(master, xfer);
+}
+
+/*
* Submit next transfer for DMA.
*/
static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
@@ -839,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
}
-/* Called from IRQ
- *
- * Must update "current_remaining_bytes" to keep track of data
- * to transfer.
- */
static void
-atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer)
{
u8 *rxp;
u16 *rxp16;
@@ -872,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
}
}
+static void
+atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+ u32 fifolr = spi_readl(as, FLR);
+ u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr);
+ u32 offset = xfer->len - as->current_remaining_bytes;
+ u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset);
+ u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset);
+ u16 rd; /* RD field is the lowest 16 bits of RDR */
+
+ /* Update the number of remaining bytes to transfer */
+ num_bytes = ((xfer->bits_per_word > 8) ?
+ (num_data << 1) :
+ num_data);
+
+ if (as->current_remaining_bytes > num_bytes)
+ as->current_remaining_bytes -= num_bytes;
+ else
+ as->current_remaining_bytes = 0;
+
+ /* Handle odd number of bytes when data are more than 8bit width */
+ if (xfer->bits_per_word > 8)
+ as->current_remaining_bytes &= ~0x1;
+
+ /* Read data */
+ while (num_data) {
+ rd = spi_readl(as, RDR);
+ if (xfer->rx_buf) {
+ if (xfer->bits_per_word > 8)
+ *words++ = rd;
+ else
+ *bytes++ = rd;
+ }
+ num_data--;
+ }
+}
+
+/* Called from IRQ
+ *
+ * Must update "current_remaining_bytes" to keep track of data
+ * to transfer.
+ */
+static void
+atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+ if (as->fifo_size)
+ atmel_spi_pump_fifo_data(as, xfer);
+ else
+ atmel_spi_pump_single_data(as, xfer);
+}
+
/* Interrupt
*
* No need for locking in this Interrupt handler: done_status is the
@@ -912,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id)
complete(&as->xfer_completion);
- } else if (pending & SPI_BIT(RDRF)) {
+ } else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) {
atmel_spi_lock(as);
if (as->current_remaining_bytes) {
@@ -996,6 +1228,8 @@ static int atmel_spi_setup(struct spi_device *spi)
csr |= SPI_BIT(CPOL);
if (!(spi->mode & SPI_CPHA))
csr |= SPI_BIT(NCPHA);
+ if (!as->use_cs_gpios)
+ csr |= SPI_BIT(CSAAT);
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
*
@@ -1009,7 +1243,9 @@ static int atmel_spi_setup(struct spi_device *spi)
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned long)spi->controller_data;
- if (gpio_is_valid(spi->cs_gpio))
+ if (!as->use_cs_gpios)
+ npcs_pin = spi->chip_select;
+ else if (gpio_is_valid(spi->cs_gpio))
npcs_pin = spi->cs_gpio;
asd = spi->controller_state;
@@ -1018,15 +1254,19 @@ static int atmel_spi_setup(struct spi_device *spi)
if (!asd)
return -ENOMEM;
- ret = gpio_request(npcs_pin, dev_name(&spi->dev));
- if (ret) {
- kfree(asd);
- return ret;
+ if (as->use_cs_gpios) {
+ ret = gpio_request(npcs_pin, dev_name(&spi->dev));
+ if (ret) {
+ kfree(asd);
+ return ret;
+ }
+
+ gpio_direction_output(npcs_pin,
+ !(spi->mode & SPI_CS_HIGH));
}
asd->npcs_pin = npcs_pin;
spi->controller_state = asd;
- gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
}
asd->csr = csr;
@@ -1338,6 +1578,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
atmel_get_caps(as);
+ as->use_cs_gpios = true;
+ if (atmel_spi_is_v2(as) &&
+ !of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) {
+ as->use_cs_gpios = false;
+ master->num_chipselect = 4;
+ }
+
as->use_dma = false;
as->use_pdc = false;
if (as->caps.has_dma_support) {
@@ -1380,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
spi_writel(as, CR, SPI_BIT(SPIEN));
+ as->fifo_size = 0;
+ if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
+ &as->fifo_size)) {
+ dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size);
+ spi_writel(as, CR, SPI_BIT(FIFOEN));
+ }
+
/* go! */
dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
(unsigned long)regs->start, irq);
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index f63864a..59705ab 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -20,18 +20,22 @@
* GNU General Public License for more details.
*/
+#include <asm/page.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
+#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/spi/spi.h>
/* SPI register offsets */
@@ -69,7 +73,8 @@
#define BCM2835_SPI_CS_CS_01 0x00000001
#define BCM2835_SPI_POLLING_LIMIT_US 30
-#define BCM2835_SPI_TIMEOUT_MS 30000
+#define BCM2835_SPI_POLLING_JIFFIES 2
+#define BCM2835_SPI_DMA_MIN_LENGTH 96
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
| SPI_NO_CS | SPI_3WIRE)
@@ -83,6 +88,7 @@ struct bcm2835_spi {
u8 *rx_buf;
int tx_len;
int rx_len;
+ bool dma_pending;
};
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
@@ -128,12 +134,15 @@ static void bcm2835_spi_reset_hw(struct spi_master *master)
/* Disable SPI interrupts and transfer */
cs &= ~(BCM2835_SPI_CS_INTR |
BCM2835_SPI_CS_INTD |
+ BCM2835_SPI_CS_DMAEN |
BCM2835_SPI_CS_TA);
/* and reset RX/TX FIFOS */
cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
/* and reset the SPI_HW */
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
+ /* as well as DLEN */
+ bcm2835_wr(bs, BCM2835_SPI_DLEN, 0);
}
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
@@ -157,43 +166,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
- struct spi_device *spi,
- struct spi_transfer *tfr,
- u32 cs,
- unsigned long xfer_time_us)
-{
- struct bcm2835_spi *bs = spi_master_get_devdata(master);
- unsigned long timeout = jiffies +
- max(4 * xfer_time_us * HZ / 1000000, 2uL);
-
- /* enable HW block without interrupts */
- bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
-
- /* set timeout to 4x the expected time, or 2 jiffies */
- /* loop until finished the transfer */
- while (bs->rx_len) {
- /* read from fifo as much as possible */
- bcm2835_rd_fifo(bs);
- /* fill in tx fifo as much as possible */
- bcm2835_wr_fifo(bs);
- /* if we still expect some data after the read,
- * check for a possible timeout
- */
- if (bs->rx_len && time_after(jiffies, timeout)) {
- /* Transfer complete - reset SPI HW */
- bcm2835_spi_reset_hw(master);
- /* and return timeout */
- return -ETIMEDOUT;
- }
- }
-
- /* Transfer complete - reset SPI HW */
- bcm2835_spi_reset_hw(master);
- /* and return without waiting for completion */
- return 0;
-}
-
static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr,
@@ -230,6 +202,329 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
return 1;
}
+/*
+ * DMA support
+ *
+ * this implementation has currently a few issues in so far as it does
+ * not work arrount limitations of the HW.
+ *
+ * the main one being that DMA transfers are limited to 16 bit
+ * (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN
+ *
+ * also we currently assume that the scatter-gather fragments are
+ * all multiple of 4 (except the last) - otherwise we would need
+ * to reset the FIFO before subsequent transfers...
+ * this also means that tx/rx transfers sg's need to be of equal size!
+ *
+ * there may be a few more border-cases we may need to address as well
+ * but unfortunately this would mean splitting up the scatter-gather
+ * list making it slightly unpractical...
+ */
+static void bcm2835_spi_dma_done(void *data)
+{
+ struct spi_master *master = data;
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+ /* reset fifo and HW */
+ bcm2835_spi_reset_hw(master);
+
+ /* and terminate tx-dma as we do not have an irq for it
+ * because when the rx dma will terminate and this callback
+ * is called the tx-dma must have finished - can't get to this
+ * situation otherwise...
+ */
+ dmaengine_terminate_all(master->dma_tx);
+
+ /* mark as no longer pending */
+ bs->dma_pending = 0;
+
+ /* and mark as completed */;
+ complete(&master->xfer_completion);
+}
+
+static int bcm2835_spi_prepare_sg(struct spi_master *master,
+ struct spi_transfer *tfr,
+ bool is_tx)
+{
+ struct dma_chan *chan;
+ struct scatterlist *sgl;
+ unsigned int nents;
+ enum dma_transfer_direction dir;
+ unsigned long flags;
+
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+
+ if (is_tx) {
+ dir = DMA_MEM_TO_DEV;
+ chan = master->dma_tx;
+ nents = tfr->tx_sg.nents;
+ sgl = tfr->tx_sg.sgl;
+ flags = 0 /* no tx interrupt */;
+
+ } else {
+ dir = DMA_DEV_TO_MEM;
+ chan = master->dma_rx;
+ nents = tfr->rx_sg.nents;
+ sgl = tfr->rx_sg.sgl;
+ flags = DMA_PREP_INTERRUPT;
+ }
+ /* prepare the channel */
+ desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+ if (!desc)
+ return -EINVAL;
+
+ /* set callback for rx */
+ if (!is_tx) {
+ desc->callback = bcm2835_spi_dma_done;
+ desc->callback_param = master;
+ }
+
+ /* submit it to DMA-engine */
+ cookie = dmaengine_submit(desc);
+
+ return dma_submit_error(cookie);
+}
+
+static inline int bcm2835_check_sg_length(struct sg_table *sgt)
+{
+ int i;
+ struct scatterlist *sgl;
+
+ /* check that the sg entries are word-sized (except for last) */
+ for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) {
+ if (sg_dma_len(sgl) % 4)
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr,
+ u32 cs)
+{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+ int ret;
+
+ /* check that the scatter gather segments are all a multiple of 4 */
+ if (bcm2835_check_sg_length(&tfr->tx_sg) ||
+ bcm2835_check_sg_length(&tfr->rx_sg)) {
+ dev_warn_once(&spi->dev,
+ "scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n");
+ return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
+ }
+
+ /* setup tx-DMA */
+ ret = bcm2835_spi_prepare_sg(master, tfr, true);
+ if (ret)
+ return ret;
+
+ /* start TX early */
+ dma_async_issue_pending(master->dma_tx);
+
+ /* mark as dma pending */
+ bs->dma_pending = 1;
+
+ /* set the DMA length */
+ bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len);
+
+ /* start the HW */
+ bcm2835_wr(bs, BCM2835_SPI_CS,
+ cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN);
+
+ /* setup rx-DMA late - to run transfers while
+ * mapping of the rx buffers still takes place
+ * this saves 10us or more.
+ */
+ ret = bcm2835_spi_prepare_sg(master, tfr, false);
+ if (ret) {
+ /* need to reset on errors */
+ dmaengine_terminate_all(master->dma_tx);
+ bcm2835_spi_reset_hw(master);
+ return ret;
+ }
+
+ /* start rx dma late */
+ dma_async_issue_pending(master->dma_rx);
+
+ /* wait for wakeup in framework */
+ return 1;
+}
+
+static bool bcm2835_spi_can_dma(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr)
+{
+ /* only run for gpio_cs */
+ if (!gpio_is_valid(spi->cs_gpio))
+ return false;
+
+ /* we start DMA efforts only on bigger transfers */
+ if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH)
+ return false;
+
+ /* BCM2835_SPI_DLEN has defined a max transfer size as
+ * 16 bit, so max is 65535
+ * we can revisit this by using an alternative transfer
+ * method - ideally this would get done without any more
+ * interaction...
+ */
+ if (tfr->len > 65535) {
+ dev_warn_once(&spi->dev,
+ "transfer size of %d too big for dma-transfer\n",
+ tfr->len);
+ return false;
+ }
+
+ /* if we run rx/tx_buf with word aligned addresses then we are OK */
+ if ((((size_t)tfr->rx_buf & 3) == 0) &&
+ (((size_t)tfr->tx_buf & 3) == 0))
+ return true;
+
+ /* otherwise we only allow transfers within the same page
+ * to avoid wasting time on dma_mapping when it is not practical
+ */
+ if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+ dev_warn_once(&spi->dev,
+ "Unaligned spi tx-transfer bridging page\n");
+ return false;
+ }
+ if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+ dev_warn_once(&spi->dev,
+ "Unaligned spi tx-transfer bridging page\n");
+ return false;
+ }
+
+ /* return OK */
+ return true;
+}
+
+static void bcm2835_dma_release(struct spi_master *master)
+{
+ if (master->dma_tx) {
+ dmaengine_terminate_all(master->dma_tx);
+ dma_release_channel(master->dma_tx);
+ master->dma_tx = NULL;
+ }
+ if (master->dma_rx) {
+ dmaengine_terminate_all(master->dma_rx);
+ dma_release_channel(master->dma_rx);
+ master->dma_rx = NULL;
+ }
+}
+
+static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
+{
+ struct dma_slave_config slave_config;
+ const __be32 *addr;
+ dma_addr_t dma_reg_base;
+ int ret;
+
+ /* base address in dma-space */
+ addr = of_get_address(master->dev.of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(dev, "could not get DMA-register address - not using dma mode\n");
+ goto err;
+ }
+ dma_reg_base = be32_to_cpup(addr);
+
+ /* get tx/rx dma */
+ master->dma_tx = dma_request_slave_channel(dev, "tx");
+ if (!master->dma_tx) {
+ dev_err(dev, "no tx-dma configuration found - not using dma mode\n");
+ goto err;
+ }
+ master->dma_rx = dma_request_slave_channel(dev, "rx");
+ if (!master->dma_rx) {
+ dev_err(dev, "no rx-dma configuration found - not using dma mode\n");
+ goto err_release;
+ }
+
+ /* configure DMAs */
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ ret = dmaengine_slave_config(master->dma_tx, &slave_config);
+ if (ret)
+ goto err_config;
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ ret = dmaengine_slave_config(master->dma_rx, &slave_config);
+ if (ret)
+ goto err_config;
+
+ /* all went well, so set can_dma */
+ master->can_dma = bcm2835_spi_can_dma;
+ master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
+ /* need to do TX AND RX DMA, so we need dummy buffers */
+ master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+
+ return;
+
+err_config:
+ dev_err(dev, "issue configuring dma: %d - not using DMA mode\n",
+ ret);
+err_release:
+ bcm2835_dma_release(master);
+err:
+ return;
+}
+
+static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *tfr,
+ u32 cs,
+ unsigned long xfer_time_us)
+{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+ unsigned long timeout;
+
+ /* enable HW block without interrupts */
+ bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
+
+ /* fill in the fifo before timeout calculations
+ * if we are interrupted here, then the data is
+ * getting transferred by the HW while we are interrupted
+ */
+ bcm2835_wr_fifo(bs);
+
+ /* set the timeout */
+ timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES;
+
+ /* loop until finished the transfer */
+ while (bs->rx_len) {
+ /* fill in tx fifo with remaining data */
+ bcm2835_wr_fifo(bs);
+
+ /* read from fifo as much as possible */
+ bcm2835_rd_fifo(bs);
+
+ /* if there is still data pending to read
+ * then check the timeout
+ */
+ if (bs->rx_len && time_after(jiffies, timeout)) {
+ dev_dbg_ratelimited(&spi->dev,
+ "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n",
+ jiffies - timeout,
+ bs->tx_len, bs->rx_len);
+ /* fall back to interrupt mode */
+ return bcm2835_spi_transfer_one_irq(master, spi,
+ tfr, cs);
+ }
+ }
+
+ /* Transfer complete - reset SPI HW */
+ bcm2835_spi_reset_hw(master);
+ /* and return without waiting for completion */
+ return 0;
+}
+
static int bcm2835_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
@@ -289,12 +584,26 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
return bcm2835_spi_transfer_one_poll(master, spi, tfr,
cs, xfer_time_us);
+ /* run in dma mode if conditions are right */
+ if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr))
+ return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs);
+
+ /* run in interrupt-mode */
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
}
static void bcm2835_spi_handle_err(struct spi_master *master,
struct spi_message *msg)
{
+ struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+ /* if an error occurred and we have an active dma, then terminate */
+ if (bs->dma_pending) {
+ dmaengine_terminate_all(master->dma_tx);
+ dmaengine_terminate_all(master->dma_rx);
+ bs->dma_pending = 0;
+ }
+ /* and reset */
bcm2835_spi_reset_hw(master);
}
@@ -464,6 +773,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
goto out_clk_disable;
}
+ bcm2835_dma_init(master, &pdev->dev);
+
/* initialise the hardware with the default polarities */
bcm2835_wr(bs, BCM2835_SPI_CS,
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
@@ -494,6 +805,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
clk_disable_unprepare(bs->clk);
+ bcm2835_dma_release(master);
+
return 0;
}
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index 5ef6638..840a498 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -180,7 +180,6 @@ int spi_bitbang_setup(struct spi_device *spi)
{
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
- int retval;
unsigned long flags;
bitbang = spi_master_get_devdata(spi->master);
@@ -197,9 +196,11 @@ int spi_bitbang_setup(struct spi_device *spi)
if (!cs->txrx_word)
return -EINVAL;
- retval = bitbang->setup_transfer(spi, NULL);
- if (retval < 0)
- return retval;
+ if (bitbang->setup_transfer) {
+ int retval = bitbang->setup_transfer(spi, NULL);
+ if (retval < 0)
+ return retval;
+ }
dev_dbg(&spi->dev, "%s, %u nsec/bit\n", __func__, 2 * cs->nsecs);
@@ -295,9 +296,11 @@ static int spi_bitbang_transfer_one(struct spi_master *master,
/* init (-1) or override (1) transfer params */
if (do_setup != 0) {
- status = bitbang->setup_transfer(spi, t);
- if (status < 0)
- break;
+ if (bitbang->setup_transfer) {
+ status = bitbang->setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ }
if (do_setup == -1)
do_setup = 0;
}
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 5e99106..987afeb 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
- if (ret < 3 || ret > 256)
+ if (ret < 1 || ret > 256)
return -EINVAL;
return ret - 1;
diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c
index 9c46a30..896add8 100644
--- a/drivers/spi/spi-fsl-cpm.c
+++ b/drivers/spi/spi-fsl-cpm.c
@@ -24,6 +24,7 @@
#include <linux/of_address.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
+#include <linux/platform_device.h>
#include "spi-fsl-cpm.h"
#include "spi-fsl-lib.h"
@@ -269,17 +270,6 @@ static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
if (mspi->flags & SPI_CPM2) {
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
out_be16(spi_base, pram_ofs);
- } else {
- struct spi_pram __iomem *pram = spi_base;
- u16 rpbase = in_be16(&pram->rpbase);
-
- /* Microcode relocation patch applied? */
- if (rpbase) {
- pram_ofs = rpbase;
- } else {
- pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
- out_be16(spi_base, pram_ofs);
- }
}
iounmap(spi_base);
@@ -292,7 +282,6 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
struct device_node *np = dev->of_node;
const u32 *iprop;
int size;
- unsigned long pram_ofs;
unsigned long bds_ofs;
if (!(mspi->flags & SPI_CPM_MODE))
@@ -319,8 +308,26 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
}
}
- pram_ofs = fsl_spi_cpm_get_pram(mspi);
- if (IS_ERR_VALUE(pram_ofs)) {
+ if (mspi->flags & SPI_CPM1) {
+ struct resource *res;
+ void *pram;
+
+ res = platform_get_resource(to_platform_device(dev),
+ IORESOURCE_MEM, 1);
+ pram = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pram))
+ mspi->pram = NULL;
+ else
+ mspi->pram = pram;
+ } else {
+ unsigned long pram_ofs = fsl_spi_cpm_get_pram(mspi);
+
+ if (IS_ERR_VALUE(pram_ofs))
+ mspi->pram = NULL;
+ else
+ mspi->pram = cpm_muram_addr(pram_ofs);
+ }
+ if (mspi->pram == NULL) {
dev_err(dev, "can't allocate spi parameter ram\n");
goto err_pram;
}
@@ -346,8 +353,6 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
goto err_dummy_rx;
}
- mspi->pram = cpm_muram_addr(pram_ofs);
-
mspi->tx_bd = cpm_muram_addr(bds_ofs);
mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
@@ -375,7 +380,8 @@ err_dummy_rx:
err_dummy_tx:
cpm_muram_free(bds_ofs);
err_bds:
- cpm_muram_free(pram_ofs);
+ if (!(mspi->flags & SPI_CPM1))
+ cpm_muram_free(cpm_muram_offset(mspi->pram));
err_pram:
fsl_spi_free_dummy_rx();
return -ENOMEM;
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 5fe54cd..86bcdd6 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -47,6 +48,7 @@
#define SPI_MCR_CLR_RXF (1 << 10)
#define SPI_TCR 0x08
+#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16)
#define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4))
#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27)
@@ -67,9 +69,11 @@
#define SPI_SR 0x2c
#define SPI_SR_EOQF 0x10000000
+#define SPI_SR_TCFQF 0x80000000
#define SPI_RSER 0x30
#define SPI_RSER_EOQFE 0x10000000
+#define SPI_RSER_TCFQE 0x80000000
#define SPI_PUSHR 0x34
#define SPI_PUSHR_CONT (1 << 31)
@@ -102,12 +106,35 @@
#define SPI_CS_ASSERT 0x02
#define SPI_CS_DROP 0x04
+#define SPI_TCR_TCNT_MAX 0x10000
+
struct chip_data {
u32 mcr_val;
u32 ctar_val;
u16 void_write_data;
};
+enum dspi_trans_mode {
+ DSPI_EOQ_MODE = 0,
+ DSPI_TCFQ_MODE,
+};
+
+struct fsl_dspi_devtype_data {
+ enum dspi_trans_mode trans_mode;
+};
+
+static const struct fsl_dspi_devtype_data vf610_data = {
+ .trans_mode = DSPI_EOQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
+ .trans_mode = DSPI_TCFQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls2085a_data = {
+ .trans_mode = DSPI_TCFQ_MODE,
+};
+
struct fsl_dspi {
struct spi_master *master;
struct platform_device *pdev;
@@ -128,9 +155,12 @@ struct fsl_dspi {
u8 cs;
u16 void_write_data;
u32 cs_change;
+ struct fsl_dspi_devtype_data *devtype_data;
wait_queue_head_t waitq;
u32 waitflags;
+
+ u32 spi_tcnt;
};
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
@@ -213,63 +243,60 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns,
}
}
-static int dspi_transfer_write(struct fsl_dspi *dspi)
+static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
{
- int tx_count = 0;
- int tx_word;
u16 d16;
- u8 d8;
- u32 dspi_pushr = 0;
- int first = 1;
- tx_word = is_double_byte_mode(dspi);
+ if (!(dspi->dataflags & TRAN_STATE_TX_VOID))
+ d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx;
+ else
+ d16 = dspi->void_write_data;
- /* If we are in word mode, but only have a single byte to transfer
- * then switch to byte mode temporarily. Will switch back at the
- * end of the transfer.
- */
- if (tx_word && (dspi->len == 1)) {
- dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
- SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
- tx_word = 0;
- }
+ dspi->tx += tx_word + 1;
+ dspi->len -= tx_word + 1;
- while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
- if (tx_word) {
- if (dspi->len == 1)
- break;
+ return SPI_PUSHR_TXDATA(d16) |
+ SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CONT;
+}
- if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
- d16 = *(u16 *)dspi->tx;
- dspi->tx += 2;
- } else {
- d16 = dspi->void_write_data;
- }
+static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word)
+{
+ u16 d;
+ unsigned int val;
- dspi_pushr = SPI_PUSHR_TXDATA(d16) |
- SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
- SPI_PUSHR_CONT;
+ regmap_read(dspi->regmap, SPI_POPR, &val);
+ d = SPI_POPR_RXDATA(val);
- dspi->len -= 2;
- } else {
- if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
+ rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d);
- d8 = *(u8 *)dspi->tx;
- dspi->tx++;
- } else {
- d8 = (u8)dspi->void_write_data;
- }
+ dspi->rx += rx_word + 1;
+}
- dspi_pushr = SPI_PUSHR_TXDATA(d8) |
- SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
- SPI_PUSHR_CONT;
+static int dspi_eoq_write(struct fsl_dspi *dspi)
+{
+ int tx_count = 0;
+ int tx_word;
+ u32 dspi_pushr = 0;
+
+ tx_word = is_double_byte_mode(dspi);
- dspi->len--;
+ while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
+ /* If we are in word mode, only have a single byte to transfer
+ * switch to byte mode temporarily. Will switch back at the
+ * end of the transfer.
+ */
+ if (tx_word && (dspi->len == 1)) {
+ dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+ regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+ tx_word = 0;
}
+ dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
/* last transfer in the transfer */
dspi_pushr |= SPI_PUSHR_EOQ;
@@ -278,11 +305,6 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
} else if (tx_word && (dspi->len == 1))
dspi_pushr |= SPI_PUSHR_EOQ;
- if (first) {
- first = 0;
- dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
- }
-
regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
tx_count++;
@@ -291,40 +313,55 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
return tx_count * (tx_word + 1);
}
-static int dspi_transfer_read(struct fsl_dspi *dspi)
+static int dspi_eoq_read(struct fsl_dspi *dspi)
{
int rx_count = 0;
int rx_word = is_double_byte_mode(dspi);
- u16 d;
while ((dspi->rx < dspi->rx_end)
&& (rx_count < DSPI_FIFO_SIZE)) {
- if (rx_word) {
- unsigned int val;
+ if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+ rx_word = 0;
- if ((dspi->rx_end - dspi->rx) == 1)
- break;
+ dspi_data_from_popr(dspi, rx_word);
+ rx_count++;
+ }
- regmap_read(dspi->regmap, SPI_POPR, &val);
- d = SPI_POPR_RXDATA(val);
+ return rx_count;
+}
- if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
- *(u16 *)dspi->rx = d;
- dspi->rx += 2;
+static int dspi_tcfq_write(struct fsl_dspi *dspi)
+{
+ int tx_word;
+ u32 dspi_pushr = 0;
- } else {
- unsigned int val;
+ tx_word = is_double_byte_mode(dspi);
- regmap_read(dspi->regmap, SPI_POPR, &val);
- d = SPI_POPR_RXDATA(val);
- if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
- *(u8 *)dspi->rx = d;
- dspi->rx++;
- }
- rx_count++;
+ if (tx_word && (dspi->len == 1)) {
+ dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+ regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+ tx_word = 0;
}
- return rx_count;
+ dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
+ if ((dspi->cs_change) && (!dspi->len))
+ dspi_pushr &= ~SPI_PUSHR_CONT;
+
+ regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
+
+ return tx_word + 1;
+}
+
+static void dspi_tcfq_read(struct fsl_dspi *dspi)
+{
+ int rx_word = is_double_byte_mode(dspi);
+
+ if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+ rx_word = 0;
+
+ dspi_data_from_popr(dspi, rx_word);
}
static int dspi_transfer_one_message(struct spi_master *master,
@@ -334,6 +371,12 @@ static int dspi_transfer_one_message(struct spi_master *master,
struct spi_device *spi = message->spi;
struct spi_transfer *transfer;
int status = 0;
+ enum dspi_trans_mode trans_mode;
+ u32 spi_tcr;
+
+ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+ dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+
message->actual_length = 0;
list_for_each_entry(transfer, &message->transfers, transfer_list) {
@@ -341,10 +384,10 @@ static int dspi_transfer_one_message(struct spi_master *master,
dspi->cur_msg = message;
dspi->cur_chip = spi_get_ctldata(spi);
dspi->cs = spi->chip_select;
+ dspi->cs_change = 0;
if (dspi->cur_transfer->transfer_list.next
== &dspi->cur_msg->transfers)
- transfer->cs_change = 1;
- dspi->cs_change = transfer->cs_change;
+ dspi->cs_change = 1;
dspi->void_write_data = dspi->cur_chip->void_write_data;
dspi->dataflags = 0;
@@ -370,8 +413,22 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
dspi->cur_chip->ctar_val);
- regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
- message->actual_length += dspi_transfer_write(dspi);
+ trans_mode = dspi->devtype_data->trans_mode;
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
+ dspi_eoq_write(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
+ dspi_tcfq_write(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+ trans_mode);
+ status = -EINVAL;
+ goto out;
+ }
if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
@@ -381,6 +438,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
udelay(transfer->delay_usecs);
}
+out:
message->status = status;
spi_finalize_current_message(master);
@@ -460,27 +518,89 @@ static void dspi_cleanup(struct spi_device *spi)
static irqreturn_t dspi_interrupt(int irq, void *dev_id)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
-
struct spi_message *msg = dspi->cur_msg;
+ enum dspi_trans_mode trans_mode;
+ u32 spi_sr, spi_tcr;
+ u32 spi_tcnt, tcnt_diff;
+ int tx_word;
- regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF);
- dspi_transfer_read(dspi);
-
- if (!dspi->len) {
+ regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+ regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+
+ if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) {
+ tx_word = is_double_byte_mode(dspi);
+
+ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+ spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+ /*
+ * The width of SPI Transfer Counter in SPI_TCR is 16bits,
+ * so the max couner is 65535. When the counter reach 65535,
+ * it will wrap around, counter reset to zero.
+ * spi_tcnt my be less than dspi->spi_tcnt, it means the
+ * counter already wrapped around.
+ * SPI Transfer Counter is a counter of transmitted frames.
+ * The size of frame maybe two bytes.
+ */
+ tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt)
+ % SPI_TCR_TCNT_MAX;
+ tcnt_diff *= (tx_word + 1);
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
- SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16));
+ tcnt_diff--;
+
+ msg->actual_length += tcnt_diff;
+
+ dspi->spi_tcnt = spi_tcnt;
+
+ trans_mode = dspi->devtype_data->trans_mode;
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ dspi_eoq_read(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ dspi_tcfq_read(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+ trans_mode);
+ return IRQ_HANDLED;
+ }
- dspi->waitflags = 1;
- wake_up_interruptible(&dspi->waitq);
- } else
- msg->actual_length += dspi_transfer_write(dspi);
+ if (!dspi->len) {
+ if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
+ regmap_update_bits(dspi->regmap,
+ SPI_CTAR(dspi->cs),
+ SPI_FRAME_BITS_MASK,
+ SPI_FRAME_BITS(16));
+ dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
+ }
+
+ dspi->waitflags = 1;
+ wake_up_interruptible(&dspi->waitq);
+ } else {
+ switch (trans_mode) {
+ case DSPI_EOQ_MODE:
+ dspi_eoq_write(dspi);
+ break;
+ case DSPI_TCFQ_MODE:
+ dspi_tcfq_write(dspi);
+ break;
+ default:
+ dev_err(&dspi->pdev->dev,
+ "unsupported trans_mode %u\n",
+ trans_mode);
+ }
+ }
+ }
return IRQ_HANDLED;
}
static const struct of_device_id fsl_dspi_dt_ids[] = {
- { .compatible = "fsl,vf610-dspi", .data = NULL, },
+ { .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
+ { .compatible = "fsl,ls1021a-v1.0-dspi",
+ .data = (void *)&ls1021a_v1_data, },
+ { .compatible = "fsl,ls2085a-dspi", .data = (void *)&ls2085a_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
@@ -494,6 +614,8 @@ static int dspi_suspend(struct device *dev)
spi_master_suspend(master);
clk_disable_unprepare(dspi->clk);
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -502,6 +624,8 @@ static int dspi_resume(struct device *dev)
struct spi_master *master = dev_get_drvdata(dev);
struct fsl_dspi *dspi = spi_master_get_devdata(master);
+ pinctrl_pm_select_default_state(dev);
+
clk_prepare_enable(dspi->clk);
spi_master_resume(master);
@@ -526,6 +650,8 @@ static int dspi_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
int ret = 0, cs_num, bus_num;
+ const struct of_device_id *of_id =
+ of_match_device(fsl_dspi_dt_ids, &pdev->dev);
master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
if (!master)
@@ -559,6 +685,13 @@ static int dspi_probe(struct platform_device *pdev)
}
master->bus_num = bus_num;
+ dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
+ if (!dspi->devtype_data) {
+ dev_err(&pdev->dev, "can't get devtype_data\n");
+ ret = -EFAULT;
+ goto out_master_put;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) {
@@ -566,7 +699,7 @@ static int dspi_probe(struct platform_device *pdev)
goto out_master_put;
}
- dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
+ dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
&dspi_regmap_config);
if (IS_ERR(dspi->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index d0a73a0..d3f05a0 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -359,14 +359,16 @@ static void fsl_espi_rw_trans(struct spi_message *m,
struct fsl_espi_transfer *trans, u8 *rx_buff)
{
struct fsl_espi_transfer *espi_trans = trans;
- unsigned int n_tx = espi_trans->n_tx;
- unsigned int n_rx = espi_trans->n_rx;
+ unsigned int total_len = espi_trans->len;
struct spi_transfer *t;
u8 *local_buf;
u8 *rx_buf = rx_buff;
unsigned int trans_len;
unsigned int addr;
- int i, pos, loop;
+ unsigned int tx_only;
+ unsigned int rx_pos = 0;
+ unsigned int pos;
+ int i, loop;
local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
if (!local_buf) {
@@ -374,36 +376,48 @@ static void fsl_espi_rw_trans(struct spi_message *m,
return;
}
- for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) {
- trans_len = n_rx - pos;
- if (trans_len > SPCOM_TRANLEN_MAX - n_tx)
- trans_len = SPCOM_TRANLEN_MAX - n_tx;
+ for (pos = 0, loop = 0; pos < total_len; pos += trans_len, loop++) {
+ trans_len = total_len - pos;
i = 0;
+ tx_only = 0;
list_for_each_entry(t, &m->transfers, transfer_list) {
if (t->tx_buf) {
memcpy(local_buf + i, t->tx_buf, t->len);
i += t->len;
+ if (!t->rx_buf)
+ tx_only += t->len;
}
}
+ /* Add additional TX bytes to compensate SPCOM_TRANLEN_MAX */
+ if (loop > 0)
+ trans_len += tx_only;
+
+ if (trans_len > SPCOM_TRANLEN_MAX)
+ trans_len = SPCOM_TRANLEN_MAX;
+
+ /* Update device offset */
if (pos > 0) {
addr = fsl_espi_cmd2addr(local_buf);
- addr += pos;
+ addr += rx_pos;
fsl_espi_addr2cmd(addr, local_buf);
}
- espi_trans->n_tx = n_tx;
- espi_trans->n_rx = trans_len;
- espi_trans->len = trans_len + n_tx;
+ espi_trans->len = trans_len;
espi_trans->tx_buf = local_buf;
espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
- memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
+ /* If there is at least one RX byte then copy it to rx_buf */
+ if (tx_only < SPCOM_TRANLEN_MAX)
+ memcpy(rx_buf + rx_pos, espi_trans->rx_buf + tx_only,
+ trans_len - tx_only);
+
+ rx_pos += trans_len - tx_only;
if (loop > 0)
- espi_trans->actual_length += espi_trans->len - n_tx;
+ espi_trans->actual_length += espi_trans->len - tx_only;
else
espi_trans->actual_length += espi_trans->len;
}
@@ -418,6 +432,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master,
u8 *rx_buf = NULL;
unsigned int n_tx = 0;
unsigned int n_rx = 0;
+ unsigned int xfer_len = 0;
struct fsl_espi_transfer espi_trans;
list_for_each_entry(t, &m->transfers, transfer_list) {
@@ -427,11 +442,13 @@ static int fsl_espi_do_one_msg(struct spi_master *master,
n_rx += t->len;
rx_buf = t->rx_buf;
}
+ if ((t->tx_buf) || (t->rx_buf))
+ xfer_len += t->len;
}
espi_trans.n_tx = n_tx;
espi_trans.n_rx = n_rx;
- espi_trans.len = n_tx + n_rx;
+ espi_trans.len = xfer_len;
espi_trans.actual_length = 0;
espi_trans.status = 0;
@@ -544,9 +561,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
/* spin until TX is done */
ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
- &reg_base->event)) & SPIE_NF) == 0, 1000, 0);
+ &reg_base->event)) & SPIE_NF), 1000, 0);
if (!ret) {
dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
+
+ /* Clear the SPIE bits */
+ mpc8xxx_spi_write_reg(&reg_base->event, events);
+ complete(&mspi->done);
return;
}
}
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index f08e812..eb7d3a6 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -674,7 +674,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
.devtype = IMX51_ECSPI,
};
-static struct platform_device_id spi_imx_devtype[] = {
+static const struct platform_device_id spi_imx_devtype[] = {
{
.name = "imx1-cspi",
.driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 4df89420..5867384 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -35,6 +35,7 @@
#include <linux/gcd.h>
#include <linux/spi/spi.h>
+#include <linux/gpio.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
@@ -242,17 +243,27 @@ 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;
+ /* 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;
- mcspi_write_chconf0(spi, l);
+ if (spi->controller_state) {
+ l = mcspi_cached_chconf0(spi);
+
+ 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)
@@ -1011,6 +1022,15 @@ static int omap2_mcspi_setup(struct spi_device *spi)
return ret;
}
+ if (gpio_is_valid(spi->cs_gpio)) {
+ 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);
if (ret < 0)
return ret;
@@ -1050,9 +1070,13 @@ 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 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,18 +1086,14 @@ 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;
struct omap2_mcspi_cs *cs;
struct omap2_mcspi_device_config *cd;
int par_override = 0;
int status = 0;
u32 chconf;
- spi = m->spi;
master = spi->master;
mcspi_dma = mcspi->dma_channels + spi->chip_select;
cs = spi->controller_state;
@@ -1090,103 +1110,84 @@ 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 (gpio_is_valid(spi->cs_gpio))
+ omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
- 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 (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 (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;
- }
+ 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);
+ mcspi_write_chconf0(spi, chconf);
- if (t->len) {
- unsigned count;
+ 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);
+ 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);
+ 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);
+ /* 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 ((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;
- break;
- }
+ 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);
- omap2_mcspi_set_enable(spi, 0);
+ if (mcspi->fifo_depth > 0)
+ omap2_mcspi_set_fifo(spi, t, 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;
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;
@@ -1197,70 +1198,64 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
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);
- 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;
+ 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 (m->is_dma_mapped || len < DMA_MIN_BYTES)
- continue;
+ 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 (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 (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 (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;
- }
+ }
+ 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(master);
- return 0;
+skip_dma_map:
+ return omap2_mcspi_work_one(mcspi, spi, t);
}
static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
@@ -1339,7 +1334,8 @@ 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->set_cs = omap2_mcspi_set_cs;
master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 8616647..8cad107 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -61,6 +61,12 @@ enum orion_spi_type {
struct orion_spi_dev {
enum orion_spi_type typ;
+ /*
+ * min_divisor and max_hz should be exclusive, the only we can
+ * have both is for managing the armada-370-spi case with old
+ * device tree
+ */
+ unsigned long max_hz;
unsigned int min_divisor;
unsigned int max_divisor;
u32 prescale_mask;
@@ -385,16 +391,54 @@ static const struct orion_spi_dev orion_spi_dev_data = {
.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
};
-static const struct orion_spi_dev armada_spi_dev_data = {
+static const struct orion_spi_dev armada_370_spi_dev_data = {
.typ = ARMADA_SPI,
- .min_divisor = 1,
+ .min_divisor = 4,
+ .max_divisor = 1920,
+ .max_hz = 50000000,
+ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_xp_spi_dev_data = {
+ .typ = ARMADA_SPI,
+ .max_hz = 50000000,
+ .max_divisor = 1920,
+ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_375_spi_dev_data = {
+ .typ = ARMADA_SPI,
+ .min_divisor = 15,
.max_divisor = 1920,
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
};
static const struct of_device_id orion_spi_of_match_table[] = {
- { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
- { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
+ {
+ .compatible = "marvell,orion-spi",
+ .data = &orion_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-370-spi",
+ .data = &armada_370_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-375-spi",
+ .data = &armada_375_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-380-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-390-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+ {
+ .compatible = "marvell,armada-xp-spi",
+ .data = &armada_xp_spi_dev_data,
+ },
+
{}
};
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
@@ -454,7 +498,23 @@ static int orion_spi_probe(struct platform_device *pdev)
goto out;
tclk_hz = clk_get_rate(spi->clk);
- master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+
+ /*
+ * With old device tree, armada-370-spi could be used with
+ * Armada XP, however for this SoC the maximum frequency is
+ * 50MHz instead of tclk/4. On Armada 370, tclk cannot be
+ * higher than 200MHz. So, in order to be able to handle both
+ * SoCs, we can take the minimum of 50MHz and tclk/4.
+ */
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-370-spi"))
+ master->max_speed_hz = min(devdata->max_hz,
+ DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
+ else if (devdata->min_divisor)
+ master->max_speed_hz =
+ DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+ else
+ master->max_speed_hz = devdata->max_hz;
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c
new file mode 100644
index 0000000..3641d0e
--- /dev/null
+++ b/drivers/spi/spi-rb4xx.c
@@ -0,0 +1,210 @@
+/*
+ * SPI controller driver for the Mikrotik RB4xx boards
+ *
+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
+ *
+ * This file was based on the patches for Linux 2.6.27.39 published by
+ * MikroTik for their RouterBoard 4xx series devices.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+struct rb4xx_spi {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
+{
+ return __raw_readl(rbspi->base + reg);
+}
+
+static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
+{
+ __raw_writel(value, rbspi->base + reg);
+}
+
+static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
+{
+ u32 regval;
+
+ regval = spi_ioc;
+ if (value & BIT(0))
+ regval |= AR71XX_SPI_IOC_DO;
+
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ do_spi_clk(rbspi, spi_ioc, byte >> i);
+}
+
+/* The CS2 pin is used to clock in a second bit per clock cycle. */
+static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
+ u8 value)
+{
+ u32 regval;
+
+ regval = spi_ioc;
+ if (value & BIT(1))
+ regval |= AR71XX_SPI_IOC_DO;
+ if (value & BIT(0))
+ regval |= AR71XX_SPI_IOC_CS2;
+
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+/* Two bits at a time, msb first */
+static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
+ do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
+}
+
+static void rb4xx_set_cs(struct spi_device *spi, bool enable)
+{
+ struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
+
+ /*
+ * Setting CS is done along with bitbanging the actual values,
+ * since it's all on the same hardware register. However the
+ * CPLD needs CS deselected after every command.
+ */
+ if (enable)
+ rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
+ AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
+}
+
+static int rb4xx_transfer_one(struct spi_master *master,
+ struct spi_device *spi, struct spi_transfer *t)
+{
+ struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
+ int i;
+ u32 spi_ioc;
+ u8 *rx_buf;
+ const u8 *tx_buf;
+
+ /*
+ * Prime the SPI register with the SPI device selected. The m25p80 boot
+ * flash and CPLD share the CS0 pin. This works because the CPLD's
+ * command set was designed to almost not clash with that of the
+ * boot flash.
+ */
+ if (spi->chip_select == 2)
+ /* MMC */
+ spi_ioc = AR71XX_SPI_IOC_CS0;
+ else
+ /* Boot flash and CPLD */
+ spi_ioc = AR71XX_SPI_IOC_CS1;
+
+ tx_buf = t->tx_buf;
+ rx_buf = t->rx_buf;
+ for (i = 0; i < t->len; ++i) {
+ if (t->tx_nbits == SPI_NBITS_DUAL)
+ /* CPLD can use two-wire transfers */
+ do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
+ else
+ do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
+ if (!rx_buf)
+ continue;
+ rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
+ }
+ spi_finalize_current_transfer(master);
+
+ return 0;
+}
+
+static int rb4xx_spi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct clk *ahb_clk;
+ struct rb4xx_spi *rbspi;
+ struct resource *r;
+ int err;
+ void __iomem *spi_base;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ spi_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(spi_base))
+ return PTR_ERR(spi_base);
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
+ if (!master)
+ return -ENOMEM;
+
+ ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(ahb_clk))
+ return PTR_ERR(ahb_clk);
+
+ master->bus_num = 0;
+ master->num_chipselect = 3;
+ master->mode_bits = SPI_TX_DUAL;
+ master->bits_per_word_mask = BIT(7);
+ master->flags = SPI_MASTER_MUST_TX;
+ master->transfer_one = rb4xx_transfer_one;
+ master->set_cs = rb4xx_set_cs;
+
+ err = devm_spi_register_master(&pdev->dev, master);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register SPI master\n");
+ return err;
+ }
+
+ err = clk_prepare_enable(ahb_clk);
+ if (err)
+ return err;
+
+ rbspi = spi_master_get_devdata(master);
+ rbspi->base = spi_base;
+ rbspi->clk = ahb_clk;
+ platform_set_drvdata(pdev, rbspi);
+
+ /* Enable SPI */
+ rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
+
+ return 0;
+}
+
+static int rb4xx_spi_remove(struct platform_device *pdev)
+{
+ struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(rbspi->clk);
+
+ return 0;
+}
+
+static struct platform_driver rb4xx_spi_drv = {
+ .probe = rb4xx_spi_probe,
+ .remove = rb4xx_spi_remove,
+ .driver = {
+ .name = "rb4xx-spi",
+ },
+};
+
+module_platform_driver(rb4xx_spi_drv);
+
+MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c
index f6bac9e..f9189a0 100644
--- a/drivers/spi/spi-rspi.c
+++ b/drivers/spi/spi-rspi.c
@@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
struct spi_transfer *xfer)
{
- if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
- /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
- int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
- xfer->rx_buf ? &xfer->rx_sg : NULL);
- if (ret != -EAGAIN)
- return 0;
- }
+ if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer))
+ return -EAGAIN;
- return -EAGAIN;
+ /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
+ return rspi_dma_transfer(rspi, &xfer->tx_sg,
+ xfer->rx_buf ? &xfer->rx_sg : NULL);
}
static int rspi_common_transfer(struct rspi_data *rspi,
@@ -724,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master,
return rspi_common_transfer(rspi, xfer);
}
-static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx,
+static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
u8 *rx, unsigned int len)
{
int i, n, ret;
@@ -771,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
if (ret != -EAGAIN)
return ret;
- ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf,
+ return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf,
xfer->rx_buf, xfer->len);
- if (ret < 0)
- return ret;
-
- return 0;
}
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
@@ -1300,7 +1293,7 @@ error1:
return ret;
}
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
{ "rspi", (kernel_ulong_t)&rspi_ops },
{ "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
{ "qspi", (kernel_ulong_t)&qspi_ops },
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index b1c6731..2a8c513 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
};
-static struct platform_device_id s3c64xx_spi_driver_ids[] = {
+static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
{
.name = "s3c2443-spi",
.driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index bcc7c63..d3370a6 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d5d7d22..cf8b91b 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -571,7 +571,7 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
return 0;
}
-static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
+static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
{
struct spi_transfer *xfer;
struct device *tx_dev, *rx_dev;
@@ -599,13 +599,32 @@ static inline int __spi_map_msg(struct spi_master *master,
return 0;
}
-static inline int spi_unmap_msg(struct spi_master *master,
- struct spi_message *msg)
+static inline int __spi_unmap_msg(struct spi_master *master,
+ struct spi_message *msg)
{
return 0;
}
#endif /* !CONFIG_HAS_DMA */
+static inline int spi_unmap_msg(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_transfer *xfer;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ /*
+ * Restore the original value of tx_buf or rx_buf if they are
+ * NULL.
+ */
+ if (xfer->tx_buf == master->dummy_tx)
+ xfer->tx_buf = NULL;
+ if (xfer->rx_buf == master->dummy_rx)
+ xfer->rx_buf = NULL;
+ }
+
+ return __spi_unmap_msg(master, msg);
+}
+
static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
{
struct spi_transfer *xfer;
@@ -979,9 +998,6 @@ void spi_finalize_current_message(struct spi_master *master)
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);
spi_unmap_msg(master, mesg);
@@ -994,9 +1010,13 @@ void spi_finalize_current_message(struct spi_master *master)
}
}
- trace_spi_message_done(mesg);
-
+ spin_lock_irqsave(&master->queue_lock, flags);
+ master->cur_msg = NULL;
master->cur_msg_prepared = false;
+ queue_kthread_work(&master->kworker, &master->pump_messages);
+ spin_unlock_irqrestore(&master->queue_lock, flags);
+
+ trace_spi_message_done(mesg);
mesg->state = NULL;
if (mesg->complete)
OpenPOWER on IntegriCloud