Commit 69c202af authored by Andrea Paterniani's avatar Andrea Paterniani Committed by Linus Torvalds

[PATCH] SPI: Freescale iMX SPI controller driver (BIS+)

Add the SPI controller driver for Freescale i.MX(S/L/1).
Main features summary:

 > Per chip setup via board specific code and/or protocol driver.
 > Per transfer setup.
 > PIO transfers.
 > DMA transfers.
 > Managing of NULL tx / rx buffer for rd only / wr only transfers.

This patch replace patch-2.6.20-rc4-spi_imx with the following changes:
 > Few cosmetic changes.
 > Function map_dma_buffers now return 0 for success and -1 for failure.
 > Solved a bug inside spi_imx_probe function (wrong error path).
 > Solved a bug inside setup function (bad undo setup for max_speed_hz).
 > For read-only transfers, always write zero bytes.

This is almost the same as the 'BIS' version sent by Andrea, except for
updating the 'DUMMY' byte so that read-only transfers shift out zeroes.
That part of the API changed recently, since some half duplex peripheral
chips require that semantic.
Signed-off-by: default avatarAndrea Paterniani <a.paterniani@swapp-eng.it>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent fdb3c18d
...@@ -75,6 +75,13 @@ config SPI_BUTTERFLY ...@@ -75,6 +75,13 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board. inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware. This same cable can be used to flash new firmware.
config SPI_IMX
tristate "Freescale iMX SPI controller"
depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
help
This enables using the Freescale iMX SPI controller in master
mode.
config SPI_MPC83xx config SPI_MPC83xx
tristate "Freescale MPC83xx SPI controller" tristate "Freescale MPC83xx SPI controller"
depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL
...@@ -94,6 +101,7 @@ config SPI_OMAP_UWIRE ...@@ -94,6 +101,7 @@ config SPI_OMAP_UWIRE
help help
This hooks up to the MicroWire controller on OMAP1 chips. This hooks up to the MicroWire controller on OMAP1 chips.
config SPI_PXA2XX config SPI_PXA2XX
tristate "PXA2xx SSP SPI master" tristate "PXA2xx SSP SPI master"
depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL
......
...@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_MASTER) += spi.o
# SPI master controller drivers (bus) # SPI master controller drivers (bus)
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
......
/*
* drivers/spi/spi_imx.c
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>
*
* 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 <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/spi/spi.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/arch/hardware.h>
#include <asm/arch/imx-dma.h>
#include <asm/arch/spi_imx.h>
/*-------------------------------------------------------------------------*/
/* 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 (0xF << 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 lenght 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)
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/* 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;
};
/* 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 int flush(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
void __iomem *regs = drv_data->regs;
volatile u32 d;
dev_dbg(&drv_data->pdev->dev, "flush\n");
do {
while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR)
d = readl(regs + SPI_RXDATA);
} while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--);
return limit;
}
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(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;
/* 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(drv_data->rx_dma))
return -1;
drv_data->rx_dma_needs_unmap = 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(drv_data->tx_dma)) {
if (drv_data->rx_dma) {
dma_unmap_single(dev,
drv_data->rx_dma,
drv_data->len,
DMA_FROM_DEVICE);
drv_data->rx_dma_needs_unmap = 0;
}
return -1;
}
drv_data->tx_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);
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);
if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_err_handler - flush failed\n");
unmap_dma_buffers(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(readl(drv_data->regs + SPI_INT_STATUS) | 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;
unsigned long limit;
status = readl(regs + SPI_INT_STATUS);
if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) {
writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
imx_dma_disable(drv_data->rx_channel);
unmap_dma_buffers(drv_data);
if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - flush failed\n");
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 */
limit = loops_per_jiffy << 1;
while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) &&
limit--);
if (limit == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - end of tx failed\n");
else
dev_dbg(&drv_data->pdev->dev,
"dma_transfer - end of tx\n");
imx_dma_disable(drv_data->rx_channel);
unmap_dma_buffers(drv_data);
/* 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);
if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - flush failed\n");
}
/* End of transfer, update total byte transfered */
msg->actual_length += drv_data->len;
/* Release chip select if requested, transfer delays are
handled in pump_transfers() */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);
/* 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);
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)) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
regs + SPI_INT_STATUS);
dev_dbg(&drv_data->pdev->dev,
"interrupt_wronly_transfer - end of tx\n");
if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"interrupt_wronly_transfer - "
"flush failed\n");
/* End of transfer, update total byte transfered */
msg->actual_length += drv_data->len;
/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);
/* Move to next transfer */
msg->state = next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
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;
irqreturn_t handled = IRQ_NONE;
unsigned long limit;
status = readl(regs + SPI_INT_STATUS);
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) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
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));
if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"interrupt_transfer - flush failed\n");
msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
return IRQ_HANDLED;
}
/* Pump data */
read(drv_data);
if (write(drv_data)) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
regs + SPI_INT_STATUS);
dev_dbg(&drv_data->pdev->dev,
"interrupt_transfer - end of tx\n");
/* Read trailing bytes */
limit = loops_per_jiffy << 1;
while ((read(drv_data) == 0) && limit--);
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");
/* End of transfer, update total byte transfered */
msg->actual_length += drv_data->len;
/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);
/* Move to next transfer */
msg->state = next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
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(u32 data_rate)
{
return imx_get_perclk2() / (4 << ((data_rate) >> 13));
}
static u32 spi_data_rate(u32 speed_hz)
{
u32 div;
u32 quantized_hz = imx_get_perclk2() >> 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(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(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 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");
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");
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_LSB_FIRST) {
status = -EINVAL;
if (first_setup) {
dev_err(&spi->dev,
"setup - "
"HW doesn't support LSB first transfer\n");
goto err_first_setup;
} else {
dev_err(&spi->dev,
"setup - "
"HW doesn't support LSB first transfer, "
"default to MSB first\n");
spi->mode &= ~SPI_LSB_FIRST;
}
}
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 == 0) {
tmp = 8;
spi->bits_per_word = 8;
} else 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(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(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(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(SPI_CONTROL_DATARATE_MIN),
spi->max_speed_hz);
err_first_setup:
kfree(chip);
return status;
}
static void cleanup(const struct spi_device *spi)
{
struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi);
kfree(chip);
}
static int 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(
drv_data->master->cdev.dev->bus_id);
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 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 = NULL;
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;
master->bus_num = pdev->id;
master->num_chipselect = platform_info->num_chipselect;
master->cleanup = cleanup;
master->setup = setup;
master->transfer = transfer;
drv_data->dummy_dma_buf = SPI_DUMMY_u32;
/* 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->bus_id, 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 */
status = imx_dma_request_by_prio(&drv_data->rx_channel,
"spi_imx_rx", DMA_PRIO_HIGH);
if (status < 0) {
dev_err(dev,
"probe - problem (%d) requesting rx channel\n",
status);
goto err_no_rxdma;
} else
imx_dma_setup_handlers(drv_data->rx_channel, NULL,
dma_err_handler, drv_data);
/* Get tx DMA channel */
status = imx_dma_request_by_prio(&drv_data->tx_channel,
"spi_imx_tx", DMA_PRIO_MEDIUM);
if (status < 0) {
dev_err(dev,
"probe - problem (%d) requesting tx channel\n",
status);
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:
spi_master_put(master);
err_no_pdata:
err_no_mem:
return status;
}
static int __devexit 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);
/* 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 suspend_devices(struct device *dev, void *pm_message)
{
pm_message_t *state = pm_message;
if (dev->power.power_state.event != state->event) {
dev_warn(dev, "pm state does not match request\n");
return -1;
}
return 0;
}
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 */
static struct platform_driver driver = {
.driver = {
.name = "imx-spi",
.bus = &platform_bus_type,
.owner = THIS_MODULE,
},
.probe = spi_imx_probe,
.remove = __devexit_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_register(&driver);
}
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, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("iMX SPI Contoller Driver");
MODULE_LICENSE("GPL");
/*
* include/asm-arm/arch-imx/spi_imx.h
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>
*
* Initial version inspired by:
* linux-2.6.17-rc3-mm1/include/asm-arm/arch-pxa/pxa2xx_spi.h
*
* 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SPI_IMX_H_
#define SPI_IMX_H_
/*-------------------------------------------------------------------------*/
/**
* struct spi_imx_master - device.platform_data for SPI controller devices.
* @num_chipselect: chipselects are used to distinguish individual
* SPI slaves, and are numbered from zero to num_chipselects - 1.
* each slave has a chipselect signal, but it's common that not
* every chipselect is connected to a slave.
* @enable_dma: if true enables DMA driven transfers.
*/
struct spi_imx_master {
u8 num_chipselect;
u8 enable_dma:1;
};
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
* struct spi_imx_chip - spi_board_info.controller_data for SPI
* slave devices, copied to spi_device.controller_data.
* @enable_loopback : used for test purpouse to internally connect RX and TX
* sections.
* @enable_dma : enables dma transfer (provided that controller driver has
* dma enabled too).
* @ins_ss_pulse : enable /SS pulse insertion between SPI burst.
* @bclk_wait : number of bclk waits between each bits_per_word SPI burst.
* @cs_control : function pointer to board-specific function to assert/deassert
* I/O port to control HW generation of devices chip-select.
*/
struct spi_imx_chip {
u8 enable_loopback:1;
u8 enable_dma:1;
u8 ins_ss_pulse:1;
u16 bclk_wait:15;
void (*cs_control)(u32 control);
};
/* Chip-select state */
#define SPI_CS_ASSERT (1 << 0)
#define SPI_CS_DEASSERT (1 << 1)
/*-------------------------------------------------------------------------*/
#endif /* SPI_IMX_H_*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment