diff options
Diffstat (limited to 'packages/linux/linux-2.6.18/atmel-spi-master-driver.patch')
-rw-r--r-- | packages/linux/linux-2.6.18/atmel-spi-master-driver.patch | 990 |
1 files changed, 0 insertions, 990 deletions
diff --git a/packages/linux/linux-2.6.18/atmel-spi-master-driver.patch b/packages/linux/linux-2.6.18/atmel-spi-master-driver.patch deleted file mode 100644 index d45f3c50c9..0000000000 --- a/packages/linux/linux-2.6.18/atmel-spi-master-driver.patch +++ /dev/null @@ -1,990 +0,0 @@ -From: Haavard Skinnemoen <hskinnemoen@atmel.com> - -On Mon, 5 Jun 2006 06:49:52 -0700 -David Brownell <david-b@pacbell.net> wrote: - -> Here's an updated version that compiles and partially runs -> in the at91 world. If the avr32 code would switch to standard -> APIs like <linux/platform_device.h> and <linux/clk.h> it should -> behave there too, somewhat. Ignore the extra debug crap. - -Here's another update which includes most of the fixes in Dave's -version and which has been verified on my STK1000 board. - -I had some trouble getting your version to turn on the display -correctly, so I've reworked it as a series of smaller changes to figure -out what broke it. You should probably verify that I didn't leave out -anything important. - -Also, I didn't find CONFIG_SPI_AT91_MANUAL_CS defined anywhere, nor did -I find cpu_is_at91sam9261() so I left out those changes. - -Can any of you test this on AT91 somehow? I'm not completely up to -speed on my AT91RM9200-EK yet. Anyone else I should Cc? - -I can send you individual patches if you want. Here's the shortlog: - -Atmel SPI Driver -spi_atmel: convert to platform_device framework -spi_atmel: fix broken parameter validation in setup() and transfer() -spi_atmel: Revert core workaround for max_speed_hz=0 -spi_atmel: Divide len by two if bits_per_word > 8 -spi_atmel: Enable ENDRX interrupt when rx_buf is set -spi_atmel: Fix incorrect locking in interrupt routine -spi_atmel: Line up variable declarations -spi_atmel: Add some FIXMEs from David Brownell's patch -spi_atmel: Header file cleanup -spi_atmel: Introduce new_1 flag and use in sck speed setting -spi_atmel: Move SPI_ATMEL above SPI_BITBANG -Make SPI_ATMEL available to AT91 - - ->>From nobody Mon Sep 17 00:00:00 2001 -From: Haavard Skinnemoen <hskinnemoen@atmel.com> -Date: Thu Apr 6 10:33:21 2006 +0200 -Subject: [PATCH] Atmel SPI Driver - -SPI master driver for the Atmel AT32/AT91 SPI Controller. - -UPDATED: - - against 2.6.17 + latest kernel.org GIT - - "at32_device" stuff removed from core of driver - - add platform_device glue, so at91 compiles - - use clock framework - - understand "old" (rm9200) vs "new" (sam9261, ap7000) silicon - (only different scbr definition, not csaat etc.) - - remove bogus spi core tweaks - - various fixes and cleanups - -The following changes from atmel-spi-driver-3.patch have been dropped: - - CONFIG_SPI_AT91_MANUAL_CS stuff. I can't find the symbol - anywhere - - DMA IRQ optimization by unmasking TXEMPTY when starting xfer - - All the code setting CSAAT. - -From: Haavard Skinnemoen <hskinnemoen@atmel.com> - -On Mon, 5 Jun 2006 06:49:52 -0700 -David Brownell <david-b@pacbell.net> wrote: - -> Here's an updated version that compiles and partially runs -> in the at91 world. If the avr32 code would switch to standard -> APIs like <linux/platform_device.h> and <linux/clk.h> it should -> behave there too, somewhat. Ignore the extra debug crap. - -Here's another update which includes most of the fixes in Dave's -version and which has been verified on my STK1000 board. - -I had some trouble getting your version to turn on the display -correctly, so I've reworked it as a series of smaller changes to figure -out what broke it. You should probably verify that I didn't leave out -anything important. - -Also, I didn't find CONFIG_SPI_AT91_MANUAL_CS defined anywhere, nor did -I find cpu_is_at91sam9261() so I left out those changes. - -Can any of you test this on AT91 somehow? I'm not completely up to -speed on my AT91RM9200-EK yet. Anyone else I should Cc? - -I can send you individual patches if you want. Here's the shortlog: - -Atmel SPI Driver -spi_atmel: convert to platform_device framework -spi_atmel: fix broken parameter validation in setup() and transfer() -spi_atmel: Revert core workaround for max_speed_hz=0 -spi_atmel: Divide len by two if bits_per_word > 8 -spi_atmel: Enable ENDRX interrupt when rx_buf is set -spi_atmel: Fix incorrect locking in interrupt routine -spi_atmel: Line up variable declarations -spi_atmel: Add some FIXMEs from David Brownell's patch -spi_atmel: Header file cleanup -spi_atmel: Introduce new_1 flag and use in sck speed setting -spi_atmel: Move SPI_ATMEL above SPI_BITBANG -Make SPI_ATMEL available to AT91 - - ->>From nobody Mon Sep 17 00:00:00 2001 -From: Haavard Skinnemoen <hskinnemoen@atmel.com> -Date: Thu Apr 6 10:33:21 2006 +0200 -Subject: [PATCH] Atmel SPI Driver - -SPI master driver for the Atmel AT32/AT91 SPI Controller. - -UPDATED: - - against 2.6.17 + latest kernel.org GIT - - "at32_device" stuff removed from core of driver - - add platform_device glue, so at91 compiles - - use clock framework - - understand "old" (rm9200) vs "new" (sam9261, ap7000) silicon - (only different scbr definition, not csaat etc.) - - remove bogus spi core tweaks - - various fixes and cleanups - -The following changes from atmel-spi-driver-3.patch have been dropped: - - CONFIG_SPI_AT91_MANUAL_CS stuff. I can't find the symbol - anywhere - - DMA IRQ optimization by unmasking TXEMPTY when starting xfer - - All the code setting CSAAT. - ---- - drivers/spi/Kconfig | 7 - drivers/spi/Makefile | 1 - drivers/spi/atmel_spi.c | 648 ++++++++++++++++++++++++++++++++++++++++++++++++ - drivers/spi/atmel_spi.h | 167 ++++++++++++ - 4 files changed, 823 insertions(+) - -Index: linux-2.6.18-avr32/drivers/spi/Kconfig -=================================================================== ---- linux-2.6.18-avr32.orig/drivers/spi/Kconfig 2006-11-29 16:55:10.000000000 +0100 -+++ linux-2.6.18-avr32/drivers/spi/Kconfig 2006-11-29 16:55:59.000000000 +0100 -@@ -51,6 +51,13 @@ config SPI_MASTER - comment "SPI Master Controller Drivers" - depends on SPI_MASTER - -+config SPI_ATMEL -+ tristate "Atmel SPI Controller" -+ depends on (ARCH_AT91 || AVR32) && SPI_MASTER -+ help -+ This selects a driver for the Atmel SPI Controller, present on -+ many AT32 (AVR32) and AT91 (ARM) chips. -+ - config SPI_BITBANG - tristate "Bitbanging SPI master" - depends on SPI_MASTER && EXPERIMENTAL -Index: linux-2.6.18-avr32/drivers/spi/Makefile -=================================================================== ---- linux-2.6.18-avr32.orig/drivers/spi/Makefile 2006-11-29 16:55:10.000000000 +0100 -+++ linux-2.6.18-avr32/drivers/spi/Makefile 2006-11-29 16:55:59.000000000 +0100 -@@ -12,6 +12,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o - - # SPI master controller drivers (bus) - obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o -+obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o - obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o - obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o - obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o -Index: linux-2.6.18-avr32/drivers/spi/atmel_spi.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.18-avr32/drivers/spi/atmel_spi.c 2006-11-29 17:10:19.000000000 +0100 -@@ -0,0 +1,648 @@ -+/* -+ * Driver for Atmel AT32 and AT91 SPI Controllers -+ * -+ * Copyright (C) 2006 Atmel Corporation -+ * -+ * 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/init.h> -+#include <linux/clk.h> -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/delay.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/interrupt.h> -+#include <linux/spi/spi.h> -+ -+#include <asm/io.h> -+#include <asm/arch/board.h> -+#include <asm/arch/gpio.h> -+ -+#include "atmel_spi.h" -+ -+/* -+ * 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; -+ -+ void __iomem *regs; -+ int irq; -+ struct clk *clk; -+ struct platform_device *pdev; -+ unsigned new_1:1; -+ -+ u8 stopping; -+ struct list_head queue; -+ struct spi_transfer *current_transfer; -+ unsigned long remaining_bytes; -+ -+ void *buffer; -+ dma_addr_t buffer_dma; -+}; -+ -+#define BUFFER_SIZE PAGE_SIZE -+#define INVALID_DMA_ADDRESS 0xffffffff -+ -+/* -+ * 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. Not so! Workaround uses nCSx pins -+ * as GPIOs; or newer controllers have CSAAT and friends. -+ * -+ * Since the CSAAT functionality is a bit weird on newer controllers -+ * as well, we use GPIO to control nCSx pins on all controllers. -+ */ -+ -+static inline void cs_activate(struct spi_device *spi) -+{ -+ unsigned gpio = (unsigned) spi->controller_data; -+ -+ dev_dbg(&spi->dev, "activate %u\n", gpio); -+ gpio_set_value(gpio, 0); -+} -+ -+static inline void cs_deactivate(struct spi_device *spi) -+{ -+ unsigned gpio = (unsigned) spi->controller_data; -+ -+ dev_dbg(&spi->dev, "DEactivate %u\n", gpio); -+ gpio_set_value(gpio, 1); -+} -+ -+/* -+ * Submit next transfer for DMA. -+ * lock is held, spi irq is blocked -+ */ -+static void atmel_spi_next_xfer(struct spi_master *master, -+ struct spi_message *msg) -+{ -+ struct atmel_spi *as = spi_master_get_devdata(master); -+ struct spi_transfer *xfer; -+ u32 imr = 0; -+ u32 len; -+ dma_addr_t tx_dma, rx_dma; -+ -+ xfer = as->current_transfer; -+ if (!xfer || as->remaining_bytes == 0) { -+ if (xfer) -+ xfer = list_entry(xfer->transfer_list.next, -+ struct spi_transfer, transfer_list); -+ else -+ xfer = list_entry(msg->transfers.next, struct spi_transfer, -+ transfer_list); -+ as->remaining_bytes = xfer->len; -+ as->current_transfer = xfer; -+ } -+ -+ len = as->remaining_bytes; -+ -+ tx_dma = xfer->tx_dma; -+ rx_dma = xfer->rx_dma; -+ -+ if (rx_dma == INVALID_DMA_ADDRESS) { -+ rx_dma = as->buffer_dma; -+ if (len > BUFFER_SIZE) -+ len = BUFFER_SIZE; -+ } -+ if (tx_dma == INVALID_DMA_ADDRESS) { -+ if (xfer->tx_buf) { -+ tx_dma = as->buffer_dma; -+ if (len > BUFFER_SIZE) -+ len = BUFFER_SIZE; -+ memcpy(as->buffer, xfer->tx_buf, len); -+ dma_sync_single_for_device(&as->pdev->dev, -+ as->buffer_dma, len, -+ DMA_TO_DEVICE); -+ } else { -+ /* Send undefined data; rx_dma is handy */ -+ tx_dma = rx_dma; -+ } -+ } -+ -+ spi_writel(as, RPR, rx_dma); -+ spi_writel(as, TPR, tx_dma); -+ -+ as->remaining_bytes -= len; -+ if (msg->spi->bits_per_word > 8) -+ len >>= 1; -+ -+ /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" -+ * mechanism might help avoid the IRQ latency between transfers -+ * -+ * We're also waiting for ENDRX before we start the next -+ * transfer because we need to handle some difficult timing -+ * issues otherwise. If we wait for ENDTX in one transfer and -+ * then starts waiting for ENDRX in the next, it's difficult -+ * to tell the difference between the ENDRX interrupt we're -+ * actually waiting for and the ENDRX interrupt of the -+ * previous transfer. -+ * -+ * It should be doable, though. Just not now... -+ */ -+ spi_writel(as, TNCR, 0); -+ spi_writel(as, RNCR, 0); -+ imr = SPI_BIT(ENDRX); -+ -+ dev_dbg(&msg->spi->dev, -+ "start xfer %p: len %u tx %p/%08x rx %p/%08x imr %08x\n", -+ xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, -+ xfer->rx_buf, xfer->rx_dma, imr); -+ -+ wmb(); -+ spi_writel(as, TCR, len); -+ spi_writel(as, RCR, len); -+ spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); -+ spi_writel(as, IER, imr); -+} -+ -+static void atmel_spi_next_message(struct spi_master *master) -+{ -+ struct atmel_spi *as = spi_master_get_devdata(master); -+ struct spi_message *msg; -+ u32 mr; -+ -+ BUG_ON(as->current_transfer); -+ -+ msg = list_entry(as->queue.next, struct spi_message, queue); -+ -+ /* Select the chip */ -+ mr = spi_readl(as, MR); -+ mr = SPI_BFINS(PCS, ~(1 << msg->spi->chip_select), mr); -+ spi_writel(as, MR, mr); -+ cs_activate(msg->spi); -+ -+ atmel_spi_next_xfer(master, msg); -+} -+ -+static void atmel_spi_dma_map_xfer(struct atmel_spi *as, -+ struct spi_transfer *xfer) -+{ -+ xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; -+ if (!(xfer->len & (L1_CACHE_BYTES - 1))) { -+ if (xfer->tx_buf -+ && !((unsigned long)xfer->tx_buf & (L1_CACHE_BYTES - 1))) -+ xfer->tx_dma = dma_map_single(&as->pdev->dev, -+ xfer->tx_buf, -+ xfer->len, -+ DMA_TO_DEVICE); -+ if (xfer->rx_buf -+ && !((unsigned long)xfer->rx_buf & (L1_CACHE_BYTES - 1))) -+ xfer->rx_dma = dma_map_single(&as->pdev->dev, -+ xfer->rx_buf, -+ xfer->len, -+ DMA_FROM_DEVICE); -+ } -+} -+ -+static irqreturn_t -+atmel_spi_interrupt(int irq, void *dev_id, struct pt_regs *regs) -+{ -+ struct spi_master *master = dev_id; -+ struct atmel_spi *as = spi_master_get_devdata(master); -+ struct spi_message *msg; -+ struct spi_transfer *xfer; -+ u32 status, pending, imr; -+ int ret = IRQ_NONE; -+ -+ imr = spi_readl(as, IMR); -+ status = spi_readl(as, SR); -+ pending = status & imr; -+pr_debug("spi irq: stat %05x imr %05x pend %05x\n", status, imr, pending); -+ -+ if (pending & (SPI_BIT(ENDTX) | SPI_BIT(ENDRX))) { -+ ret = IRQ_HANDLED; -+ -+ spi_writel(as, IDR, pending); -+ spin_lock(&as->lock); -+ -+ xfer = as->current_transfer; -+ msg = list_entry(as->queue.next, struct spi_message, queue); -+ -+ /* -+ * If the rx buffer wasn't aligned, we used a bounce -+ * buffer for the transfer. Copy the data back and -+ * make the bounce buffer ready for re-use. -+ */ -+ if (xfer->rx_buf && xfer->rx_dma == INVALID_DMA_ADDRESS) { -+ unsigned int len = xfer->len; -+ if (len > BUFFER_SIZE) -+ len = BUFFER_SIZE; -+ -+ dma_sync_single_for_cpu(&as->pdev->dev, as->buffer_dma, -+ len, DMA_FROM_DEVICE); -+ memcpy((xfer->rx_buf + xfer->len -+ - len - as->remaining_bytes), -+ as->buffer, len); -+ } -+ -+ -+ if (as->remaining_bytes == 0) { -+ msg->actual_length += xfer->len; -+ -+ if (!msg->is_dma_mapped) { -+ if (xfer->tx_dma != INVALID_DMA_ADDRESS) -+ dma_unmap_single(master->cdev.dev, -+ xfer->tx_dma, -+ xfer->len, -+ DMA_TO_DEVICE); -+ if (xfer->rx_dma != INVALID_DMA_ADDRESS) -+ dma_unmap_single(master->cdev.dev, -+ xfer->rx_dma, -+ xfer->len, -+ DMA_FROM_DEVICE); -+ } -+ -+ /* REVISIT: udelay in irq is unfriendly */ -+ if (xfer->delay_usecs) -+ udelay(xfer->delay_usecs); -+ -+ if (msg->transfers.prev == &xfer->transfer_list) { -+ -+ /* report completed message */ -+ cs_deactivate(msg->spi); -+ list_del(&msg->queue); -+ msg->status = 0; -+ -+ dev_dbg(master->cdev.dev, -+ "xfer complete: %u bytes transferred\n", -+ msg->actual_length); -+ -+ spin_unlock(&as->lock); -+ msg->complete(msg->context); -+ spin_lock(&as->lock); -+ -+ as->current_transfer = NULL; -+ -+ /* continue; complete() may have queued requests */ -+ if (list_empty(&as->queue) || as->stopping) -+ spi_writel(as, PTCR, SPI_BIT(RXTDIS) -+ | SPI_BIT(TXTDIS)); -+ else -+ atmel_spi_next_message(master); -+ } else { -+ if (xfer->cs_change) { -+ cs_deactivate(msg->spi); -+ udelay(1); -+ cs_activate(msg->spi); -+ } -+ -+ /* -+ * Not done yet. Submit the next transfer. -+ * -+ * FIXME handle protocol options for xfer -+ */ -+ atmel_spi_next_xfer(master, msg); -+ } -+ } else { -+ /* -+ * Keep going, we still have data to send in -+ * the current transfer. -+ */ -+ atmel_spi_next_xfer(master, msg); -+ } -+ spin_unlock(&as->lock); -+ } -+ -+ return ret; -+} -+ -+static int atmel_spi_setup(struct spi_device *spi) -+{ -+ struct atmel_spi *as; -+ u32 scbr, csr; -+ unsigned int bits = spi->bits_per_word; -+ unsigned long bus_hz, sck_hz; -+ unsigned int npcs_pin; -+ int ret; -+ -+ as = spi_master_get_devdata(spi->master); -+ -+ if (as->stopping) -+ return -ESHUTDOWN; -+ -+ if (spi->chip_select > spi->master->num_chipselect) { -+ dev_dbg(&spi->dev, -+ "setup: invalid chipselect %u (%u defined)\n", -+ spi->chip_select, spi->master->num_chipselect); -+ return -EINVAL; -+ } -+ -+ if (bits == 0) -+ bits = 8; -+ if (bits < 8 || bits > 16) { -+ dev_dbg(&spi->dev, -+ "setup: invalid bits_per_word %u (8 to 16)\n", -+ bits); -+ return -EINVAL; -+ } -+ -+ if (spi->mode & (SPI_CS_HIGH | SPI_LSB_FIRST)) { -+ dev_dbg(&spi->dev, "setup: unsupported mode %u\n", spi->mode); -+ return -EINVAL; -+ } -+ -+ /* speed zero convention is used by some upper layers */ -+ bus_hz = clk_get_rate(as->clk); -+ if (spi->max_speed_hz) { -+ /* assume div32/fdiv/mbz == 0 */ -+ if (!as->new_1) -+ bus_hz /= 2; -+ scbr = ((bus_hz + spi->max_speed_hz - 1) -+ / spi->max_speed_hz); -+ if (scbr >= (1 << SPI_SCBR_SIZE)) { -+ dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u\n", -+ spi->max_speed_hz, scbr); -+ return -EINVAL; -+ } -+ } else -+ scbr = 0xff; -+ sck_hz = bus_hz / scbr; -+ -+ csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8); -+ if (spi->mode & SPI_CPOL) -+ csr |= SPI_BIT(CPOL); -+ if (!(spi->mode & SPI_CPHA)) -+ csr |= SPI_BIT(NCPHA); -+ -+ /* TODO: DLYBS and DLYBCT */ -+ csr |= SPI_BF(DLYBS, 10); -+ csr |= SPI_BF(DLYBCT, 10); -+ -+ npcs_pin = (unsigned int)spi->controller_data; -+ if (!spi->controller_state) { -+ ret = gpio_request(npcs_pin, "spi_npcs"); -+ if (ret) -+ return ret; -+ spi->controller_state = (void *)npcs_pin; -+ } -+ -+ gpio_set_value(npcs_pin, 1); -+ -+ dev_dbg(&spi->dev, -+ "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", -+ sck_hz, bits, spi->mode, spi->chip_select, csr); -+ -+ spi_writel(as, CSR0 + 4 * spi->chip_select, csr); -+ -+ return 0; -+} -+ -+static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) -+{ -+ struct atmel_spi *as; -+ struct spi_transfer *xfer; -+ unsigned long flags; -+ struct device *controller = spi->master->cdev.dev; -+ -+ as = spi_master_get_devdata(spi->master); -+ -+ dev_dbg(controller, "new message %p submitted for %s\n", -+ msg, spi->dev.bus_id); -+ -+ if (unlikely(list_empty(&msg->transfers) -+ || !spi->max_speed_hz)) -+ return -EINVAL; -+ -+ if (as->stopping) -+ return -ESHUTDOWN; -+ -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ if (!(xfer->tx_buf || xfer->rx_buf)) { -+ dev_dbg(&spi->dev, "missing rx or tx buf\n"); -+ 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; -+ } -+ } -+ -+ /* scrub dcache "early" */ -+ if (!msg->is_dma_mapped) { -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) -+ atmel_spi_dma_map_xfer(as, xfer); -+ } -+ -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ dev_dbg(controller, -+ " xfer %p: len %u tx %p/%08x rx %p/%08x\n", -+ xfer, xfer->len, -+ xfer->tx_buf, xfer->tx_dma, -+ xfer->rx_buf, xfer->rx_dma); -+ } -+ -+ msg->status = -EINPROGRESS; -+ msg->actual_length = 0; -+ -+ spin_lock_irqsave(&as->lock, flags); -+ list_add_tail(&msg->queue, &as->queue); -+ if (!as->current_transfer) -+ atmel_spi_next_message(spi->master); -+ spin_unlock_irqrestore(&as->lock, flags); -+ -+ return 0; -+} -+ -+static void atmel_spi_cleanup(const struct spi_device *spi) -+{ -+ if (spi->controller_state) -+ gpio_free((unsigned int)spi->controller_data); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int __devinit atmel_spi_probe(struct platform_device *pdev) -+{ -+ struct resource *regs; -+ int irq; -+ struct clk *clk; -+ int ret; -+ struct spi_master *master; -+ struct atmel_spi *as; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) -+ return -ENXIO; -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) -+ return irq; -+ -+ clk = clk_get(&pdev->dev, "pclk"); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ -+ /* setup spi core then atmel-specific driver state */ -+ ret = -ENOMEM; -+ master = spi_alloc_master(&pdev->dev, sizeof *as); -+ if (!master) -+ goto out_free; -+ -+ master->bus_num = pdev->id; -+ master->num_chipselect = 4; -+ master->setup = atmel_spi_setup; -+ master->transfer = atmel_spi_transfer; -+ master->cleanup = atmel_spi_cleanup; -+ platform_set_drvdata(pdev, master); -+ -+ as = spi_master_get_devdata(master); -+ -+ as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, -+ &as->buffer_dma, GFP_KERNEL); -+ if (!as->buffer) -+ goto out_free; -+ -+ spin_lock_init(&as->lock); -+ INIT_LIST_HEAD(&as->queue); -+ as->pdev = pdev; -+ as->regs = ioremap(regs->start, (regs->end - regs->start) + 1); -+ if (!as->regs) -+ goto out_free_buffer; -+ as->irq = irq; -+ as->clk = clk; -+#if !defined(CONFIG_ARCH_AT91RM9200) -+ /* if (!cpu_is_at91rm9200()) */ -+ as->new_1 = 1; -+#endif -+ -+ ret = request_irq(irq, atmel_spi_interrupt, 0, -+ pdev->dev.bus_id, master); -+ if (ret) -+ goto out_unmap_regs; -+ -+ /* Initialize the hardware */ -+ clk_enable(clk); -+ spi_writel(as, CR, SPI_BIT(SWRST)); -+ 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)); -+ -+ /* go! */ -+ dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", -+ (unsigned long)regs->start, irq); -+ -+ ret = spi_register_master(master); -+ if (ret) -+ goto out_reset_hw; -+ -+ return 0; -+ -+out_reset_hw: -+ spi_writel(as, CR, SPI_BIT(SWRST)); -+ clk_disable(clk); -+ free_irq(irq, master); -+out_unmap_regs: -+ iounmap(as->regs); -+out_free_buffer: -+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, -+ as->buffer_dma); -+out_free: -+ clk_put(clk); -+ spi_master_put(master); -+ return ret; -+} -+ -+static int __devexit 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; -+ -+ /* reset the hardware and block queue progress */ -+ spin_lock_irq(&as->lock); -+ as->stopping = 1; -+ spi_writel(as, CR, SPI_BIT(SWRST)); -+ spi_readl(as, SR); -+ spin_unlock_irq(&as->lock); -+ -+ /* Terminate remaining queued transfers */ -+ list_for_each_entry(msg, &as->queue, queue) { -+ /* REVISIT unmapping the dma is sort of a NOP on ARM, -+ * but we shouldn't depend on that... -+ */ -+ msg->status = -ESHUTDOWN; -+ msg->complete(msg->context); -+ } -+ -+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, -+ as->buffer_dma); -+ -+ clk_disable(as->clk); -+ clk_put(as->clk); -+ free_irq(as->irq, master); -+ iounmap(as->regs); -+ -+ spi_unregister_master(master); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+ -+static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct atmel_spi *as = spi_master_get_devdata(master); -+ -+ clk_disable(as->clk); -+ return 0; -+} -+ -+static int atmel_spi_resume(struct platform_device *pdev) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct atmel_spi *as = spi_master_get_devdata(master); -+ -+ clk_enable(as->clk); -+ return 0; -+} -+ -+#else -+#define atmel_spi_suspend NULL -+#define atmel_spi_resume NULL -+#endif -+ -+ -+static struct platform_driver atmel_spi_driver = { -+ .driver = { -+ .name = "atmel_spi", -+ .owner = THIS_MODULE, -+ }, -+ .probe = atmel_spi_probe, -+ .suspend = atmel_spi_suspend, -+ .resume = atmel_spi_resume, -+ .remove = __devexit_p(atmel_spi_remove), -+}; -+ -+static int __init atmel_spi_init(void) -+{ -+ return platform_driver_register(&atmel_spi_driver); -+} -+module_init(atmel_spi_init); -+ -+static void __exit atmel_spi_exit(void) -+{ -+ platform_driver_unregister(&atmel_spi_driver); -+} -+module_exit(atmel_spi_exit); -+ -+MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver"); -+MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.18-avr32/drivers/spi/atmel_spi.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.18-avr32/drivers/spi/atmel_spi.h 2006-11-29 16:55:59.000000000 +0100 -@@ -0,0 +1,167 @@ -+/* -+ * Register definitions for Atmel Serial Peripheral Interface (SPI) -+ * -+ * Copyright (C) 2006 Atmel Corporation -+ * -+ * 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. -+ */ -+#ifndef __ATMEL_SPI_H__ -+#define __ATMEL_SPI_H__ -+ -+/* SPI register offsets */ -+#define SPI_CR 0x0000 -+#define SPI_MR 0x0004 -+#define SPI_RDR 0x0008 -+#define SPI_TDR 0x000c -+#define SPI_SR 0x0010 -+#define SPI_IER 0x0014 -+#define SPI_IDR 0x0018 -+#define SPI_IMR 0x001c -+#define SPI_CSR0 0x0030 -+#define SPI_CSR1 0x0034 -+#define SPI_CSR2 0x0038 -+#define SPI_CSR3 0x003c -+#define SPI_RPR 0x0100 -+#define SPI_RCR 0x0104 -+#define SPI_TPR 0x0108 -+#define SPI_TCR 0x010c -+#define SPI_RNPR 0x0110 -+#define SPI_RNCR 0x0114 -+#define SPI_TNPR 0x0118 -+#define SPI_TNCR 0x011c -+#define SPI_PTCR 0x0120 -+#define SPI_PTSR 0x0124 -+ -+/* Bitfields in CR */ -+#define SPI_SPIEN_OFFSET 0 -+#define SPI_SPIEN_SIZE 1 -+#define SPI_SPIDIS_OFFSET 1 -+#define SPI_SPIDIS_SIZE 1 -+#define SPI_SWRST_OFFSET 7 -+#define SPI_SWRST_SIZE 1 -+#define SPI_LASTXFER_OFFSET 24 -+#define SPI_LASTXFER_SIZE 1 -+ -+/* Bitfields in MR */ -+#define SPI_MSTR_OFFSET 0 -+#define SPI_MSTR_SIZE 1 -+#define SPI_PS_OFFSET 1 -+#define SPI_PS_SIZE 1 -+#define SPI_PCSDEC_OFFSET 2 -+#define SPI_PCSDEC_SIZE 1 -+#define SPI_FDIV_OFFSET 3 -+#define SPI_FDIV_SIZE 1 -+#define SPI_MODFDIS_OFFSET 4 -+#define SPI_MODFDIS_SIZE 1 -+#define SPI_LLB_OFFSET 7 -+#define SPI_LLB_SIZE 1 -+#define SPI_PCS_OFFSET 16 -+#define SPI_PCS_SIZE 4 -+#define SPI_DLYBCS_OFFSET 24 -+#define SPI_DLYBCS_SIZE 8 -+ -+/* Bitfields in RDR */ -+#define SPI_RD_OFFSET 0 -+#define SPI_RD_SIZE 16 -+ -+/* Bitfields in TDR */ -+#define SPI_TD_OFFSET 0 -+#define SPI_TD_SIZE 16 -+ -+/* Bitfields in SR */ -+#define SPI_RDRF_OFFSET 0 -+#define SPI_RDRF_SIZE 1 -+#define SPI_TDRE_OFFSET 1 -+#define SPI_TDRE_SIZE 1 -+#define SPI_MODF_OFFSET 2 -+#define SPI_MODF_SIZE 1 -+#define SPI_OVRES_OFFSET 3 -+#define SPI_OVRES_SIZE 1 -+#define SPI_ENDRX_OFFSET 4 -+#define SPI_ENDRX_SIZE 1 -+#define SPI_ENDTX_OFFSET 5 -+#define SPI_ENDTX_SIZE 1 -+#define SPI_RXBUFF_OFFSET 6 -+#define SPI_RXBUFF_SIZE 1 -+#define SPI_TXBUFE_OFFSET 7 -+#define SPI_TXBUFE_SIZE 1 -+#define SPI_NSSR_OFFSET 8 -+#define SPI_NSSR_SIZE 1 -+#define SPI_TXEMPTY_OFFSET 9 -+#define SPI_TXEMPTY_SIZE 1 -+#define SPI_SPIENS_OFFSET 16 -+#define SPI_SPIENS_SIZE 1 -+ -+/* Bitfields in CSR0 */ -+#define SPI_CPOL_OFFSET 0 -+#define SPI_CPOL_SIZE 1 -+#define SPI_NCPHA_OFFSET 1 -+#define SPI_NCPHA_SIZE 1 -+#define SPI_CSAAT_OFFSET 3 -+#define SPI_CSAAT_SIZE 1 -+#define SPI_BITS_OFFSET 4 -+#define SPI_BITS_SIZE 4 -+#define SPI_SCBR_OFFSET 8 -+#define SPI_SCBR_SIZE 8 -+#define SPI_DLYBS_OFFSET 16 -+#define SPI_DLYBS_SIZE 8 -+#define SPI_DLYBCT_OFFSET 24 -+#define SPI_DLYBCT_SIZE 8 -+ -+/* Bitfields in RCR */ -+#define SPI_RXCTR_OFFSET 0 -+#define SPI_RXCTR_SIZE 16 -+ -+/* Bitfields in TCR */ -+#define SPI_TXCTR_OFFSET 0 -+#define SPI_TXCTR_SIZE 16 -+ -+/* Bitfields in RNCR */ -+#define SPI_RXNCR_OFFSET 0 -+#define SPI_RXNCR_SIZE 16 -+ -+/* Bitfields in TNCR */ -+#define SPI_TXNCR_OFFSET 0 -+#define SPI_TXNCR_SIZE 16 -+ -+/* Bitfields in PTCR */ -+#define SPI_RXTEN_OFFSET 0 -+#define SPI_RXTEN_SIZE 1 -+#define SPI_RXTDIS_OFFSET 1 -+#define SPI_RXTDIS_SIZE 1 -+#define SPI_TXTEN_OFFSET 8 -+#define SPI_TXTEN_SIZE 1 -+#define SPI_TXTDIS_OFFSET 9 -+#define SPI_TXTDIS_SIZE 1 -+ -+/* Constants for BITS */ -+#define SPI_BITS_8_BPT 0 -+#define SPI_BITS_9_BPT 1 -+#define SPI_BITS_10_BPT 2 -+#define SPI_BITS_11_BPT 3 -+#define SPI_BITS_12_BPT 4 -+#define SPI_BITS_13_BPT 5 -+#define SPI_BITS_14_BPT 6 -+#define SPI_BITS_15_BPT 7 -+#define SPI_BITS_16_BPT 8 -+ -+/* Bit manipulation macros */ -+#define SPI_BIT(name) \ -+ (1 << SPI_##name##_OFFSET) -+#define SPI_BF(name,value) \ -+ (((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET) -+#define SPI_BFEXT(name,value) \ -+ (((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1)) -+#define SPI_BFINS(name,value,old) \ -+ ( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \ -+ | SPI_BF(name,value)) -+ -+/* Register access macros */ -+#define spi_readl(port,reg) \ -+ __raw_readl((port)->regs + SPI_##reg) -+#define spi_writel(port,reg,value) \ -+ __raw_writel((value), (port)->regs + SPI_##reg) -+ -+#endif /* __ATMEL_SPI_H__ */ |