From a419aef8b858a2bdb98df60336063d28df4b272f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 18 Aug 2009 11:18:35 -0700 Subject: trivial: remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- drivers/spi/omap_uwire.c | 2 +- drivers/spi/spi_s3c24xx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c index 8980a5640bd..e75ba9b2889 100644 --- a/drivers/spi/omap_uwire.c +++ b/drivers/spi/omap_uwire.c @@ -213,7 +213,7 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) unsigned bits = ust->bits_per_word; unsigned bytes; u16 val, w; - int status = 0;; + int status = 0; if (!t->tx_buf && !t->rx_buf) return 0; diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 3f3119d760d..6ba8aece90b 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -388,7 +388,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) err_no_iores: err_no_pdata: - spi_master_put(hw->master);; + spi_master_put(hw->master); err_nomem: return err; -- cgit v1.2.3 From 07fcaa2486ca4f5c67bebedfa56e705c4dd23fc2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 22 Sep 2009 16:45:56 -0700 Subject: spi: remove i.MX SPI driver This driver is in a non working state at the moment and will be replaced by a bitbang driver which can also handle the newer i.MX variants Signed-off-by: Sascha Hauer Cc: Guennadi Liakhovetski Acked-by: David Brownell Acked-by: Andrea Paterniani Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 7 - drivers/spi/Makefile | 1 - drivers/spi/spi_imx.c | 1770 ------------------------------------------------- 3 files changed, 1778 deletions(-) delete mode 100644 drivers/spi/spi_imx.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2c733c27db2..86056d1409b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -116,13 +116,6 @@ config SPI_GPIO GPIO operations, you should be able to leverage that for better speed with a custom version of this driver; see the source code. -config SPI_IMX - tristate "Freescale iMX SPI controller" - depends on ARCH_MX1 && EXPERIMENTAL - help - This enables using the Freescale iMX SPI controller in master - mode. - config SPI_LM70_LLP tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)" depends on PARPORT && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3de408d294b..0f20a70b2fa 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o -obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c deleted file mode 100644 index c195e45f7f3..00000000000 --- a/drivers/spi/spi_imx.c +++ /dev/null @@ -1,1770 +0,0 @@ -/* - * drivers/spi/spi_imx.c - * - * Copyright (C) 2006 SWAPP - * Andrea Paterniani - * - * Initial version inspired by: - * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -/*-------------------------------------------------------------------------*/ -/* SPI Registers offsets from peripheral base address */ -#define SPI_RXDATA (0x00) -#define SPI_TXDATA (0x04) -#define SPI_CONTROL (0x08) -#define SPI_INT_STATUS (0x0C) -#define SPI_TEST (0x10) -#define SPI_PERIOD (0x14) -#define SPI_DMA (0x18) -#define SPI_RESET (0x1C) - -/* SPI Control Register Bit Fields & Masks */ -#define SPI_CONTROL_BITCOUNT_MASK (0xF) /* Bit Count Mask */ -#define SPI_CONTROL_BITCOUNT(n) (((n) - 1) & SPI_CONTROL_BITCOUNT_MASK) -#define SPI_CONTROL_POL (0x1 << 4) /* Clock Polarity Mask */ -#define SPI_CONTROL_POL_ACT_HIGH (0x0 << 4) /* Active high pol. (0=idle) */ -#define SPI_CONTROL_POL_ACT_LOW (0x1 << 4) /* Active low pol. (1=idle) */ -#define SPI_CONTROL_PHA (0x1 << 5) /* Clock Phase Mask */ -#define SPI_CONTROL_PHA_0 (0x0 << 5) /* Clock Phase 0 */ -#define SPI_CONTROL_PHA_1 (0x1 << 5) /* Clock Phase 1 */ -#define SPI_CONTROL_SSCTL (0x1 << 6) /* /SS Waveform Select Mask */ -#define SPI_CONTROL_SSCTL_0 (0x0 << 6) /* Master: /SS stays low between SPI burst - Slave: RXFIFO advanced by BIT_COUNT */ -#define SPI_CONTROL_SSCTL_1 (0x1 << 6) /* Master: /SS insert pulse between SPI burst - Slave: RXFIFO advanced by /SS rising edge */ -#define SPI_CONTROL_SSPOL (0x1 << 7) /* /SS Polarity Select Mask */ -#define SPI_CONTROL_SSPOL_ACT_LOW (0x0 << 7) /* /SS Active low */ -#define SPI_CONTROL_SSPOL_ACT_HIGH (0x1 << 7) /* /SS Active high */ -#define SPI_CONTROL_XCH (0x1 << 8) /* Exchange */ -#define SPI_CONTROL_SPIEN (0x1 << 9) /* SPI Module Enable */ -#define SPI_CONTROL_MODE (0x1 << 10) /* SPI Mode Select Mask */ -#define SPI_CONTROL_MODE_SLAVE (0x0 << 10) /* SPI Mode Slave */ -#define SPI_CONTROL_MODE_MASTER (0x1 << 10) /* SPI Mode Master */ -#define SPI_CONTROL_DRCTL (0x3 << 11) /* /SPI_RDY Control Mask */ -#define SPI_CONTROL_DRCTL_0 (0x0 << 11) /* Ignore /SPI_RDY */ -#define SPI_CONTROL_DRCTL_1 (0x1 << 11) /* /SPI_RDY falling edge triggers input */ -#define SPI_CONTROL_DRCTL_2 (0x2 << 11) /* /SPI_RDY active low level triggers input */ -#define SPI_CONTROL_DATARATE (0x7 << 13) /* Data Rate Mask */ -#define SPI_PERCLK2_DIV_MIN (0) /* PERCLK2:4 */ -#define SPI_PERCLK2_DIV_MAX (7) /* PERCLK2:512 */ -#define SPI_CONTROL_DATARATE_MIN (SPI_PERCLK2_DIV_MAX << 13) -#define SPI_CONTROL_DATARATE_MAX (SPI_PERCLK2_DIV_MIN << 13) -#define SPI_CONTROL_DATARATE_BAD (SPI_CONTROL_DATARATE_MIN + 1) - -/* SPI Interrupt/Status Register Bit Fields & Masks */ -#define SPI_STATUS_TE (0x1 << 0) /* TXFIFO Empty Status */ -#define SPI_STATUS_TH (0x1 << 1) /* TXFIFO Half Status */ -#define SPI_STATUS_TF (0x1 << 2) /* TXFIFO Full Status */ -#define SPI_STATUS_RR (0x1 << 3) /* RXFIFO Data Ready Status */ -#define SPI_STATUS_RH (0x1 << 4) /* RXFIFO Half Status */ -#define SPI_STATUS_RF (0x1 << 5) /* RXFIFO Full Status */ -#define SPI_STATUS_RO (0x1 << 6) /* RXFIFO Overflow */ -#define SPI_STATUS_BO (0x1 << 7) /* Bit Count Overflow */ -#define SPI_STATUS (0xFF) /* SPI Status Mask */ -#define SPI_INTEN_TE (0x1 << 8) /* TXFIFO Empty Interrupt Enable */ -#define SPI_INTEN_TH (0x1 << 9) /* TXFIFO Half Interrupt Enable */ -#define SPI_INTEN_TF (0x1 << 10) /* TXFIFO Full Interrupt Enable */ -#define SPI_INTEN_RE (0x1 << 11) /* RXFIFO Data Ready Interrupt Enable */ -#define SPI_INTEN_RH (0x1 << 12) /* RXFIFO Half Interrupt Enable */ -#define SPI_INTEN_RF (0x1 << 13) /* RXFIFO Full Interrupt Enable */ -#define SPI_INTEN_RO (0x1 << 14) /* RXFIFO Overflow Interrupt Enable */ -#define SPI_INTEN_BO (0x1 << 15) /* Bit Count Overflow Interrupt Enable */ -#define SPI_INTEN (0xFF << 8) /* SPI Interrupt Enable Mask */ - -/* SPI Test Register Bit Fields & Masks */ -#define SPI_TEST_TXCNT (0xF << 0) /* TXFIFO Counter */ -#define SPI_TEST_RXCNT_LSB (4) /* RXFIFO Counter LSB */ -#define SPI_TEST_RXCNT (0xF << 4) /* RXFIFO Counter */ -#define SPI_TEST_SSTATUS (0xF << 8) /* State Machine Status */ -#define SPI_TEST_LBC (0x1 << 14) /* Loop Back Control */ - -/* SPI Period Register Bit Fields & Masks */ -#define SPI_PERIOD_WAIT (0x7FFF << 0) /* Wait Between Transactions */ -#define SPI_PERIOD_MAX_WAIT (0x7FFF) /* Max Wait Between - Transactions */ -#define SPI_PERIOD_CSRC (0x1 << 15) /* Period Clock Source Mask */ -#define SPI_PERIOD_CSRC_BCLK (0x0 << 15) /* Period Clock Source is - Bit Clock */ -#define SPI_PERIOD_CSRC_32768 (0x1 << 15) /* Period Clock Source is - 32.768 KHz Clock */ - -/* SPI DMA Register Bit Fields & Masks */ -#define SPI_DMA_RHDMA (0x1 << 4) /* RXFIFO Half Status */ -#define SPI_DMA_RFDMA (0x1 << 5) /* RXFIFO Full Status */ -#define SPI_DMA_TEDMA (0x1 << 6) /* TXFIFO Empty Status */ -#define SPI_DMA_THDMA (0x1 << 7) /* TXFIFO Half Status */ -#define SPI_DMA_RHDEN (0x1 << 12) /* RXFIFO Half DMA Request Enable */ -#define SPI_DMA_RFDEN (0x1 << 13) /* RXFIFO Full DMA Request Enable */ -#define SPI_DMA_TEDEN (0x1 << 14) /* TXFIFO Empty DMA Request Enable */ -#define SPI_DMA_THDEN (0x1 << 15) /* TXFIFO Half DMA Request Enable */ - -/* SPI Soft Reset Register Bit Fields & Masks */ -#define SPI_RESET_START (0x1) /* Start */ - -/* Default SPI configuration values */ -#define SPI_DEFAULT_CONTROL \ -( \ - SPI_CONTROL_BITCOUNT(16) | \ - SPI_CONTROL_POL_ACT_HIGH | \ - SPI_CONTROL_PHA_0 | \ - SPI_CONTROL_SPIEN | \ - SPI_CONTROL_SSCTL_1 | \ - SPI_CONTROL_MODE_MASTER | \ - SPI_CONTROL_DRCTL_0 | \ - SPI_CONTROL_DATARATE_MIN \ -) -#define SPI_DEFAULT_ENABLE_LOOPBACK (0) -#define SPI_DEFAULT_ENABLE_DMA (0) -#define SPI_DEFAULT_PERIOD_WAIT (8) -/*-------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/* TX/RX SPI FIFO size */ -#define SPI_FIFO_DEPTH (8) -#define SPI_FIFO_BYTE_WIDTH (2) -#define SPI_FIFO_OVERFLOW_MARGIN (2) - -/* DMA burst length for half full/empty request trigger */ -#define SPI_DMA_BLR (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2) - -/* Dummy char output to achieve reads. - Choosing something different from all zeroes may help pattern recogition - for oscilloscope analysis, but may break some drivers. */ -#define SPI_DUMMY_u8 0 -#define SPI_DUMMY_u16 ((SPI_DUMMY_u8 << 8) | SPI_DUMMY_u8) -#define SPI_DUMMY_u32 ((SPI_DUMMY_u16 << 16) | SPI_DUMMY_u16) - -/** - * Macro to change a u32 field: - * @r : register to edit - * @m : bit mask - * @v : new value for the field correctly bit-alligned -*/ -#define u32_EDIT(r, m, v) r = (r & ~(m)) | (v) - -/* Message state */ -#define START_STATE ((void*)0) -#define RUNNING_STATE ((void*)1) -#define DONE_STATE ((void*)2) -#define ERROR_STATE ((void*)-1) - -/* Queue state */ -#define QUEUE_RUNNING (0) -#define QUEUE_STOPPED (1) - -#define IS_DMA_ALIGNED(x) (((u32)(x) & 0x03) == 0) -#define DMA_ALIGNMENT 4 -/*-------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/* Driver data structs */ - -/* Context */ -struct driver_data { - /* Driver model hookup */ - struct platform_device *pdev; - - /* SPI framework hookup */ - struct spi_master *master; - - /* IMX hookup */ - struct spi_imx_master *master_info; - - /* Memory resources and SPI regs virtual address */ - struct resource *ioarea; - void __iomem *regs; - - /* SPI RX_DATA physical address */ - dma_addr_t rd_data_phys; - - /* Driver message queue */ - struct workqueue_struct *workqueue; - struct work_struct work; - spinlock_t lock; - struct list_head queue; - int busy; - int run; - - /* Message Transfer pump */ - struct tasklet_struct pump_transfers; - - /* Current message, transfer and state */ - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct chip_data *cur_chip; - - /* Rd / Wr buffers pointers */ - size_t len; - void *tx; - void *tx_end; - void *rx; - void *rx_end; - - u8 rd_only; - u8 n_bytes; - int cs_change; - - /* Function pointers */ - irqreturn_t (*transfer_handler)(struct driver_data *drv_data); - void (*cs_control)(u32 command); - - /* DMA setup */ - int rx_channel; - int tx_channel; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - int rx_dma_needs_unmap; - int tx_dma_needs_unmap; - size_t tx_map_len; - u32 dummy_dma_buf ____cacheline_aligned; - - struct clk *clk; -}; - -/* Runtime state */ -struct chip_data { - u32 control; - u32 period; - u32 test; - - u8 enable_dma:1; - u8 bits_per_word; - u8 n_bytes; - u32 max_speed_hz; - - void (*cs_control)(u32 command); -}; -/*-------------------------------------------------------------------------*/ - - -static void pump_messages(struct work_struct *work); - -static void flush(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - u32 control; - - dev_dbg(&drv_data->pdev->dev, "flush\n"); - - /* Wait for end of transaction */ - do { - control = readl(regs + SPI_CONTROL); - } while (control & SPI_CONTROL_XCH); - - /* Release chip select if requested, transfer delays are - handled in pump_transfers */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); - - /* Disable SPI to flush FIFOs */ - writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL); - writel(control, regs + SPI_CONTROL); -} - -static void restore_state(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - struct chip_data *chip = drv_data->cur_chip; - - /* Load chip registers */ - dev_dbg(&drv_data->pdev->dev, - "restore_state\n" - " test = 0x%08X\n" - " control = 0x%08X\n", - chip->test, - chip->control); - writel(chip->test, regs + SPI_TEST); - writel(chip->period, regs + SPI_PERIOD); - writel(0, regs + SPI_INT_STATUS); - writel(chip->control, regs + SPI_CONTROL); -} - -static void null_cs_control(u32 command) -{ -} - -static inline u32 data_to_write(struct driver_data *drv_data) -{ - return ((u32)(drv_data->tx_end - drv_data->tx)) / drv_data->n_bytes; -} - -static inline u32 data_to_read(struct driver_data *drv_data) -{ - return ((u32)(drv_data->rx_end - drv_data->rx)) / drv_data->n_bytes; -} - -static int write(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - void *tx = drv_data->tx; - void *tx_end = drv_data->tx_end; - u8 n_bytes = drv_data->n_bytes; - u32 remaining_writes; - u32 fifo_avail_space; - u32 n; - u16 d; - - /* Compute how many fifo writes to do */ - remaining_writes = (u32)(tx_end - tx) / n_bytes; - fifo_avail_space = SPI_FIFO_DEPTH - - (readl(regs + SPI_TEST) & SPI_TEST_TXCNT); - if (drv_data->rx && (fifo_avail_space > SPI_FIFO_OVERFLOW_MARGIN)) - /* Fix misunderstood receive overflow */ - fifo_avail_space -= SPI_FIFO_OVERFLOW_MARGIN; - n = min(remaining_writes, fifo_avail_space); - - dev_dbg(&drv_data->pdev->dev, - "write type %s\n" - " remaining writes = %d\n" - " fifo avail space = %d\n" - " fifo writes = %d\n", - (n_bytes == 1) ? "u8" : "u16", - remaining_writes, - fifo_avail_space, - n); - - if (n > 0) { - /* Fill SPI TXFIFO */ - if (drv_data->rd_only) { - tx += n * n_bytes; - while (n--) - writel(SPI_DUMMY_u16, regs + SPI_TXDATA); - } else { - if (n_bytes == 1) { - while (n--) { - d = *(u8*)tx; - writel(d, regs + SPI_TXDATA); - tx += 1; - } - } else { - while (n--) { - d = *(u16*)tx; - writel(d, regs + SPI_TXDATA); - tx += 2; - } - } - } - - /* Trigger transfer */ - writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, - regs + SPI_CONTROL); - - /* Update tx pointer */ - drv_data->tx = tx; - } - - return (tx >= tx_end); -} - -static int read(struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - void *rx = drv_data->rx; - void *rx_end = drv_data->rx_end; - u8 n_bytes = drv_data->n_bytes; - u32 remaining_reads; - u32 fifo_rxcnt; - u32 n; - u16 d; - - /* Compute how many fifo reads to do */ - remaining_reads = (u32)(rx_end - rx) / n_bytes; - fifo_rxcnt = (readl(regs + SPI_TEST) & SPI_TEST_RXCNT) >> - SPI_TEST_RXCNT_LSB; - n = min(remaining_reads, fifo_rxcnt); - - dev_dbg(&drv_data->pdev->dev, - "read type %s\n" - " remaining reads = %d\n" - " fifo rx count = %d\n" - " fifo reads = %d\n", - (n_bytes == 1) ? "u8" : "u16", - remaining_reads, - fifo_rxcnt, - n); - - if (n > 0) { - /* Read SPI RXFIFO */ - if (n_bytes == 1) { - while (n--) { - d = readl(regs + SPI_RXDATA); - *((u8*)rx) = d; - rx += 1; - } - } else { - while (n--) { - d = readl(regs + SPI_RXDATA); - *((u16*)rx) = d; - rx += 2; - } - } - - /* Update rx pointer */ - drv_data->rx = rx; - } - - return (rx >= rx_end); -} - -static void *next_transfer(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - struct spi_transfer *trans = drv_data->cur_transfer; - - /* Move to next transfer */ - if (trans->transfer_list.next != &msg->transfers) { - drv_data->cur_transfer = - list_entry(trans->transfer_list.next, - struct spi_transfer, - transfer_list); - return RUNNING_STATE; - } - - return DONE_STATE; -} - -static int map_dma_buffers(struct driver_data *drv_data) -{ - struct spi_message *msg; - struct device *dev; - void *buf; - - drv_data->rx_dma_needs_unmap = 0; - drv_data->tx_dma_needs_unmap = 0; - - if (!drv_data->master_info->enable_dma || - !drv_data->cur_chip->enable_dma) - return -1; - - msg = drv_data->cur_msg; - dev = &msg->spi->dev; - if (msg->is_dma_mapped) { - if (drv_data->tx_dma) - /* The caller provided at least dma and cpu virtual - address for write; pump_transfers() will consider the - transfer as write only if cpu rx virtual address is - NULL */ - return 0; - - if (drv_data->rx_dma) { - /* The caller provided dma and cpu virtual address to - performe read only transfer --> - use drv_data->dummy_dma_buf for dummy writes to - achive reads */ - buf = &drv_data->dummy_dma_buf; - drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); - drv_data->tx_dma = dma_map_single(dev, - buf, - drv_data->tx_map_len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return -1; - - drv_data->tx_dma_needs_unmap = 1; - - /* Flags transfer as rd_only for pump_transfers() DMA - regs programming (should be redundant) */ - drv_data->tx = NULL; - - return 0; - } - } - - if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) - return -1; - - if (drv_data->tx == NULL) { - /* Read only message --> use drv_data->dummy_dma_buf for dummy - writes to achive reads */ - buf = &drv_data->dummy_dma_buf; - drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf); - } else { - buf = drv_data->tx; - drv_data->tx_map_len = drv_data->len; - } - drv_data->tx_dma = dma_map_single(dev, - buf, - drv_data->tx_map_len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return -1; - drv_data->tx_dma_needs_unmap = 1; - - /* NULL rx means write-only transfer and no map needed - * since rx DMA will not be used */ - if (drv_data->rx) { - buf = drv_data->rx; - drv_data->rx_dma = dma_map_single(dev, - buf, - drv_data->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) { - if (drv_data->tx_dma) { - dma_unmap_single(dev, - drv_data->tx_dma, - drv_data->tx_map_len, - DMA_TO_DEVICE); - drv_data->tx_dma_needs_unmap = 0; - } - return -1; - } - drv_data->rx_dma_needs_unmap = 1; - } - - return 0; -} - -static void unmap_dma_buffers(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - struct device *dev = &msg->spi->dev; - - if (drv_data->rx_dma_needs_unmap) { - dma_unmap_single(dev, - drv_data->rx_dma, - drv_data->len, - DMA_FROM_DEVICE); - drv_data->rx_dma_needs_unmap = 0; - } - if (drv_data->tx_dma_needs_unmap) { - dma_unmap_single(dev, - drv_data->tx_dma, - drv_data->tx_map_len, - DMA_TO_DEVICE); - drv_data->tx_dma_needs_unmap = 0; - } -} - -/* Caller already set message->status (dma is already blocked) */ -static void giveback(struct spi_message *message, struct driver_data *drv_data) -{ - void __iomem *regs = drv_data->regs; - - /* Bring SPI to sleep; restore_state() and pump_transfer() - will do new setup */ - writel(0, regs + SPI_INT_STATUS); - writel(0, regs + SPI_DMA); - - /* Unconditioned deselct */ - drv_data->cs_control(SPI_CS_DEASSERT); - - message->state = NULL; - if (message->complete) - message->complete(message->context); - - drv_data->cur_msg = NULL; - drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; - queue_work(drv_data->workqueue, &drv_data->work); -} - -static void dma_err_handler(int channel, void *data, int errcode) -{ - struct driver_data *drv_data = data; - struct spi_message *msg = drv_data->cur_msg; - - dev_dbg(&drv_data->pdev->dev, "dma_err_handler\n"); - - /* Disable both rx and tx dma channels */ - imx_dma_disable(drv_data->rx_channel); - imx_dma_disable(drv_data->tx_channel); - unmap_dma_buffers(drv_data); - - flush(drv_data); - - msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); -} - -static void dma_tx_handler(int channel, void *data) -{ - struct driver_data *drv_data = data; - - dev_dbg(&drv_data->pdev->dev, "dma_tx_handler\n"); - - imx_dma_disable(channel); - - /* Now waits for TX FIFO empty */ - writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS); -} - -static irqreturn_t dma_transfer(struct driver_data *drv_data) -{ - u32 status; - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - - status = readl(regs + SPI_INT_STATUS); - - if ((status & (SPI_INTEN_RO | SPI_STATUS_RO)) - == (SPI_INTEN_RO | SPI_STATUS_RO)) { - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); - - imx_dma_disable(drv_data->tx_channel); - imx_dma_disable(drv_data->rx_channel); - unmap_dma_buffers(drv_data); - - flush(drv_data); - - dev_warn(&drv_data->pdev->dev, - "dma_transfer - fifo overun\n"); - - msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } - - if (status & SPI_STATUS_TE) { - writel(status & ~SPI_INTEN_TE, regs + SPI_INT_STATUS); - - if (drv_data->rx) { - /* Wait end of transfer before read trailing data */ - while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) - cpu_relax(); - - imx_dma_disable(drv_data->rx_channel); - unmap_dma_buffers(drv_data); - - /* Release chip select if requested, transfer delays are - handled in pump_transfers() */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); - - /* Calculate number of trailing data and read them */ - dev_dbg(&drv_data->pdev->dev, - "dma_transfer - test = 0x%08X\n", - readl(regs + SPI_TEST)); - drv_data->rx = drv_data->rx_end - - ((readl(regs + SPI_TEST) & - SPI_TEST_RXCNT) >> - SPI_TEST_RXCNT_LSB)*drv_data->n_bytes; - read(drv_data); - } else { - /* Write only transfer */ - unmap_dma_buffers(drv_data); - - flush(drv_data); - } - - /* End of transfer, update total byte transfered */ - msg->actual_length += drv_data->len; - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } - - /* Opps problem detected */ - return IRQ_NONE; -} - -static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - u32 status; - irqreturn_t handled = IRQ_NONE; - - status = readl(regs + SPI_INT_STATUS); - - if (status & SPI_INTEN_TE) { - /* TXFIFO Empty Interrupt on the last transfered word */ - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); - dev_dbg(&drv_data->pdev->dev, - "interrupt_wronly_transfer - end of tx\n"); - - flush(drv_data); - - /* Update total byte transfered */ - msg->actual_length += drv_data->len; - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } else { - while (status & SPI_STATUS_TH) { - dev_dbg(&drv_data->pdev->dev, - "interrupt_wronly_transfer - status = 0x%08X\n", - status); - - /* Pump data */ - if (write(drv_data)) { - /* End of TXFIFO writes, - now wait until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - return IRQ_HANDLED; - } - - status = readl(regs + SPI_INT_STATUS); - - /* We did something */ - handled = IRQ_HANDLED; - } - } - - return handled; -} - -static irqreturn_t interrupt_transfer(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - void __iomem *regs = drv_data->regs; - u32 status, control; - irqreturn_t handled = IRQ_NONE; - unsigned long limit; - - status = readl(regs + SPI_INT_STATUS); - - if (status & SPI_INTEN_TE) { - /* TXFIFO Empty Interrupt on the last transfered word */ - writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS); - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - end of tx\n"); - - if (msg->state == ERROR_STATE) { - /* RXFIFO overrun was detected and message aborted */ - flush(drv_data); - } else { - /* Wait for end of transaction */ - do { - control = readl(regs + SPI_CONTROL); - } while (control & SPI_CONTROL_XCH); - - /* Release chip select if requested, transfer delays are - handled in pump_transfers */ - if (drv_data->cs_change) - drv_data->cs_control(SPI_CS_DEASSERT); - - /* Read trailing bytes */ - limit = loops_per_jiffy << 1; - while ((read(drv_data) == 0) && --limit) - cpu_relax(); - - if (limit == 0) - dev_err(&drv_data->pdev->dev, - "interrupt_transfer - " - "trailing byte read failed\n"); - else - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - end of rx\n"); - - /* Update total byte transfered */ - msg->actual_length += drv_data->len; - - /* Move to next transfer */ - msg->state = next_transfer(drv_data); - } - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); - - return IRQ_HANDLED; - } else { - while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) { - dev_dbg(&drv_data->pdev->dev, - "interrupt_transfer - status = 0x%08X\n", - status); - - if (status & SPI_STATUS_RO) { - /* RXFIFO overrun, abort message end wait - until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - - dev_warn(&drv_data->pdev->dev, - "interrupt_transfer - fifo overun\n" - " data not yet written = %d\n" - " data not yet read = %d\n", - data_to_write(drv_data), - data_to_read(drv_data)); - - msg->state = ERROR_STATE; - - return IRQ_HANDLED; - } - - /* Pump data */ - read(drv_data); - if (write(drv_data)) { - /* End of TXFIFO writes, - now wait until TXFIFO is empty */ - writel(SPI_INTEN_TE, regs + SPI_INT_STATUS); - return IRQ_HANDLED; - } - - status = readl(regs + SPI_INT_STATUS); - - /* We did something */ - handled = IRQ_HANDLED; - } - } - - return handled; -} - -static irqreturn_t spi_int(int irq, void *dev_id) -{ - struct driver_data *drv_data = (struct driver_data *)dev_id; - - if (!drv_data->cur_msg) { - dev_err(&drv_data->pdev->dev, - "spi_int - bad message state\n"); - /* Never fail */ - return IRQ_HANDLED; - } - - return drv_data->transfer_handler(drv_data); -} - -static inline u32 spi_speed_hz(struct driver_data *drv_data, u32 data_rate) -{ - return clk_get_rate(drv_data->clk) / (4 << ((data_rate) >> 13)); -} - -static u32 spi_data_rate(struct driver_data *drv_data, u32 speed_hz) -{ - u32 div; - u32 quantized_hz = clk_get_rate(drv_data->clk) >> 2; - - for (div = SPI_PERCLK2_DIV_MIN; - div <= SPI_PERCLK2_DIV_MAX; - div++, quantized_hz >>= 1) { - if (quantized_hz <= speed_hz) - /* Max available speed LEQ required speed */ - return div << 13; - } - return SPI_CONTROL_DATARATE_BAD; -} - -static void pump_transfers(unsigned long data) -{ - struct driver_data *drv_data = (struct driver_data *)data; - struct spi_message *message; - struct spi_transfer *transfer, *previous; - struct chip_data *chip; - void __iomem *regs; - u32 tmp, control; - - dev_dbg(&drv_data->pdev->dev, "pump_transfer\n"); - - message = drv_data->cur_msg; - - /* Handle for abort */ - if (message->state == ERROR_STATE) { - message->status = -EIO; - giveback(message, drv_data); - return; - } - - /* Handle end of message */ - if (message->state == DONE_STATE) { - message->status = 0; - giveback(message, drv_data); - return; - } - - chip = drv_data->cur_chip; - - /* Delay if requested at end of transfer*/ - transfer = drv_data->cur_transfer; - if (message->state == RUNNING_STATE) { - previous = list_entry(transfer->transfer_list.prev, - struct spi_transfer, - transfer_list); - if (previous->delay_usecs) - udelay(previous->delay_usecs); - } else { - /* START_STATE */ - message->state = RUNNING_STATE; - drv_data->cs_control = chip->cs_control; - } - - transfer = drv_data->cur_transfer; - drv_data->tx = (void *)transfer->tx_buf; - drv_data->tx_end = drv_data->tx + transfer->len; - drv_data->rx = transfer->rx_buf; - drv_data->rx_end = drv_data->rx + transfer->len; - drv_data->rx_dma = transfer->rx_dma; - drv_data->tx_dma = transfer->tx_dma; - drv_data->len = transfer->len; - drv_data->cs_change = transfer->cs_change; - drv_data->rd_only = (drv_data->tx == NULL); - - regs = drv_data->regs; - control = readl(regs + SPI_CONTROL); - - /* Bits per word setup */ - tmp = transfer->bits_per_word; - if (tmp == 0) { - /* Use device setup */ - tmp = chip->bits_per_word; - drv_data->n_bytes = chip->n_bytes; - } else - /* Use per-transfer setup */ - drv_data->n_bytes = (tmp <= 8) ? 1 : 2; - u32_EDIT(control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); - - /* Speed setup (surely valid because already checked) */ - tmp = transfer->speed_hz; - if (tmp == 0) - tmp = chip->max_speed_hz; - tmp = spi_data_rate(drv_data, tmp); - u32_EDIT(control, SPI_CONTROL_DATARATE, tmp); - - writel(control, regs + SPI_CONTROL); - - /* Assert device chip-select */ - drv_data->cs_control(SPI_CS_ASSERT); - - /* DMA cannot read/write SPI FIFOs other than 16 bits at a time; hence - if bits_per_word is less or equal 8 PIO transfers are performed. - Moreover DMA is convinient for transfer length bigger than FIFOs - byte size. */ - if ((drv_data->n_bytes == 2) && - (drv_data->len > SPI_FIFO_DEPTH*SPI_FIFO_BYTE_WIDTH) && - (map_dma_buffers(drv_data) == 0)) { - dev_dbg(&drv_data->pdev->dev, - "pump dma transfer\n" - " tx = %p\n" - " tx_dma = %08X\n" - " rx = %p\n" - " rx_dma = %08X\n" - " len = %d\n", - drv_data->tx, - (unsigned int)drv_data->tx_dma, - drv_data->rx, - (unsigned int)drv_data->rx_dma, - drv_data->len); - - /* Ensure we have the correct interrupt handler */ - drv_data->transfer_handler = dma_transfer; - - /* Trigger transfer */ - writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, - regs + SPI_CONTROL); - - /* Setup tx DMA */ - if (drv_data->tx) - /* Linear source address */ - CCR(drv_data->tx_channel) = - CCR_DMOD_FIFO | - CCR_SMOD_LINEAR | - CCR_SSIZ_32 | CCR_DSIZ_16 | - CCR_REN; - else - /* Read only transfer -> fixed source address for - dummy write to achive read */ - CCR(drv_data->tx_channel) = - CCR_DMOD_FIFO | - CCR_SMOD_FIFO | - CCR_SSIZ_32 | CCR_DSIZ_16 | - CCR_REN; - - imx_dma_setup_single( - drv_data->tx_channel, - drv_data->tx_dma, - drv_data->len, - drv_data->rd_data_phys + 4, - DMA_MODE_WRITE); - - if (drv_data->rx) { - /* Setup rx DMA for linear destination address */ - CCR(drv_data->rx_channel) = - CCR_DMOD_LINEAR | - CCR_SMOD_FIFO | - CCR_DSIZ_32 | CCR_SSIZ_16 | - CCR_REN; - imx_dma_setup_single( - drv_data->rx_channel, - drv_data->rx_dma, - drv_data->len, - drv_data->rd_data_phys, - DMA_MODE_READ); - imx_dma_enable(drv_data->rx_channel); - - /* Enable SPI interrupt */ - writel(SPI_INTEN_RO, regs + SPI_INT_STATUS); - - /* Set SPI to request DMA service on both - Rx and Tx half fifo watermark */ - writel(SPI_DMA_RHDEN | SPI_DMA_THDEN, regs + SPI_DMA); - } else - /* Write only access -> set SPI to request DMA - service on Tx half fifo watermark */ - writel(SPI_DMA_THDEN, regs + SPI_DMA); - - imx_dma_enable(drv_data->tx_channel); - } else { - dev_dbg(&drv_data->pdev->dev, - "pump pio transfer\n" - " tx = %p\n" - " rx = %p\n" - " len = %d\n", - drv_data->tx, - drv_data->rx, - drv_data->len); - - /* Ensure we have the correct interrupt handler */ - if (drv_data->rx) - drv_data->transfer_handler = interrupt_transfer; - else - drv_data->transfer_handler = interrupt_wronly_transfer; - - /* Enable SPI interrupt */ - if (drv_data->rx) - writel(SPI_INTEN_TH | SPI_INTEN_RO, - regs + SPI_INT_STATUS); - else - writel(SPI_INTEN_TH, regs + SPI_INT_STATUS); - } -} - -static void pump_messages(struct work_struct *work) -{ - struct driver_data *drv_data = - container_of(work, struct driver_data, work); - unsigned long flags; - - /* Lock queue and check for queue work */ - spin_lock_irqsave(&drv_data->lock, flags); - if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { - drv_data->busy = 0; - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - } - - /* Make sure we are not already running a message */ - if (drv_data->cur_msg) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return; - } - - /* Extract head of queue */ - drv_data->cur_msg = list_entry(drv_data->queue.next, - struct spi_message, queue); - list_del_init(&drv_data->cur_msg->queue); - drv_data->busy = 1; - spin_unlock_irqrestore(&drv_data->lock, flags); - - /* Initial message state */ - drv_data->cur_msg->state = START_STATE; - drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, - struct spi_transfer, - transfer_list); - - /* Setup the SPI using the per chip configuration */ - drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); - restore_state(drv_data); - - /* Mark as busy and launch transfers */ - tasklet_schedule(&drv_data->pump_transfers); -} - -static int transfer(struct spi_device *spi, struct spi_message *msg) -{ - struct driver_data *drv_data = spi_master_get_devdata(spi->master); - u32 min_speed_hz, max_speed_hz, tmp; - struct spi_transfer *trans; - unsigned long flags; - - msg->actual_length = 0; - - /* Per transfer setup check */ - min_speed_hz = spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN); - max_speed_hz = spi->max_speed_hz; - list_for_each_entry(trans, &msg->transfers, transfer_list) { - tmp = trans->bits_per_word; - if (tmp > 16) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "invalid transfer bits_per_word (%d bits)\n", - tmp); - goto msg_rejected; - } - tmp = trans->speed_hz; - if (tmp) { - if (tmp < min_speed_hz) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - min_speed_hz, - tmp); - goto msg_rejected; - } else if (tmp > max_speed_hz) { - dev_err(&drv_data->pdev->dev, - "message rejected : " - "transfer speed (%d Hz) exceeds " - "device max speed (%d Hz)\n", - tmp, - max_speed_hz); - goto msg_rejected; - } - } - } - - /* Message accepted */ - msg->status = -EINPROGRESS; - msg->state = START_STATE; - - spin_lock_irqsave(&drv_data->lock, flags); - if (drv_data->run == QUEUE_STOPPED) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -ESHUTDOWN; - } - - list_add_tail(&msg->queue, &drv_data->queue); - if (drv_data->run == QUEUE_RUNNING && !drv_data->busy) - queue_work(drv_data->workqueue, &drv_data->work); - - spin_unlock_irqrestore(&drv_data->lock, flags); - return 0; - -msg_rejected: - /* Message rejected and not queued */ - msg->status = -EINVAL; - msg->state = ERROR_STATE; - if (msg->complete) - msg->complete(msg->context); - return -EINVAL; -} - -/* On first setup bad values must free chip_data memory since will cause - spi_new_device to fail. Bad value setup from protocol driver are simply not - applied and notified to the calling driver. */ -static int setup(struct spi_device *spi) -{ - struct driver_data *drv_data = spi_master_get_devdata(spi->master); - struct spi_imx_chip *chip_info; - struct chip_data *chip; - int first_setup = 0; - u32 tmp; - int status = 0; - - /* Get controller data */ - chip_info = spi->controller_data; - - /* Get controller_state */ - chip = spi_get_ctldata(spi); - if (chip == NULL) { - first_setup = 1; - - chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) { - dev_err(&spi->dev, - "setup - cannot allocate controller state\n"); - return -ENOMEM; - } - chip->control = SPI_DEFAULT_CONTROL; - - if (chip_info == NULL) { - /* spi_board_info.controller_data not is supplied */ - chip_info = kzalloc(sizeof(struct spi_imx_chip), - GFP_KERNEL); - if (!chip_info) { - dev_err(&spi->dev, - "setup - " - "cannot allocate controller data\n"); - status = -ENOMEM; - goto err_first_setup; - } - /* Set controller data default value */ - chip_info->enable_loopback = - SPI_DEFAULT_ENABLE_LOOPBACK; - chip_info->enable_dma = SPI_DEFAULT_ENABLE_DMA; - chip_info->ins_ss_pulse = 1; - chip_info->bclk_wait = SPI_DEFAULT_PERIOD_WAIT; - chip_info->cs_control = null_cs_control; - } - } - - /* Now set controller state based on controller data */ - - if (first_setup) { - /* SPI loopback */ - if (chip_info->enable_loopback) - chip->test = SPI_TEST_LBC; - else - chip->test = 0; - - /* SPI dma driven */ - chip->enable_dma = chip_info->enable_dma; - - /* SPI /SS pulse between spi burst */ - if (chip_info->ins_ss_pulse) - u32_EDIT(chip->control, - SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_1); - else - u32_EDIT(chip->control, - SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_0); - - /* SPI bclk waits between each bits_per_word spi burst */ - if (chip_info->bclk_wait > SPI_PERIOD_MAX_WAIT) { - dev_err(&spi->dev, - "setup - " - "bclk_wait exceeds max allowed (%d)\n", - SPI_PERIOD_MAX_WAIT); - goto err_first_setup; - } - chip->period = SPI_PERIOD_CSRC_BCLK | - (chip_info->bclk_wait & SPI_PERIOD_WAIT); - } - - /* SPI mode */ - tmp = spi->mode; - if (tmp & SPI_CS_HIGH) { - u32_EDIT(chip->control, - SPI_CONTROL_SSPOL, SPI_CONTROL_SSPOL_ACT_HIGH); - } - switch (tmp & SPI_MODE_3) { - case SPI_MODE_0: - tmp = 0; - break; - case SPI_MODE_1: - tmp = SPI_CONTROL_PHA_1; - break; - case SPI_MODE_2: - tmp = SPI_CONTROL_POL_ACT_LOW; - break; - default: - /* SPI_MODE_3 */ - tmp = SPI_CONTROL_PHA_1 | SPI_CONTROL_POL_ACT_LOW; - break; - } - u32_EDIT(chip->control, SPI_CONTROL_POL | SPI_CONTROL_PHA, tmp); - - /* SPI word width */ - tmp = spi->bits_per_word; - if (tmp > 16) { - status = -EINVAL; - dev_err(&spi->dev, - "setup - " - "invalid bits_per_word (%d)\n", - tmp); - if (first_setup) - goto err_first_setup; - else { - /* Undo setup using chip as backup copy */ - tmp = chip->bits_per_word; - spi->bits_per_word = tmp; - } - } - chip->bits_per_word = tmp; - u32_EDIT(chip->control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1); - chip->n_bytes = (tmp <= 8) ? 1 : 2; - - /* SPI datarate */ - tmp = spi_data_rate(drv_data, spi->max_speed_hz); - if (tmp == SPI_CONTROL_DATARATE_BAD) { - status = -EINVAL; - dev_err(&spi->dev, - "setup - " - "HW min speed (%d Hz) exceeds required " - "max speed (%d Hz)\n", - spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), - spi->max_speed_hz); - if (first_setup) - goto err_first_setup; - else - /* Undo setup using chip as backup copy */ - spi->max_speed_hz = chip->max_speed_hz; - } else { - u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp); - /* Actual rounded max_speed_hz */ - tmp = spi_speed_hz(drv_data, tmp); - spi->max_speed_hz = tmp; - chip->max_speed_hz = tmp; - } - - /* SPI chip-select management */ - if (chip_info->cs_control) - chip->cs_control = chip_info->cs_control; - else - chip->cs_control = null_cs_control; - - /* Save controller_state */ - spi_set_ctldata(spi, chip); - - /* Summary */ - dev_dbg(&spi->dev, - "setup succeded\n" - " loopback enable = %s\n" - " dma enable = %s\n" - " insert /ss pulse = %s\n" - " period wait = %d\n" - " mode = %d\n" - " bits per word = %d\n" - " min speed = %d Hz\n" - " rounded max speed = %d Hz\n", - chip->test & SPI_TEST_LBC ? "Yes" : "No", - chip->enable_dma ? "Yes" : "No", - chip->control & SPI_CONTROL_SSCTL ? "Yes" : "No", - chip->period & SPI_PERIOD_WAIT, - spi->mode, - spi->bits_per_word, - spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), - spi->max_speed_hz); - return status; - -err_first_setup: - kfree(chip); - return status; -} - -static void cleanup(struct spi_device *spi) -{ - kfree(spi_get_ctldata(spi)); -} - -static int __init init_queue(struct driver_data *drv_data) -{ - INIT_LIST_HEAD(&drv_data->queue); - spin_lock_init(&drv_data->lock); - - drv_data->run = QUEUE_STOPPED; - drv_data->busy = 0; - - tasklet_init(&drv_data->pump_transfers, - pump_transfers, (unsigned long)drv_data); - - INIT_WORK(&drv_data->work, pump_messages); - drv_data->workqueue = create_singlethread_workqueue( - dev_name(drv_data->master->dev.parent)); - if (drv_data->workqueue == NULL) - return -EBUSY; - - return 0; -} - -static int start_queue(struct driver_data *drv_data) -{ - unsigned long flags; - - spin_lock_irqsave(&drv_data->lock, flags); - - if (drv_data->run == QUEUE_RUNNING || drv_data->busy) { - spin_unlock_irqrestore(&drv_data->lock, flags); - return -EBUSY; - } - - drv_data->run = QUEUE_RUNNING; - drv_data->cur_msg = NULL; - drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; - spin_unlock_irqrestore(&drv_data->lock, flags); - - queue_work(drv_data->workqueue, &drv_data->work); - - return 0; -} - -static int stop_queue(struct driver_data *drv_data) -{ - unsigned long flags; - unsigned limit = 500; - int status = 0; - - spin_lock_irqsave(&drv_data->lock, flags); - - /* This is a bit lame, but is optimized for the common execution path. - * A wait_queue on the drv_data->busy could be used, but then the common - * execution path (pump_messages) would be required to call wake_up or - * friends on every SPI message. Do this instead */ - drv_data->run = QUEUE_STOPPED; - while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) { - spin_unlock_irqrestore(&drv_data->lock, flags); - msleep(10); - spin_lock_irqsave(&drv_data->lock, flags); - } - - if (!list_empty(&drv_data->queue) || drv_data->busy) - status = -EBUSY; - - spin_unlock_irqrestore(&drv_data->lock, flags); - - return status; -} - -static int destroy_queue(struct driver_data *drv_data) -{ - int status; - - status = stop_queue(drv_data); - if (status != 0) - return status; - - if (drv_data->workqueue) - destroy_workqueue(drv_data->workqueue); - - return 0; -} - -static int __init spi_imx_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct spi_imx_master *platform_info; - struct spi_master *master; - struct driver_data *drv_data; - struct resource *res; - int irq, status = 0; - - platform_info = dev->platform_data; - if (platform_info == NULL) { - dev_err(&pdev->dev, "probe - no platform data supplied\n"); - status = -ENODEV; - goto err_no_pdata; - } - - /* Allocate master with space for drv_data */ - master = spi_alloc_master(dev, sizeof(struct driver_data)); - if (!master) { - dev_err(&pdev->dev, "probe - cannot alloc spi_master\n"); - status = -ENOMEM; - goto err_no_mem; - } - drv_data = spi_master_get_devdata(master); - drv_data->master = master; - drv_data->master_info = platform_info; - drv_data->pdev = pdev; - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - - master->bus_num = pdev->id; - master->num_chipselect = platform_info->num_chipselect; - master->dma_alignment = DMA_ALIGNMENT; - master->cleanup = cleanup; - master->setup = setup; - master->transfer = transfer; - - drv_data->dummy_dma_buf = SPI_DUMMY_u32; - - drv_data->clk = clk_get(&pdev->dev, "perclk2"); - if (IS_ERR(drv_data->clk)) { - dev_err(&pdev->dev, "probe - cannot get clock\n"); - status = PTR_ERR(drv_data->clk); - goto err_no_clk; - } - clk_enable(drv_data->clk); - - /* Find and map resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "probe - MEM resources not defined\n"); - status = -ENODEV; - goto err_no_iores; - } - drv_data->ioarea = request_mem_region(res->start, - res->end - res->start + 1, - pdev->name); - if (drv_data->ioarea == NULL) { - dev_err(&pdev->dev, "probe - cannot reserve region\n"); - status = -ENXIO; - goto err_no_iores; - } - drv_data->regs = ioremap(res->start, res->end - res->start + 1); - if (drv_data->regs == NULL) { - dev_err(&pdev->dev, "probe - cannot map IO\n"); - status = -ENXIO; - goto err_no_iomap; - } - drv_data->rd_data_phys = (dma_addr_t)res->start; - - /* Attach to IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "probe - IRQ resource not defined\n"); - status = -ENODEV; - goto err_no_irqres; - } - status = request_irq(irq, spi_int, IRQF_DISABLED, - dev_name(dev), drv_data); - if (status < 0) { - dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status); - goto err_no_irqres; - } - - /* Setup DMA if requested */ - drv_data->tx_channel = -1; - drv_data->rx_channel = -1; - if (platform_info->enable_dma) { - /* Get rx DMA channel */ - drv_data->rx_channel = imx_dma_request_by_prio("spi_imx_rx", - DMA_PRIO_HIGH); - if (drv_data->rx_channel < 0) { - dev_err(dev, - "probe - problem (%d) requesting rx channel\n", - drv_data->rx_channel); - goto err_no_rxdma; - } else - imx_dma_setup_handlers(drv_data->rx_channel, NULL, - dma_err_handler, drv_data); - - /* Get tx DMA channel */ - drv_data->tx_channel = imx_dma_request_by_prio("spi_imx_tx", - DMA_PRIO_MEDIUM); - if (drv_data->tx_channel < 0) { - dev_err(dev, - "probe - problem (%d) requesting tx channel\n", - drv_data->tx_channel); - imx_dma_free(drv_data->rx_channel); - goto err_no_txdma; - } else - imx_dma_setup_handlers(drv_data->tx_channel, - dma_tx_handler, dma_err_handler, - drv_data); - - /* Set request source and burst length for allocated channels */ - switch (drv_data->pdev->id) { - case 1: - /* Using SPI1 */ - RSSR(drv_data->rx_channel) = DMA_REQ_SPI1_R; - RSSR(drv_data->tx_channel) = DMA_REQ_SPI1_T; - break; - case 2: - /* Using SPI2 */ - RSSR(drv_data->rx_channel) = DMA_REQ_SPI2_R; - RSSR(drv_data->tx_channel) = DMA_REQ_SPI2_T; - break; - default: - dev_err(dev, "probe - bad SPI Id\n"); - imx_dma_free(drv_data->rx_channel); - imx_dma_free(drv_data->tx_channel); - status = -ENODEV; - goto err_no_devid; - } - BLR(drv_data->rx_channel) = SPI_DMA_BLR; - BLR(drv_data->tx_channel) = SPI_DMA_BLR; - } - - /* Load default SPI configuration */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - writel(SPI_DEFAULT_CONTROL, drv_data->regs + SPI_CONTROL); - - /* Initial and start queue */ - status = init_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem initializing queue\n"); - goto err_init_queue; - } - status = start_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem starting queue\n"); - goto err_start_queue; - } - - /* Register with the SPI framework */ - platform_set_drvdata(pdev, drv_data); - status = spi_register_master(master); - if (status != 0) { - dev_err(&pdev->dev, "probe - problem registering spi master\n"); - goto err_spi_register; - } - - dev_dbg(dev, "probe succeded\n"); - return 0; - -err_init_queue: -err_start_queue: -err_spi_register: - destroy_queue(drv_data); - -err_no_rxdma: -err_no_txdma: -err_no_devid: - free_irq(irq, drv_data); - -err_no_irqres: - iounmap(drv_data->regs); - -err_no_iomap: - release_resource(drv_data->ioarea); - kfree(drv_data->ioarea); - -err_no_iores: - clk_disable(drv_data->clk); - clk_put(drv_data->clk); - -err_no_clk: - spi_master_put(master); - -err_no_pdata: -err_no_mem: - return status; -} - -static int __exit spi_imx_remove(struct platform_device *pdev) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - int irq; - int status = 0; - - if (!drv_data) - return 0; - - tasklet_kill(&drv_data->pump_transfers); - - /* Remove the queue */ - status = destroy_queue(drv_data); - if (status != 0) { - dev_err(&pdev->dev, "queue remove failed (%d)\n", status); - return status; - } - - /* Reset SPI */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - - /* Release DMA */ - if (drv_data->master_info->enable_dma) { - RSSR(drv_data->rx_channel) = 0; - RSSR(drv_data->tx_channel) = 0; - imx_dma_free(drv_data->tx_channel); - imx_dma_free(drv_data->rx_channel); - } - - /* Release IRQ */ - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, drv_data); - - clk_disable(drv_data->clk); - clk_put(drv_data->clk); - - /* Release map resources */ - iounmap(drv_data->regs); - release_resource(drv_data->ioarea); - kfree(drv_data->ioarea); - - /* Disconnect from the SPI framework */ - spi_unregister_master(drv_data->master); - spi_master_put(drv_data->master); - - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - - dev_dbg(&pdev->dev, "remove succeded\n"); - - return 0; -} - -static void spi_imx_shutdown(struct platform_device *pdev) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - - /* Reset SPI */ - writel(SPI_RESET_START, drv_data->regs + SPI_RESET); - writel(0, drv_data->regs + SPI_RESET); - - dev_dbg(&pdev->dev, "shutdown succeded\n"); -} - -#ifdef CONFIG_PM - -static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - int status = 0; - - status = stop_queue(drv_data); - if (status != 0) { - dev_warn(&pdev->dev, "suspend cannot stop queue\n"); - return status; - } - - dev_dbg(&pdev->dev, "suspended\n"); - - return 0; -} - -static int spi_imx_resume(struct platform_device *pdev) -{ - struct driver_data *drv_data = platform_get_drvdata(pdev); - int status = 0; - - /* Start the queue running */ - status = start_queue(drv_data); - if (status != 0) - dev_err(&pdev->dev, "problem starting queue (%d)\n", status); - else - dev_dbg(&pdev->dev, "resumed\n"); - - return status; -} -#else -#define spi_imx_suspend NULL -#define spi_imx_resume NULL -#endif /* CONFIG_PM */ - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:spi_imx"); - -static struct platform_driver driver = { - .driver = { - .name = "spi_imx", - .owner = THIS_MODULE, - }, - .remove = __exit_p(spi_imx_remove), - .shutdown = spi_imx_shutdown, - .suspend = spi_imx_suspend, - .resume = spi_imx_resume, -}; - -static int __init spi_imx_init(void) -{ - return platform_driver_probe(&driver, spi_imx_probe); -} -module_init(spi_imx_init); - -static void __exit spi_imx_exit(void) -{ - platform_driver_unregister(&driver); -} -module_exit(spi_imx_exit); - -MODULE_AUTHOR("Andrea Paterniani, "); -MODULE_DESCRIPTION("iMX SPI Controller Driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7a8fa725b21ae19a3d9a2de196a440da8c9085a6 Mon Sep 17 00:00:00 2001 From: Jouni Hogander Date: Tue, 22 Sep 2009 16:45:58 -0700 Subject: spi: omap2_mcspi use BIT(n) Convert bit shifted values into BIT format Signed-off-by: Jouni Hogander Signed-off-by: Tony Lindgren Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/omap2_mcspi.c | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 9b80ad36dbb..ae785cc627b 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -59,40 +59,40 @@ /* per-register bitmasks: */ -#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) -#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) -#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0) -#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE BIT(4) +#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2) +#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) +#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET BIT(1) -#define OMAP2_MCSPI_SYSSTATUS_RESETDONE (1 << 0) +#define OMAP2_MCSPI_SYSSTATUS_RESETDONE BIT(0) -#define OMAP2_MCSPI_MODULCTRL_SINGLE (1 << 0) -#define OMAP2_MCSPI_MODULCTRL_MS (1 << 2) -#define OMAP2_MCSPI_MODULCTRL_STEST (1 << 3) +#define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) +#define OMAP2_MCSPI_MODULCTRL_MS BIT(2) +#define OMAP2_MCSPI_MODULCTRL_STEST BIT(3) -#define OMAP2_MCSPI_CHCONF_PHA (1 << 0) -#define OMAP2_MCSPI_CHCONF_POL (1 << 1) +#define OMAP2_MCSPI_CHCONF_PHA BIT(0) +#define OMAP2_MCSPI_CHCONF_POL BIT(1) #define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) -#define OMAP2_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP2_MCSPI_CHCONF_EPOL BIT(6) #define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) -#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) -#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) +#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) #define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) -#define OMAP2_MCSPI_CHCONF_DMAW (1 << 14) -#define OMAP2_MCSPI_CHCONF_DMAR (1 << 15) -#define OMAP2_MCSPI_CHCONF_DPE0 (1 << 16) -#define OMAP2_MCSPI_CHCONF_DPE1 (1 << 17) -#define OMAP2_MCSPI_CHCONF_IS (1 << 18) -#define OMAP2_MCSPI_CHCONF_TURBO (1 << 19) -#define OMAP2_MCSPI_CHCONF_FORCE (1 << 20) +#define OMAP2_MCSPI_CHCONF_DMAW BIT(14) +#define OMAP2_MCSPI_CHCONF_DMAR BIT(15) +#define OMAP2_MCSPI_CHCONF_DPE0 BIT(16) +#define OMAP2_MCSPI_CHCONF_DPE1 BIT(17) +#define OMAP2_MCSPI_CHCONF_IS BIT(18) +#define OMAP2_MCSPI_CHCONF_TURBO BIT(19) +#define OMAP2_MCSPI_CHCONF_FORCE BIT(20) -#define OMAP2_MCSPI_CHSTAT_RXS (1 << 0) -#define OMAP2_MCSPI_CHSTAT_TXS (1 << 1) -#define OMAP2_MCSPI_CHSTAT_EOT (1 << 2) +#define OMAP2_MCSPI_CHSTAT_RXS BIT(0) +#define OMAP2_MCSPI_CHSTAT_TXS BIT(1) +#define OMAP2_MCSPI_CHSTAT_EOT BIT(2) -#define OMAP2_MCSPI_CHCTRL_EN (1 << 0) +#define OMAP2_MCSPI_CHCTRL_EN BIT(0) -#define OMAP2_MCSPI_WAKEUPENABLE_WKEN (1 << 0) +#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) /* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { -- cgit v1.2.3 From 44dab88e7cc99d1d2caa9a8936e09d9a98a6761a Mon Sep 17 00:00:00 2001 From: "Steven A. Falco" Date: Tue, 22 Sep 2009 16:45:58 -0700 Subject: spi: add spi_ppc4xx driver This adds a SPI driver for the SPI controller found in the IBM/AMCC 4xx PowerPC's. Signed-off-by: Stefan Roese Signed-off-by: Wolfgang Ocker Acked-by: Josh Boyer Signed-off-by: Steven A. Falco Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi_ppc4xx.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 620 insertions(+) create mode 100644 drivers/spi/spi_ppc4xx.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 86056d1409b..3ac072f6db3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -171,6 +171,13 @@ config SPI_PL022 controller. If you have an embedded system with an AMBA(R) bus and a PL022 controller, say Y or M here. +config SPI_PPC4xx + tristate "PPC4xx SPI Controller" + depends on PPC32 && 4xx && SPI_MASTER + select SPI_BITBANG + help + This selects a driver for the PPC4xx SPI Controller. + config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on ARCH_PXA && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 0f20a70b2fa..bdd1f3a1e54 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_ORION) += orion_spi.o obj-$(CONFIG_SPI_PL022) += amba-pl022.o obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o +obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c new file mode 100644 index 00000000000..140a18d6cf3 --- /dev/null +++ b/drivers/spi/spi_ppc4xx.c @@ -0,0 +1,612 @@ +/* + * SPI_PPC4XX SPI controller driver. + * + * Copyright (C) 2007 Gary Jennejohn + * Copyright 2008 Stefan Roese , DENX Software Engineering + * Copyright 2009 Harris Corporation, Steven A. Falco + * + * Based in part on drivers/spi/spi_s3c24xx.c + * + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * 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. + */ + +/* + * The PPC4xx SPI controller has no FIFO so each sent/received byte will + * generate an interrupt to the CPU. This can cause high CPU utilization. + * This driver allows platforms to reduce the interrupt load on the CPU + * during SPI transfers by setting max_speed_hz via the device tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* bits in mode register - bit 0 is MSb */ + +/* + * SPI_PPC4XX_MODE_SCP = 0 means "data latched on trailing edge of clock" + * SPI_PPC4XX_MODE_SCP = 1 means "data latched on leading edge of clock" + * Note: This is the inverse of CPHA. + */ +#define SPI_PPC4XX_MODE_SCP (0x80 >> 3) + +/* SPI_PPC4XX_MODE_SPE = 1 means "port enabled" */ +#define SPI_PPC4XX_MODE_SPE (0x80 >> 4) + +/* + * SPI_PPC4XX_MODE_RD = 0 means "MSB first" - this is the normal mode + * SPI_PPC4XX_MODE_RD = 1 means "LSB first" - this is bit-reversed mode + * Note: This is identical to SPI_LSB_FIRST. + */ +#define SPI_PPC4XX_MODE_RD (0x80 >> 5) + +/* + * SPI_PPC4XX_MODE_CI = 0 means "clock idles low" + * SPI_PPC4XX_MODE_CI = 1 means "clock idles high" + * Note: This is identical to CPOL. + */ +#define SPI_PPC4XX_MODE_CI (0x80 >> 6) + +/* + * SPI_PPC4XX_MODE_IL = 0 means "loopback disable" + * SPI_PPC4XX_MODE_IL = 1 means "loopback enable" + */ +#define SPI_PPC4XX_MODE_IL (0x80 >> 7) + +/* bits in control register */ +/* starts a transfer when set */ +#define SPI_PPC4XX_CR_STR (0x80 >> 7) + +/* bits in status register */ +/* port is busy with a transfer */ +#define SPI_PPC4XX_SR_BSY (0x80 >> 6) +/* RxD ready */ +#define SPI_PPC4XX_SR_RBR (0x80 >> 7) + +/* clock settings (SCP and CI) for various SPI modes */ +#define SPI_CLK_MODE0 (SPI_PPC4XX_MODE_SCP | 0) +#define SPI_CLK_MODE1 (0 | 0) +#define SPI_CLK_MODE2 (SPI_PPC4XX_MODE_SCP | SPI_PPC4XX_MODE_CI) +#define SPI_CLK_MODE3 (0 | SPI_PPC4XX_MODE_CI) + +#define DRIVER_NAME "spi_ppc4xx_of" + +struct spi_ppc4xx_regs { + u8 mode; + u8 rxd; + u8 txd; + u8 cr; + u8 sr; + u8 dummy; + /* + * Clock divisor modulus register + * This uses the follwing formula: + * SCPClkOut = OPBCLK/(4(CDM + 1)) + * or + * CDM = (OPBCLK/4*SCPClkOut) - 1 + * bit 0 is the MSb! + */ + u8 cdm; +}; + +/* SPI Controller driver's private data. */ +struct ppc4xx_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + u64 mapbase; + u64 mapsize; + int irqnum; + /* need this to set the SPI clock */ + unsigned int opb_freq; + + /* for transfers */ + int len; + int count; + /* data buffers */ + const unsigned char *tx; + unsigned char *rx; + + int *gpios; + + struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */ + struct spi_master *master; + struct device *dev; +}; + +/* need this so we can set the clock in the chipselect routine */ +struct spi_ppc4xx_cs { + u8 mode; +}; + +static int spi_ppc4xx_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct ppc4xx_spi *hw; + u8 data; + + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", + t->tx_buf, t->rx_buf, t->len); + + hw = spi_master_get_devdata(spi->master); + + hw->tx = t->tx_buf; + hw->rx = t->rx_buf; + hw->len = t->len; + hw->count = 0; + + /* send the first byte */ + data = hw->tx ? hw->tx[0] : 0; + out_8(&hw->regs->txd, data); + out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR); + wait_for_completion(&hw->done); + + return hw->count; +} + +static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master); + struct spi_ppc4xx_cs *cs = spi->controller_state; + int scr; + u8 cdm = 0; + u32 speed; + u8 bits_per_word; + + /* Start with the generic configuration for this device. */ + bits_per_word = spi->bits_per_word; + speed = spi->max_speed_hz; + + /* + * Modify the configuration if the transfer overrides it. Do not allow + * the transfer to overwrite the generic configuration with zeros. + */ + if (t) { + if (t->bits_per_word) + bits_per_word = t->bits_per_word; + + if (t->speed_hz) + speed = min(t->speed_hz, spi->max_speed_hz); + } + + if (bits_per_word != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", + bits_per_word); + return -EINVAL; + } + + if (!speed || (speed > spi->max_speed_hz)) { + dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed); + return -EINVAL; + } + + /* Write new configration */ + out_8(&hw->regs->mode, cs->mode); + + /* Set the clock */ + /* opb_freq was already divided by 4 */ + scr = (hw->opb_freq / speed) - 1; + if (scr > 0) + cdm = min(scr, 0xff); + + dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", cdm, speed); + + if (in_8(&hw->regs->cdm) != cdm) + out_8(&hw->regs->cdm, cdm); + + spin_lock(&hw->bitbang.lock); + if (!hw->bitbang.busy) { + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* Need to ndelay here? */ + } + spin_unlock(&hw->bitbang.lock); + + return 0; +} + +static int spi_ppc4xx_setup(struct spi_device *spi) +{ + struct spi_ppc4xx_cs *cs = spi->controller_state; + + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", + spi->bits_per_word); + return -EINVAL; + } + + if (!spi->max_speed_hz) { + dev_err(&spi->dev, "invalid max_speed_hz (must be non-zero)\n"); + return -EINVAL; + } + + if (cs == NULL) { + cs = kzalloc(sizeof *cs, GFP_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + /* + * We set all bits of the SPI0_MODE register, so, + * no need to read-modify-write + */ + cs->mode = SPI_PPC4XX_MODE_SPE; + + switch (spi->mode & (SPI_CPHA | SPI_CPOL)) { + case SPI_MODE_0: + cs->mode |= SPI_CLK_MODE0; + break; + case SPI_MODE_1: + cs->mode |= SPI_CLK_MODE1; + break; + case SPI_MODE_2: + cs->mode |= SPI_CLK_MODE2; + break; + case SPI_MODE_3: + cs->mode |= SPI_CLK_MODE3; + break; + } + + if (spi->mode & SPI_LSB_FIRST) + cs->mode |= SPI_PPC4XX_MODE_RD; + + return 0; +} + +static void spi_ppc4xx_chipsel(struct spi_device *spi, int value) +{ + struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master); + unsigned int cs = spi->chip_select; + unsigned int cspol; + + /* + * If there are no chip selects at all, or if this is the special + * case of a non-existent (dummy) chip select, do nothing. + */ + + if (!hw->master->num_chipselect || hw->gpios[cs] == -EEXIST) + return; + + cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; + if (value == BITBANG_CS_INACTIVE) + cspol = !cspol; + + gpio_set_value(hw->gpios[cs], cspol); +} + +static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id) +{ + struct ppc4xx_spi *hw; + u8 status; + u8 data; + unsigned int count; + + hw = (struct ppc4xx_spi *)dev_id; + + status = in_8(&hw->regs->sr); + if (!status) + return IRQ_NONE; + + /* + * BSY de-asserts one cycle after the transfer is complete. The + * interrupt is asserted after the transfer is complete. The exact + * relationship is not documented, hence this code. + */ + + if (unlikely(status & SPI_PPC4XX_SR_BSY)) { + u8 lstatus; + int cnt = 0; + + dev_dbg(hw->dev, "got interrupt but spi still busy?\n"); + do { + ndelay(10); + lstatus = in_8(&hw->regs->sr); + } while (++cnt < 100 && lstatus & SPI_PPC4XX_SR_BSY); + + if (cnt >= 100) { + dev_err(hw->dev, "busywait: too many loops!\n"); + complete(&hw->done); + return IRQ_HANDLED; + } else { + /* status is always 1 (RBR) here */ + status = in_8(&hw->regs->sr); + dev_dbg(hw->dev, "loops %d status %x\n", cnt, status); + } + } + + count = hw->count; + hw->count++; + + /* RBR triggered this interrupt. Therefore, data must be ready. */ + data = in_8(&hw->regs->rxd); + if (hw->rx) + hw->rx[count] = data; + + count++; + + if (count < hw->len) { + data = hw->tx ? hw->tx[count] : 0; + out_8(&hw->regs->txd, data); + out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR); + } else { + complete(&hw->done); + } + + return IRQ_HANDLED; +} + +static void spi_ppc4xx_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + +static void spi_ppc4xx_enable(struct ppc4xx_spi *hw) +{ + /* + * On all 4xx PPC's the SPI bus is shared/multiplexed with + * the 2nd I2C bus. We need to enable the the SPI bus before + * using it. + */ + + /* need to clear bit 14 to enable SPC */ + dcri_clrset(SDR0, SDR0_PFC1, 0x80000000 >> 14, 0); +} + +static void free_gpios(struct ppc4xx_spi *hw) +{ + if (hw->master->num_chipselect) { + int i; + for (i = 0; i < hw->master->num_chipselect; i++) + if (gpio_is_valid(hw->gpios[i])) + gpio_free(hw->gpios[i]); + + kfree(hw->gpios); + hw->gpios = NULL; + } +} + +/* + * of_device layer stuff... + */ +static int __init spi_ppc4xx_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct ppc4xx_spi *hw; + struct spi_master *master; + struct spi_bitbang *bbp; + struct resource resource; + struct device_node *np = op->node; + struct device *dev = &op->dev; + struct device_node *opbnp; + int ret; + int num_gpios; + const unsigned int *clk; + + master = spi_alloc_master(dev, sizeof *hw); + if (master == NULL) + return -ENOMEM; + dev_set_drvdata(dev, master); + hw = spi_master_get_devdata(master); + hw->master = spi_master_get(master); + hw->dev = dev; + + init_completion(&hw->done); + + /* + * A count of zero implies a single SPI device without any chip-select. + * Note that of_gpio_count counts all gpios assigned to this spi master. + * This includes both "null" gpio's and real ones. + */ + num_gpios = of_gpio_count(np); + if (num_gpios) { + int i; + + hw->gpios = kzalloc(sizeof(int) * num_gpios, GFP_KERNEL); + if (!hw->gpios) { + ret = -ENOMEM; + goto free_master; + } + + for (i = 0; i < num_gpios; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(np, i, &flags); + hw->gpios[i] = gpio; + + if (gpio_is_valid(gpio)) { + /* Real CS - set the initial state. */ + ret = gpio_request(gpio, np->name); + if (ret < 0) { + dev_err(dev, "can't request gpio " + "#%d: %d\n", i, ret); + goto free_gpios; + } + + gpio_direction_output(gpio, + !!(flags & OF_GPIO_ACTIVE_LOW)); + } else if (gpio == -EEXIST) { + ; /* No CS, but that's OK. */ + } else { + dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); + ret = -EINVAL; + goto free_gpios; + } + } + } + + /* Setup the state for the bitbang driver */ + bbp = &hw->bitbang; + bbp->master = hw->master; + bbp->setup_transfer = spi_ppc4xx_setupxfer; + bbp->chipselect = spi_ppc4xx_chipsel; + bbp->txrx_bufs = spi_ppc4xx_txrx; + bbp->use_dma = 0; + bbp->master->setup = spi_ppc4xx_setup; + bbp->master->cleanup = spi_ppc4xx_cleanup; + + /* Allocate bus num dynamically. */ + bbp->master->bus_num = -1; + + /* the spi->mode bits understood by this driver: */ + bbp->master->mode_bits = + SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST; + + /* this many pins in all GPIO controllers */ + bbp->master->num_chipselect = num_gpios; + + /* Get the clock for the OPB */ + opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb"); + if (opbnp == NULL) { + dev_err(dev, "OPB: cannot find node\n"); + ret = -ENODEV; + goto free_gpios; + } + /* Get the clock (Hz) for the OPB */ + clk = of_get_property(opbnp, "clock-frequency", NULL); + if (clk == NULL) { + dev_err(dev, "OPB: no clock-frequency property set\n"); + of_node_put(opbnp); + ret = -ENODEV; + goto free_gpios; + } + hw->opb_freq = *clk; + hw->opb_freq >>= 2; + of_node_put(opbnp); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_err(dev, "error while parsing device node resource\n"); + goto free_gpios; + } + hw->mapbase = resource.start; + hw->mapsize = resource.end - resource.start + 1; + + /* Sanity check */ + if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) { + dev_err(dev, "too small to map registers\n"); + ret = -EINVAL; + goto free_gpios; + } + + /* Request IRQ */ + hw->irqnum = irq_of_parse_and_map(np, 0); + ret = request_irq(hw->irqnum, spi_ppc4xx_int, + IRQF_DISABLED, "spi_ppc4xx_of", (void *)hw); + if (ret) { + dev_err(dev, "unable to allocate interrupt\n"); + goto free_gpios; + } + + if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) { + dev_err(dev, "resource unavailable\n"); + ret = -EBUSY; + goto request_mem_error; + } + + hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs)); + + if (!hw->regs) { + dev_err(dev, "unable to memory map registers\n"); + ret = -ENXIO; + goto map_io_error; + } + + spi_ppc4xx_enable(hw); + + /* Finally register our spi controller */ + dev->dma_mask = 0; + ret = spi_bitbang_start(bbp); + if (ret) { + dev_err(dev, "failed to register SPI master\n"); + goto unmap_regs; + } + + dev_info(dev, "driver initialized\n"); + of_register_spi_devices(master, np); + + return 0; + +unmap_regs: + iounmap(hw->regs); +map_io_error: + release_mem_region(hw->mapbase, hw->mapsize); +request_mem_error: + free_irq(hw->irqnum, hw); +free_gpios: + free_gpios(hw); +free_master: + dev_set_drvdata(dev, NULL); + spi_master_put(master); + + dev_err(dev, "initialization failed\n"); + return ret; +} + +static int __exit spi_ppc4xx_of_remove(struct of_device *op) +{ + struct spi_master *master = dev_get_drvdata(&op->dev); + struct ppc4xx_spi *hw = spi_master_get_devdata(master); + + spi_bitbang_stop(&hw->bitbang); + dev_set_drvdata(&op->dev, NULL); + release_mem_region(hw->mapbase, hw->mapsize); + free_irq(hw->irqnum, hw); + iounmap(hw->regs); + free_gpios(hw); + return 0; +} + +static struct of_device_id spi_ppc4xx_of_match[] = { + { .compatible = "ibm,ppc4xx-spi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match); + +static struct of_platform_driver spi_ppc4xx_of_driver = { + .match_table = spi_ppc4xx_of_match, + .probe = spi_ppc4xx_of_probe, + .remove = __exit_p(spi_ppc4xx_of_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init spi_ppc4xx_init(void) +{ + return of_register_platform_driver(&spi_ppc4xx_of_driver); +} +module_init(spi_ppc4xx_init); + +static void __exit spi_ppc4xx_exit(void) +{ + of_unregister_platform_driver(&spi_ppc4xx_of_driver); +} +module_exit(spi_ppc4xx_exit); + +MODULE_AUTHOR("Gary Jennejohn & Stefan Roese"); +MODULE_DESCRIPTION("Simple PPC4xx SPI Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From f33b29ee3309ba2cd56ade8e905b3640a7dea909 Mon Sep 17 00:00:00 2001 From: "linus.walleij@stericsson.com" Date: Tue, 22 Sep 2009 16:46:01 -0700 Subject: spi: add default selection of PL022 for ARM reference platforms This makes the PL022 driver a default choice for any RealView and Versatile boards plus the integrator IMPD1 which all contain the PL022 PrimeCell. This will make it a default choice if and only if a user selects SPI support for their board. Signed-off-by: Linus Walleij Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3ac072f6db3..927a8b77e60 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -166,6 +166,9 @@ config SPI_PL022 tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)" depends on ARM_AMBA && EXPERIMENTAL default y if MACH_U300 + default y if ARCH_REALVIEW + default y if INTEGRATOR_IMPD1 + default y if ARCH_VERSATILE help This selects the ARM(R) AMBA(R) PrimeCell PL022 SSP controller. If you have an embedded system with an AMBA(R) -- cgit v1.2.3 From b5f3294f0be5496aec01e5aa709a5fab8bb2f225 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 22 Sep 2009 16:46:02 -0700 Subject: spi: add SPI driver for most known i.MX SoCs This driver has been tested on i.MX1/i.MX27/i.MX35 with an AT25 type EEPROM and on i.MX27/i.MX31 with a Freescale MC13783 PMIC. Signed-off-by: Sascha Hauer Tested-by: Guennadi Liakhovetski Acked-by: David Brownell Cc: Andrea Paterniani Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/mxc_spi.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 694 insertions(+) create mode 100644 drivers/spi/mxc_spi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 927a8b77e60..cd3acfe9d13 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -116,6 +116,14 @@ config SPI_GPIO GPIO operations, you should be able to leverage that for better speed with a custom version of this driver; see the source code. +config SPI_IMX + tristate "Freescale i.MX SPI controllers" + depends on ARCH_MXC + select SPI_BITBANG + help + This enables using the Freescale i.MX SPI controllers in master + mode. + config SPI_LM70_LLP tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)" depends on PARPORT && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index bdd1f3a1e54..fbfc5f22f4f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o +obj-$(CONFIG_SPI_IMX) += mxc_spi.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c new file mode 100644 index 00000000000..b1447236ae8 --- /dev/null +++ b/drivers/spi/mxc_spi.c @@ -0,0 +1,685 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 Juergen Beisert + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "spi_imx" + +#define MXC_CSPIRXDATA 0x00 +#define MXC_CSPITXDATA 0x04 +#define MXC_CSPICTRL 0x08 +#define MXC_CSPIINT 0x0c +#define MXC_RESET 0x1c + +/* generic defines to abstract from the different register layouts */ +#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ +#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ + +struct mxc_spi_config { + unsigned int speed_hz; + unsigned int bpw; + unsigned int mode; + int cs; +}; + +struct mxc_spi_data { + struct spi_bitbang bitbang; + + struct completion xfer_done; + void *base; + int irq; + struct clk *clk; + unsigned long spi_clk; + int *chipselect; + + unsigned int count; + void (*tx)(struct mxc_spi_data *); + void (*rx)(struct mxc_spi_data *); + void *rx_buf; + const void *tx_buf; + unsigned int txfifo; /* number of words pushed in tx FIFO */ + + /* SoC specific functions */ + void (*intctrl)(struct mxc_spi_data *, int); + int (*config)(struct mxc_spi_data *, struct mxc_spi_config *); + void (*trigger)(struct mxc_spi_data *); + int (*rx_available)(struct mxc_spi_data *); +}; + +#define MXC_SPI_BUF_RX(type) \ +static void mxc_spi_buf_rx_##type(struct mxc_spi_data *mxc_spi) \ +{ \ + unsigned int val = readl(mxc_spi->base + MXC_CSPIRXDATA); \ + \ + if (mxc_spi->rx_buf) { \ + *(type *)mxc_spi->rx_buf = val; \ + mxc_spi->rx_buf += sizeof(type); \ + } \ +} + +#define MXC_SPI_BUF_TX(type) \ +static void mxc_spi_buf_tx_##type(struct mxc_spi_data *mxc_spi) \ +{ \ + type val = 0; \ + \ + if (mxc_spi->tx_buf) { \ + val = *(type *)mxc_spi->tx_buf; \ + mxc_spi->tx_buf += sizeof(type); \ + } \ + \ + mxc_spi->count -= sizeof(type); \ + \ + writel(val, mxc_spi->base + MXC_CSPITXDATA); \ +} + +MXC_SPI_BUF_RX(u8) +MXC_SPI_BUF_TX(u8) +MXC_SPI_BUF_RX(u16) +MXC_SPI_BUF_TX(u16) +MXC_SPI_BUF_RX(u32) +MXC_SPI_BUF_TX(u32) + +/* First entry is reserved, second entry is valid only if SDHC_SPIEN is set + * (which is currently not the case in this driver) + */ +static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, + 256, 384, 512, 768, 1024}; + +/* MX21, MX27 */ +static unsigned int mxc_spi_clkdiv_1(unsigned int fin, + unsigned int fspi) +{ + int i, max; + + if (cpu_is_mx21()) + max = 18; + else + max = 16; + + for (i = 2; i < max; i++) + if (fspi * mxc_clkdivs[i] >= fin) + return i; + + return max; +} + +/* MX1, MX31, MX35 */ +static unsigned int mxc_spi_clkdiv_2(unsigned int fin, + unsigned int fspi) +{ + int i, div = 4; + + for (i = 0; i < 7; i++) { + if (fspi * div >= fin) + return i; + div <<= 1; + } + + return 7; +} + +#define MX31_INTREG_TEEN (1 << 0) +#define MX31_INTREG_RREN (1 << 3) + +#define MX31_CSPICTRL_ENABLE (1 << 0) +#define MX31_CSPICTRL_MASTER (1 << 1) +#define MX31_CSPICTRL_XCH (1 << 2) +#define MX31_CSPICTRL_POL (1 << 4) +#define MX31_CSPICTRL_PHA (1 << 5) +#define MX31_CSPICTRL_SSCTL (1 << 6) +#define MX31_CSPICTRL_SSPOL (1 << 7) +#define MX31_CSPICTRL_BC_SHIFT 8 +#define MX35_CSPICTRL_BL_SHIFT 20 +#define MX31_CSPICTRL_CS_SHIFT 24 +#define MX35_CSPICTRL_CS_SHIFT 12 +#define MX31_CSPICTRL_DR_SHIFT 16 + +#define MX31_CSPISTATUS 0x14 +#define MX31_STATUS_RR (1 << 3) + +/* These functions also work for the i.MX35, but be aware that + * the i.MX35 has a slightly different register layout for bits + * we do not use here. + */ +static void mx31_intctrl(struct mxc_spi_data *mxc_spi, int enable) +{ + unsigned int val = 0; + + if (enable & MXC_INT_TE) + val |= MX31_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX31_INTREG_RREN; + + writel(val, mxc_spi->base + MXC_CSPIINT); +} + +static void mx31_trigger(struct mxc_spi_data *mxc_spi) +{ + unsigned int reg; + + reg = readl(mxc_spi->base + MXC_CSPICTRL); + reg |= MX31_CSPICTRL_XCH; + writel(reg, mxc_spi->base + MXC_CSPICTRL); +} + +static int mx31_config(struct mxc_spi_data *mxc_spi, + struct mxc_spi_config *config) +{ + unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; + + reg |= mxc_spi_clkdiv_2(mxc_spi->spi_clk, config->speed_hz) << + MX31_CSPICTRL_DR_SHIFT; + + if (cpu_is_mx31()) + reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; + else if (cpu_is_mx35()) { + reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; + reg |= MX31_CSPICTRL_SSCTL; + } + + if (config->mode & SPI_CPHA) + reg |= MX31_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX31_CSPICTRL_POL; + if (config->mode & SPI_CS_HIGH) + reg |= MX31_CSPICTRL_SSPOL; + if (config->cs < 0) { + if (cpu_is_mx31()) + reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; + else if (cpu_is_mx35()) + reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; + } + + writel(reg, mxc_spi->base + MXC_CSPICTRL); + + return 0; +} + +static int mx31_rx_available(struct mxc_spi_data *mxc_spi) +{ + return readl(mxc_spi->base + MX31_CSPISTATUS) & MX31_STATUS_RR; +} + +#define MX27_INTREG_RR (1 << 4) +#define MX27_INTREG_TEEN (1 << 9) +#define MX27_INTREG_RREN (1 << 13) + +#define MX27_CSPICTRL_POL (1 << 5) +#define MX27_CSPICTRL_PHA (1 << 6) +#define MX27_CSPICTRL_SSPOL (1 << 8) +#define MX27_CSPICTRL_XCH (1 << 9) +#define MX27_CSPICTRL_ENABLE (1 << 10) +#define MX27_CSPICTRL_MASTER (1 << 11) +#define MX27_CSPICTRL_DR_SHIFT 14 +#define MX27_CSPICTRL_CS_SHIFT 19 + +static void mx27_intctrl(struct mxc_spi_data *mxc_spi, int enable) +{ + unsigned int val = 0; + + if (enable & MXC_INT_TE) + val |= MX27_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX27_INTREG_RREN; + + writel(val, mxc_spi->base + MXC_CSPIINT); +} + +static void mx27_trigger(struct mxc_spi_data *mxc_spi) +{ + unsigned int reg; + + reg = readl(mxc_spi->base + MXC_CSPICTRL); + reg |= MX27_CSPICTRL_XCH; + writel(reg, mxc_spi->base + MXC_CSPICTRL); +} + +static int mx27_config(struct mxc_spi_data *mxc_spi, + struct mxc_spi_config *config) +{ + unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; + + reg |= mxc_spi_clkdiv_1(mxc_spi->spi_clk, config->speed_hz) << + MX27_CSPICTRL_DR_SHIFT; + reg |= config->bpw - 1; + + if (config->mode & SPI_CPHA) + reg |= MX27_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX27_CSPICTRL_POL; + if (config->mode & SPI_CS_HIGH) + reg |= MX27_CSPICTRL_SSPOL; + if (config->cs < 0) + reg |= (config->cs + 32) << MX27_CSPICTRL_CS_SHIFT; + + writel(reg, mxc_spi->base + MXC_CSPICTRL); + + return 0; +} + +static int mx27_rx_available(struct mxc_spi_data *mxc_spi) +{ + return readl(mxc_spi->base + MXC_CSPIINT) & MX27_INTREG_RR; +} + +#define MX1_INTREG_RR (1 << 3) +#define MX1_INTREG_TEEN (1 << 8) +#define MX1_INTREG_RREN (1 << 11) + +#define MX1_CSPICTRL_POL (1 << 4) +#define MX1_CSPICTRL_PHA (1 << 5) +#define MX1_CSPICTRL_XCH (1 << 8) +#define MX1_CSPICTRL_ENABLE (1 << 9) +#define MX1_CSPICTRL_MASTER (1 << 10) +#define MX1_CSPICTRL_DR_SHIFT 13 + +static void mx1_intctrl(struct mxc_spi_data *mxc_spi, int enable) +{ + unsigned int val = 0; + + if (enable & MXC_INT_TE) + val |= MX1_INTREG_TEEN; + if (enable & MXC_INT_RR) + val |= MX1_INTREG_RREN; + + writel(val, mxc_spi->base + MXC_CSPIINT); +} + +static void mx1_trigger(struct mxc_spi_data *mxc_spi) +{ + unsigned int reg; + + reg = readl(mxc_spi->base + MXC_CSPICTRL); + reg |= MX1_CSPICTRL_XCH; + writel(reg, mxc_spi->base + MXC_CSPICTRL); +} + +static int mx1_config(struct mxc_spi_data *mxc_spi, + struct mxc_spi_config *config) +{ + unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; + + reg |= mxc_spi_clkdiv_2(mxc_spi->spi_clk, config->speed_hz) << + MX1_CSPICTRL_DR_SHIFT; + reg |= config->bpw - 1; + + if (config->mode & SPI_CPHA) + reg |= MX1_CSPICTRL_PHA; + if (config->mode & SPI_CPOL) + reg |= MX1_CSPICTRL_POL; + + writel(reg, mxc_spi->base + MXC_CSPICTRL); + + return 0; +} + +static int mx1_rx_available(struct mxc_spi_data *mxc_spi) +{ + return readl(mxc_spi->base + MXC_CSPIINT) & MX1_INTREG_RR; +} + +static void mxc_spi_chipselect(struct spi_device *spi, int is_active) +{ + struct mxc_spi_data *mxc_spi = spi_master_get_devdata(spi->master); + unsigned int cs = 0; + int gpio = mxc_spi->chipselect[spi->chip_select]; + struct mxc_spi_config config; + + if (spi->mode & SPI_CS_HIGH) + cs = 1; + + if (is_active == BITBANG_CS_INACTIVE) { + if (gpio >= 0) + gpio_set_value(gpio, !cs); + return; + } + + config.bpw = spi->bits_per_word; + config.speed_hz = spi->max_speed_hz; + config.mode = spi->mode; + config.cs = mxc_spi->chipselect[spi->chip_select]; + + mxc_spi->config(mxc_spi, &config); + + /* Initialize the functions for transfer */ + if (config.bpw <= 8) { + mxc_spi->rx = mxc_spi_buf_rx_u8; + mxc_spi->tx = mxc_spi_buf_tx_u8; + } else if (config.bpw <= 16) { + mxc_spi->rx = mxc_spi_buf_rx_u16; + mxc_spi->tx = mxc_spi_buf_tx_u16; + } else if (config.bpw <= 32) { + mxc_spi->rx = mxc_spi_buf_rx_u32; + mxc_spi->tx = mxc_spi_buf_tx_u32; + } else + BUG(); + + if (gpio >= 0) + gpio_set_value(gpio, cs); + + return; +} + +static void mxc_spi_push(struct mxc_spi_data *mxc_spi) +{ + while (mxc_spi->txfifo < 8) { + if (!mxc_spi->count) + break; + mxc_spi->tx(mxc_spi); + mxc_spi->txfifo++; + } + + mxc_spi->trigger(mxc_spi); +} + +static irqreturn_t mxc_spi_isr(int irq, void *dev_id) +{ + struct mxc_spi_data *mxc_spi = dev_id; + + while (mxc_spi->rx_available(mxc_spi)) { + mxc_spi->rx(mxc_spi); + mxc_spi->txfifo--; + } + + if (mxc_spi->count) { + mxc_spi_push(mxc_spi); + return IRQ_HANDLED; + } + + if (mxc_spi->txfifo) { + /* No data left to push, but still waiting for rx data, + * enable receive data available interrupt. + */ + mxc_spi->intctrl(mxc_spi, MXC_INT_RR); + return IRQ_HANDLED; + } + + mxc_spi->intctrl(mxc_spi, 0); + complete(&mxc_spi->xfer_done); + + return IRQ_HANDLED; +} + +static int mxc_spi_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct mxc_spi_data *mxc_spi = spi_master_get_devdata(spi->master); + struct mxc_spi_config config; + + config.bpw = t ? t->bits_per_word : spi->bits_per_word; + config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; + config.mode = spi->mode; + + mxc_spi->config(mxc_spi, &config); + + return 0; +} + +static int mxc_spi_transfer(struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct mxc_spi_data *mxc_spi = spi_master_get_devdata(spi->master); + + mxc_spi->tx_buf = transfer->tx_buf; + mxc_spi->rx_buf = transfer->rx_buf; + mxc_spi->count = transfer->len; + mxc_spi->txfifo = 0; + + init_completion(&mxc_spi->xfer_done); + + mxc_spi_push(mxc_spi); + + mxc_spi->intctrl(mxc_spi, MXC_INT_TE); + + wait_for_completion(&mxc_spi->xfer_done); + + return transfer->len; +} + +static int mxc_spi_setup(struct spi_device *spi) +{ + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + pr_debug("%s: mode %d, %u bpw, %d hz\n", __func__, + spi->mode, spi->bits_per_word, spi->max_speed_hz); + + mxc_spi_chipselect(spi, BITBANG_CS_INACTIVE); + + return 0; +} + +static void mxc_spi_cleanup(struct spi_device *spi) +{ +} + +static int __init mxc_spi_probe(struct platform_device *pdev) +{ + struct spi_imx_master *mxc_platform_info; + struct spi_master *master; + struct mxc_spi_data *mxc_spi; + struct resource *res; + int i, ret; + + mxc_platform_info = (struct spi_imx_master *)pdev->dev.platform_data; + if (!mxc_platform_info) { + dev_err(&pdev->dev, "can't get the platform data\n"); + return -EINVAL; + } + + master = spi_alloc_master(&pdev->dev, sizeof(struct mxc_spi_data)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + master->bus_num = pdev->id; + master->num_chipselect = mxc_platform_info->num_chipselect; + + mxc_spi = spi_master_get_devdata(master); + mxc_spi->bitbang.master = spi_master_get(master); + mxc_spi->chipselect = mxc_platform_info->chipselect; + + for (i = 0; i < master->num_chipselect; i++) { + if (mxc_spi->chipselect[i] < 0) + continue; + ret = gpio_request(mxc_spi->chipselect[i], DRIVER_NAME); + if (ret) { + i--; + while (i > 0) + if (mxc_spi->chipselect[i] >= 0) + gpio_free(mxc_spi->chipselect[i--]); + dev_err(&pdev->dev, "can't get cs gpios"); + goto out_master_put; + } + gpio_direction_output(mxc_spi->chipselect[i], 1); + } + + mxc_spi->bitbang.chipselect = mxc_spi_chipselect; + mxc_spi->bitbang.setup_transfer = mxc_spi_setupxfer; + mxc_spi->bitbang.txrx_bufs = mxc_spi_transfer; + mxc_spi->bitbang.master->setup = mxc_spi_setup; + mxc_spi->bitbang.master->cleanup = mxc_spi_cleanup; + + init_completion(&mxc_spi->xfer_done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get platform resource\n"); + ret = -ENOMEM; + goto out_gpio_free; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + ret = -EBUSY; + goto out_gpio_free; + } + + mxc_spi->base = ioremap(res->start, resource_size(res)); + if (!mxc_spi->base) { + ret = -EINVAL; + goto out_release_mem; + } + + mxc_spi->irq = platform_get_irq(pdev, 0); + if (!mxc_spi->irq) { + ret = -EINVAL; + goto out_iounmap; + } + + ret = request_irq(mxc_spi->irq, mxc_spi_isr, 0, DRIVER_NAME, mxc_spi); + if (ret) { + dev_err(&pdev->dev, "can't get irq%d: %d\n", mxc_spi->irq, ret); + goto out_iounmap; + } + + if (cpu_is_mx31() || cpu_is_mx35()) { + mxc_spi->intctrl = mx31_intctrl; + mxc_spi->config = mx31_config; + mxc_spi->trigger = mx31_trigger; + mxc_spi->rx_available = mx31_rx_available; + } else if (cpu_is_mx27() || cpu_is_mx21()) { + mxc_spi->intctrl = mx27_intctrl; + mxc_spi->config = mx27_config; + mxc_spi->trigger = mx27_trigger; + mxc_spi->rx_available = mx27_rx_available; + } else if (cpu_is_mx1()) { + mxc_spi->intctrl = mx1_intctrl; + mxc_spi->config = mx1_config; + mxc_spi->trigger = mx1_trigger; + mxc_spi->rx_available = mx1_rx_available; + } else + BUG(); + + mxc_spi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(mxc_spi->clk)) { + dev_err(&pdev->dev, "unable to get clock\n"); + ret = PTR_ERR(mxc_spi->clk); + goto out_free_irq; + } + + clk_enable(mxc_spi->clk); + mxc_spi->spi_clk = clk_get_rate(mxc_spi->clk); + + if (!cpu_is_mx31() || !cpu_is_mx35()) + writel(1, mxc_spi->base + MXC_RESET); + + mxc_spi->intctrl(mxc_spi, 0); + + ret = spi_bitbang_start(&mxc_spi->bitbang); + if (ret) { + dev_err(&pdev->dev, "bitbang start failed with %d\n", ret); + goto out_clk_put; + } + + dev_info(&pdev->dev, "probed\n"); + + return ret; + +out_clk_put: + clk_disable(mxc_spi->clk); + clk_put(mxc_spi->clk); +out_free_irq: + free_irq(mxc_spi->irq, mxc_spi); +out_iounmap: + iounmap(mxc_spi->base); +out_release_mem: + release_mem_region(res->start, resource_size(res)); +out_gpio_free: + for (i = 0; i < master->num_chipselect; i++) + if (mxc_spi->chipselect[i] >= 0) + gpio_free(mxc_spi->chipselect[i]); +out_master_put: + spi_master_put(master); + kfree(master); + platform_set_drvdata(pdev, NULL); + return ret; +} + +static int __exit mxc_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct mxc_spi_data *mxc_spi = spi_master_get_devdata(master); + int i; + + spi_bitbang_stop(&mxc_spi->bitbang); + + writel(0, mxc_spi->base + MXC_CSPICTRL); + clk_disable(mxc_spi->clk); + clk_put(mxc_spi->clk); + free_irq(mxc_spi->irq, mxc_spi); + iounmap(mxc_spi->base); + + for (i = 0; i < master->num_chipselect; i++) + if (mxc_spi->chipselect[i] >= 0) + gpio_free(mxc_spi->chipselect[i]); + + spi_master_put(master); + + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxc_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mxc_spi_probe, + .remove = __exit_p(mxc_spi_remove), +}; + +static int __init mxc_spi_init(void) +{ + return platform_driver_register(&mxc_spi_driver); +} + +static void __exit mxc_spi_exit(void) +{ + platform_driver_unregister(&mxc_spi_driver); +} + +module_init(mxc_spi_init); +module_exit(mxc_spi_exit); + +MODULE_DESCRIPTION("SPI Master Controller driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 75368bf6c2876d8f33abfe77aa3864869a3893eb Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 22 Sep 2009 16:46:04 -0700 Subject: spi: add support for device table matching With this patch spi drivers can use standard spi_driver.id_table and MODULE_DEVICE_TABLE() mechanisms to bind against the devices. Just like we do with I2C drivers. This is useful when a single driver supports several variants of devices but it is not possible to detect them in run-time (like non-JEDEC chips probing in drivers/mtd/devices/m25p80.c), and when platform_data usage is overkill. This patch also makes life a lot easier on OpenFirmware platforms, since with OF we extensively use proper device IDs in modaliases. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: David Woodhouse Cc: Grant Likely Cc: Jean Delvare Cc: Ben Dooks Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 70845ccd85c..8518a6eb63f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -59,9 +59,32 @@ static struct device_attribute spi_dev_attrs[] = { * and the sysfs version makes coldplug work too. */ +static const struct spi_device_id *spi_match_id(const struct spi_device_id *id, + const struct spi_device *sdev) +{ + while (id->name[0]) { + if (!strcmp(sdev->modalias, id->name)) + return id; + id++; + } + return NULL; +} + +const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) +{ + const struct spi_driver *sdrv = to_spi_driver(sdev->dev.driver); + + return spi_match_id(sdrv->id_table, sdev); +} +EXPORT_SYMBOL_GPL(spi_get_device_id); + static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); + const struct spi_driver *sdrv = to_spi_driver(drv); + + if (sdrv->id_table) + return !!spi_match_id(sdrv->id_table, spi); return strcmp(spi->modalias, drv->name) == 0; } -- cgit v1.2.3 From e0626e3844e8f430fc1a4417f523a00797df7ca6 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 22 Sep 2009 16:46:08 -0700 Subject: spi: prefix modalias with "spi:" This makes it consistent with other buses (platform, i2c, vio, ...). I'm not sure why we use the prefixes, but there must be a reason. This was easy enough to do it, and I did it. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: David Woodhouse Cc: Grant Likely Cc: Jean Delvare Cc: Ben Dooks Cc: Benjamin Herrenschmidt Cc: Dmitry Torokhov Cc: Samuel Ortiz Cc: "John W. Linville" Acked-by: Mike Frysinger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 3 ++- drivers/spi/spidev.c | 1 + drivers/spi/tle62x0.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8518a6eb63f..49e84860c8d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -93,7 +94,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) { const struct spi_device *spi = to_spi_device(dev); - add_uevent_var(env, "MODALIAS=%s", spi->modalias); + add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); return 0; } diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 606e7a40a8d..f921bd1109e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -688,3 +688,4 @@ module_exit(spidev_exit); MODULE_AUTHOR("Andrea Paterniani, "); MODULE_DESCRIPTION("User mode SPI device interface"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:spidev"); diff --git a/drivers/spi/tle62x0.c b/drivers/spi/tle62x0.c index 455991fbe28..bf9540f5fb9 100644 --- a/drivers/spi/tle62x0.c +++ b/drivers/spi/tle62x0.c @@ -329,3 +329,4 @@ module_exit(tle62x0_exit); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("TLE62x0 SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:tle62x0"); -- cgit v1.2.3 From 5b61a749e8fd0a45a5e37c267d20a43ef0590d68 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 22 Sep 2009 16:46:10 -0700 Subject: pxa2xx_spi: register earlier Register pxa2xx_spi earlier so it can be used with cpufreq Signed-off-by: Daniel Ribeiro Acked-by: Mark Brown Cc: David Brownell Cc: Eric Miao Cc: Mark Brown Cc: Daniel Ribeiro Cc: Samuel Ortiz Cc: Liam Girdwood Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index d949dbf1141..31dd56f0dae 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1729,7 +1729,7 @@ static int __init pxa2xx_spi_init(void) { return platform_driver_probe(&driver, pxa2xx_spi_probe); } -module_init(pxa2xx_spi_init); +subsys_initcall(pxa2xx_spi_init); static void __exit pxa2xx_spi_exit(void) { -- cgit v1.2.3 From 1a0c220f791be9e15fd897adee257e72ed4134f8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 22 Sep 2009 16:46:12 -0700 Subject: spi_s3c24xx: fix header includes The driver includes where it should be including and also includes and without using anything from these. Signed-off-by: Ben Dooks Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_s3c24xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 6ba8aece90b..0be4da0917e 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -20,14 +20,11 @@ #include #include #include +#include #include #include -#include -#include -#include - #include #include -- cgit v1.2.3 From b5e3afb5e32c9acf69fcc17961c3fddc47e9cc06 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 22 Sep 2009 16:46:13 -0700 Subject: spi_s3c24xx: use resource_size() to get resource size Change the use of (res->end - res->start) to use resource_size() to get the size of the resource. Signed-off-by; Ben Dooks Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_s3c24xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 0be4da0917e..705841fb4ca 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -299,7 +299,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_no_iores; } - hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, + hw->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); if (hw->ioarea == NULL) { @@ -308,7 +308,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) goto err_no_iores; } - hw->regs = ioremap(res->start, (res->end - res->start)+1); + hw->regs = ioremap(res->start, resource_size(res)); if (hw->regs == NULL) { dev_err(&pdev->dev, "Cannot map IO\n"); err = -ENXIO; -- cgit v1.2.3 From 6d61320707ac2960bc820616bd5921885b874780 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 22 Sep 2009 16:46:13 -0700 Subject: spi_s3c24xx: use dev_pm_ops Change the spi_s3c24xx driver to use dev_pm_ops to avoid the following warning during probe: Platform driver 's3c2410-spi' needs updating - please use dev_pm_ops Signed-off-by: Ben Dooks Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_s3c24xx.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 705841fb4ca..03695b67ebd 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -418,9 +418,9 @@ static int __exit s3c24xx_spi_remove(struct platform_device *dev) #ifdef CONFIG_PM -static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) +static int s3c24xx_spi_suspend(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(pdev); + struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); if (hw->pdata && hw->pdata->gpio_setup) hw->pdata->gpio_setup(hw->pdata, 0); @@ -429,27 +429,31 @@ static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) return 0; } -static int s3c24xx_spi_resume(struct platform_device *pdev) +static int s3c24xx_spi_resume(struct device *dev) { - struct s3c24xx_spi *hw = platform_get_drvdata(pdev); + struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); s3c24xx_spi_initialsetup(hw); return 0; } +static struct dev_pm_ops s3c24xx_spi_pmops = { + .suspend = s3c24xx_spi_suspend, + .resume = s3c24xx_spi_resume, +}; + +#define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops #else -#define s3c24xx_spi_suspend NULL -#define s3c24xx_spi_resume NULL -#endif +#define S3C24XX_SPI_PMOPS NULL +#endif /* CONFIG_PM */ MODULE_ALIAS("platform:s3c2410-spi"); static struct platform_driver s3c24xx_spi_driver = { .remove = __exit_p(s3c24xx_spi_remove), - .suspend = s3c24xx_spi_suspend, - .resume = s3c24xx_spi_resume, .driver = { .name = "s3c2410-spi", .owner = THIS_MODULE, + .pm = S3C24XX_SPI_PMOPS, }, }; -- cgit v1.2.3 From 570327d9f48b0be5ca626bcfd80266b7ee2001aa Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 22 Sep 2009 16:46:14 -0700 Subject: spi_s3c24xx: cache device setup data With the update to the spi_bitbang driver, the transfer setup code is being called more often, and thus is often re-doing calculations that have been done before. The SPI layer allows our driver to add its own data to each device so add a result cache to each device. This should also remove the problem where we where directly setting up registers in the setup call which meant we might overwrite the state of an extant transfer., Signed-off-by: Ben Dooks Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi_s3c24xx.c | 126 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 37 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 03695b67ebd..33d94f76b9e 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -28,6 +28,20 @@ #include #include +/** + * s3c24xx_spi_devstate - per device data + * @hz: Last frequency calculated for @sppre field. + * @mode: Last mode setting for the @spcon field. + * @spcon: Value to write to the SPCON register. + * @sppre: Value to write to the SPPRE register. + */ +struct s3c24xx_spi_devstate { + unsigned int hz; + unsigned int mode; + u8 spcon; + u8 sppre; +}; + struct s3c24xx_spi { /* bitbang has to be first */ struct spi_bitbang bitbang; @@ -68,43 +82,31 @@ static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol) static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) { + struct s3c24xx_spi_devstate *cs = spi->controller_state; struct s3c24xx_spi *hw = to_hw(spi); unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; - unsigned int spcon; + + /* change the chipselect state and the state of the spi engine clock */ switch (value) { case BITBANG_CS_INACTIVE: hw->set_cs(hw->pdata, spi->chip_select, cspol^1); + writeb(cs->spcon, hw->regs + S3C2410_SPCON); break; case BITBANG_CS_ACTIVE: - spcon = readb(hw->regs + S3C2410_SPCON); - - if (spi->mode & SPI_CPHA) - spcon |= S3C2410_SPCON_CPHA_FMTB; - else - spcon &= ~S3C2410_SPCON_CPHA_FMTB; - - if (spi->mode & SPI_CPOL) - spcon |= S3C2410_SPCON_CPOL_HIGH; - else - spcon &= ~S3C2410_SPCON_CPOL_HIGH; - - spcon |= S3C2410_SPCON_ENSCK; - - /* write new configration */ - - writeb(spcon, hw->regs + S3C2410_SPCON); + writeb(cs->spcon | S3C2410_SPCON_ENSCK, + hw->regs + S3C2410_SPCON); hw->set_cs(hw->pdata, spi->chip_select, cspol); - break; } } -static int s3c24xx_spi_setupxfer(struct spi_device *spi, - struct spi_transfer *t) +static int s3c24xx_spi_update_state(struct spi_device *spi, + struct spi_transfer *t) { struct s3c24xx_spi *hw = to_hw(spi); + struct s3c24xx_spi_devstate *cs = spi->controller_state; unsigned int bpw; unsigned int hz; unsigned int div; @@ -124,41 +126,89 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi, return -EINVAL; } - clk = clk_get_rate(hw->clk); - div = DIV_ROUND_UP(clk, hz * 2) - 1; + if (spi->mode != cs->mode) { + u8 spcon = SPCON_DEFAULT; - if (div > 255) - div = 255; + if (spi->mode & SPI_CPHA) + spcon |= S3C2410_SPCON_CPHA_FMTB; - dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n", - div, hz, clk / (2 * (div + 1))); + if (spi->mode & SPI_CPOL) + spcon |= S3C2410_SPCON_CPOL_HIGH; + cs->mode = spi->mode; + cs->spcon = spcon; + } - writeb(div, hw->regs + S3C2410_SPPRE); + if (cs->hz != hz) { + clk = clk_get_rate(hw->clk); + div = DIV_ROUND_UP(clk, hz * 2) - 1; - spin_lock(&hw->bitbang.lock); - if (!hw->bitbang.busy) { - hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); - /* need to ndelay for 0.5 clocktick ? */ + if (div > 255) + div = 255; + + dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n", + div, hz, clk / (2 * (div + 1))); + + cs->hz = hz; + cs->sppre = div; } - spin_unlock(&hw->bitbang.lock); return 0; } +static int s3c24xx_spi_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct s3c24xx_spi_devstate *cs = spi->controller_state; + struct s3c24xx_spi *hw = to_hw(spi); + int ret; + + ret = s3c24xx_spi_update_state(spi, t); + if (!ret) + writeb(cs->sppre, hw->regs + S3C2410_SPPRE); + + return ret; +} + static int s3c24xx_spi_setup(struct spi_device *spi) { + struct s3c24xx_spi_devstate *cs = spi->controller_state; + struct s3c24xx_spi *hw = to_hw(spi); int ret; - ret = s3c24xx_spi_setupxfer(spi, NULL); - if (ret < 0) { - dev_err(&spi->dev, "setupxfer returned %d\n", ret); + /* allocate settings on the first call */ + if (!cs) { + cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); + if (!cs) { + dev_err(&spi->dev, "no memory for controller state\n"); + return -ENOMEM; + } + + cs->spcon = SPCON_DEFAULT; + cs->hz = -1; + spi->controller_state = cs; + } + + /* initialise the state from the device */ + ret = s3c24xx_spi_update_state(spi, NULL); + if (ret) return ret; + + spin_lock(&hw->bitbang.lock); + if (!hw->bitbang.busy) { + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* need to ndelay for 0.5 clocktick ? */ } + spin_unlock(&hw->bitbang.lock); return 0; } +static void s3c24xx_spi_cleanup(struct spi_device *spi) +{ + kfree(spi->controller_state); +} + static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -286,7 +336,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; hw->bitbang.chipselect = s3c24xx_spi_chipsel; hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; - hw->bitbang.master->setup = s3c24xx_spi_setup; + + hw->master->setup = s3c24xx_spi_setup; + hw->master->cleanup = s3c24xx_spi_cleanup; dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); -- cgit v1.2.3 From 0644c48672a3146c01bf7314d440edecf8742d62 Mon Sep 17 00:00:00 2001 From: dmitry pervushin Date: Tue, 22 Sep 2009 16:46:15 -0700 Subject: spi: Freescale STMP driver Add SPI driver for Freescale STMP 3xxx-based boards [dbrownell@users.sourceforge.net: cleanup sequence, spi_unregister_master] Signed-off-by: dmitry pervushin Signed-off-by: David Brownell Cc: Kumar Gala Cc: Timur Tabi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi_stmp.c | 679 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 686 insertions(+) create mode 100644 drivers/spi/spi_stmp.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index cd3acfe9d13..4b6f7cba3b3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -222,6 +222,12 @@ config SPI_SH_SCI help SPI driver for SuperH SCI blocks. +config SPI_STMP3XXX + tristate "Freescale STMP37xx/378x SPI/SSP controller" + depends on ARCH_STMP3XXX && SPI_MASTER + help + SPI driver for Freescale STMP37xx/378x SoC SSP interface + config SPI_TXX9 tristate "Toshiba TXx9 SPI controller" depends on GENERIC_GPIO && CPU_TX49XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index fbfc5f22f4f..6d7a3f82c54 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o +obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/spi_stmp.c b/drivers/spi/spi_stmp.c new file mode 100644 index 00000000000..d871dc23909 --- /dev/null +++ b/drivers/spi/spi_stmp.c @@ -0,0 +1,679 @@ +/* + * Freescale STMP378X SPI master driver + * + * Author: dmitry pervushin + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* 0 means DMA mode(recommended, default), !0 - PIO mode */ +static int pio; +static int clock; + +/* default timeout for busy waits is 2 seconds */ +#define STMP_SPI_TIMEOUT (2 * HZ) + +struct stmp_spi { + int id; + + void * __iomem regs; /* vaddr of the control registers */ + + int irq, err_irq; + u32 dma; + struct stmp3xxx_dma_descriptor d; + + u32 speed_khz; + u32 saved_timings; + u32 divider; + + struct clk *clk; + struct device *master_dev; + + struct work_struct work; + struct workqueue_struct *workqueue; + + /* lock protects queue access */ + spinlock_t lock; + struct list_head queue; + + struct completion done; +}; + +#define busy_wait(cond) \ + ({ \ + unsigned long end_jiffies = jiffies + STMP_SPI_TIMEOUT; \ + bool succeeded = false; \ + do { \ + if (cond) { \ + succeeded = true; \ + break; \ + } \ + cpu_relax(); \ + } while (time_before(end_jiffies, jiffies)); \ + succeeded; \ + }) + +/** + * stmp_spi_init_hw + * Initialize the SSP port + */ +static int stmp_spi_init_hw(struct stmp_spi *ss) +{ + int err = 0; + void *pins = ss->master_dev->platform_data; + + err = stmp3xxx_request_pin_group(pins, dev_name(ss->master_dev)); + if (err) + goto out; + + ss->clk = clk_get(NULL, "ssp"); + if (IS_ERR(ss->clk)) { + err = PTR_ERR(ss->clk); + goto out_free_pins; + } + clk_enable(ss->clk); + + stmp3xxx_reset_block(ss->regs, false); + stmp3xxx_dma_reset_channel(ss->dma); + + return 0; + +out_free_pins: + stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); +out: + return err; +} + +static void stmp_spi_release_hw(struct stmp_spi *ss) +{ + void *pins = ss->master_dev->platform_data; + + if (ss->clk && !IS_ERR(ss->clk)) { + clk_disable(ss->clk); + clk_put(ss->clk); + } + stmp3xxx_release_pin_group(pins, dev_name(ss->master_dev)); +} + +static int stmp_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + u8 bits_per_word; + u32 hz; + struct stmp_spi *ss = spi_master_get_devdata(spi->master); + u16 rate; + + bits_per_word = spi->bits_per_word; + if (t && t->bits_per_word) + bits_per_word = t->bits_per_word; + + /* + * Calculate speed: + * - by default, use maximum speed from ssp clk + * - if device overrides it, use it + * - if transfer specifies other speed, use transfer's one + */ + hz = 1000 * ss->speed_khz / ss->divider; + if (spi->max_speed_hz) + hz = min(hz, spi->max_speed_hz); + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); + + if (hz == 0) { + dev_err(&spi->dev, "Cannot continue with zero clock\n"); + return -EINVAL; + } + + if (bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, bits_per_word); + return -EINVAL; + } + + dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %uHz/%d = %uHz\n", + hz, ss->speed_khz, ss->divider, + ss->speed_khz * 1000 / ss->divider); + + if (ss->speed_khz * 1000 / ss->divider < hz) { + dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", + __func__, hz); + return -EINVAL; + } + + rate = 1000 * ss->speed_khz/ss->divider/hz; + + writel(BF(ss->divider, SSP_TIMING_CLOCK_DIVIDE) | + BF(rate - 1, SSP_TIMING_CLOCK_RATE), + HW_SSP_TIMING + ss->regs); + + writel(BF(1 /* mode SPI */, SSP_CTRL1_SSP_MODE) | + BF(4 /* 8 bits */, SSP_CTRL1_WORD_LENGTH) | + ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0) | + (pio ? 0 : BM_SSP_CTRL1_DMA_ENABLE), + ss->regs + HW_SSP_CTRL1); + + return 0; +} + +static int stmp_spi_setup(struct spi_device *spi) +{ + /* spi_setup() does basic checks, + * stmp_spi_setup_transfer() does more later + */ + if (spi->bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, spi->bits_per_word); + return -EINVAL; + } + return 0; +} + +static inline u32 stmp_spi_cs(unsigned cs) +{ + return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) | + ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0); +} + +static int stmp_spi_txrx_dma(struct stmp_spi *ss, int cs, + unsigned char *buf, dma_addr_t dma_buf, int len, + int first, int last, bool write) +{ + u32 c0 = 0; + dma_addr_t spi_buf_dma = dma_buf; + int status = 0; + enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + c0 |= (first ? BM_SSP_CTRL0_LOCK_CS : 0); + c0 |= (last ? BM_SSP_CTRL0_IGNORE_CRC : 0); + c0 |= (write ? 0 : BM_SSP_CTRL0_READ); + c0 |= BM_SSP_CTRL0_DATA_XFER; + + c0 |= stmp_spi_cs(cs); + + c0 |= BF(len, SSP_CTRL0_XFER_COUNT); + + if (!dma_buf) + spi_buf_dma = dma_map_single(ss->master_dev, buf, len, dir); + + ss->d.command->cmd = + BF(len, APBH_CHn_CMD_XFER_COUNT) | + BF(1, APBH_CHn_CMD_CMDWORDS) | + BM_APBH_CHn_CMD_WAIT4ENDCMD | + BM_APBH_CHn_CMD_IRQONCMPLT | + BF(write ? BV_APBH_CHn_CMD_COMMAND__DMA_READ : + BV_APBH_CHn_CMD_COMMAND__DMA_WRITE, + APBH_CHn_CMD_COMMAND); + ss->d.command->pio_words[0] = c0; + ss->d.command->buf_ptr = spi_buf_dma; + + stmp3xxx_dma_reset_channel(ss->dma); + stmp3xxx_dma_clear_interrupt(ss->dma); + stmp3xxx_dma_enable_interrupt(ss->dma); + init_completion(&ss->done); + stmp3xxx_dma_go(ss->dma, &ss->d, 1); + wait_for_completion(&ss->done); + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & BM_SSP_CTRL0_RUN)) + status = ETIMEDOUT; + + if (!dma_buf) + dma_unmap_single(ss->master_dev, spi_buf_dma, len, dir); + + return status; +} + +static inline void stmp_spi_enable(struct stmp_spi *ss) +{ + stmp3xxx_setl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); + stmp3xxx_clearl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); +} + +static inline void stmp_spi_disable(struct stmp_spi *ss) +{ + stmp3xxx_clearl(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0); + stmp3xxx_setl(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0); +} + +static int stmp_spi_txrx_pio(struct stmp_spi *ss, int cs, + unsigned char *buf, int len, + bool first, bool last, bool write) +{ + if (first) + stmp_spi_enable(ss); + + stmp3xxx_setl(stmp_spi_cs(cs), ss->regs + HW_SSP_CTRL0); + + while (len--) { + if (last && len <= 0) + stmp_spi_disable(ss); + + stmp3xxx_clearl(BM_SSP_CTRL0_XFER_COUNT, + ss->regs + HW_SSP_CTRL0); + stmp3xxx_setl(1, ss->regs + HW_SSP_CTRL0); + + if (write) + stmp3xxx_clearl(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0); + else + stmp3xxx_setl(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0); + + /* Run! */ + stmp3xxx_setl(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0); + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & + BM_SSP_CTRL0_RUN)) + break; + + if (write) + writel(*buf, ss->regs + HW_SSP_DATA); + + /* Set TRANSFER */ + stmp3xxx_setl(BM_SSP_CTRL0_DATA_XFER, ss->regs + HW_SSP_CTRL0); + + if (!write) { + if (busy_wait((readl(ss->regs + HW_SSP_STATUS) & + BM_SSP_STATUS_FIFO_EMPTY))) + break; + *buf = readl(ss->regs + HW_SSP_DATA) & 0xFF; + } + + if (!busy_wait(readl(ss->regs + HW_SSP_CTRL0) & + BM_SSP_CTRL0_RUN)) + break; + + /* advance to the next byte */ + buf++; + } + + return len < 0 ? 0 : -ETIMEDOUT; +} + +static int stmp_spi_handle_message(struct stmp_spi *ss, struct spi_message *m) +{ + bool first, last; + struct spi_transfer *t, *tmp_t; + int status = 0; + int cs; + + cs = m->spi->chip_select; + + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + + first = (&t->transfer_list == m->transfers.next); + last = (&t->transfer_list == m->transfers.prev); + + if (first || t->speed_hz || t->bits_per_word) + stmp_spi_setup_transfer(m->spi, t); + + /* reject "not last" transfers which request to change cs */ + if (t->cs_change && !last) { + dev_err(&m->spi->dev, + "Message with t->cs_change has been skipped\n"); + continue; + } + + if (t->tx_buf) { + status = pio ? + stmp_spi_txrx_pio(ss, cs, (void *)t->tx_buf, + t->len, first, last, true) : + stmp_spi_txrx_dma(ss, cs, (void *)t->tx_buf, + t->tx_dma, t->len, first, last, true); +#ifdef DEBUG + if (t->len < 0x10) + print_hex_dump_bytes("Tx ", + DUMP_PREFIX_OFFSET, + t->tx_buf, t->len); + else + pr_debug("Tx: %d bytes\n", t->len); +#endif + } + if (t->rx_buf) { + status = pio ? + stmp_spi_txrx_pio(ss, cs, t->rx_buf, + t->len, first, last, false) : + stmp_spi_txrx_dma(ss, cs, t->rx_buf, + t->rx_dma, t->len, first, last, false); +#ifdef DEBUG + if (t->len < 0x10) + print_hex_dump_bytes("Rx ", + DUMP_PREFIX_OFFSET, + t->rx_buf, t->len); + else + pr_debug("Rx: %d bytes\n", t->len); +#endif + } + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (status) + break; + + } + return status; +} + +/** + * stmp_spi_handle - handle messages from the queue + */ +static void stmp_spi_handle(struct work_struct *w) +{ + struct stmp_spi *ss = container_of(w, struct stmp_spi, work); + unsigned long flags; + struct spi_message *m; + + spin_lock_irqsave(&ss->lock, flags); + while (!list_empty(&ss->queue)) { + m = list_entry(ss->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&ss->lock, flags); + + m->status = stmp_spi_handle_message(ss, m); + m->complete(m->context); + + spin_lock_irqsave(&ss->lock, flags); + } + spin_unlock_irqrestore(&ss->lock, flags); + + return; +} + +/** + * stmp_spi_transfer - perform message transfer. + * Called indirectly from spi_async, queues all the messages to + * spi_handle_message. + * @spi: spi device + * @m: message to be queued + */ +static int stmp_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct stmp_spi *ss = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->status = -EINPROGRESS; + spin_lock_irqsave(&ss->lock, flags); + list_add_tail(&m->queue, &ss->queue); + queue_work(ss->workqueue, &ss->work); + spin_unlock_irqrestore(&ss->lock, flags); + return 0; +} + +static irqreturn_t stmp_spi_irq(int irq, void *dev_id) +{ + struct stmp_spi *ss = dev_id; + + stmp3xxx_dma_clear_interrupt(ss->dma); + complete(&ss->done); + return IRQ_HANDLED; +} + +static irqreturn_t stmp_spi_irq_err(int irq, void *dev_id) +{ + struct stmp_spi *ss = dev_id; + u32 c1, st; + + c1 = readl(ss->regs + HW_SSP_CTRL1); + st = readl(ss->regs + HW_SSP_STATUS); + dev_err(ss->master_dev, "%s: status = 0x%08X, c1 = 0x%08X\n", + __func__, st, c1); + stmp3xxx_clearl(c1 & 0xCCCC0000, ss->regs + HW_SSP_CTRL1); + + return IRQ_HANDLED; +} + +static int __devinit stmp_spi_probe(struct platform_device *dev) +{ + int err = 0; + struct spi_master *master; + struct stmp_spi *ss; + struct resource *r; + + master = spi_alloc_master(&dev->dev, sizeof(struct stmp_spi)); + if (master == NULL) { + err = -ENOMEM; + goto out0; + } + master->flags = SPI_MASTER_HALF_DUPLEX; + + ss = spi_master_get_devdata(master); + platform_set_drvdata(dev, master); + + /* Get resources(memory, IRQ) associated with the device */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + err = -ENODEV; + goto out_put_master; + } + ss->regs = ioremap(r->start, resource_size(r)); + if (!ss->regs) { + err = -EINVAL; + goto out_put_master; + } + + ss->master_dev = &dev->dev; + ss->id = dev->id; + + INIT_WORK(&ss->work, stmp_spi_handle); + INIT_LIST_HEAD(&ss->queue); + spin_lock_init(&ss->lock); + + ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev)); + if (!ss->workqueue) { + err = -ENXIO; + goto out_put_master; + } + master->transfer = stmp_spi_transfer; + master->setup = stmp_spi_setup; + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA; + + ss->irq = platform_get_irq(dev, 0); + if (ss->irq < 0) { + err = ss->irq; + goto out_put_master; + } + ss->err_irq = platform_get_irq(dev, 1); + if (ss->err_irq < 0) { + err = ss->err_irq; + goto out_put_master; + } + + r = platform_get_resource(dev, IORESOURCE_DMA, 0); + if (r == NULL) { + err = -ENODEV; + goto out_put_master; + } + + ss->dma = r->start; + err = stmp3xxx_dma_request(ss->dma, &dev->dev, dev_name(&dev->dev)); + if (err) + goto out_put_master; + + err = stmp3xxx_dma_allocate_command(ss->dma, &ss->d); + if (err) + goto out_free_dma; + + master->bus_num = dev->id; + master->num_chipselect = 1; + + /* SPI controller initializations */ + err = stmp_spi_init_hw(ss); + if (err) { + dev_dbg(&dev->dev, "cannot initialize hardware\n"); + goto out_free_dma_desc; + } + + if (clock) { + dev_info(&dev->dev, "clock rate forced to %d\n", clock); + clk_set_rate(ss->clk, clock); + } + ss->speed_khz = clk_get_rate(ss->clk); + ss->divider = 2; + dev_info(&dev->dev, "max possible speed %d = %ld/%d kHz\n", + ss->speed_khz, clk_get_rate(ss->clk), ss->divider); + + /* Register for SPI interrupt */ + err = request_irq(ss->irq, stmp_spi_irq, 0, + dev_name(&dev->dev), ss); + if (err) { + dev_dbg(&dev->dev, "request_irq failed, %d\n", err); + goto out_release_hw; + } + + /* ..and shared interrupt for all SSP controllers */ + err = request_irq(ss->err_irq, stmp_spi_irq_err, IRQF_SHARED, + dev_name(&dev->dev), ss); + if (err) { + dev_dbg(&dev->dev, "request_irq(error) failed, %d\n", err); + goto out_free_irq; + } + + err = spi_register_master(master); + if (err) { + dev_dbg(&dev->dev, "cannot register spi master, %d\n", err); + goto out_free_irq_2; + } + dev_info(&dev->dev, "at (mapped) 0x%08X, irq=%d, bus %d, %s mode\n", + (u32)ss->regs, ss->irq, master->bus_num, + pio ? "PIO" : "DMA"); + return 0; + +out_free_irq_2: + free_irq(ss->err_irq, ss); +out_free_irq: + free_irq(ss->irq, ss); +out_free_dma_desc: + stmp3xxx_dma_free_command(ss->dma, &ss->d); +out_free_dma: + stmp3xxx_dma_release(ss->dma); +out_release_hw: + stmp_spi_release_hw(ss); +out_put_master: + if (ss->workqueue) + destroy_workqueue(ss->workqueue); + if (ss->regs) + iounmap(ss->regs); + platform_set_drvdata(dev, NULL); + spi_master_put(master); +out0: + return err; +} + +static int __devexit stmp_spi_remove(struct platform_device *dev) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(dev); + if (master == NULL) + goto out0; + ss = spi_master_get_devdata(master); + + spi_unregister_master(master); + + free_irq(ss->err_irq, ss); + free_irq(ss->irq, ss); + stmp3xxx_dma_free_command(ss->dma, &ss->d); + stmp3xxx_dma_release(ss->dma); + stmp_spi_release_hw(ss); + destroy_workqueue(ss->workqueue); + iounmap(ss->regs); + spi_master_put(master); + platform_set_drvdata(dev, NULL); +out0: + return 0; +} + +#ifdef CONFIG_PM +static int stmp_spi_suspend(struct platform_device *pdev, pm_message_t pmsg) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(pdev); + ss = spi_master_get_devdata(master); + + ss->saved_timings = readl(HW_SSP_TIMING + ss->regs); + clk_disable(ss->clk); + + return 0; +} + +static int stmp_spi_resume(struct platform_device *pdev) +{ + struct stmp_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(pdev); + ss = spi_master_get_devdata(master); + + clk_enable(ss->clk); + stmp3xxx_reset_block(ss->regs, false); + writel(ss->saved_timings, ss->regs + HW_SSP_TIMING); + + return 0; +} + +#else +#define stmp_spi_suspend NULL +#define stmp_spi_resume NULL +#endif + +static struct platform_driver stmp_spi_driver = { + .probe = stmp_spi_probe, + .remove = __devexit_p(stmp_spi_remove), + .driver = { + .name = "stmp3xxx_ssp", + .owner = THIS_MODULE, + }, + .suspend = stmp_spi_suspend, + .resume = stmp_spi_resume, +}; + +static int __init stmp_spi_init(void) +{ + return platform_driver_register(&stmp_spi_driver); +} + +static void __exit stmp_spi_exit(void) +{ + platform_driver_unregister(&stmp_spi_driver); +} + +module_init(stmp_spi_init); +module_exit(stmp_spi_exit); +module_param(pio, int, S_IRUGO); +module_param(clock, int, S_IRUGO); +MODULE_AUTHOR("dmitry pervushin "); +MODULE_DESCRIPTION("STMP3xxx SPI/SSP driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From a41ae1ad907655b2efbb9b1a97736ab1451e1649 Mon Sep 17 00:00:00 2001 From: Hemanth V Date: Tue, 22 Sep 2009 16:46:16 -0700 Subject: spi: McSPI off-mode support Add context save/restore feature to McSPI driver. Signed-off-by: Hemanth V Reviewed-by: Aaro Koskinen Signed-off-by: Kevin Hilman Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/omap2_mcspi.c | 134 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 34 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index ae785cc627b..e96ad270956 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -41,6 +41,9 @@ #define OMAP2_MCSPI_MAX_FREQ 48000000 +/* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ +#define OMAP2_MCSPI_MAX_CTRL 4 + #define OMAP2_MCSPI_REVISION 0x00 #define OMAP2_MCSPI_SYSCONFIG 0x10 #define OMAP2_MCSPI_SYSSTATUS 0x14 @@ -131,8 +134,21 @@ struct omap2_mcspi_cs { void __iomem *base; unsigned long phys; int word_len; + /* Context save and restore shadow register */ + u32 chconf0; +}; + +/* used for context save and restore, structure members to be updated whenever + * corresponding registers are modified. + */ +struct omap2_mcspi_regs { + u32 sysconfig; + u32 modulctrl; + u32 wakeupenable; }; +static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; + static struct workqueue_struct *omap2_mcspi_wq; #define MOD_REG_BIT(val, mask, set) do { \ @@ -172,12 +188,27 @@ static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) return __raw_readl(cs->base + idx); } +static inline u32 mcspi_cached_chconf0(const struct spi_device *spi) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + return cs->chconf0; +} + +static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + + cs->chconf0 = val; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val); +} + static void omap2_mcspi_set_dma_req(const struct spi_device *spi, int is_read, int enable) { u32 l, rw; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); if (is_read) /* 1 is read, 0 write */ rw = OMAP2_MCSPI_CHCONF_DMAR; @@ -185,7 +216,7 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, rw = OMAP2_MCSPI_CHCONF_DMAW; MOD_REG_BIT(l, rw, enable); - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); } static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) @@ -200,9 +231,9 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) { u32 l; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); } static void omap2_mcspi_set_master_mode(struct spi_master *master) @@ -217,6 +248,41 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); + + omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l; +} + +static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) +{ + struct spi_master *spi_cntrl; + spi_cntrl = mcspi->master; + + /* McSPI: context restore */ + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); + + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_SYSCONFIG, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].sysconfig); + + mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, + omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); +} +static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) +{ + clk_disable(mcspi->ick); + clk_disable(mcspi->fck); +} + +static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) +{ + if (clk_enable(mcspi->ick)) + return -ENODEV; + if (clk_enable(mcspi->fck)) + return -ENODEV; + + omap2_mcspi_restore_ctx(mcspi); + + return 0; } static unsigned @@ -357,7 +423,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) c = count; word_len = cs->word_len; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; /* We store the pre-calculated register addresses on stack to speed @@ -397,8 +463,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %02x\n", @@ -436,8 +501,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", @@ -475,8 +539,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) * more word i/o: switch to rx+tx */ if (c == 0 && tx == NULL) - mcspi_write_cs_reg(spi, - OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", @@ -505,10 +568,12 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, { struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; + struct spi_master *spi_cntrl; u32 l = 0, div = 0; u8 word_len = spi->bits_per_word; mcspi = spi_master_get_devdata(spi->master); + spi_cntrl = mcspi->master; if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; @@ -522,7 +587,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, } else div = 15; - l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l = mcspi_cached_chconf0(spi); /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. @@ -554,7 +619,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, else l &= ~OMAP2_MCSPI_CHCONF_PHA; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", OMAP2_MCSPI_MAX_FREQ / (1 << div), @@ -647,6 +712,7 @@ static int omap2_mcspi_setup(struct spi_device *spi) return -ENOMEM; cs->base = mcspi->base + spi->chip_select * 0x14; cs->phys = mcspi->phys + spi->chip_select * 0x14; + cs->chconf0 = 0; spi->controller_state = cs; } @@ -657,11 +723,11 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + return -ENODEV; + ret = omap2_mcspi_setup_transfer(spi, NULL); - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); return ret; } @@ -693,8 +759,8 @@ static void omap2_mcspi_work(struct work_struct *work) mcspi = container_of(work, struct omap2_mcspi, work); spin_lock_irq(&mcspi->lock); - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + goto out; /* We only enable one channel at a time -- the one whose message is * at the head of the queue -- although this controller would gladly @@ -741,13 +807,13 @@ static void omap2_mcspi_work(struct work_struct *work) cs_active = 1; } - chconf = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + chconf = mcspi_cached_chconf0(spi); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; 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; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, chconf); + mcspi_write_chconf0(spi, chconf); if (t->len) { unsigned count; @@ -796,9 +862,9 @@ static void omap2_mcspi_work(struct work_struct *work) spin_lock_irq(&mcspi->lock); } - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); +out: spin_unlock_irq(&mcspi->lock); } @@ -885,8 +951,8 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) struct spi_master *master = mcspi->master; u32 tmp; - clk_enable(mcspi->ick); - clk_enable(mcspi->fck); + if (omap2_mcspi_enable_clocks(mcspi)) + return -1; mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, OMAP2_MCSPI_SYSCONFIG_SOFTRESET); @@ -894,18 +960,18 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); - mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, - OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | - OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | - OMAP2_MCSPI_SYSCONFIG_SMARTIDLE); + tmp = OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP2_MCSPI_SYSCONFIG_SMARTIDLE; + mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, tmp); + omap2_mcspi_ctx[master->bus_num - 1].sysconfig = tmp; - mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, - OMAP2_MCSPI_WAKEUPENABLE_WKEN); + tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; + mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); + omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp; omap2_mcspi_set_master_mode(master); - - clk_disable(mcspi->fck); - clk_disable(mcspi->ick); + omap2_mcspi_disable_clocks(mcspi); return 0; } -- cgit v1.2.3 From 89c05372d08f3982eeb94d1ea22a60a5eaa8cd6d Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 22 Sep 2009 16:46:17 -0700 Subject: spi: McSPI saves CHCONFx too Previous restore was lazy and only restored CHxCONF when it was needed by a specific chip select. This could cause occasional errors on an SPI bus where multiple chip selects are in use. Signed-off-by: Tero Kristo Signed-off-by: Kevin Hilman Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/omap2_mcspi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index e96ad270956..85b7aea7337 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -134,6 +134,7 @@ struct omap2_mcspi_cs { void __iomem *base; unsigned long phys; int word_len; + struct list_head node; /* Context save and restore shadow register */ u32 chconf0; }; @@ -145,6 +146,7 @@ struct omap2_mcspi_regs { u32 sysconfig; u32 modulctrl; u32 wakeupenable; + struct list_head cs; }; static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; @@ -255,6 +257,7 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) { struct spi_master *spi_cntrl; + struct omap2_mcspi_cs *cs; spi_cntrl = mcspi->master; /* McSPI: context restore */ @@ -266,6 +269,10 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); + + list_for_each_entry(cs, &omap2_mcspi_ctx[spi_cntrl->bus_num - 1].cs, + node) + __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) { @@ -714,6 +721,9 @@ static int omap2_mcspi_setup(struct spi_device *spi) cs->phys = mcspi->phys + spi->chip_select * 0x14; cs->chconf0 = 0; spi->controller_state = cs; + /* Link this to context save list */ + list_add_tail(&cs->node, + &omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs); } if (mcspi_dma->dma_rx_channel == -1 @@ -736,10 +746,15 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; + struct omap2_mcspi_cs *cs; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + /* Unlink controller state from context save list */ + cs = spi->controller_state; + list_del(&cs->node); + kfree(spi->controller_state); if (mcspi_dma->dma_rx_channel != -1) { @@ -1104,6 +1119,7 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) spin_lock_init(&mcspi->lock); INIT_LIST_HEAD(&mcspi->msg_queue); + INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs); mcspi->ick = clk_get(&pdev->dev, "ick"); if (IS_ERR(mcspi->ick)) { -- cgit v1.2.3 From 7869c0b9ed44404bbc675ef76f8ccb3be5496f39 Mon Sep 17 00:00:00 2001 From: Syed Rafiuddin Date: Tue, 22 Sep 2009 16:46:18 -0700 Subject: spi: McSPI support for OMAP4 tAdd adds McSPI support for OMAP4430 SDP platform. All the base addresses are changed between OMAP1/2/3 and OMAP4. The fields of the resource structures are filled at runtime to have McSPI support on OMAP4. Signed-off-by: Syed Rafiuddin Acked-by: Kevin Hilman Acked-by: Tony Lindgren Acked-by: Santosh Shilimkar Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/omap2_mcspi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index 85b7aea7337..ba1a872b221 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c @@ -1014,7 +1014,8 @@ static u8 __initdata spi2_txdma_id[] = { OMAP24XX_DMA_SPI2_TX1, }; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) \ + || defined(CONFIG_ARCH_OMAP4) static u8 __initdata spi3_rxdma_id[] = { OMAP24XX_DMA_SPI3_RX0, OMAP24XX_DMA_SPI3_RX1, @@ -1026,7 +1027,7 @@ static u8 __initdata spi3_txdma_id[] = { }; #endif -#ifdef CONFIG_ARCH_OMAP3 +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) static u8 __initdata spi4_rxdma_id[] = { OMAP34XX_DMA_SPI4_RX0, }; @@ -1056,14 +1057,15 @@ static int __init omap2_mcspi_probe(struct platform_device *pdev) txdma_id = spi2_txdma_id; num_chipselect = 2; break; -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) \ + || defined(CONFIG_ARCH_OMAP4) case 3: rxdma_id = spi3_rxdma_id; txdma_id = spi3_txdma_id; num_chipselect = 2; break; #endif -#ifdef CONFIG_ARCH_OMAP3 +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4) case 4: rxdma_id = spi4_rxdma_id; txdma_id = spi4_txdma_id; -- cgit v1.2.3 From 568d0697f42771425ae9f1e9a3db769fef7e10b6 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 22 Sep 2009 16:46:18 -0700 Subject: spi: handle TX-only/RX-only Support two new half-duplex SPI implementation restrictions, for links that talk to TX-only or RX-only devices. (Existing half-duplex flavors support both transfer directions, just not at the same time.) Move spi_async() into the spi.c core, and stop inlining it. Then make that function perform error checks and reject messages that demand more than the underlying controller can support. Based on a patch from Marek Szyprowski which did this only for the bitbanged GPIO driver. Cc: Marek Szyprowski Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/spi.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 49e84860c8d..b76f2468a84 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -663,6 +663,65 @@ int spi_setup(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_setup); +/** + * spi_async - asynchronous SPI transfer + * @spi: device with which data will be exchanged + * @message: describes the data transfers, including completion callback + * Context: any (irqs may be blocked, etc) + * + * This call may be used in_irq and other contexts which can't sleep, + * as well as from task contexts which can sleep. + * + * The completion callback is invoked in a context which can't sleep. + * Before that invocation, the value of message->status is undefined. + * When the callback is issued, message->status holds either zero (to + * indicate complete success) or a negative error code. After that + * callback returns, the driver which issued the transfer request may + * deallocate the associated memory; it's no longer in use by any SPI + * core or controller driver code. + * + * Note that although all messages to a spi_device are handled in + * FIFO order, messages may go to different devices in other orders. + * Some device might be higher priority, or have various "hard" access + * time requirements, for example. + * + * On detection of any fault during the transfer, processing of + * the entire message is aborted, and the device is deselected. + * Until returning from the associated message completion callback, + * no other spi_message queued to that device will be processed. + * (This rule applies equally to all the synchronous transfer calls, + * which are wrappers around this core asynchronous primitive.) + */ +int spi_async(struct spi_device *spi, struct spi_message *message) +{ + struct spi_master *master = spi->master; + + /* Half-duplex links include original MicroWire, and ones with + * only one data pin like SPI_3WIRE (switches direction) or where + * either MOSI or MISO is missing. They can also be caused by + * software limitations. + */ + if ((master->flags & SPI_MASTER_HALF_DUPLEX) + || (spi->mode & SPI_3WIRE)) { + struct spi_transfer *xfer; + unsigned flags = master->flags; + + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (xfer->rx_buf && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) + return -EINVAL; + if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) + return -EINVAL; + } + } + + message->spi = spi; + message->status = -EINPROGRESS; + return master->transfer(spi, message); +} +EXPORT_SYMBOL_GPL(spi_async); + /*-------------------------------------------------------------------------*/ -- cgit v1.2.3