diff options
Diffstat (limited to 'drivers/spi/spi-atmel.c')
| -rw-r--r-- | drivers/spi/spi-atmel.c | 106 |
1 files changed, 72 insertions, 34 deletions
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 656d137db25..ab2ed75d42f 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -22,9 +22,8 @@ #include <linux/platform_data/atmel.h> #include <linux/of.h> -#include <asm/io.h> -#include <asm/gpio.h> -#include <mach/cpu.h> +#include <linux/io.h> +#include <linux/gpio.h> /* SPI register offsets */ #define SPI_CR 0x0000 @@ -39,6 +38,7 @@ #define SPI_CSR1 0x0034 #define SPI_CSR2 0x0038 #define SPI_CSR3 0x003c +#define SPI_VERSION 0x00fc #define SPI_RPR 0x0100 #define SPI_RCR 0x0104 #define SPI_TPR 0x0108 @@ -71,6 +71,8 @@ #define SPI_FDIV_SIZE 1 #define SPI_MODFDIS_OFFSET 4 #define SPI_MODFDIS_SIZE 1 +#define SPI_WDRBT_OFFSET 5 +#define SPI_WDRBT_SIZE 1 #define SPI_LLB_OFFSET 7 #define SPI_LLB_SIZE 1 #define SPI_PCS_OFFSET 16 @@ -180,6 +182,11 @@ #define spi_writel(port,reg,value) \ __raw_writel((value), (port)->regs + SPI_##reg) +struct atmel_spi_caps { + bool is_spi2; + bool has_wdrbt; + bool has_dma_support; +}; /* * The core SPI transfer engine just talks to a register bank to set up @@ -201,9 +208,12 @@ struct atmel_spi { unsigned long current_remaining_bytes; struct spi_transfer *next_transfer; unsigned long next_remaining_bytes; + int done_status; void *buffer; dma_addr_t buffer_dma; + + struct atmel_spi_caps caps; }; /* Controller-specific per-slave state */ @@ -222,14 +232,10 @@ struct atmel_spi_device { * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs) * - SPI_CSRx.CSAAT * - SPI_CSRx.SBCR allows faster clocking - * - * We can determine the controller version by reading the VERSION - * register, but I haven't checked that it exists on all chips, and - * this is cheaper anyway. */ -static bool atmel_spi_is_v2(void) +static bool atmel_spi_is_v2(struct atmel_spi *as) { - return !cpu_is_at91rm9200(); + return as->caps.is_spi2; } /* @@ -250,11 +256,6 @@ static bool atmel_spi_is_v2(void) * Master on Chip Select 0.") No workaround exists for that ... so for * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, * and (c) will trigger that first erratum in some cases. - * - * TODO: Test if the atmel_spi_is_v2() branch below works on - * AT91RM9200 if we use some other register than CSR0. However, don't - * do this unconditionally since AP7000 has an errata where the BITS - * field in CSR0 overrides all other CSRs. */ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) @@ -263,15 +264,24 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) unsigned active = spi->mode & SPI_CS_HIGH; u32 mr; - if (atmel_spi_is_v2()) { - /* - * Always use CSR0. This ensures that the clock - * switches to the correct idle polarity before we - * toggle the CS. + if (atmel_spi_is_v2(as)) { + spi_writel(as, CSR0 + 4 * spi->chip_select, asd->csr); + /* For the low SPI version, there is a issue that PDC transfer + * on CS1,2,3 needs SPI_CSR0.BITS config as SPI_CSR1,2,3.BITS */ spi_writel(as, CSR0, asd->csr); - spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS) - | SPI_BIT(MSTR)); + if (as->caps.has_wdrbt) { + spi_writel(as, MR, + SPI_BF(PCS, ~(0x01 << spi->chip_select)) + | SPI_BIT(WDRBT) + | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); + } else { + spi_writel(as, MR, + SPI_BF(PCS, ~(0x01 << spi->chip_select)) + | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); + } mr = spi_readl(as, MR); gpio_set_value(asd->npcs_pin, active); } else { @@ -318,7 +328,7 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) asd->npcs_pin, active ? " (low)" : "", mr); - if (atmel_spi_is_v2() || spi->chip_select != 0) + if (atmel_spi_is_v2(as) || spi->chip_select != 0) gpio_set_value(asd->npcs_pin, !active); } @@ -544,15 +554,15 @@ static void atmel_spi_dma_unmap_xfer(struct spi_master *master, static void atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, - struct spi_message *msg, int status, int stay) + struct spi_message *msg, int stay) { - if (!stay || status < 0) + if (!stay || as->done_status < 0) cs_deactivate(as, msg->spi); else as->stay = msg->spi; list_del(&msg->queue); - msg->status = status; + msg->status = as->done_status; dev_dbg(master->dev.parent, "xfer complete: %u bytes transferred\n", @@ -564,6 +574,7 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, as->current_transfer = NULL; as->next_transfer = NULL; + as->done_status = 0; /* continue if needed */ if (list_empty(&as->queue) || as->stopping) @@ -641,7 +652,8 @@ atmel_spi_interrupt(int irq, void *dev_id) /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); - atmel_spi_msg_done(master, as, msg, -EIO, 0); + as->done_status = -EIO; + atmel_spi_msg_done(master, as, msg, 0); } else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) { ret = IRQ_HANDLED; @@ -659,7 +671,7 @@ atmel_spi_interrupt(int irq, void *dev_id) if (atmel_spi_xfer_is_last(msg, xfer)) { /* report completed message */ - atmel_spi_msg_done(master, as, msg, 0, + atmel_spi_msg_done(master, as, msg, xfer->cs_change); } else { if (xfer->cs_change) { @@ -719,7 +731,7 @@ static int atmel_spi_setup(struct spi_device *spi) } /* see notes above re chipselect */ - if (!atmel_spi_is_v2() + if (!atmel_spi_is_v2(as) && spi->chip_select == 0 && (spi->mode & SPI_CS_HIGH)) { dev_dbg(&spi->dev, "setup: can't be active-high\n"); @@ -728,7 +740,7 @@ static int atmel_spi_setup(struct spi_device *spi) /* v1 chips start out at half the peripheral bus speed. */ bus_hz = clk_get_rate(as->clk); - if (!atmel_spi_is_v2()) + if (!atmel_spi_is_v2(as)) bus_hz /= 2; if (spi->max_speed_hz) { @@ -804,7 +816,7 @@ static int atmel_spi_setup(struct spi_device *spi) "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", bus_hz / scbr, bits, spi->mode, spi->chip_select, csr); - if (!atmel_spi_is_v2()) + if (!atmel_spi_is_v2(as)) spi_writel(as, CSR0 + 4 * spi->chip_select, csr); return 0; @@ -910,6 +922,23 @@ static void atmel_spi_cleanup(struct spi_device *spi) kfree(asd); } +static inline unsigned int atmel_get_version(struct atmel_spi *as) +{ + return spi_readl(as, VERSION) & 0x00000fff; +} + +static void atmel_get_caps(struct atmel_spi *as) +{ + unsigned int version; + + version = atmel_get_version(as); + dev_info(&as->pdev->dev, "version: 0x%x\n", version); + + as->caps.is_spi2 = version > 0x121; + as->caps.has_wdrbt = version >= 0x210; + as->caps.has_dma_support = version >= 0x212; +} + /*-------------------------------------------------------------------------*/ static int atmel_spi_probe(struct platform_device *pdev) @@ -970,6 +999,8 @@ static int atmel_spi_probe(struct platform_device *pdev) as->irq = irq; as->clk = clk; + atmel_get_caps(as); + ret = request_irq(irq, atmel_spi_interrupt, 0, dev_name(&pdev->dev), master); if (ret) @@ -979,7 +1010,12 @@ static int atmel_spi_probe(struct platform_device *pdev) clk_enable(clk); spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ - spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); + if (as->caps.has_wdrbt) { + spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS) + | SPI_BIT(MSTR)); + } else { + spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); + } spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); @@ -1014,6 +1050,7 @@ static int atmel_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); struct spi_message *msg; + struct spi_transfer *xfer; /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); @@ -1025,9 +1062,10 @@ static int atmel_spi_remove(struct platform_device *pdev) /* Terminate remaining queued transfers */ list_for_each_entry(msg, &as->queue, queue) { - /* REVISIT unmapping the dma is a NOP on ARM and AVR32 - * but we shouldn't depend on that... - */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!msg->is_dma_mapped) + atmel_spi_dma_unmap_xfer(master, xfer); + } msg->status = -ESHUTDOWN; msg->complete(msg->context); } |