Doing this in combination with "atmel_spi: fix hang due to missed
interrupt" appears to eliminate the overruns I'm seeing when using
JFFS2-on-DataFlash as /usr filesystem on the ATNGW100.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
The "atmel" releases have been running the DataFlash at 8 MHz for a
while, so this brings mainline in sync with them.
I think the only way to increase the bus speed beyond this is to use
faster RAM. I'll look into using SRAM bounce buffers next.
I'll apply this patch to my avr32 tree. David, I'll let you decide
whether the other three patches are important/safe enough for 2.6.27.
I've verified that they don't break DataFlash on ATNGW100, but they
could use some testing on other setups, in particular AT91RM9200 and
other AT91 chips.
arch/avr32/boards/atngw100/setup.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c
index c7fe94d..12b21ac 100644
--- a/arch/avr32/boards/atngw100/setup.c
+++ b/arch/avr32/boards/atngw100/setup.c
@@ -47,7 +47,7 @@ static struct eth_platform_data __initdata eth_data[2];
static struct spi_board_info spi0_board_info[] __initdata = {
{
.modalias = "mtd_dataflash",
- .max_speed_hz = 10000000,
+ .max_speed_hz = 8000000,
.chip_select = 0,
},
};
--
1.5.6.3
This solves several issues:
* It fixes the wrong idle clock polarity issue in a cleaner and less
expensive way.
* It handles the AT32AP7000 errata "SPI Chip Select 0 BITS field
overrides other Chip Selects". Other chips, e.g. AT91SAM9261, have
similar issues.
Currently, the AT91RM9200 code path is left alone. But it might be
interesting to try the same technique on RM9200 using a different CSR
register.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
drivers/spi/atmel_spi.c | 95 ++++++++++++++++++++++++++++++++--------------
1 files changed, 66 insertions(+), 29 deletions(-)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 88239ca..f4d60ec 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -51,6 +51,12 @@ struct atmel_spi {
dma_addr_t buffer_dma;
};
+/* Controller-specific per-slave state */
+struct atmel_spi_device {
+ unsigned int npcs_pin;
+ u32 csr;
+};
+
#define BUFFER_SIZE PAGE_SIZE
#define INVALID_DMA_ADDRESS 0xffffffff
@@ -89,39 +95,54 @@ 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)
{
- unsigned gpio = (unsigned) spi->controller_data;
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned active = spi->mode & SPI_CS_HIGH;
- u32 mr;
- int i;
- u32 csr;
- u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
-
- /* Make sure clock polarity is correct */
- for (i = 0; i < spi->master->num_chipselect; i++) {
- csr = spi_readl(as, CSR0 + 4 * i);
- if ((csr ^ cpol) & SPI_BIT(CPOL))
- spi_writel(as, CSR0 + 4 * i, csr ^ SPI_BIT(CPOL));
- }
- mr = spi_readl(as, MR);
- mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
-
- dev_dbg(&spi->dev, "activate %u%s, mr %08x\n",
- gpio, active ? " (high)" : "",
- 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.
+ */
+ spi_writel(as, CSR0, asd->csr);
+ spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
+ | SPI_BIT(MSTR));
+ spi_readl(as, MR);
+ gpio_set_value(asd->npcs_pin, active);
+ } else {
+ u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
+ int i;
+ u32 mr;
+ u32 csr;
+
+ /* Make sure clock polarity is correct */
+ for (i = 0; i < spi->master->num_chipselect; i++) {
+ csr = spi_readl(as, CSR0 + 4 * i);
+ if ((csr ^ cpol) & SPI_BIT(CPOL))
+ spi_writel(as, CSR0 + 4 * i,
+ csr ^ SPI_BIT(CPOL));
+ }
- if (atmel_spi_is_v2() || spi->chip_select != 0)
- gpio_set_value(gpio, active);
- spi_writel(as, MR, mr);
+ mr = spi_readl(as, MR);
+ mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
+ if (spi->chip_select != 0)
+ gpio_set_value(asd->npcs_pin, active);
+ spi_writel(as, MR, mr);
+ }
}
static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
{
- unsigned gpio = (unsigned) spi->controller_data;
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned active = spi->mode & SPI_CS_HIGH;
u32 mr;
@@ -135,11 +156,11 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
}
dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n",
- gpio, active ? " (low)" : "",
+ asd->npcs_pin, active ? " (low)" : "",
mr);
if (atmel_spi_is_v2() || spi->chip_select != 0)
- gpio_set_value(gpio, !active);
+ gpio_set_value(asd->npcs_pin, !active);
}
static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
@@ -511,6 +532,7 @@ atmel_spi_interrupt(int irq, void *dev_id)
static int atmel_spi_setup(struct spi_device *spi)
{
struct atmel_spi *as;
+ struct atmel_spi_device *asd;
u32 scbr, csr;
unsigned int bits = spi->bits_per_word;
unsigned long bus_hz;
@@ -595,11 +617,20 @@ static int atmel_spi_setup(struct spi_device *spi)
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned int)spi->controller_data;
- if (!spi->controller_state) {
+ asd = spi->controller_state;
+ if (!asd) {
+ asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL);
+ if (!asd)
+ return -ENOMEM;
+
ret = gpio_request(npcs_pin, spi->dev.bus_id);
- if (ret)
+ if (ret) {
+ kfree(asd);
return ret;
- spi->controller_state = (void *)npcs_pin;
+ }
+
+ asd->npcs_pin = npcs_pin;
+ spi->controller_state = asd;
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
} else {
unsigned long flags;
@@ -611,11 +642,14 @@ static int atmel_spi_setup(struct spi_device *spi)
spin_unlock_irqrestore(&as->lock, flags);
}
+ asd->csr = csr;
+
dev_dbg(&spi->dev,
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
bus_hz / scbr, bits, spi->mode, spi->chip_select, csr);
- spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
+ if (!atmel_spi_is_v2())
+ spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
return 0;
}
@@ -690,10 +724,11 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
static void atmel_spi_cleanup(struct spi_device *spi)
{
struct atmel_spi *as = spi_master_get_devdata(spi->master);
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned gpio = (unsigned) spi->controller_data;
unsigned long flags;
- if (!spi->controller_state)
+ if (!asd)
return;
spin_lock_irqsave(&as->lock, flags);
@@ -703,7 +738,9 @@ static void atmel_spi_cleanup(struct spi_device *spi)
}
spin_unlock_irqrestore(&as->lock, flags);
+ spi->controller_state = NULL;
gpio_free(gpio);
+ kfree(asd);
}
/*-------------------------------------------------------------------------*/
--
1.5.6.3
Currently, we have a flag called "new_1" which is basically equivalent
to cpu_is_at91rm9200(). The latter is also called directly a few places.
Clean up this mess by introducing a atmel_spi_v2() function for
determining the controller version, and move all version dependent code
over to use it. This allows us to remove the new_1 flag.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
drivers/spi/atmel_spi.c | 40 ++++++++++++++++++++++------------------
1 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 95190c6..88239ca 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -30,13 +30,6 @@
* The core SPI transfer engine just talks to a register bank to set up
* DMA transfers; transfer queue progress is driven by IRQs. The clock
* framework provides the base clock, subdivided for each spi_device.
- *
- * Newer controllers, marked with "new_1" flag, have:
- * - CR.LASTXFER
- * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero)
- * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs)
- * - SPI_CSRx.CSAAT
- * - SPI_CSRx.SBCR allows faster clocking
*/
struct atmel_spi {
spinlock_t lock;
@@ -45,7 +38,6 @@ struct atmel_spi {
int irq;
struct clk *clk;
struct platform_device *pdev;
- unsigned new_1:1;
struct spi_device *stay;
u8 stopping;
@@ -63,6 +55,23 @@ struct atmel_spi {
#define INVALID_DMA_ADDRESS 0xffffffff
/*
+ * Version 2 of the SPI controller has
+ * - CR.LASTXFER
+ * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero)
+ * - 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)
+{
+ return !cpu_is_at91rm9200();
+}
+
+/*
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
* they assume that spi slave device state will not change on deselect, so
* that automagic deselection is OK. ("NPCSx rises if no data is to be
@@ -105,7 +114,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
gpio, active ? " (high)" : "",
mr);
- if (!(cpu_is_at91rm9200() && spi->chip_select == 0))
+ if (atmel_spi_is_v2() || spi->chip_select != 0)
gpio_set_value(gpio, active);
spi_writel(as, MR, mr);
}
@@ -129,7 +138,7 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
gpio, active ? " (low)" : "",
mr);
- if (!(cpu_is_at91rm9200() && spi->chip_select == 0))
+ if (atmel_spi_is_v2() || spi->chip_select != 0)
gpio_set_value(gpio, !active);
}
@@ -536,19 +545,16 @@ static int atmel_spi_setup(struct spi_device *spi)
}
/* see notes above re chipselect */
- if (cpu_is_at91rm9200()
+ if (!atmel_spi_is_v2()
&& spi->chip_select == 0
&& (spi->mode & SPI_CS_HIGH)) {
dev_dbg(&spi->dev, "setup: can't be active-high\n");
return -EINVAL;
}
- /*
- * Pre-new_1 chips start out at half the peripheral
- * bus speed.
- */
+ /* v1 chips start out at half the peripheral bus speed. */
bus_hz = clk_get_rate(as->clk);
- if (!as->new_1)
+ if (!atmel_spi_is_v2())
bus_hz /= 2;
if (spi->max_speed_hz) {
@@ -755,8 +761,6 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
goto out_free_buffer;
as->irq = irq;
as->clk = clk;
- if (!cpu_is_at91rm9200())
- as->new_1 = 1;
ret = request_irq(irq, atmel_spi_interrupt, 0,
pdev->dev.bus_id, master);
--
1.5.6.3
Allow drivers to set the bits_per_word and speed_hz fields of struct
spi_transfer to nonzero values. Doing this makes the transfers somewhat
more expensive as it prevents chaining and may require a division when
the transfer is submitted.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
drivers/spi/atmel_spi.c | 138 ++++++++++++++++++++++++++++++++++-------------
1 files changed, 100 insertions(+), 38 deletions(-)
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index f4d60ec..0bdb4d5 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -39,6 +39,7 @@ struct atmel_spi {
struct clk *clk;
struct platform_device *pdev;
struct spi_device *stay;
+ unsigned long base_hz;
u8 stopping;
struct list_head queue;
@@ -102,6 +103,15 @@ static bool atmel_spi_is_v2(void)
* field in CSR0 overrides all other CSRs.
*/
+static void atmel_spi_set_csr(struct atmel_spi *as,
+ struct spi_device *spi, u32 csr)
+{
+ if (atmel_spi_is_v2())
+ spi_writel(as, CSR0, csr);
+ else
+ spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
+}
+
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
{
struct atmel_spi_device *asd = spi->controller_state;
@@ -113,7 +123,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
* switches to the correct idle polarity before we
* toggle the CS.
*/
- spi_writel(as, CSR0, asd->csr);
+ atmel_spi_set_csr(as, spi, asd->csr);
spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
| SPI_BIT(MSTR));
spi_readl(as, MR);
@@ -169,9 +179,15 @@ static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
return msg->transfers.prev == &xfer->transfer_list;
}
-static inline int atmel_spi_xfer_can_be_chained(struct spi_transfer *xfer)
+static inline bool atmel_spi_xfer_needs_preproc(struct spi_transfer *xfer)
{
- return xfer->delay_usecs == 0 && !xfer->cs_change;
+ return xfer->bits_per_word || xfer->speed_hz;
+}
+
+static inline bool atmel_spi_xfer_needs_postproc(struct spi_transfer *xfer)
+{
+ return xfer->delay_usecs == 0 || xfer->cs_change
+ || xfer->bits_per_word || xfer->speed_hz;
}
static void atmel_spi_next_xfer_data(struct spi_master *master,
@@ -228,6 +244,28 @@ static void atmel_spi_next_xfer(struct spi_master *master,
xfer = NULL;
if (xfer) {
+ /*
+ * Per-transfer overrides prevent chaining before and
+ * after, so it should be safe to alter the CSR
+ * registers here.
+ */
+ if (atmel_spi_xfer_needs_preproc(xfer)) {
+ struct spi_device *spi = msg->spi;
+ struct atmel_spi_device *asd = spi->controller_state;
+ unsigned int bits = xfer->bits_per_word;
+ u32 csr = asd->csr;
+
+ if (bits)
+ csr = SPI_BFINS(BITS, csr, bits - 8);
+ if (xfer->speed_hz) {
+ u32 scbr = DIV_ROUND_UP(as->base_hz,
+ xfer->speed_hz);
+ csr = SPI_BFINS(SCBR, csr, scbr);
+ }
+
+ atmel_spi_set_csr(as, spi, csr);
+ }
+
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
len = xfer->len;
@@ -257,10 +295,12 @@ static void atmel_spi_next_xfer(struct spi_master *master,
if (remaining > 0)
len = remaining;
else if (!atmel_spi_xfer_is_last(msg, xfer)
- && atmel_spi_xfer_can_be_chained(xfer)) {
+ && !atmel_spi_xfer_needs_postproc(xfer)) {
xfer = list_entry(xfer->transfer_list.next,
struct spi_transfer, transfer_list);
len = xfer->len;
+ if (atmel_spi_xfer_needs_preproc(xfer))
+ xfer = NULL;
} else
xfer = NULL;
@@ -409,6 +449,45 @@ atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as,
atmel_spi_next_message(master);
}
+static void atmel_spi_xfer_done(struct spi_master *master,
+ struct spi_transfer *xfer, struct spi_message *msg)
+{
+ struct atmel_spi *as = spi_master_get_devdata(master);
+ msg->actual_length += xfer->len;
+
+ if (!msg->is_dma_mapped)
+ atmel_spi_dma_unmap_xfer(master, xfer);
+
+ /* REVISIT: udelay in irq is unfriendly */
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+
+ if (xfer->bits_per_word || xfer->speed_hz) {
+ /*
+ * Protocol options were overridden by this transfer;
+ * revert to default settings.
+ */
+ struct spi_device *spi = msg->spi;
+ struct atmel_spi_device *asd = spi->controller_state;
+
+ atmel_spi_set_csr(as, spi, asd->csr);
+ }
+
+ if (atmel_spi_xfer_is_last(msg, xfer)) {
+ /* report completed message */
+ atmel_spi_msg_done(master, as, msg, 0, xfer->cs_change);
+ } else {
+ if (xfer->cs_change) {
+ cs_deactivate(as, msg->spi);
+ udelay(1);
+ cs_activate(as, msg->spi);
+ }
+
+ /* Not done yet. Submit the next transfer. */
+ atmel_spi_next_xfer(master, msg);
+ }
+}
+
static irqreturn_t
atmel_spi_interrupt(int irq, void *dev_id)
{
@@ -484,41 +563,14 @@ atmel_spi_interrupt(int irq, void *dev_id)
spi_writel(as, IDR, pending);
- if (as->current_remaining_bytes == 0) {
- msg->actual_length += xfer->len;
-
- if (!msg->is_dma_mapped)
- atmel_spi_dma_unmap_xfer(master, xfer);
-
- /* REVISIT: udelay in irq is unfriendly */
- if (xfer->delay_usecs)
- udelay(xfer->delay_usecs);
-
- if (atmel_spi_xfer_is_last(msg, xfer)) {
- /* report completed message */
- atmel_spi_msg_done(master, as, msg, 0,
- xfer->cs_change);
- } else {
- if (xfer->cs_change) {
- cs_deactivate(as, msg->spi);
- udelay(1);
- cs_activate(as, msg->spi);
- }
-
- /*
- * Not done yet. Submit the next transfer.
- *
- * FIXME handle protocol options for xfer
- */
- atmel_spi_next_xfer(master, msg);
- }
- } else {
+ if (as->current_remaining_bytes == 0)
+ atmel_spi_xfer_done(master, xfer, msg);
+ else
/*
* Keep going, we still have data to send in
* the current transfer.
*/
atmel_spi_next_xfer(master, msg);
- }
}
spin_unlock(&as->lock);
@@ -578,6 +630,7 @@ static int atmel_spi_setup(struct spi_device *spi)
bus_hz = clk_get_rate(as->clk);
if (!atmel_spi_is_v2())
bus_hz /= 2;
+ as->base_hz = bus_hz;
if (spi->max_speed_hz) {
/*
@@ -679,10 +732,19 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
return -EINVAL;
}
- /* FIXME implement these protocol options!! */
- if (xfer->bits_per_word || xfer->speed_hz) {
- dev_dbg(&spi->dev, "no protocol options yet\n");
- return -ENOPROTOOPT;
+ if (xfer->bits_per_word && (xfer->bits_per_word < 8
+ || xfer->bits_per_word > 16)) {
+ dev_dbg(&spi->dev, "unsupported bits_per_word\n");
+ return -EINVAL;
+ }
+ if (xfer->speed_hz) {
+ unsigned long divider;
+ divider = DIV_ROUND_UP(as->base_hz, xfer->speed_hz);
+
+ if (divider > 255) {
+ dev_dbg(&spi->dev, "speed_hz too low\n");
+ return -EINVAL;
+ }
}
/*
--
1.5.6.3