summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig18
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/amba-pl022.c823
-rw-r--r--drivers/spi/stm_msp.c1929
4 files changed, 2540 insertions, 231 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f3af1..4c71e7a9086 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -90,6 +90,20 @@ config SPI_BITBANG
need it. You only need to select this explicitly to support driver
modules that aren't part of this kernel tree.
+config STM_MSP_SPI
+ tristate "STM MSP CONTROLLER (SPI master)"
+ default y
+ help
+ This enables using the STM MSP controller in master
+ mode.
+
+config SPI_WORKQUEUE
+ bool "SPI_WORKQUEUE"
+ depends on STM_MSP_SPI
+ default n
+ help
+ This feature allow SPI works to be deferred in MSP driver.
+
config SPI_BUTTERFLY
tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)"
depends on PARPORT
@@ -216,8 +230,8 @@ config SPI_ORION
This enables using the SPI master controller on the Orion chips.
config SPI_PL022
- tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
- depends on ARM_AMBA && EXPERIMENTAL
+ tristate "ARM AMBA PL022 SSP controller"
+ depends on ARM_AMBA
default y if MACH_U300
default y if ARCH_REALVIEW
default y if INTEGRATOR_IMPD1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18217a..e1771805521 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o
+obj-$(CONFIG_STM_MSP_SPI) += stm_msp.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index f0a1418ce66..2e20f60f027 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -27,7 +27,6 @@
/*
* TODO:
* - add timeout on polled transfers
- * - add generic DMA framework support
*/
#include <linux/init.h>
@@ -45,6 +44,10 @@
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/pm_runtime.h>
/*
* This macro is used to define some register default values.
@@ -320,6 +323,7 @@ enum ssp_writing {
* @extended_cr: 32 bit wide control register 0 with extra
* features and extra features in CR1 as found in the ST variants
* @pl023: supports a subset of the ST extensions called "PL023"
+ * @loopback: supports loopback mode
*/
struct vendor_data {
int fifodepth;
@@ -327,6 +331,7 @@ struct vendor_data {
bool unidir;
bool extended_cr;
bool pl023;
+ bool loopback;
};
/**
@@ -335,12 +340,13 @@ struct vendor_data {
* @vendor: Vendor data for the IP block
* @phybase: The physical memory where the SSP device resides
* @virtbase: The virtual memory where the SSP is mapped
+ * @clk: Pointer to the SPI clock structure.
* @master: SPI framework hookup
* @master_info: controller-specific data from machine setup
- * @regs: SSP controller register's virtual address
- * @pump_messages: Work struct for scheduling work to the workqueue
- * @lock: spinlock to syncronise access to driver data
* @workqueue: a workqueue on which any spi_message request is queued
+ * @pump_messages: Work struct for scheduling work to the workqueue
+ * @queue_lock: spinlock to synchronise access to driver data
+ * @queue: list of spi_message to be serviced
* @busy: workqueue is busy
* @run: workqueue is running
* @pump_transfers: Tasklet used in Interrupt Transfer mode
@@ -351,8 +357,18 @@ struct vendor_data {
* @tx_end: end position in TX buffer to be read
* @rx: current position in RX buffer to be written
* @rx_end: end position in RX buffer to be written
- * @readingtype: the type of read currently going on
- * @writingtype: the type or write currently going on
+ * @read: data width used for rx
+ * @write: data width used for tx
+ * @exp_fifo_level: counter used to keep a track of amount data written to and
+ * read from FIFO.
+ * @dma_rx_channel: Rx DMA channel descriptor
+ * @dma_tx_channel: Tx DMA channel descriptor
+ * @sgt_rx: Pointer to the rx buffer sg table.
+ * @sgt_tx: Pointer to the tx buffer sg table.
+ * @dummypage: Page used when no tx/rx buffer is supplied for transmission.
+ * @regulator: Pointer to the SPI regulator structure.
+ * @rx_lev_trig: RX FIFO Trigger Level
+ * @tx_lev_trig: TX FIFO Trigger Level
*/
struct pl022 {
struct amba_device *adev;
@@ -381,6 +397,16 @@ struct pl022 {
enum ssp_reading read;
enum ssp_writing write;
u32 exp_fifo_level;
+ /* DMA settings */
+#ifdef CONFIG_DMA_ENGINE
+ struct dma_chan *dma_rx_channel;
+ struct dma_chan *dma_tx_channel;
+ struct sg_table sgt_rx;
+ struct sg_table sgt_tx;
+ char *dummypage;
+#endif
+ enum ssp_rx_level_trig rx_lev_trig;
+ enum ssp_tx_level_trig tx_lev_trig;
};
/**
@@ -406,7 +432,7 @@ struct chip_data {
u16 dmacr;
u16 cpsr;
u8 n_bytes;
- u8 enable_dma:1;
+ bool enable_dma;
enum ssp_reading read;
enum ssp_writing write;
void (*cs_control) (u32 command);
@@ -503,8 +529,14 @@ static void giveback(struct pl022 *pl022)
msg->state = NULL;
if (msg->complete)
msg->complete(msg->context);
+
+ /* disable the SPI/SSP operation */
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+
/* This message is completed, so let's turn off the clock! */
clk_disable(pl022->clk);
+ pm_runtime_put(&pl022->adev->dev);
}
/**
@@ -762,6 +794,375 @@ static void *next_transfer(struct pl022 *pl022)
}
return STATE_DONE;
}
+
+/*
+ * This DMA functionality is only compiled in if we have
+ * access to the generic DMA devices/DMA engine.
+ */
+#ifdef CONFIG_DMA_ENGINE
+static void unmap_free_dma_scatter(struct pl022 *pl022)
+{
+ /* Unmap and free the SG tables */
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+ sg_free_table(&pl022->sgt_rx);
+ sg_free_table(&pl022->sgt_tx);
+}
+
+static void dma_callback(void *data)
+{
+ struct pl022 *pl022 = data;
+ struct spi_message *msg = pl022->cur_msg;
+
+ BUG_ON(!pl022->sgt_rx.sgl);
+
+#ifdef VERBOSE_DEBUG
+ /*
+ * Optionally dump out buffers to inspect contents, this is
+ * good if you want to convince yourself that the loopback
+ * read/write contents are the same, when adopting to a new
+ * DMA engine.
+ */
+ {
+ struct scatterlist *sg;
+ unsigned int i;
+
+ dma_sync_sg_for_cpu(&pl022->adev->dev,
+ pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents,
+ DMA_FROM_DEVICE);
+
+ for_each_sg(pl022->sgt_rx.sgl, sg, pl022->sgt_rx.nents, i) {
+ dev_dbg(&pl022->adev->dev, "SPI RX SG ENTRY: %d", i);
+ print_hex_dump(KERN_ERR, "SPI RX: ",
+ DUMP_PREFIX_OFFSET,
+ 16,
+ 1,
+ sg_virt(sg),
+ sg_dma_len(sg),
+ 1);
+ }
+ for_each_sg(pl022->sgt_tx.sgl, sg, pl022->sgt_tx.nents, i) {
+ dev_dbg(&pl022->adev->dev, "SPI TX SG ENTRY: %d", i);
+ print_hex_dump(KERN_ERR, "SPI TX: ",
+ DUMP_PREFIX_OFFSET,
+ 16,
+ 1,
+ sg_virt(sg),
+ sg_dma_len(sg),
+ 1);
+ }
+ }
+#endif
+
+ unmap_free_dma_scatter(pl022);
+
+ /* Update total bytes transfered */
+ msg->actual_length += pl022->cur_transfer->len;
+ if (pl022->cur_transfer->cs_change)
+ pl022->cur_chip->
+ cs_control(SSP_CHIP_DESELECT);
+
+ /* Move to next transfer */
+ msg->state = next_transfer(pl022);
+ tasklet_schedule(&pl022->pump_transfers);
+}
+
+static void setup_dma_scatter(struct pl022 *pl022,
+ void *buffer,
+ unsigned int length,
+ struct sg_table *sgtab)
+{
+ struct scatterlist *sg;
+ int bytesleft = length;
+ void *bufp = buffer;
+ int mapbytes;
+ int i;
+
+ if (buffer) {
+ for_each_sg(sgtab->sgl, sg, sgtab->nents, i) {
+ /*
+ * If there are less bytes left than what fits
+ * in the current page (plus page alignment offset)
+ * we just feed in this, else we stuff in as much
+ * as we can.
+ */
+ if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
+ mapbytes = bytesleft;
+ else
+ mapbytes = PAGE_SIZE - offset_in_page(bufp);
+ sg_set_page(sg, virt_to_page(bufp),
+ mapbytes, offset_in_page(bufp));
+ bufp += mapbytes;
+ bytesleft -= mapbytes;
+ dev_dbg(&pl022->adev->dev,
+ "set RX/TX target page @ %p, %d bytes, %d left\n",
+ bufp, mapbytes, bytesleft);
+ }
+ } else {
+ /* Map the dummy buffer on every page */
+ for_each_sg(sgtab->sgl, sg, sgtab->nents, i) {
+ if (bytesleft < PAGE_SIZE)
+ mapbytes = bytesleft;
+ else
+ mapbytes = PAGE_SIZE;
+ sg_set_page(sg, virt_to_page(pl022->dummypage),
+ mapbytes, 0);
+ bytesleft -= mapbytes;
+ dev_dbg(&pl022->adev->dev,
+ "set RX/TX to dummy page %d bytes, %d left\n",
+ mapbytes, bytesleft);
+
+ }
+ }
+ BUG_ON(bytesleft);
+}
+
+/**
+ * configure_dma - configures the channels for the next transfer
+ * @pl022: SSP driver's private data structure
+ */
+static int configure_dma(struct pl022 *pl022)
+{
+ struct dma_slave_config rx_conf = {
+ .src_addr = SSP_DR(pl022->phybase),
+ .direction = DMA_FROM_DEVICE,
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = SSP_DR(pl022->phybase),
+ .direction = DMA_TO_DEVICE,
+ };
+ unsigned int pages;
+ int ret;
+ int sglen;
+ struct dma_chan *rxchan = pl022->dma_rx_channel;
+ struct dma_chan *txchan = pl022->dma_tx_channel;
+ struct dma_async_tx_descriptor *rxdesc;
+ struct dma_async_tx_descriptor *txdesc;
+ dma_cookie_t cookie;
+
+ /* DMA burstsize should be same as the FIFO trigger level */
+ rx_conf.src_maxburst = pl022->rx_lev_trig ? 1 <<
+ (pl022->rx_lev_trig + 1) : pl022->rx_lev_trig;
+ tx_conf.dst_maxburst = pl022->tx_lev_trig ? 1 <<
+ (pl022->tx_lev_trig + 1) : pl022->tx_lev_trig;
+
+ /* Check that the channels are available */
+ if (!rxchan || !txchan)
+ return -ENODEV;
+
+ switch (pl022->read) {
+ case READING_NULL:
+ /* Use the same as for writing */
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ break;
+ case READING_U8:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case READING_U16:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case READING_U32:
+ rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ }
+
+ switch (pl022->write) {
+ case WRITING_NULL:
+ /* Use the same as for reading */
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ break;
+ case WRITING_U8:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ break;
+ case WRITING_U16:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case WRITING_U32:
+ tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;;
+ break;
+ }
+
+ /* SPI pecularity: we need to read and write the same width */
+ if (rx_conf.src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ rx_conf.src_addr_width = tx_conf.dst_addr_width;
+ if (tx_conf.dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ tx_conf.dst_addr_width = rx_conf.src_addr_width;
+ BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
+
+ rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &rx_conf);
+ txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
+ (unsigned long) &tx_conf);
+
+ /* Create sglists for the transfers */
+ pages = (pl022->cur_transfer->len >> PAGE_SHIFT) + 1;
+ dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages);
+
+ ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_KERNEL);
+ if (ret)
+ goto err_alloc_rx_sg;
+
+ ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_KERNEL);
+ if (ret)
+ goto err_alloc_tx_sg;
+
+ /* Fill in the scatterlists for the RX+TX buffers */
+ setup_dma_scatter(pl022, pl022->rx,
+ pl022->cur_transfer->len, &pl022->sgt_rx);
+ setup_dma_scatter(pl022, pl022->tx,
+ pl022->cur_transfer->len, &pl022->sgt_tx);
+
+ /* Map DMA buffers */
+ sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents, DMA_FROM_DEVICE);
+ if (!sglen)
+ goto err_rx_sgmap;
+
+ sglen = dma_map_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+ if (!sglen)
+ goto err_tx_sgmap;
+
+ /* Send both scatterlists */
+ rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
+ pl022->sgt_rx.sgl,
+ pl022->sgt_rx.nents,
+ DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rxdesc)
+ goto err_rxdesc;
+
+ txdesc = txchan->device->device_prep_slave_sg(txchan,
+ pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc)
+ goto err_txdesc;
+
+ /* Put the callback on the RX transfer only, that should finish last */
+ rxdesc->callback = dma_callback;
+ rxdesc->callback_param = pl022;
+
+ /* Submit and fire RX and TX with TX last so we're ready to read! */
+ cookie = rxdesc->tx_submit(rxdesc);
+ if (dma_submit_error(cookie))
+ goto err_submit_rx;
+ cookie = txdesc->tx_submit(txdesc);
+ if (dma_submit_error(cookie))
+ goto err_submit_tx;
+ rxchan->device->device_issue_pending(rxchan);
+ txchan->device->device_issue_pending(txchan);
+
+ return 0;
+
+err_submit_tx:
+err_submit_rx:
+err_txdesc:
+ txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+err_rxdesc:
+ rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_tx.sgl,
+ pl022->sgt_tx.nents, DMA_TO_DEVICE);
+err_tx_sgmap:
+ dma_unmap_sg(&pl022->adev->dev, pl022->sgt_rx.sgl,
+ pl022->sgt_tx.nents, DMA_FROM_DEVICE);
+err_rx_sgmap:
+ sg_free_table(&pl022->sgt_tx);
+err_alloc_tx_sg:
+ sg_free_table(&pl022->sgt_rx);
+err_alloc_rx_sg:
+ return -ENOMEM;
+}
+
+static int __init pl022_dma_probe(struct pl022 *pl022)
+{
+ dma_cap_mask_t mask;
+
+ /* Try to acquire a generic DMA engine slave channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ /*
+ * We need both RX and TX channels to do DMA, else do none
+ * of them.
+ */
+ pl022->dma_rx_channel = dma_request_channel(mask,
+ pl022->master_info->dma_filter,
+ pl022->master_info->dma_rx_param);
+ if (!pl022->dma_rx_channel) {
+ dev_err(&pl022->adev->dev, "no RX DMA channel!\n");
+ goto err_no_rxchan;
+ }
+
+ pl022->dma_tx_channel = dma_request_channel(mask,
+ pl022->master_info->dma_filter,
+ pl022->master_info->dma_tx_param);
+ if (!pl022->dma_tx_channel) {
+ dev_err(&pl022->adev->dev, "no TX DMA channel!\n");
+ goto err_no_txchan;
+ }
+
+ pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!pl022->dummypage) {
+ dev_err(&pl022->adev->dev, "no DMA dummypage!\n");
+ goto err_no_dummypage;
+ }
+
+ dev_info(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n",
+ dma_chan_name(pl022->dma_rx_channel),
+ dma_chan_name(pl022->dma_tx_channel));
+
+ return 0;
+
+err_no_dummypage:
+ dma_release_channel(pl022->dma_tx_channel);
+err_no_txchan:
+ dma_release_channel(pl022->dma_rx_channel);
+ pl022->dma_rx_channel = NULL;
+err_no_rxchan:
+ return -ENODEV;
+}
+
+static void terminate_dma(struct pl022 *pl022)
+{
+ struct dma_chan *rxchan = pl022->dma_rx_channel;
+ struct dma_chan *txchan = pl022->dma_tx_channel;
+
+ rxchan->device->device_control(rxchan, DMA_TERMINATE_ALL, 0);
+ txchan->device->device_control(txchan, DMA_TERMINATE_ALL, 0);
+ unmap_free_dma_scatter(pl022);
+}
+
+static void pl022_dma_remove(struct pl022 *pl022)
+{
+ if (pl022->busy)
+ terminate_dma(pl022);
+ if (pl022->dma_tx_channel)
+ dma_release_channel(pl022->dma_tx_channel);
+ if (pl022->dma_rx_channel)
+ dma_release_channel(pl022->dma_rx_channel);
+ kfree(pl022->dummypage);
+}
+
+#else
+static inline int configure_dma(struct pl022 *pl022)
+{
+ return -ENODEV;
+}
+
+static inline int pl022_dma_probe(struct pl022 *pl022)
+{
+ return 0;
+}
+
+static inline void pl022_dma_remove(struct pl022 *pl022)
+{
+}
+#endif
+
/**
* pl022_interrupt_handler - Interrupt handler for SSP controller
*
@@ -793,14 +1194,17 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
if (unlikely(!irq_status))
return IRQ_NONE;
- /* This handles the error code interrupts */
+ /*
+ * This handles the FIFO interrupts, the timeout
+ * interrupts are flatly ignored, they cannot be
+ * trusted.
+ */
if (unlikely(irq_status & SSP_MIS_MASK_RORMIS)) {
/*
* Overrun interrupt - bail out since our Data has been
* corrupted
*/
- dev_err(&pl022->adev->dev,
- "FIFO overrun\n");
+ dev_err(&pl022->adev->dev, "FIFO overrun\n");
if (readw(SSP_SR(pl022->virtbase)) & SSP_SR_MASK_RFF)
dev_err(&pl022->adev->dev,
"RXFIFO is full\n");
@@ -895,8 +1299,8 @@ static int set_up_next_transfer(struct pl022 *pl022,
}
/**
- * pump_transfers - Tasklet function which schedules next interrupt transfer
- * when running in interrupt transfer mode.
+ * pump_transfers - Tasklet function which schedules next transfer
+ * when running in interrupt or DMA transfer mode.
* @data: SSP driver private data structure
*
*/
@@ -953,65 +1357,23 @@ static void pump_transfers(unsigned long data)
}
/* Flush the FIFOs and let's go! */
flush(pl022);
- writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
-}
-
-/**
- * NOT IMPLEMENTED
- * configure_dma - It configures the DMA pipes for DMA transfers
- * @data: SSP driver's private data structure
- *
- */
-static int configure_dma(void *data)
-{
- struct pl022 *pl022 = data;
- dev_dbg(&pl022->adev->dev, "configure DMA\n");
- return -ENOTSUPP;
-}
-/**
- * do_dma_transfer - It handles transfers of the current message
- * if it is DMA xfer.
- * NOT FULLY IMPLEMENTED
- * @data: SSP driver's private data structure
- */
-static void do_dma_transfer(void *data)
-{
- struct pl022 *pl022 = data;
-
- if (configure_dma(data)) {
- dev_dbg(&pl022->adev->dev, "configuration of DMA Failed!\n");
- goto err_config_dma;
- }
-
- /* TODO: Implememt DMA setup of pipes here */
-
- /* Enable target chip, set up transfer */
- pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
- if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
- /* Error path */
- pl022->cur_msg->state = STATE_ERROR;
- pl022->cur_msg->status = -EIO;
- giveback(pl022);
+ if (pl022->cur_chip->enable_dma) {
+ if (configure_dma(pl022)) {
+ dev_dbg(&pl022->adev->dev,
+ "configuration of DMA failed, fall back to interrupt mode\n");
+ goto err_config_dma;
+ }
return;
}
- /* Enable SSP */
- writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
- SSP_CR1(pl022->virtbase));
-
- /* TODO: Enable the DMA transfer here */
- return;
- err_config_dma:
- pl022->cur_msg->state = STATE_ERROR;
- pl022->cur_msg->status = -EIO;
- giveback(pl022);
- return;
+err_config_dma:
+ writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
}
-static void do_interrupt_transfer(void *data)
+static void do_interrupt_dma_transfer(struct pl022 *pl022)
{
- struct pl022 *pl022 = data;
+ u32 irqflags = ENABLE_ALL_INTERRUPTS;
/* Enable target chip */
pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
@@ -1022,15 +1384,26 @@ static void do_interrupt_transfer(void *data)
giveback(pl022);
return;
}
+ /* If we're using DMA, set up DMA here */
+ if (pl022->cur_chip->enable_dma) {
+ /* Configure DMA transfer */
+ if (configure_dma(pl022)) {
+ dev_dbg(&pl022->adev->dev,
+ "configuration of DMA failed, fall back to interrupt mode\n");
+ goto err_config_dma;
+ }
+ /* Disable interrupts in DMA mode, IRQ from DMA controller */
+ irqflags = DISABLE_ALL_INTERRUPTS;
+ }
+err_config_dma:
/* Enable SSP, turn on interrupts */
writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE),
SSP_CR1(pl022->virtbase));
- writew(ENABLE_ALL_INTERRUPTS, SSP_IMSC(pl022->virtbase));
+ writew(irqflags, SSP_IMSC(pl022->virtbase));
}
-static void do_polling_transfer(void *data)
+static void do_polling_transfer(struct pl022 *pl022)
{
- struct pl022 *pl022 = data;
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
@@ -1100,7 +1473,7 @@ static void do_polling_transfer(void *data)
*
* This function checks if there is any spi message in the queue that
* needs processing and delegate control to appropriate function
- * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * do_polling_transfer()/do_interrupt_dma_transfer()
* based on the kind of the transfer
*
*/
@@ -1142,16 +1515,15 @@ static void pump_messages(struct work_struct *work)
* We enable the clock here, then the clock will be disabled when
* giveback() is called in each method (poll/interrupt/DMA)
*/
+ pm_runtime_get_sync(&pl022->adev->dev);
clk_enable(pl022->clk);
restore_state(pl022);
flush(pl022);
if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
do_polling_transfer(pl022);
- else if (pl022->cur_chip->xfer_type == INTERRUPT_TRANSFER)
- do_interrupt_transfer(pl022);
else
- do_dma_transfer(pl022);
+ do_interrupt_dma_transfer(pl022);
}
@@ -1246,100 +1618,56 @@ static int destroy_queue(struct pl022 *pl022)
}
static int verify_controller_parameters(struct pl022 *pl022,
- struct pl022_config_chip *chip_info)
+ struct pl022_config_chip const *chip_info)
{
- if ((chip_info->lbm != LOOPBACK_ENABLED)
- && (chip_info->lbm != LOOPBACK_DISABLED)) {
- dev_err(chip_info->dev,
- "loopback Mode is configured incorrectly\n");
- return -EINVAL;
- }
if ((chip_info->iface < SSP_INTERFACE_MOTOROLA_SPI)
|| (chip_info->iface > SSP_INTERFACE_UNIDIRECTIONAL)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"interface is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->iface == SSP_INTERFACE_UNIDIRECTIONAL) &&
(!pl022->vendor->unidir)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"unidirectional mode not supported in this "
"hardware version\n");
return -EINVAL;
}
if ((chip_info->hierarchy != SSP_MASTER)
&& (chip_info->hierarchy != SSP_SLAVE)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"hierarchy is configured incorrectly\n");
return -EINVAL;
}
- if (((chip_info->clk_freq).cpsdvsr < CPSDVR_MIN)
- || ((chip_info->clk_freq).cpsdvsr > CPSDVR_MAX)) {
- dev_err(chip_info->dev,
- "cpsdvsr is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->endian_rx != SSP_RX_MSB)
- && (chip_info->endian_rx != SSP_RX_LSB)) {
- dev_err(chip_info->dev,
- "RX FIFO endianess is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->endian_tx != SSP_TX_MSB)
- && (chip_info->endian_tx != SSP_TX_LSB)) {
- dev_err(chip_info->dev,
- "TX FIFO endianess is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->data_size < SSP_DATA_BITS_4)
- || (chip_info->data_size > SSP_DATA_BITS_32)) {
- dev_err(chip_info->dev,
- "DATA Size is configured incorrectly\n");
- return -EINVAL;
- }
if ((chip_info->com_mode != INTERRUPT_TRANSFER)
&& (chip_info->com_mode != DMA_TRANSFER)
&& (chip_info->com_mode != POLLING_TRANSFER)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Communication mode is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->rx_lev_trig < SSP_RX_1_OR_MORE_ELEM)
|| (chip_info->rx_lev_trig > SSP_RX_32_OR_MORE_ELEM)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"RX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->tx_lev_trig < SSP_TX_1_OR_MORE_EMPTY_LOC)
|| (chip_info->tx_lev_trig > SSP_TX_32_OR_MORE_EMPTY_LOC)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"TX FIFO Trigger Level is configured incorrectly\n");
return -EINVAL;
}
- if (chip_info->iface == SSP_INTERFACE_MOTOROLA_SPI) {
- if ((chip_info->clk_phase != SSP_CLK_FIRST_EDGE)
- && (chip_info->clk_phase != SSP_CLK_SECOND_EDGE)) {
- dev_err(chip_info->dev,
- "Clock Phase is configured incorrectly\n");
- return -EINVAL;
- }
- if ((chip_info->clk_pol != SSP_CLK_POL_IDLE_LOW)
- && (chip_info->clk_pol != SSP_CLK_POL_IDLE_HIGH)) {
- dev_err(chip_info->dev,
- "Clock Polarity is configured incorrectly\n");
- return -EINVAL;
- }
- }
if (chip_info->iface == SSP_INTERFACE_NATIONAL_MICROWIRE) {
if ((chip_info->ctrl_len < SSP_BITS_4)
|| (chip_info->ctrl_len > SSP_BITS_32)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"CTRL LEN is configured incorrectly\n");
return -EINVAL;
}
if ((chip_info->wait_state != SSP_MWIRE_WAIT_ZERO)
&& (chip_info->wait_state != SSP_MWIRE_WAIT_ONE)) {
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Wait State is configured incorrectly\n");
return -EINVAL;
}
@@ -1349,23 +1677,18 @@ static int verify_controller_parameters(struct pl022 *pl022,
SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
&& (chip_info->duplex !=
SSP_MICROWIRE_CHANNEL_HALF_DUPLEX))
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Microwire duplex mode is configured incorrectly\n");
return -EINVAL;
} else {
if (chip_info->duplex != SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
- dev_err(chip_info->dev,
+ dev_err(&pl022->adev->dev,
"Microwire half duplex mode requested,"
" but this is only available in the"
" ST version of PL022\n");
return -EINVAL;
}
}
- if (chip_info->cs_control == NULL) {
- dev_warn(chip_info->dev,
- "Chip Select Function is NULL for this chip\n");
- chip_info->cs_control = null_cs_control;
- }
return 0;
}
@@ -1465,22 +1788,24 @@ static int calculate_effective_freq(struct pl022 *pl022,
return 0;
}
-/**
- * NOT IMPLEMENTED
- * process_dma_info - Processes the DMA info provided by client drivers
- * @chip_info: chip info provided by client device
- * @chip: Runtime state maintained by the SSP controller for each spi device
- *
- * This function processes and stores DMA config provided by client driver
- * into the runtime state maintained by the SSP controller driver
+
+/*
+ * A piece of default chip info unless the platform
+ * supplies it.
*/
-static int process_dma_info(struct pl022_config_chip *chip_info,
- struct chip_data *chip)
-{
- dev_err(chip_info->dev,
- "cannot process DMA info, DMA not implemented!\n");
- return -ENOTSUPP;
-}
+static const struct pl022_config_chip pl022_default_chip_info = {
+ .com_mode = POLLING_TRANSFER,
+ .iface = SSP_INTERFACE_MOTOROLA_SPI,
+ .hierarchy = SSP_SLAVE,
+ .slave_tx_disable = DO_NOT_DRIVE_TX,
+ .rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
+ .tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
+ .ctrl_len = SSP_BITS_8,
+ .wait_state = SSP_MWIRE_WAIT_ZERO,
+ .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
+ .cs_control = null_cs_control,
+};
+
/**
* pl022_setup - setup function registered to SPI master framework
@@ -1494,23 +1819,15 @@ static int process_dma_info(struct pl022_config_chip *chip_info,
* controller hardware here, that is not done until the actual transfer
* commence.
*/
-
-/* FIXME: JUST GUESSING the spi->mode bits understood by this driver */
-#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
- | SPI_LSB_FIRST | SPI_LOOP)
-
static int pl022_setup(struct spi_device *spi)
{
- struct pl022_config_chip *chip_info;
+ struct pl022_config_chip const *chip_info;
struct chip_data *chip;
+ struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
int status = 0;
struct pl022 *pl022 = spi_master_get_devdata(spi->master);
-
- if (spi->mode & ~MODEBITS) {
- dev_dbg(&spi->dev, "unsupported mode bits %x\n",
- spi->mode & ~MODEBITS);
- return -EINVAL;
- }
+ unsigned int bits = spi->bits_per_word;
+ u32 tmp;
if (!spi->max_speed_hz)
return -EINVAL;
@@ -1533,48 +1850,13 @@ static int pl022_setup(struct spi_device *spi)
chip_info = spi->controller_data;
if (chip_info == NULL) {
+ chip_info = &pl022_default_chip_info;
/* spi_board_info.controller_data not is supplied */
dev_dbg(&spi->dev,
"using default controller_data settings\n");
-
- chip_info =
- kzalloc(sizeof(struct pl022_config_chip), GFP_KERNEL);
-
- if (!chip_info) {
- dev_err(&spi->dev,
- "cannot allocate controller data\n");
- status = -ENOMEM;
- goto err_first_setup;
- }
-
- dev_dbg(&spi->dev, "allocated memory for controller data\n");
-
- /* Pointer back to the SPI device */
- chip_info->dev = &spi->dev;
- /*
- * Set controller data default values:
- * Polling is supported by default
- */
- chip_info->lbm = LOOPBACK_DISABLED;
- chip_info->com_mode = POLLING_TRANSFER;
- chip_info->iface = SSP_INTERFACE_MOTOROLA_SPI;
- chip_info->hierarchy = SSP_SLAVE;
- chip_info->slave_tx_disable = DO_NOT_DRIVE_TX;
- chip_info->endian_tx = SSP_TX_LSB;
- chip_info->endian_rx = SSP_RX_LSB;
- chip_info->data_size = SSP_DATA_BITS_12;
- chip_info->rx_lev_trig = SSP_RX_1_OR_MORE_ELEM;
- chip_info->tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC;
- chip_info->clk_phase = SSP_CLK_SECOND_EDGE;
- chip_info->clk_pol = SSP_CLK_POL_IDLE_LOW;
- chip_info->ctrl_len = SSP_BITS_8;
- chip_info->wait_state = SSP_MWIRE_WAIT_ZERO;
- chip_info->duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX;
- chip_info->cs_control = null_cs_control;
- } else {
+ } else
dev_dbg(&spi->dev,
"using user supplied controller_data settings\n");
- }
/*
* We can override with custom divisors, else we use the board
@@ -1584,29 +1866,51 @@ static int pl022_setup(struct spi_device *spi)
&& (0 == chip_info->clk_freq.scr)) {
status = calculate_effective_freq(pl022,
spi->max_speed_hz,
- &chip_info->clk_freq);
+ &clk_freq);
if (status < 0)
goto err_config_params;
} else {
- if ((chip_info->clk_freq.cpsdvsr % 2) != 0)
- chip_info->clk_freq.cpsdvsr =
- chip_info->clk_freq.cpsdvsr - 1;
+ memcpy(&clk_freq, &chip_info->clk_freq, sizeof(clk_freq));
+ if ((clk_freq.cpsdvsr % 2) != 0)
+ clk_freq.cpsdvsr =
+ clk_freq.cpsdvsr - 1;
}
+ if ((clk_freq.cpsdvsr < CPSDVR_MIN)
+ || (clk_freq.cpsdvsr > CPSDVR_MAX)) {
+ dev_err(&spi->dev,
+ "cpsdvsr is configured incorrectly\n");
+ goto err_config_params;
+ }
+
+
status = verify_controller_parameters(pl022, chip_info);
if (status) {
dev_err(&spi->dev, "controller data is incorrect");
goto err_config_params;
}
+
+ pl022->rx_lev_trig = chip_info->rx_lev_trig;
+ pl022->tx_lev_trig = chip_info->tx_lev_trig;
+
/* Now set controller state based on controller data */
chip->xfer_type = chip_info->com_mode;
- chip->cs_control = chip_info->cs_control;
-
- if (chip_info->data_size <= 8) {
- dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n");
+ if (!chip_info->cs_control) {
+ chip->cs_control = null_cs_control;
+ dev_warn(&spi->dev,
+ "chip select function is NULL for this chip\n");
+ } else
+ chip->cs_control = chip_info->cs_control;
+
+ if (bits <= 3) {
+ /* PL022 doesn't support less than 4-bits */
+ status = -ENOTSUPP;
+ goto err_config_params;
+ } else if (bits <= 8) {
+ dev_dbg(&spi->dev, "4 <= n <=8 bits per word\n");
chip->n_bytes = 1;
chip->read = READING_U8;
chip->write = WRITING_U8;
- } else if (chip_info->data_size <= 16) {
+ } else if (bits <= 16) {
dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n");
chip->n_bytes = 2;
chip->read = READING_U16;
@@ -1623,6 +1927,7 @@ static int pl022_setup(struct spi_device *spi)
dev_err(&spi->dev,
"a standard pl022 can only handle "
"1 <= n <= 16 bit words\n");
+ status = -ENOTSUPP;
goto err_config_params;
}
}
@@ -1634,17 +1939,14 @@ static int pl022_setup(struct spi_device *spi)
chip->cpsr = 0;
if ((chip_info->com_mode == DMA_TRANSFER)
&& ((pl022->master_info)->enable_dma)) {
- chip->enable_dma = 1;
+ chip->enable_dma = true;
dev_dbg(&spi->dev, "DMA mode set in controller state\n");
- status = process_dma_info(chip_info, chip);
- if (status < 0)
- goto err_config_params;
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_RXDMAE, 0);
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_ENABLED,
SSP_DMACR_MASK_TXDMAE, 1);
} else {
- chip->enable_dma = 0;
+ chip->enable_dma = false;
dev_dbg(&spi->dev, "DMA mode NOT set in controller state\n");
SSP_WRITE_BITS(chip->dmacr, SSP_DMA_DISABLED,
SSP_DMACR_MASK_RXDMAE, 0);
@@ -1652,10 +1954,12 @@ static int pl022_setup(struct spi_device *spi)
SSP_DMACR_MASK_TXDMAE, 1);
}
- chip->cpsr = chip_info->clk_freq.cpsdvsr;
+ chip->cpsr = clk_freq.cpsdvsr;
/* Special setup for the ST micro extended control registers */
if (pl022->vendor->extended_cr) {
+ u32 etx;
+
if (pl022->vendor->pl023) {
/* These bits are only in the PL023 */
SSP_WRITE_BITS(chip->cr1, chip_info->clkdelay,
@@ -1671,29 +1975,51 @@ static int pl022_setup(struct spi_device *spi)
SSP_WRITE_BITS(chip->cr1, chip_info->wait_state,
SSP_CR1_MASK_MWAIT_ST, 6);
}
- SSP_WRITE_BITS(chip->cr0, chip_info->data_size,
+ SSP_WRITE_BITS(chip->cr0, bits - 1,
SSP_CR0_MASK_DSS_ST, 0);
- SSP_WRITE_BITS(chip->cr1, chip_info->endian_rx,
- SSP_CR1_MASK_RENDN_ST, 4);
- SSP_WRITE_BITS(chip->cr1, chip_info->endian_tx,
- SSP_CR1_MASK_TENDN_ST, 5);
+
+ if (spi->mode & SPI_LSB_FIRST) {
+ tmp = SSP_RX_LSB;
+ etx = SSP_TX_LSB;
+ } else {
+ tmp = SSP_RX_MSB;
+ etx = SSP_TX_MSB;
+ }
+ SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_RENDN_ST, 4);
+ SSP_WRITE_BITS(chip->cr1, etx, SSP_CR1_MASK_TENDN_ST, 5);
SSP_WRITE_BITS(chip->cr1, chip_info->rx_lev_trig,
SSP_CR1_MASK_RXIFLSEL_ST, 7);
SSP_WRITE_BITS(chip->cr1, chip_info->tx_lev_trig,
SSP_CR1_MASK_TXIFLSEL_ST, 10);
} else {
- SSP_WRITE_BITS(chip->cr0, chip_info->data_size,
+ SSP_WRITE_BITS(chip->cr0, bits - 1,
SSP_CR0_MASK_DSS, 0);
SSP_WRITE_BITS(chip->cr0, chip_info->iface,
SSP_CR0_MASK_FRF, 4);
}
+
/* Stuff that is common for all versions */
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_pol, SSP_CR0_MASK_SPO, 6);
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_phase, SSP_CR0_MASK_SPH, 7);
- SSP_WRITE_BITS(chip->cr0, chip_info->clk_freq.scr, SSP_CR0_MASK_SCR, 8);
+ if (spi->mode & SPI_CPOL)
+ tmp = SSP_CLK_POL_IDLE_HIGH;
+ else
+ tmp = SSP_CLK_POL_IDLE_LOW;
+ SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPO, 6);
+
+ if (spi->mode & SPI_CPHA)
+ tmp = SSP_CLK_SECOND_EDGE;
+ else
+ tmp = SSP_CLK_FIRST_EDGE;
+ SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPH, 7);
+
+ SSP_WRITE_BITS(chip->cr0, clk_freq.scr, SSP_CR0_MASK_SCR, 8);
/* Loopback is available on all versions except PL023 */
- if (!pl022->vendor->pl023)
- SSP_WRITE_BITS(chip->cr1, chip_info->lbm, SSP_CR1_MASK_LBM, 0);
+ if (pl022->vendor->loopback) {
+ if (spi->mode & SPI_LOOP)
+ tmp = LOOPBACK_ENABLED;
+ else
+ tmp = LOOPBACK_DISABLED;
+ SSP_WRITE_BITS(chip->cr1, tmp, SSP_CR1_MASK_LBM, 0);
+ }
SSP_WRITE_BITS(chip->cr1, SSP_DISABLED, SSP_CR1_MASK_SSE, 1);
SSP_WRITE_BITS(chip->cr1, chip_info->hierarchy, SSP_CR1_MASK_MS, 2);
SSP_WRITE_BITS(chip->cr1, chip_info->slave_tx_disable, SSP_CR1_MASK_SOD, 3);
@@ -1702,7 +2028,7 @@ static int pl022_setup(struct spi_device *spi)
spi_set_ctldata(spi, chip);
return status;
err_config_params:
- err_first_setup:
+ spi_set_ctldata(spi, NULL);
kfree(chip);
return status;
}
@@ -1723,7 +2049,7 @@ static void pl022_cleanup(struct spi_device *spi)
}
-static int __init
+static int __devinit
pl022_probe(struct amba_device *adev, struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -1764,12 +2090,21 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
master->setup = pl022_setup;
master->transfer = pl022_transfer;
+ /*
+ * Supports mode 0-3, loopback, and active low CS. Transfers are
+ * always MS bit first on the original pl022.
+ */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
+ if (pl022->vendor->extended_cr)
+ master->mode_bits |= SPI_LSB_FIRST;
+
dev_dbg(&adev->dev, "BUSNO: %d\n", master->bus_num);
status = amba_request_regions(adev, NULL);
if (status)
goto err_no_ioregion;
+ pl022->phybase = adev->res.start;
pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res));
if (pl022->virtbase == NULL) {
status = -ENOMEM;
@@ -1778,6 +2113,9 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
+ pm_runtime_enable(dev);
+ pm_runtime_resume(dev);
+
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
status = PTR_ERR(pl022->clk);
@@ -1798,6 +2136,14 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status);
goto err_no_irq;
}
+
+ /* Get DMA channels */
+ if (platform_info->enable_dma) {
+ status = pl022_dma_probe(pl022);
+ if (status != 0)
+ goto err_no_dma;
+ }
+
/* Initialize and start queue */
status = init_queue(pl022);
if (status != 0) {
@@ -1824,6 +2170,8 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
err_start_queue:
err_init_queue:
destroy_queue(pl022);
+ pl022_dma_remove(pl022);
+ err_no_dma:
free_irq(adev->irq[0], pl022);
err_no_irq:
clk_put(pl022->clk);
@@ -1838,7 +2186,7 @@ pl022_probe(struct amba_device *adev, struct amba_id *id)
return status;
}
-static int __exit
+static int __devexit
pl022_remove(struct amba_device *adev)
{
struct pl022 *pl022 = amba_get_drvdata(adev);
@@ -1854,7 +2202,9 @@ pl022_remove(struct amba_device *adev)
return status;
}
load_ssp_default_config(pl022);
+ pl022_dma_remove(pl022);
free_irq(adev->irq[0], pl022);
+ pm_runtime_disable(&adev->dev);
clk_disable(pl022->clk);
clk_put(pl022->clk);
iounmap(pl022->virtbase);
@@ -1879,9 +2229,6 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
return status;
}
- clk_enable(pl022->clk);
- load_ssp_default_config(pl022);
- clk_disable(pl022->clk);
dev_dbg(&adev->dev, "suspended\n");
return 0;
}
@@ -1911,6 +2258,7 @@ static struct vendor_data vendor_arm = {
.unidir = false,
.extended_cr = false,
.pl023 = false,
+ .loopback = true,
};
@@ -1920,6 +2268,7 @@ static struct vendor_data vendor_st = {
.unidir = false,
.extended_cr = true,
.pl023 = false,
+ .loopback = true,
};
static struct vendor_data vendor_st_pl023 = {
@@ -1928,6 +2277,16 @@ static struct vendor_data vendor_st_pl023 = {
.unidir = false,
.extended_cr = true,
.pl023 = true,
+ .loopback = false,
+};
+
+static struct vendor_data vendor_db5500_pl023 = {
+ .fifodepth = 32,
+ .max_bpw = 32,
+ .unidir = false,
+ .extended_cr = true,
+ .pl023 = true,
+ .loopback = true,
};
static struct amba_id pl022_ids[] = {
@@ -1961,6 +2320,12 @@ static struct amba_id pl022_ids[] = {
.mask = 0xffffffff,
.data = &vendor_st_pl023,
},
+ {
+ .id = 0x00080023,
+ .mask = 0xffffffff,
+ .data = &vendor_db5500_pl023,
+ .name = "db5500-spi",
+ },
{ 0, 0 },
};
@@ -1970,7 +2335,7 @@ static struct amba_driver pl022_driver = {
},
.id_table = pl022_ids,
.probe = pl022_probe,
- .remove = __exit_p(pl022_remove),
+ .remove = __devexit_p(pl022_remove),
.suspend = pl022_suspend,
.resume = pl022_resume,
};
@@ -1981,7 +2346,7 @@ static int __init pl022_init(void)
return amba_driver_register(&pl022_driver);
}
-module_init(pl022_init);
+subsys_initcall(pl022_init);
static void __exit pl022_exit(void)
{
diff --git a/drivers/spi/stm_msp.c b/drivers/spi/stm_msp.c
new file mode 100644
index 00000000000..6b9d7bb75e5
--- /dev/null
+++ b/drivers/spi/stm_msp.c
@@ -0,0 +1,1929 @@
+/*
+ * drivers/spi/stm_msp.c
+ *
+ * Copyright (C) 2010 STMicroelectronics Pvt. Ltd.
+ *
+ * Author: Sachin Verma <sachin.verma@st.com>
+ *
+ * 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/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/bus.h>
+#include <linux/spi/stm_msp.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+
+/**
+ * MSP Controller Register Offsets
+ */
+#define MSP_DR(r) (r + 0x000)
+#define MSP_GCR(r) (r + 0x004)
+#define MSP_TCF(r) (r + 0x008)
+#define MSP_RCF(r) (r + 0x00C)
+#define MSP_SRG(r) (r + 0x010)
+#define MSP_FLR(r) (r + 0x014)
+#define MSP_DMACR(r) (r + 0x018)
+#define MSP_IMSC(r) (r + 0x020)
+#define MSP_RIS(r) (r + 0x024)
+#define MSP_MIS(r) (r + 0x028)
+#define MSP_ICR(r) (r + 0x02C)
+#define MSP_MCR(r) (r + 0x030)
+#define MSP_RCV(r) (r + 0x034)
+#define MSP_RCM(r) (r + 0x038)
+#define MSP_TCE0(r) (r + 0x040)
+#define MSP_TCE1(r) (r + 0x044)
+#define MSP_TCE2(r) (r + 0x048)
+#define MSP_TCE3(r) (r + 0x04C)
+#define MSP_RCE0(r) (r + 0x060)
+#define MSP_RCE1(r) (r + 0x064)
+#define MSP_RCE2(r) (r + 0x068)
+#define MSP_RCE3(r) (r + 0x06C)
+#define MSP_PID0(r) (r + 0xFE0)
+#define MSP_PID1(r) (r + 0xFE4)
+#define MSP_PID2(r) (r + 0xFE8)
+#define MSP_PID3(r) (r + 0xFEC)
+
+/**
+ * MSP Global Configuration Register - MSP_GCR
+ */
+#define MSP_GCR_MASK_RXEN ((u32)(0x1UL << 0))
+#define MSP_GCR_MASK_RFFEN ((u32)(0x1UL << 1))
+#define MSP_GCR_MASK_RFSPOL ((u32)(0x1UL << 2))
+#define MSP_GCR_MASK_DCM ((u32)(0x1UL << 3))
+#define MSP_GCR_MASK_RFSSEL ((u32)(0x1UL << 4))
+#define MSP_GCR_MASK_RCKPOL ((u32)(0x1UL << 5))
+#define MSP_GCR_MASK_RCKSEL ((u32)(0x1UL << 6))
+#define MSP_GCR_MASK_LBM ((u32)(0x1UL << 7))
+#define MSP_GCR_MASK_TXEN ((u32)(0x1UL << 8))
+#define MSP_GCR_MASK_TFFEN ((u32)(0x1UL << 9))
+#define MSP_GCR_MASK_TFSPOL ((u32)(0x1UL << 10))
+#define MSP_GCR_MASK_TFSSEL ((u32)(0x3UL << 11))
+#define MSP_GCR_MASK_TCKPOL ((u32)(0x1UL << 13))
+#define MSP_GCR_MASK_TCKSEL ((u32)(0x1UL << 14))
+#define MSP_GCR_MASK_TXDDL ((u32)(0x1UL << 15))
+#define MSP_GCR_MASK_SGEN ((u32)(0x1UL << 16))
+#define MSP_GCR_MASK_SCKPOL ((u32)(0x1UL << 17))
+#define MSP_GCR_MASK_SCKSEL ((u32)(0x3UL << 18))
+#define MSP_GCR_MASK_FGEN ((u32)(0x1UL << 20))
+#define MSP_GCR_MASK_SPICKM ((u32)(0x3UL << 21))
+#define MSP_GCR_MASK_SPIBME ((u32)(0x1UL << 23))
+
+/**
+ * MSP Transmit Configuration Register - MSP_TCF
+ */
+#define MSP_TCF_MASK_TP1ELEN ((u32)(0x7UL << 0))
+#define MSP_TCF_MASK_TP1FLEN ((u32)(0x7FUL << 3))
+#define MSP_TCF_MASK_TDTYP ((u32)(0x3UL << 10))
+#define MSP_TCF_MASK_TENDN ((u32)(0x1UL << 12))
+#define MSP_TCF_MASK_TDDLY ((u32)(0x3UL << 13))
+#define MSP_TCF_MASK_TFSIG ((u32)(0x1UL << 15))
+#define MSP_TCF_MASK_TP2ELEN ((u32)(0x7UL << 16))
+#define MSP_TCF_MASK_TP2FLEN ((u32)(0x7FUL << 19))
+#define MSP_TCF_MASK_TP2SM ((u32)(0x1UL << 26))
+#define MSP_TCF_MASK_TP2EN ((u32)(0x1UL << 27))
+#define MSP_TCF_MASK_TBSWAP ((u32)(0x3UL << 28))
+
+/**
+ * MSP Receive Configuration Register - MSP_RCF
+ */
+#define MSP_RCF_MASK_RP1ELEN ((u32)(0x7UL << 0))
+#define MSP_RCF_MASK_RP1FLEN ((u32)(0x7FUL << 3))
+#define MSP_RCF_MASK_RDTYP ((u32)(0x3UL << 10))
+#define MSP_RCF_MASK_RENDN ((u32)(0x1UL << 12))
+#define MSP_RCF_MASK_RDDLY ((u32)(0x3UL << 13))
+#define MSP_RCF_MASK_RFSIG ((u32)(0x1UL << 15))
+#define MSP_RCF_MASK_RP2ELEN ((u32)(0x7UL << 16))
+#define MSP_RCF_MASK_RP2FLEN ((u32)(0x7FUL << 19))
+#define MSP_RCF_MASK_RP2SM ((u32)(0x1UL << 26))
+#define MSP_RCF_MASK_RP2EN ((u32)(0x1UL << 27))
+#define MSP_RCF_MASK_RBSWAP ((u32)(0x3UL << 28))
+
+/**
+ * MSP Sample Rate Generator Register - MSP_SRG
+ */
+#define MSP_SRG_MASK_SCKDIV ((u32)(0x3FFUL << 0))
+#define MSP_SRG_MASK_FRWID ((u32)(0x3FUL << 10))
+#define MSP_SRG_MASK_FRPER ((u32)(0x1FFFUL << 16))
+
+/**
+ * MSP Flag Register - MSP_FLR
+ */
+#define MSP_FLR_MASK_RBUSY ((u32)(0x1UL << 0))
+#define MSP_FLR_MASK_RFE ((u32)(0x1UL << 1))
+#define MSP_FLR_MASK_RFU ((u32)(0x1UL << 2))
+#define MSP_FLR_MASK_TBUSY ((u32)(0x1UL << 3))
+#define MSP_FLR_MASK_TFE ((u32)(0x1UL << 4))
+#define MSP_FLR_MASK_TFU ((u32)(0x1UL << 5))
+
+/**
+ * MSP DMA Control Register - MSP_DMACR
+ */
+#define MSP_DMACR_MASK_RDMAE ((u32)(0x1UL << 0))
+#define MSP_DMACR_MASK_TDMAE ((u32)(0x1UL << 1))
+
+/**
+ * MSP Interrupt Mask Set/Clear Register - MSP_IMSC
+ */
+#define MSP_IMSC_MASK_RXIM ((u32)(0x1UL << 0))
+#define MSP_IMSC_MASK_ROEIM ((u32)(0x1UL << 1))
+#define MSP_IMSC_MASK_RSEIM ((u32)(0x1UL << 2))
+#define MSP_IMSC_MASK_RFSIM ((u32)(0x1UL << 3))
+#define MSP_IMSC_MASK_TXIM ((u32)(0x1UL << 4))
+#define MSP_IMSC_MASK_TUEIM ((u32)(0x1UL << 5))
+#define MSP_IMSC_MASK_TSEIM ((u32)(0x1UL << 6))
+#define MSP_IMSC_MASK_TFSIM ((u32)(0x1UL << 7))
+#define MSP_IMSC_MASK_RFOIM ((u32)(0x1UL << 8))
+#define MSP_IMSC_MASK_TFOIM ((u32)(0x1UL << 9))
+
+/**
+ * MSP Raw Interrupt status Register - MSP_RIS
+ */
+#define MSP_RIS_MASK_RXRIS ((u32)(0x1UL << 0))
+#define MSP_RIS_MASK_ROERIS ((u32)(0x1UL << 1))
+#define MSP_RIS_MASK_RSERIS ((u32)(0x1UL << 2))
+#define MSP_RIS_MASK_RFSRIS ((u32)(0x1UL << 3))
+#define MSP_RIS_MASK_TXRIS ((u32)(0x1UL << 4))
+#define MSP_RIS_MASK_TUERIS ((u32)(0x1UL << 5))
+#define MSP_RIS_MASK_TSERIS ((u32)(0x1UL << 6))
+#define MSP_RIS_MASK_TFSRIS ((u32)(0x1UL << 7))
+#define MSP_RIS_MASK_RFORIS ((u32)(0x1UL << 8))
+#define MSP_RIS_MASK_TFORIS ((u32)(0x1UL << 9))
+
+/**
+ * MSP Masked Interrupt status Register - MSP_MIS
+ */
+#define MSP_MIS_MASK_RXMIS ((u32)(0x1UL << 0))
+#define MSP_MIS_MASK_ROEMIS ((u32)(0x1UL << 1))
+#define MSP_MIS_MASK_RSEMIS ((u32)(0x1UL << 2))
+#define MSP_MIS_MASK_RFSMIS ((u32)(0x1UL << 3))
+#define MSP_MIS_MASK_TXMIS ((u32)(0x1UL << 4))
+#define MSP_MIS_MASK_TUEMIS ((u32)(0x1UL << 5))
+#define MSP_MIS_MASK_TSEMIS ((u32)(0x1UL << 6))
+#define MSP_MIS_MASK_TFSMIS ((u32)(0x1UL << 7))
+#define MSP_MIS_MASK_RFOMIS ((u32)(0x1UL << 8))
+#define MSP_MIS_MASK_TFOMIS ((u32)(0x1UL << 9))
+
+/**
+ * MSP Interrupt Clear Register - MSP_ICR
+ */
+#define MSP_ICR_MASK_ROEIC ((u32)(0x1UL << 1))
+#define MSP_ICR_MASK_RSEIC ((u32)(0x1UL << 2))
+#define MSP_ICR_MASK_RFSIC ((u32)(0x1UL << 3))
+#define MSP_ICR_MASK_TUEIC ((u32)(0x1UL << 5))
+#define MSP_ICR_MASK_TSEIC ((u32)(0x1UL << 6))
+#define MSP_ICR_MASK_TFSIC ((u32)(0x1UL << 7))
+
+#define GEN_MASK_BITS(val, mask, sb) ((u32)((((u32)val) << (sb)) & (mask)))
+#define MSP_WBITS(reg, val, mask, sb) ((reg) = (((reg) & ~(mask)) |\
+ (((val) << (sb)) & (mask))))
+#define DEFAULT_MSP_REG_DMACR 0x00000000
+#define DEFAULT_MSP_REG_SRG 0x1FFF0000
+
+#define DEFAULT_MSP_REG_GCR ( \
+ GEN_MASK_BITS(MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0) |\
+ GEN_MASK_BITS(MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1) |\
+ GEN_MASK_BITS(MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7) |\
+ GEN_MASK_BITS(MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8) |\
+ GEN_MASK_BITS(MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9) |\
+ GEN_MASK_BITS(MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10)|\
+ GEN_MASK_BITS(MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11) |\
+ GEN_MASK_BITS(MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13) |\
+ GEN_MASK_BITS(MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14) |\
+ GEN_MASK_BITS(MSP_TRANSMIT_DATA_WITHOUT_DELAY, MSP_GCR_MASK_TXDDL, 15)|\
+ GEN_MASK_BITS(MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16)|\
+ GEN_MASK_BITS(MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18) |\
+ GEN_MASK_BITS(MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20) |\
+ GEN_MASK_BITS(MSP_SPI_PHASE_ZERO_CYCLE_DELAY, MSP_GCR_MASK_SPICKM, 21)|\
+ GEN_MASK_BITS(SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23)\
+ )
+#define DEFAULT_MSP_REG_RCF ( \
+ GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_RCF_MASK_RP1ELEN, 0) | \
+ GEN_MASK_BITS(MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15) |\
+ GEN_MASK_BITS(MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13) | \
+ GEN_MASK_BITS(MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12) \
+ )
+
+#define DEFAULT_MSP_REG_TCF ( \
+ GEN_MASK_BITS(MSP_DATA_BITS_32, MSP_TCF_MASK_TP1ELEN, 0) | \
+ GEN_MASK_BITS(MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15) |\
+ GEN_MASK_BITS(MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13) | \
+ GEN_MASK_BITS(MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12) \
+ )
+
+/**
+ * MSP Receiver/Transmitter states (enabled or disabled)
+ */
+#define MSP_RECEIVER_DISABLED 0
+#define MSP_RECEIVER_ENABLED 1
+#define MSP_TRANSMITTER_DISABLED 0
+#define MSP_TRANSMITTER_ENABLED 1
+
+/**
+ * MSP Receiver/Transmitter FIFO constants
+ */
+#define MSP_LOOPBACK_DISABLED 0
+#define MSP_LOOPBACK_ENABLED 1
+
+#define MSP_TX_FIFO_DISABLED 0
+#define MSP_TX_FIFO_ENABLED 1
+#define MSP_TX_ENDIANESS_MSB 0
+#define MSP_TX_ENDIANESS_LSB 1
+
+#define MSP_RX_FIFO_DISABLED 0
+#define MSP_RX_FIFO_ENABLED 1
+#define MSP_RX_ENDIANESS_MSB 0
+#define MSP_RX_ENDIANESS_LSB 1
+
+#define MSP_TX_FRAME_SYNC_EXT 0x0
+#define MSP_TX_FRAME_SYNC_INT 0x2
+#define MSP_TX_FRAME_SYNC_INT_CFG 0x3
+
+#define MSP_TX_FRAME_SYNC_POL_HIGH 0
+#define MSP_TX_FRAME_SYNC_POL_LOW 1
+
+#define MSP_HANDLE_RX_FRAME_SYNC_PULSE 0
+#define MSP_IGNORE_RX_FRAME_SYNC_PULSE 1
+
+#define MSP_RX_NO_DATA_DELAY 0x0
+#define MSP_RX_1BIT_DATA_DELAY 0x1
+#define MSP_RX_2BIT_DATA_DELAY 0x2
+#define MSP_RX_3BIT_DATA_DELAY 0x3
+
+#define MSP_HANDLE_TX_FRAME_SYNC_PULSE 0
+#define MSP_IGNORE_TX_FRAME_SYNC_PULSE 1
+
+#define MSP_TX_NO_DATA_DELAY 0x0
+#define MSP_TX_1BIT_DATA_DELAY 0x1
+#define MSP_TX_2BIT_DATA_DELAY 0x2
+#define MSP_TX_3BIT_DATA_DELAY 0x3
+
+#define MSP_TX_CLOCK_POL_LOW 0
+#define MSP_TX_CLOCK_POL_HIGH 1
+
+#define MSP_SPI_PHASE_ZERO_CYCLE_DELAY 0x2
+#define MSP_SPI_PHASE_HALF_CYCLE_DELAY 0x3
+
+#define MSP_IS_SPI_SLAVE 0
+#define MSP_IS_SPI_MASTER 1
+
+#define MSP_FRAME_GEN_DISABLE 0
+#define MSP_FRAME_GEN_ENABLE 1
+
+#define MSP_SAMPLE_RATE_GEN_DISABLE 0
+#define MSP_SAMPLE_RATE_GEN_ENABLE 1
+
+#define SPI_BURST_MODE_DISABLE 0
+#define SPI_BURST_MODE_ENABLE 1
+
+#define MSP_TRANSMIT_DATA_WITHOUT_DELAY 0
+#define MSP_TRANSMIT_DATA_WITH_DELAY 1
+
+#define MSP_CLOCK_INTERNAL 0x0 /* 48 MHz */
+
+/* SRG is derived from MSPSCK pin but is resynchronized on MSPRFS
+ * (Receive Frame Sync signal) */
+#define MSP_CLOCK_EXTERNAL 0x2
+#define MSP_CLOCK_EXTERNAL_RESYNC 0x3
+
+#define DISABLE_ALL_MSP_INTERRUPTS (0x0)
+#define ENABLE_ALL_MSP_INTERRUPTS (0x333)
+#define CLEAR_ALL_MSP_INTERRUPTS (0xEE)
+#define DEFAULT_MSP_CLK (48000000)
+#define MAX_SCKDIV (1023)
+
+#define MSP_FIFO_DEPTH 8
+
+/**
+ * Queue State
+ */
+#define QUEUE_RUNNING (0)
+#define QUEUE_STOPPED (1)
+
+#define START_STATE ((void *)0)
+#define RUNNING_STATE ((void *)1)
+#define DONE_STATE ((void *)2)
+#define ERROR_STATE ((void *)-1)
+
+/* Default values */
+#define SPI_DEFAULT_MAX_SPEED_HZ 48000
+#define SPI_TRANSFER_TIMEOUT_MS 5000
+
+/* CONTROLLER COMMANDS */
+enum cntlr_commands {
+ DISABLE_CONTROLLER = 0,
+ ENABLE_CONTROLLER ,
+ DISABLE_ALL_INTERRUPT ,
+ ENABLE_ALL_INTERRUPT ,
+ FLUSH_FIFO ,
+ RESTORE_STATE ,
+ LOAD_DEFAULT_CONFIG ,
+ CLEAR_ALL_INTERRUPT,
+};
+
+struct stm_msp {
+ struct amba_device *adev;
+ struct spi_master *master;
+ struct stm_msp_controller *master_info;
+ void __iomem *regs;
+ struct clk *clk;
+#ifdef CONFIG_SPI_WORKQUEUE
+ struct workqueue_struct *workqueue;
+#endif
+ struct work_struct spi_work;
+ spinlock_t lock;
+ struct list_head queue;
+ int busy;
+ int run;
+ struct tasklet_struct pump_transfers;
+ struct timer_list spi_notify_timer;
+ int spi_io_error;
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct chip_data *cur_chip;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ void (*write)(struct stm_msp *stm_msp);
+ void (*read)(struct stm_msp *stm_msp);
+ void (*delay)(struct stm_msp *stm_msp);
+};
+
+/**
+ * struct chip_data - To maintain runtime state of SPICntlr for each client chip
+ * @ctr_regs: void pointer which is assigned a struct having regs of the cntlr.
+ * @chip_id: Chip Id assigned to this client to identify it.
+ * @n_bytes: how many bytes(power of 2) reqd for a given data width of client
+ * @write: function to be used to write when doing xfer for this chip
+ * @null_write: function to be used for dummy write for receiving data.
+ * @read: function to be used to read when doing xfer for this chip
+ * @null_read: function to be used to for dummy read while writting data.
+ * @cs_control: chip select callback provided by chip
+ * @xfer_type: polling/interrupt
+ *
+ * Runtime state of the SPI controller, maintained per chip,
+ * This would be set according to the current message that would be served
+ */
+struct chip_data {
+ void *ctr_regs;
+ u32 chip_id;
+ u8 n_bytes;
+ void (*write) (struct stm_msp *stm_msp);
+ void (*null_write) (struct stm_msp *stm_msp);
+ void (*read) (struct stm_msp *stm_msp);
+ void (*null_read) (struct stm_msp *stm_msp);
+ void (*delay) (struct stm_msp *stm_msp);
+ void (*cs_control) (u32 command);
+ int xfer_type;
+};
+
+/**
+ * struct msp_regs - Used to store MSP controller registry values
+ * used by the driver.
+ * @gcr: global configuration register
+ * @tcf: transmit configuration register
+ * @rcf: receive configuration register
+ * @srg: sample rate generator register
+ * @dmacr: DMA configuration register
+ */
+struct msp_regs {
+ u32 gcr;
+ u32 tcf;
+ u32 rcf;
+ u32 srg;
+ u32 dmacr;
+};
+
+/**
+ * stm_msp_controller_cmd - To execute controller commands for MSP
+ * @stm_msp: SPI driver private data structure
+ * @cmd: Command which is to be executed on the controller
+ */
+static int stm_msp_controller_cmd(struct stm_msp *stm_msp, int cmd)
+{
+ int retval = 0;
+ struct msp_regs *msp_regs = NULL;
+
+ switch (cmd) {
+ case DISABLE_CONTROLLER: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Disabling MSP controller...\n");
+ writel((readl(MSP_GCR(stm_msp->regs)) &
+ (~(MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN))),
+ MSP_GCR(stm_msp->regs));
+ break;
+ }
+ case ENABLE_CONTROLLER: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Enabling MSP controller...\n");
+ writel((readl(MSP_GCR(stm_msp->regs)) |
+ (MSP_GCR_MASK_TXEN | MSP_GCR_MASK_RXEN)),
+ MSP_GCR(stm_msp->regs));
+ break;
+ }
+ case DISABLE_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Disabling all MSP interrupts...\n");
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ break;
+ }
+ case ENABLE_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Enabling all MSP interrupts...\n");
+ writel(ENABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ break;
+ }
+ case CLEAR_ALL_INTERRUPT: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Clearing all MSP interrupts...\n");
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ case FLUSH_FIFO: {
+ unsigned long limit = loops_per_jiffy << 1;
+
+ dev_dbg(&stm_msp->adev->dev, "MSP FIFO flushed\n");
+
+ do {
+ while (!(readl(MSP_FLR(stm_msp->regs)) &
+ MSP_FLR_MASK_RFE)) {
+ readl(MSP_DR(stm_msp->regs));
+ }
+ } while ((readl(MSP_FLR(stm_msp->regs)) &
+ (MSP_FLR_MASK_TBUSY | MSP_FLR_MASK_RBUSY)) &&
+ limit--);
+
+ retval = limit;
+ break;
+ }
+ case RESTORE_STATE: {
+ msp_regs =
+ (struct msp_regs *)stm_msp->cur_chip->ctr_regs;
+
+ dev_dbg(&stm_msp->adev->dev,
+ "Restoring MSP state...\n");
+
+ writel(msp_regs->gcr, MSP_GCR(stm_msp->regs));
+ writel(msp_regs->tcf, MSP_TCF(stm_msp->regs));
+ writel(msp_regs->rcf, MSP_RCF(stm_msp->regs));
+ writel(msp_regs->srg, MSP_SRG(stm_msp->regs));
+ writel(msp_regs->dmacr, MSP_DMACR(stm_msp->regs));
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ case LOAD_DEFAULT_CONFIG: {
+ dev_dbg(&stm_msp->adev->dev,
+ "Loading default MSP config...\n");
+
+ writel(DEFAULT_MSP_REG_GCR, MSP_GCR(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_TCF, MSP_TCF(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_RCF, MSP_RCF(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_SRG, MSP_SRG(stm_msp->regs));
+ writel(DEFAULT_MSP_REG_DMACR, MSP_DMACR(stm_msp->regs));
+ writel(DISABLE_ALL_MSP_INTERRUPTS,
+ MSP_IMSC(stm_msp->regs));
+ writel(CLEAR_ALL_MSP_INTERRUPTS,
+ MSP_ICR(stm_msp->regs));
+ break;
+ }
+ default:
+ dev_dbg(&stm_msp->adev->dev, "Unknown command\n");
+ retval = -1;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * giveback - current spi_message is over, schedule next spi_message
+ * @message: current SPI message
+ * @stm_msp: spi driver private data structure
+ *
+ * current spi_message is over, schedule next spi_message and call
+ * callback of this msg.
+ */
+static void giveback(struct spi_message *message, struct stm_msp *stm_msp)
+{
+ struct spi_transfer *last_transfer;
+ unsigned long flags;
+ struct spi_message *msg;
+ void (*curr_cs_control)(u32 command);
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+ msg = stm_msp->cur_msg;
+
+ curr_cs_control = stm_msp->cur_chip->cs_control;
+
+ stm_msp->cur_msg = NULL;
+ stm_msp->cur_transfer = NULL;
+ stm_msp->cur_chip = NULL;
+#ifdef CONFIG_SPI_WORKQUEUE
+ queue_work(stm_msp->workqueue, &stm_msp->spi_work);
+#else
+ schedule_work(&stm_msp->spi_work);
+#endif
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ last_transfer = list_entry(msg->transfers.prev,
+ struct spi_transfer, transfer_list);
+
+ if (!last_transfer->cs_change)
+ curr_cs_control(SPI_CHIP_DESELECT);
+
+ msg->state = NULL;
+
+ if (msg->complete)
+ msg->complete(msg->context);
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ clk_disable(stm_msp->clk);
+}
+
+/**
+ * spi_notify - Handles Polling hang issue over spi bus.
+ * @data: main driver data
+ * Context: Process.
+ *
+ * This is used to handle error condition in transfer and receive function used
+ * in polling mode.
+ * Sometimes due to passing wrong protocol desc , polling transfer may hang.
+ * To prevent this, timer is added.
+ *
+ * Returns void.
+ */
+static void spi_notify(unsigned long data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ stm_msp->spi_io_error = 1;
+
+ dev_err(&stm_msp->adev->dev,
+ "Polling is taking time, maybe device not responding\n");
+
+ del_timer(&stm_msp->spi_notify_timer);
+}
+
+/**
+ * stm_msp_transfer - transfer function registered to SPI master framework
+ * @spi: spi device which is requesting transfer
+ * @msg: spi message which is to handled is queued to driver queue
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will queue the spi_message in the queue of driver if
+ * the queue is not stopped and return.
+ */
+static int stm_msp_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (stm_msp->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return -ESHUTDOWN;
+ }
+ dev_err(&spi->dev, "Regular request (No infinite DMA ongoing)\n");
+
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = START_STATE;
+
+ list_add_tail(&msg->queue, &stm_msp->queue);
+
+ if ((stm_msp->run == QUEUE_RUNNING) && (!stm_msp->busy))
+#ifdef CONFIG_SPI_WORKQUEUE
+ queue_work(stm_msp->workqueue, &stm_msp->spi_work);
+#else
+ schedule_work(&stm_msp->spi_work);
+#endif
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return 0;
+}
+
+/**
+ * next_transfer - Move to the Next transfer in the current spi message
+ * @stm_msp: spi driver private data structure
+ *
+ * This function moves though the linked list of spi transfers in the
+ * current spi message and returns with the state of current spi
+ * message i.e whether its last transfer is done(DONE_STATE) or
+ * Next transfer is ready(RUNNING_STATE)
+ */
+static void *next_transfer(struct stm_msp *stm_msp)
+{
+ struct spi_message *msg = stm_msp->cur_msg;
+ struct spi_transfer *trans = stm_msp->cur_transfer;
+
+ /* Move to next transfer */
+ if (trans->transfer_list.next != &msg->transfers) {
+ stm_msp->cur_transfer = list_entry(trans->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ return RUNNING_STATE;
+ }
+ return DONE_STATE;
+}
+
+static void do_interrupt_transfer(void *data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+
+ stm_msp->tx = (void *)stm_msp->cur_transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+
+ stm_msp->rx = (void *)stm_msp->cur_transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write : stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read : stm_msp->cur_chip->null_read;
+
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+
+ stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+}
+
+static void do_polling_transfer(void *data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ struct chip_data *chip;
+ unsigned long limit = 0;
+ u32 timer_expire = 0;
+
+ chip = stm_msp->cur_chip;
+ message = stm_msp->cur_msg;
+
+ while (message->state != DONE_STATE) {
+ /* Handle for abort */
+ if (message->state == ERROR_STATE)
+ break;
+
+ transfer = stm_msp->cur_transfer;
+
+ /* Delay if requested at end of 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);
+
+ if (previous->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ } else {
+ /* START_STATE */
+ message->state = RUNNING_STATE;
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ }
+
+ /* Configuration Changing Per Transfer */
+ stm_msp->tx = (void *)transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+ stm_msp->rx = (void *)transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write :
+ stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read :
+ stm_msp->cur_chip->null_read;
+ stm_msp->delay = stm_msp->cur_chip->delay;
+
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+
+ timer_expire = stm_msp->cur_transfer->len / 1024;
+
+ if (!timer_expire)
+ timer_expire = SPI_TRANSFER_TIMEOUT_MS;
+ else
+ timer_expire =
+ (stm_msp->cur_transfer->len / 1024) *
+ SPI_TRANSFER_TIMEOUT_MS;
+
+ stm_msp->spi_notify_timer.expires =
+ jiffies + msecs_to_jiffies(timer_expire);
+
+ add_timer(&stm_msp->spi_notify_timer);
+
+ dev_dbg(&stm_msp->adev->dev, "Polling transfer ongoing...\n");
+
+ while (stm_msp->tx < stm_msp->tx_end) {
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ stm_msp->read(stm_msp);
+ stm_msp->write(stm_msp);
+
+ stm_msp_controller_cmd(stm_msp, ENABLE_CONTROLLER);
+
+ if (stm_msp->delay)
+ stm_msp->delay(stm_msp);
+
+ if (stm_msp->spi_io_error == 1)
+ break;
+ }
+
+ del_timer(&stm_msp->spi_notify_timer);
+
+ if (stm_msp->spi_io_error == 1)
+ goto out;
+
+ limit = loops_per_jiffy << 1;
+
+ while ((stm_msp->rx < stm_msp->rx_end) && (limit--))
+ stm_msp->read(stm_msp);
+
+ /* Update total byte transfered */
+ message->actual_length += stm_msp->cur_transfer->len;
+
+ if (stm_msp->cur_transfer->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_DESELECT);
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+
+ /* Move to next transfer */
+ message->state = next_transfer(stm_msp);
+ }
+out:
+ /* Handle end of message */
+ if (message->state == DONE_STATE)
+ message->status = 0;
+ else
+ message->status = -EIO;
+
+ giveback(message, stm_msp);
+
+ stm_msp->spi_io_error = 0; /* Reset state for further transfers */
+
+ return;
+}
+
+/**
+ * pump_messages - Workqueue function which processes spi message queue
+ * @work: pointer to work
+ *
+ * This function checks if there is any spi message in the queue that
+ * needs processing and delegate control to appropriate function
+ * do_polling_transfer()/do_interrupt_transfer()/do_dma_transfer()
+ * based on the kind of the transfer
+ *
+ */
+static void pump_messages(struct work_struct *work)
+{
+ struct stm_msp *stm_msp = container_of(work, struct stm_msp, spi_work);
+ unsigned long flags;
+
+ /* Lock queue and check for queue work */
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (list_empty(&stm_msp->queue) || stm_msp->run == QUEUE_STOPPED) {
+ dev_dbg(&stm_msp->adev->dev, "work_queue: Queue Empty\n");
+ stm_msp->busy = 0;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return;
+ }
+ /* Make sure we are not already running a message */
+ if (stm_msp->cur_msg) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return;
+ }
+
+ clk_enable(stm_msp->clk);
+
+ /* Extract head of queue */
+ stm_msp->cur_msg = list_entry(stm_msp->queue.next,
+ struct spi_message,
+ queue);
+
+ list_del_init(&stm_msp->cur_msg->queue);
+ stm_msp->busy = 1;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ /* Initial message state */
+ stm_msp->cur_msg->state = START_STATE;
+ stm_msp->cur_transfer = list_entry(stm_msp->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Setup the SPI using the per chip configuration */
+ stm_msp->cur_chip = spi_get_ctldata(stm_msp->cur_msg->spi);
+ stm_msp_controller_cmd(stm_msp, RESTORE_STATE);
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+
+ if (stm_msp->cur_chip->xfer_type == SPI_POLLING_TRANSFER)
+ do_polling_transfer(stm_msp);
+ else if (stm_msp->cur_chip->xfer_type == SPI_INTERRUPT_TRANSFER)
+ do_interrupt_transfer(stm_msp);
+}
+
+/**
+ * pump_transfers - Tasklet function which schedules next interrupt xfer
+ * @data: spi driver private data structure
+ */
+static void pump_transfers(unsigned long data)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+
+ message = stm_msp->cur_msg;
+
+ /* Handle for abort */
+ if (message->state == ERROR_STATE) {
+ message->status = -EIO;
+ giveback(message, stm_msp);
+ return;
+ }
+
+ /* Handle end of message */
+ if (message->state == DONE_STATE) {
+ message->status = 0;
+ giveback(message, stm_msp);
+ return;
+ }
+ transfer = stm_msp->cur_transfer;
+
+ /* Delay if requested at end of 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);
+ if (previous->cs_change)
+ stm_msp->cur_chip->cs_control(SPI_CHIP_SELECT);
+ } else {
+ /* START_STATE */
+ message->state = RUNNING_STATE;
+ }
+ stm_msp->tx = (void *)transfer->tx_buf;
+ stm_msp->tx_end = stm_msp->tx + stm_msp->cur_transfer->len;
+ stm_msp->rx = (void *)transfer->rx_buf;
+ stm_msp->rx_end = stm_msp->rx + stm_msp->cur_transfer->len;
+
+ stm_msp->write = stm_msp->tx ?
+ stm_msp->cur_chip->write : stm_msp->cur_chip->null_write;
+ stm_msp->read = stm_msp->rx ?
+ stm_msp->cur_chip->read : stm_msp->cur_chip->null_read;
+
+ stm_msp_controller_cmd(stm_msp, FLUSH_FIFO);
+ stm_msp_controller_cmd(stm_msp, ENABLE_ALL_INTERRUPT);
+}
+
+static int init_queue(struct stm_msp *stm_msp)
+{
+ INIT_LIST_HEAD(&stm_msp->queue);
+ spin_lock_init(&stm_msp->lock);
+
+ stm_msp->run = QUEUE_STOPPED;
+ stm_msp->busy = 0;
+
+ tasklet_init(&stm_msp->pump_transfers, pump_transfers,
+ (unsigned long)stm_msp);
+ INIT_WORK(&stm_msp->spi_work, pump_messages);
+
+#ifdef CONFIG_SPI_WORKQUEUE
+ stm_msp->workqueue = create_singlethread_workqueue(
+ dev_name(&stm_msp->master->dev));
+
+ if (stm_msp->workqueue == NULL)
+ return -EBUSY;
+#endif /* CONFIG_SPI_WORKQUEUE */
+
+ init_timer(&stm_msp->spi_notify_timer);
+
+ stm_msp->spi_notify_timer.expires = jiffies + msecs_to_jiffies(1000);
+ stm_msp->spi_notify_timer.function = spi_notify;
+ stm_msp->spi_notify_timer.data = (unsigned long)stm_msp;
+
+ return 0;
+}
+
+static int start_queue(struct stm_msp *stm_msp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ if (stm_msp->run == QUEUE_RUNNING || stm_msp->busy) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return -EBUSY;
+ }
+
+ stm_msp->run = QUEUE_RUNNING;
+ stm_msp->cur_msg = NULL;
+ stm_msp->cur_transfer = NULL;
+ stm_msp->cur_chip = NULL;
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ return 0;
+}
+
+static int stop_queue(struct stm_msp *stm_msp)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+ int status = 0;
+
+ spin_lock_irqsave(&stm_msp->lock, flags);
+
+ /* This is a bit lame, but is optimized for the common execution path.
+ * A wait_queue on the stm_msp->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 */
+
+ stm_msp->run = QUEUE_STOPPED;
+
+ while (!list_empty(&stm_msp->queue) && stm_msp->busy && limit--) {
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&stm_msp->lock, flags);
+ }
+
+ if (!list_empty(&stm_msp->queue) || stm_msp->busy)
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&stm_msp->lock, flags);
+
+ return status;
+}
+
+static int destroy_queue(struct stm_msp *stm_msp)
+{
+ int status;
+
+ status = stop_queue(stm_msp);
+
+ if (status != 0)
+ return status;
+#ifdef CONFIG_SPI_WORKQUEUE
+ destroy_workqueue(stm_msp->workqueue);
+#endif
+ del_timer_sync(&stm_msp->spi_notify_timer);
+
+ return 0;
+}
+
+/**
+ * stm_msp_null_writer - To Write Dummy Data in Data register
+ * @stm_msp: spi driver private data structure
+ *
+ * This function is set as a write function for transfer which have
+ * Tx transfer buffer as NULL. It simply writes '0' in the Data
+ * register
+ */
+static void stm_msp_null_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel(0x0, MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == 8)
+ return;
+ }
+}
+
+/**
+ * stm_msp_null_reader - To read data from Data register and discard it
+ * @stm_msp: spi driver private data structure
+ *
+ * This function is set as a reader function for transfer which have
+ * Rx Transfer buffer as null. Read Data is rejected
+ */
+static void stm_msp_null_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u8_writer - Write FIFO data in Data register as a 8 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u8_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel((u32)(*(u8 *)(stm_msp->tx)), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u8_reader - Read FIFO data in Data register as a 8 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u8_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u8 *)(stm_msp->rx) = (u8)readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u16_writer - Write FIFO data in Data register as a 16 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u16_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ writel((u32)(*(u16 *)(stm_msp->tx)), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u16_reader - Read FIFO data in Data register as a 16 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u16_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u16 *)(stm_msp->rx) = (u16)readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_u32_writer - Write FIFO data in Data register as a 32 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function writes data in Tx FIFO till it is not full
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary write ptr tx in stm_msp which maintains
+ * current write position in transfer buffer. we do not write data more than
+ * FIFO depth
+ */
+void stm_msp_u32_writer(struct stm_msp *stm_msp)
+{
+ u32 cur_write = 0;
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_TFU) ||
+ (stm_msp->tx >= stm_msp->tx_end))
+ return;
+
+ /* Write Data to Data Register */
+ writel(*(u32 *)(stm_msp->tx), MSP_DR(stm_msp->regs));
+ stm_msp->tx += (stm_msp->cur_chip->n_bytes);
+ cur_write++;
+
+ if (cur_write == MSP_FIFO_DEPTH)
+ return;
+ }
+}
+
+/**
+ * stm_msp_u32_reader - Read FIFO data in Data register as a 32 Bit Data
+ * @stm_msp: spi driver private data structure
+ *
+ * This function reads data in Rx FIFO till it is not empty
+ * which is indicated by the status register or our transfer is complete.
+ * It also updates the temporary Read ptr rx in stm_msp which maintains
+ * current read position in transfer buffer
+ */
+void stm_msp_u32_reader(struct stm_msp *stm_msp)
+{
+ u32 status;
+
+ while (1) {
+ status = readl(MSP_FLR(stm_msp->regs));
+
+ if ((status & MSP_FLR_MASK_RFE) ||
+ (stm_msp->rx >= stm_msp->rx_end))
+ return;
+
+ *(u32 *)(stm_msp->rx) = readl(MSP_DR(stm_msp->regs));
+ stm_msp->rx += (stm_msp->cur_chip->n_bytes);
+ }
+}
+
+/**
+ * stm_msp_interrupt_handler - Interrupt hanlder function
+ */
+static irqreturn_t stm_msp_interrupt_handler(int irq, void *dev_id)
+{
+ struct stm_msp *stm_msp = (struct stm_msp *)dev_id;
+ struct spi_message *msg = stm_msp->cur_msg;
+ u32 irq_status = 0;
+ u32 flag = 0;
+
+ if (!msg) {
+ dev_err(&stm_msp->adev->dev,
+ "Bad message state in interrupt handler");
+ /* Never fail */
+ return IRQ_HANDLED;
+ }
+
+ /* Read the Interrupt Status Register */
+ irq_status = readl(MSP_MIS(stm_msp->regs));
+
+ if (irq_status) {
+ if (irq_status & MSP_MIS_MASK_ROEMIS) { /* Overrun interrupt */
+ /* Bail out our Data has been corrupted */
+ dev_dbg(&stm_msp->adev->dev,
+ "Received ROR interrupt\n");
+
+ stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, DISABLE_CONTROLLER);
+ msg->state = ERROR_STATE;
+ tasklet_schedule(&stm_msp->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ stm_msp->read(stm_msp);
+ stm_msp->write(stm_msp);
+
+ if ((stm_msp->tx == stm_msp->tx_end) && (flag == 0)) {
+ flag = 1;
+ /* Disable Transmit interrupt */
+ writel(readl(MSP_IMSC(stm_msp->regs)) &
+ (~MSP_IMSC_MASK_TXIM) & (~MSP_IMSC_MASK_TFOIM),
+ (stm_msp->regs + 0x14));
+ }
+
+ /* Clearing any Xmit underrun error. Overrun already handled */
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+
+ if (stm_msp->rx == stm_msp->rx_end) {
+ stm_msp_controller_cmd(stm_msp, DISABLE_ALL_INTERRUPT);
+ stm_msp_controller_cmd(stm_msp, CLEAR_ALL_INTERRUPT);
+
+ dev_dbg(&stm_msp->adev->dev,
+ "Interrupt transfer completed.\n");
+
+ /* Update total bytes transfered */
+ msg->actual_length += stm_msp->cur_transfer->len;
+
+ if (stm_msp->cur_transfer->cs_change)
+ stm_msp->cur_chip->cs_control(
+ SPI_CHIP_DESELECT);
+
+ /* Move to next transfer */
+ msg->state = next_transfer(stm_msp);
+ tasklet_schedule(&stm_msp->pump_transfers);
+ return IRQ_HANDLED;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * stm_msp_cleanup - cleanup function registered to SPI master framework
+ * @spi: spi device which is requesting cleanup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. It will free the runtime state of chip.
+ */
+static void stm_msp_cleanup(struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi);
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ struct spi_master *master;
+ master = stm_msp->master;
+
+ if (chip) {
+ kfree(chip->ctr_regs);
+ kfree(chip);
+ spi_set_ctldata(spi, NULL);
+ }
+}
+
+/**
+ * null_cs_control - Dummy chip select function
+ * @command: select/delect the chip
+ *
+ * If no chip select function is provided by client this is used as dummy
+ * chip select
+ */
+static void null_cs_control(u32 command)
+{
+ /* Nothing to do */
+ (void)command;
+}
+
+static int verify_msp_controller_parameters(struct stm_msp_config_chip
+ *chip_info)
+{
+
+ /* FIXME: check clock params */
+ if ((chip_info->lbm != SPI_LOOPBACK_ENABLED) &&
+ (chip_info->lbm != SPI_LOOPBACK_DISABLED)) {
+ dev_dbg(chip_info->dev,
+ "Loopback Mode is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->hierarchy != SPI_MASTER) &&
+ (chip_info->hierarchy != SPI_SLAVE)) {
+ dev_dbg(chip_info->dev,
+ "hierarchy is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->endian_rx != SPI_FIFO_MSB) &&
+ (chip_info->endian_rx != SPI_FIFO_LSB)) {
+ dev_dbg(chip_info->dev,
+ "Rx FIFO endianess is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->endian_tx != SPI_FIFO_MSB) &&
+ (chip_info->endian_tx != SPI_FIFO_LSB)) {
+ dev_dbg(chip_info->dev,
+ "Tx FIFO endianess is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->data_size < MSP_DATA_BITS_8) ||
+ (chip_info->data_size > MSP_DATA_BITS_32)) {
+ dev_dbg(chip_info->dev,
+ "MSP DATA Size is configured incorrectly\n");
+ return -1;
+ }
+ if ((chip_info->com_mode != SPI_INTERRUPT_TRANSFER) &&
+ (chip_info->com_mode != SPI_POLLING_TRANSFER)) {
+ dev_dbg(chip_info->dev,
+ "Communication mode is configured incorrectly\n");
+ return -1;
+ }
+ if (((chip_info->proto_params).clk_phase !=
+ SPI_CLK_ZERO_CYCLE_DELAY) &&
+ ((chip_info->proto_params).clk_phase !=
+ SPI_CLK_HALF_CYCLE_DELAY)) {
+ dev_dbg(chip_info->dev,
+ "Clock Phase is configured incorrectly\n");
+ return -1;
+ }
+ if (((chip_info->proto_params).clk_pol !=
+ SPI_CLK_POL_IDLE_LOW) &&
+ ((chip_info->proto_params).clk_pol !=
+ SPI_CLK_POL_IDLE_HIGH)) {
+ dev_dbg(chip_info->dev,
+ "Clk Polarity configured incorrectly\n");
+ return -1;
+ }
+ if (chip_info->cs_control == NULL) {
+ dev_dbg(chip_info->dev,
+ "Chip Select Function is NULL for this chip\n");
+ chip_info->cs_control = null_cs_control;
+ }
+ return 0;
+}
+
+static struct stm_msp_config_chip *allocate_default_msp_chip_cfg(
+ struct spi_device *spi)
+{
+ struct stm_msp_config_chip *chip_info;
+
+ chip_info = kzalloc(sizeof(struct stm_msp_config_chip), GFP_KERNEL);
+
+ if (!chip_info) {
+ dev_err(&spi->dev, "setup - cannot allocate controller data");
+ return NULL;
+ }
+ dev_dbg(&spi->dev, "Allocated Memory for controller data\n");
+
+ chip_info->lbm = SPI_LOOPBACK_DISABLED;
+ chip_info->com_mode = SPI_POLLING_TRANSFER;
+ chip_info->hierarchy = SPI_MASTER;
+ chip_info->endian_tx = SPI_FIFO_LSB;
+ chip_info->endian_rx = SPI_FIFO_LSB;
+ chip_info->data_size = MSP_DATA_BITS_32;
+
+ if (spi->max_speed_hz != 0)
+ chip_info->freq = spi->max_speed_hz;
+ else
+ chip_info->freq = SPI_DEFAULT_MAX_SPEED_HZ;
+
+ chip_info->proto_params.clk_phase = SPI_CLK_HALF_CYCLE_DELAY;
+ chip_info->proto_params.clk_pol = SPI_CLK_POL_IDLE_LOW;
+ chip_info->cs_control = null_cs_control;
+
+ return chip_info;
+}
+
+static void stm_msp_delay(struct stm_msp *stm_msp)
+{
+ udelay(15);
+
+ while (readl(MSP_FLR(stm_msp->regs)) &
+ (MSP_FLR_MASK_RBUSY | MSP_FLR_MASK_TBUSY))
+ udelay(1);
+}
+
+/**
+ * stm_msp_setup - setup function registered to SPI master framework
+ * @spi: spi device which is requesting setup
+ *
+ * This function is registered to the SPI framework for this SPI master
+ * controller. If it is the first time when setup is called by this device,
+ * this function will initialize the runtime state for this chip and save
+ * the same in the device structure. Else it will update the runtime info
+ * with the updated chip info.
+ */
+static int stm_msp_setup(struct spi_device *spi)
+{
+ struct stm_msp_config_chip *chip_info;
+ struct chip_data *curr_cfg;
+ struct spi_master *master;
+ int status = 0;
+ u16 sckdiv = 0;
+ s16 bus_num = 0;
+ struct stm_msp *stm_msp = spi_master_get_devdata(spi->master);
+ struct msp_regs *msp_regs;
+ master = stm_msp->master;
+ bus_num = master->bus_num - 1;
+
+ /* Get controller data */
+ chip_info = spi->controller_data;
+ /* Get controller_state */
+ curr_cfg = spi_get_ctldata(spi);
+
+ if (curr_cfg == NULL) {
+ curr_cfg = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+
+ if (!curr_cfg) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate controller state");
+ return -ENOMEM;
+ }
+
+ curr_cfg->chip_id = spi->chip_select;
+ curr_cfg->ctr_regs = kzalloc(sizeof(struct msp_regs),
+ GFP_KERNEL);
+
+ if (curr_cfg->ctr_regs == NULL) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate mem for regs");
+ goto err_first_setup;
+ }
+
+ dev_err(&stm_msp->adev->dev,
+ "chip Id = %d\n", curr_cfg->chip_id);
+
+ if (chip_info == NULL) {
+ chip_info = allocate_default_msp_chip_cfg(spi);
+
+ if (!chip_info) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - cannot allocate cntlr data");
+ status = -ENOMEM;
+ goto err_first_setup;
+ }
+
+ spi->controller_data = chip_info;
+ }
+ }
+
+ /* Pointer back to the SPI device */
+ chip_info->dev = &spi->dev;
+
+ if (chip_info->freq == 0) {
+ /* Calculate Specific Freq. */
+ if ((MSP_INTERNAL_CLK == chip_info->clk_freq.clk_src) ||
+ (MSP_EXTERNAL_CLK == chip_info->clk_freq.clk_src)) {
+ sckdiv = chip_info->clk_freq.sckdiv;
+ } else {
+ status = -1;
+ dev_err(&stm_msp->adev->dev,
+ "setup - controller clock data is incorrect");
+ goto err_config_params;
+ }
+ } else {
+ /* Calculate Effective Freq. */
+ sckdiv = (DEFAULT_MSP_CLK / (chip_info->freq)) - 1;
+
+ if (sckdiv > MAX_SCKDIV) {
+ dev_dbg(&stm_msp->adev->dev,
+ "SPI: Cannot set frequency less than 48Khz,"
+ "setting lowest(48 Khz)\n");
+ sckdiv = MAX_SCKDIV;
+ }
+ }
+
+ status = verify_msp_controller_parameters(chip_info);
+
+ if (status) {
+ dev_err(&stm_msp->adev->dev,
+ "setup - controller data is incorrect");
+ goto err_config_params;
+ }
+
+ /* Now set controller state based on controller data */
+ curr_cfg->xfer_type = chip_info->com_mode;
+ curr_cfg->cs_control = chip_info->cs_control;
+ curr_cfg->delay = stm_msp_delay;
+
+ curr_cfg->null_write = stm_msp_null_writer;
+ curr_cfg->null_read = stm_msp_null_reader;
+
+ if (chip_info->data_size <= MSP_DATA_BITS_8) {
+ dev_dbg(&stm_msp->adev->dev, "Less than 8 bits per word...\n");
+
+ curr_cfg->n_bytes = 1;
+ curr_cfg->read = stm_msp_u8_reader;
+ curr_cfg->write = stm_msp_u8_writer;
+ } else if (chip_info->data_size <= MSP_DATA_BITS_16) {
+ dev_dbg(&stm_msp->adev->dev, "Less than 16 bits per word...\n");
+
+ curr_cfg->n_bytes = 2;
+ curr_cfg->read = stm_msp_u16_reader;
+ curr_cfg->write = stm_msp_u16_writer;
+ } else {
+ dev_dbg(&stm_msp->adev->dev, "Less than 32 bits per word...\n");
+
+ curr_cfg->n_bytes = 4;
+ curr_cfg->read = stm_msp_u32_reader;
+ curr_cfg->write = stm_msp_u32_writer;
+ }
+
+ /* Now initialize all register settings reqd. for this chip */
+
+ msp_regs = (struct msp_regs *)(curr_cfg->ctr_regs);
+ msp_regs->gcr = 0x0;
+ msp_regs->tcf = 0x0;
+ msp_regs->rcf = 0x0;
+ msp_regs->srg = 0x0;
+ msp_regs->dmacr = 0x0;
+
+ MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_RDMAE, 0);
+ MSP_WBITS(msp_regs->dmacr, 0x0, MSP_DMACR_MASK_TDMAE, 1);
+
+ /* GCR Reg Config */
+
+ MSP_WBITS(msp_regs->gcr,
+ MSP_RECEIVER_DISABLED, MSP_GCR_MASK_RXEN, 0);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_RX_FIFO_ENABLED, MSP_GCR_MASK_RFFEN, 1);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TRANSMITTER_DISABLED, MSP_GCR_MASK_TXEN, 8);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FIFO_ENABLED, MSP_GCR_MASK_TFFEN, 9);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FRAME_SYNC_POL_LOW, MSP_GCR_MASK_TFSPOL, 10);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_FRAME_SYNC_INT, MSP_GCR_MASK_TFSSEL, 11);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TRANSMIT_DATA_WITH_DELAY, MSP_GCR_MASK_TXDDL, 15);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SAMPLE_RATE_GEN_ENABLE, MSP_GCR_MASK_SGEN, 16);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_CLOCK_INTERNAL, MSP_GCR_MASK_SCKSEL, 18);
+ MSP_WBITS(msp_regs->gcr,
+ MSP_FRAME_GEN_ENABLE, MSP_GCR_MASK_FGEN, 20);
+ MSP_WBITS(msp_regs->gcr,
+ SPI_BURST_MODE_DISABLE, MSP_GCR_MASK_SPIBME, 23);
+
+ if (chip_info->lbm == SPI_LOOPBACK_ENABLED)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_LOOPBACK_ENABLED, MSP_GCR_MASK_LBM, 7);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_LOOPBACK_DISABLED, MSP_GCR_MASK_LBM, 7);
+
+ if (chip_info->hierarchy == SPI_MASTER)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_IS_SPI_MASTER, MSP_GCR_MASK_TCKSEL, 14);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_IS_SPI_SLAVE, MSP_GCR_MASK_TCKSEL, 14);
+
+ if (chip_info->proto_params.clk_phase == SPI_CLK_ZERO_CYCLE_DELAY)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SPI_PHASE_ZERO_CYCLE_DELAY,
+ MSP_GCR_MASK_SPICKM, 21);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_SPI_PHASE_HALF_CYCLE_DELAY,
+ MSP_GCR_MASK_SPICKM, 21);
+
+ if (chip_info->proto_params.clk_pol == SPI_CLK_POL_IDLE_HIGH)
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_CLOCK_POL_HIGH, MSP_GCR_MASK_TCKPOL, 13);
+ else
+ MSP_WBITS(msp_regs->gcr,
+ MSP_TX_CLOCK_POL_LOW, MSP_GCR_MASK_TCKPOL, 13);
+
+ /* RCF Reg Config */
+ MSP_WBITS(msp_regs->rcf,
+ MSP_IGNORE_RX_FRAME_SYNC_PULSE, MSP_RCF_MASK_RFSIG, 15);
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_1BIT_DATA_DELAY, MSP_RCF_MASK_RDDLY, 13);
+
+ if (chip_info->endian_rx == SPI_FIFO_LSB)
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_ENDIANESS_LSB, MSP_RCF_MASK_RENDN, 12);
+ else
+ MSP_WBITS(msp_regs->rcf,
+ MSP_RX_ENDIANESS_MSB, MSP_RCF_MASK_RENDN, 12);
+
+ MSP_WBITS(msp_regs->rcf, chip_info->data_size, MSP_RCF_MASK_RP1ELEN, 0);
+
+ /* TCF Reg Config */
+
+ MSP_WBITS(msp_regs->tcf,
+ MSP_IGNORE_TX_FRAME_SYNC_PULSE, MSP_TCF_MASK_TFSIG, 15);
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_1BIT_DATA_DELAY, MSP_TCF_MASK_TDDLY, 13);
+
+ if (chip_info->endian_rx == SPI_FIFO_LSB)
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_ENDIANESS_LSB, MSP_TCF_MASK_TENDN, 12);
+ else
+ MSP_WBITS(msp_regs->tcf,
+ MSP_TX_ENDIANESS_MSB, MSP_TCF_MASK_TENDN, 12);
+ MSP_WBITS(msp_regs->tcf, chip_info->data_size, MSP_TCF_MASK_TP1ELEN, 0);
+
+ /* SRG Reg Config */
+
+ MSP_WBITS(msp_regs->srg, sckdiv, MSP_SRG_MASK_SCKDIV, 0);
+
+ /* Save controller_state */
+ spi_set_ctldata(spi, curr_cfg);
+
+ return status;
+
+err_config_params:
+err_first_setup:
+
+ kfree(curr_cfg);
+ return status;
+}
+
+static int __init stm_msp_probe(struct amba_device *adev, struct amba_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct stm_msp_controller *platform_info = adev->dev.platform_data;
+ struct spi_master *master;
+ struct stm_msp *stm_msp = NULL; /* Data for this driver */
+ int irq, status = 0;
+
+ dev_info(dev, "STM MSP driver, device ID: 0x%08x\n", adev->periphid);
+
+ if (platform_info == NULL) {
+ dev_err(dev, "probe - no platform data supplied\n");
+ status = -ENODEV;
+ goto err_no_pdata;
+ }
+
+ /* Allocate master with space for data */
+ master = spi_alloc_master(dev, sizeof(struct stm_msp));
+
+ if (master == NULL) {
+ dev_err(dev, "probe - cannot alloc spi_master\n");
+ status = -ENOMEM;
+ goto err_no_mem;
+ }
+
+ stm_msp = spi_master_get_devdata(master);
+ stm_msp->master = master;
+ stm_msp->master_info = platform_info;
+ stm_msp->adev = adev;
+
+ stm_msp->clk = clk_get(&adev->dev, NULL);
+
+ if (IS_ERR(stm_msp->clk)) {
+ dev_err(dev, "probe - cannot find clock\n");
+ status = PTR_ERR(stm_msp->clk);
+ goto free_master;
+ }
+
+ /* Fetch the Resources, using platform data */
+ status = amba_request_regions(adev, NULL);
+
+ if (status) {
+ status = -ENODEV;
+ goto disable_clk;
+ }
+
+ /* Get Hold of Device Register Area... */
+ stm_msp->regs = ioremap(adev->res.start, resource_size(&adev->res));
+
+ if (stm_msp->regs == NULL) {
+ status = -ENODEV;
+ goto disable_clk;
+ }
+
+ irq = adev->irq[0];
+
+ if (irq <= 0) {
+ status = -ENODEV;
+ goto err_no_iores;
+ }
+
+ stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG);
+
+ /* Required Info for an SPI controller */
+ /* Bus Number Which Assigned to this SPI controller on this board */
+ master->bus_num = (u16) platform_info->id;
+ master->num_chipselect = platform_info->num_chipselect;
+ master->setup = stm_msp_setup;
+ master->cleanup = (void *)stm_msp_cleanup;
+ master->transfer = stm_msp_transfer;
+
+ dev_dbg(dev, "BUSNO: %d\n", master->bus_num);
+
+ /* Initialize and start queue */
+ status = init_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem initializing queue\n");
+ goto err_init_queue;
+ }
+
+ status = start_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem starting queue\n");
+ goto err_start_queue;
+ }
+
+ amba_set_drvdata(adev, stm_msp);
+
+ dev_dbg(dev, "probe succeded\n");
+ dev_dbg(dev, "Bus No = %d, IRQ Line = %d, Virtual Addr: %x\n",
+ master->bus_num, irq, (u32)(stm_msp->regs));
+
+ status = request_irq(stm_msp->adev->irq[0],
+ stm_msp_interrupt_handler,
+ 0, stm_msp->master_info->device_name,
+ stm_msp);
+
+ if (status < 0) {
+ dev_err(dev, "probe - cannot get IRQ (%d)\n", status);
+ goto err_irq;
+ }
+
+ /* Register with the SPI framework */
+ status = spi_register_master(master);
+
+ if (status != 0) {
+ dev_err(dev, "probe - problem registering spi master\n");
+ goto err_spi_register;
+ }
+
+ return 0;
+
+err_spi_register:
+ free_irq(stm_msp->adev->irq[0], stm_msp);
+err_irq:
+err_init_queue:
+err_start_queue:
+ destroy_queue(stm_msp);
+err_no_iores:
+ iounmap(stm_msp->regs);
+disable_clk:
+ clk_put(stm_msp->clk);
+free_master:
+ spi_master_put(master);
+err_no_mem:
+err_no_pdata:
+ return status;
+}
+
+static int __exit stm_msp_remove(struct amba_device *adev)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ if (!stm_msp)
+ return 0;
+
+ /* Remove the queue */
+ status = destroy_queue(stm_msp);
+
+ if (status != 0) {
+ dev_err(&adev->dev, "queue remove failed (%d)\n", status);
+ return status;
+ }
+
+ stm_msp_controller_cmd(stm_msp, LOAD_DEFAULT_CONFIG);
+
+ /* Release map resources */
+ iounmap(stm_msp->regs);
+ amba_release_regions(adev);
+ tasklet_disable(&stm_msp->pump_transfers);
+ free_irq(stm_msp->adev->irq[0], stm_msp);
+
+ /* Disconnect from the SPI framework */
+ spi_unregister_master(stm_msp->master);
+
+ clk_put(stm_msp->clk);
+
+ /* Prevent double remove */
+ amba_set_drvdata(adev, NULL);
+ dev_dbg(&adev->dev, "remove succeded\n");
+ return status;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * stm_msp_suspend - MSP suspend function registered with PM framework.
+ * @dev: Reference to amba device structure of the device
+ * @state: power mgmt state.
+ *
+ * This function is invoked when the system is going into sleep, called
+ * by the power management framework of the linux kernel.
+ */
+static int stm_msp_suspend(struct amba_device *adev, pm_message_t state)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ status = stop_queue(stm_msp);
+
+ if (status != 0) {
+ dev_warn(&adev->dev, "suspend cannot stop queue\n");
+ return status;
+ }
+
+ dev_dbg(&adev->dev, "suspended\n");
+ return 0;
+}
+
+/**
+ * stm_msp_resume - MSP Resume function registered with PM framework.
+ * @dev: Reference to amba device structure of the device
+ *
+ * This function is invoked when the system is coming out of sleep, called
+ * by the power management framework of the linux kernel.
+ */
+static int stm_msp_resume(struct amba_device *adev)
+{
+ struct stm_msp *stm_msp = amba_get_drvdata(adev);
+ int status = 0;
+
+ /* Start the queue running */
+ status = start_queue(stm_msp);
+
+ if (status)
+ dev_err(&adev->dev, "problem starting queue (%d)\n", status);
+ else
+ dev_dbg(&adev->dev, "resumed\n");
+
+ return status;
+}
+
+#else
+#define stm_msp_suspend NULL
+#define stm_msp_resume NULL
+#endif /* CONFIG_PM */
+
+static struct amba_id stm_msp_ids[] = {
+ {
+ .id = MSP_PER_ID,
+ .mask = MSP_PER_MASK,
+ },
+ {
+ 0,
+ 0,
+ },
+};
+
+static struct amba_driver stm_msp_driver = {
+ .drv = {
+ .name = "MSP",
+ },
+ .id_table = stm_msp_ids,
+ .probe = stm_msp_probe,
+ .remove = __exit_p(stm_msp_remove),
+ .resume = stm_msp_resume,
+ .suspend = stm_msp_suspend,
+};
+
+static int __init stm_msp_init(void)
+{
+ return amba_driver_register(&stm_msp_driver);
+}
+
+static void __exit stm_msp_exit(void)
+{
+ amba_driver_unregister(&stm_msp_driver);
+}
+
+module_init(stm_msp_init);
+module_exit(stm_msp_exit);
+
+MODULE_AUTHOR("Sachin Verma <sachin.verma@st.com>");
+MODULE_DESCRIPTION("STM MSP (SPI protocol) Driver");
+MODULE_LICENSE("GPL");