aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-21 14:37:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-21 14:37:00 -0700
commit70ada77920723fbc2b35e9b301022fb1e166b41b (patch)
treef30f24135eff89020d8ae21d6c7a83cf5c812585 /drivers
parentb22793f7fdc38d73c4bb4299a313deef56dcfe66 (diff)
parent2764c500be0c1f057349ee6c81557239de060f87 (diff)
Merge branch 'next-spi' of git://git.secretlab.ca/git/linux-2.6
* 'next-spi' of git://git.secretlab.ca/git/linux-2.6: (53 commits) spi/omap2_mcspi: Verify TX reg is empty after TX only xfer with DMA spi/omap2_mcspi: disable channel after TX_ONLY transfer in PIO mode spi/bfin_spi: namespace local structs spi/bfin_spi: init early spi/bfin_spi: check per-transfer bits_per_word spi/bfin_spi: warn when CS is driven by hardware (CPHA=0) spi/bfin_spi: cs should be always low when a new transfer begins spi/bfin_spi: fix typo in comment spi/bfin_spi: reject unsupported SPI modes spi/bfin_spi: use dma_disable_irq_nosync() in irq handler spi/bfin_spi: combine duplicate SPI_CTL read/write logic spi/bfin_spi: reset ctl_reg bits when setup is run again on a device spi/bfin_spi: push all size checks into the transfer function spi/bfin_spi: use nosync when disabling the IRQ from the IRQ handler spi/bfin_spi: sync hardware state before reprogramming everything spi/bfin_spi: save/restore state when suspending/resuming spi/bfin_spi: redo GPIO CS handling Blackfin: SPI: expand SPI bitmasks spi/bfin_spi: use the SPI namespaced bit names spi/bfin_spi: drop extra memory we don't need ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/ab8500-spi.c5
-rw-r--r--drivers/spi/Kconfig30
-rw-r--r--drivers/spi/Makefile9
-rw-r--r--drivers/spi/amba-pl022.c751
-rw-r--r--drivers/spi/atmel_spi.c14
-rw-r--r--drivers/spi/omap2_mcspi.c81
-rw-r--r--drivers/spi/orion_spi.c4
-rw-r--r--drivers/spi/spi_bfin5xx.c844
-rw-r--r--drivers/spi/spi_fsl_espi.c748
-rw-r--r--drivers/spi/spi_fsl_lib.c237
-rw-r--r--drivers/spi/spi_fsl_lib.h124
-rw-r--r--drivers/spi/spi_fsl_spi.c (renamed from drivers/spi/spi_mpc8xxx.c)552
-rw-r--r--drivers/spi/spi_s3c64xx.c158
-rw-r--r--drivers/spi/spi_topcliff_pch.c1303
14 files changed, 3741 insertions, 1119 deletions
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
index e1c8b62b086..01b6d584442 100644
--- a/drivers/mfd/ab8500-spi.c
+++ b/drivers/mfd/ab8500-spi.c
@@ -83,6 +83,11 @@ static int __devinit ab8500_spi_probe(struct spi_device *spi)
struct ab8500 *ab8500;
int ret;
+ spi->bits_per_word = 24;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
if (!ab8500)
return -ENOMEM;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f3af1..9949c252c23 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -182,12 +182,27 @@ config SPI_MPC512x_PSC
This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode.
-config SPI_MPC8xxx
- tristate "Freescale MPC8xxx SPI controller"
+config SPI_FSL_LIB
+ tristate
depends on FSL_SOC
+
+config SPI_FSL_SPI
+ tristate "Freescale SPI controller"
+ depends on FSL_SOC
+ select SPI_FSL_LIB
help
- This enables using the Freescale MPC8xxx SPI controllers in master
- mode.
+ This enables using the Freescale SPI controllers in master mode.
+ MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
+ MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
+
+config SPI_FSL_ESPI
+ tristate "Freescale eSPI controller"
+ depends on FSL_SOC
+ select SPI_FSL_LIB
+ help
+ This enables using the Freescale eSPI controllers in master mode.
+ From MPC8536, 85xx platform uses the controller, and all P10xx,
+ P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
@@ -298,6 +313,13 @@ config SPI_STMP3XXX
help
SPI driver for Freescale STMP37xx/378x SoC SSP interface
+config SPI_TOPCLIFF_PCH
+ tristate "Topcliff PCH SPI Controller"
+ depends on PCI
+ help
+ SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
+ used in some x86 embedded processors.
+
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
depends on GENERIC_GPIO && CPU_TX49XX
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18217a..557aaadf56b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -2,9 +2,7 @@
# Makefile for kernel SPI drivers.
#
-ifeq ($(CONFIG_SPI_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
+ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# small core, mostly translating board-specific
# config declarations into driver model code
@@ -34,11 +32,14 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
-obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
+obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o
+obj-$(CONFIG_SPI_FSL_ESPI) += spi_fsl_espi.o
+obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o
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_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.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 4c37c4e2864..fb3d1b31772 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,9 @@
#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>
/*
* This macro is used to define some register default values.
@@ -381,6 +383,14 @@ 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
};
/**
@@ -406,7 +416,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);
@@ -763,6 +773,371 @@ 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,
+ .src_maxburst = pl022->vendor->fifodepth >> 1,
+ };
+ struct dma_slave_config tx_conf = {
+ .dst_addr = SSP_DR(pl022->phybase),
+ .direction = DMA_TO_DEVICE,
+ .dst_maxburst = pl022->vendor->fifodepth >> 1,
+ };
+ 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;
+
+ /* 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
*
@@ -794,14 +1169,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");
@@ -896,8 +1274,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
*
*/
@@ -954,65 +1332,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);
@@ -1023,15 +1359,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;
@@ -1101,7 +1448,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
*
*/
@@ -1150,10 +1497,8 @@ static void pump_messages(struct work_struct *work)
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);
}
@@ -1248,100 +1593,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;
}
@@ -1350,24 +1651,20 @@ static int verify_controller_parameters(struct pl022 *pl022,
if ((chip_info->duplex !=
SSP_MICROWIRE_CHANNEL_FULL_DUPLEX)
&& (chip_info->duplex !=
- SSP_MICROWIRE_CHANNEL_HALF_DUPLEX))
- dev_err(chip_info->dev,
+ SSP_MICROWIRE_CHANNEL_HALF_DUPLEX)) {
+ 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;
}
@@ -1467,22 +1764,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
@@ -1496,23 +1795,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;
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;
@@ -1535,48 +1826,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
@@ -1586,29 +1842,48 @@ 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;
}
+
/* 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;
@@ -1625,6 +1900,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;
}
}
@@ -1636,9 +1912,8 @@ 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,
@@ -1646,7 +1921,7 @@ static int pl022_setup(struct spi_device *spi)
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);
@@ -1654,10 +1929,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,
@@ -1673,29 +1950,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->pl023) {
+ 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);
@@ -1704,7 +2003,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;
}
@@ -1766,12 +2065,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;
@@ -1798,6 +2106,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) {
@@ -1826,6 +2142,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);
@@ -1856,6 +2174,7 @@ pl022_remove(struct amba_device *adev)
return status;
}
load_ssp_default_config(pl022);
+ pl022_dma_remove(pl022);
free_irq(adev->irq[0], pl022);
clk_disable(pl022->clk);
clk_put(pl022->clk);
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index c4e04428992..154529aacc0 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -654,6 +654,8 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
struct spi_transfer *xfer;
unsigned long flags;
struct device *controller = spi->master->dev.parent;
+ u8 bits;
+ struct atmel_spi_device *asd;
as = spi_master_get_devdata(spi->master);
@@ -672,8 +674,18 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
return -EINVAL;
}
+ if (xfer->bits_per_word) {
+ asd = spi->controller_state;
+ bits = (asd->csr >> 4) & 0xf;
+ if (bits != xfer->bits_per_word - 8) {
+ dev_dbg(&spi->dev, "you can't yet change "
+ "bits_per_word in transfers\n");
+ return -ENOPROTOOPT;
+ }
+ }
+
/* FIXME implement these protocol options!! */
- if (xfer->bits_per_word || xfer->speed_hz) {
+ if (xfer->speed_hz) {
dev_dbg(&spi->dev, "no protocol options yet\n");
return -ENOPROTOOPT;
}
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c
index b3a94ca0a75..2a651e61bfb 100644
--- a/drivers/spi/omap2_mcspi.c
+++ b/drivers/spi/omap2_mcspi.c
@@ -296,6 +296,19 @@ static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
return 0;
}
+static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!(__raw_readl(reg) & bit)) {
+ if (time_after(jiffies, timeout))
+ return -1;
+ cpu_relax();
+ }
+ return 0;
+}
+
static unsigned
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
{
@@ -309,11 +322,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
u32 l;
u8 * rx;
const u8 * tx;
+ void __iomem *chstat_reg;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
l = mcspi_cached_chconf0(spi);
+ chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+
count = xfer->len;
c = count;
word_len = cs->word_len;
@@ -382,6 +398,16 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
if (tx != NULL) {
wait_for_completion(&mcspi_dma->dma_tx_completion);
dma_unmap_single(NULL, xfer->tx_dma, count, DMA_TO_DEVICE);
+
+ /* for TX_ONLY mode, be sure all words have shifted out */
+ if (rx == NULL) {
+ if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_TXS) < 0)
+ dev_err(&spi->dev, "TXS timed out\n");
+ else if (mcspi_wait_for_reg_bit(chstat_reg,
+ OMAP2_MCSPI_CHSTAT_EOT) < 0)
+ dev_err(&spi->dev, "EOT timed out\n");
+ }
}
if (rx != NULL) {
@@ -435,19 +461,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
return count;
}
-static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
-{
- unsigned long timeout;
-
- timeout = jiffies + msecs_to_jiffies(1000);
- while (!(__raw_readl(reg) & bit)) {
- if (time_after(jiffies, timeout))
- return -1;
- cpu_relax();
- }
- return 0;
-}
-
static unsigned
omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
{
@@ -489,10 +502,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_err(&spi->dev, "TXS timed out\n");
goto out;
}
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "write-%d %02x\n",
+ dev_vdbg(&spi->dev, "write-%d %02x\n",
word_len, *tx);
-#endif
__raw_writel(*tx++, tx_reg);
}
if (rx != NULL) {
@@ -506,10 +517,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
omap2_mcspi_set_enable(spi, 0);
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %02x\n",
+ dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
-#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev,
@@ -522,10 +531,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %02x\n",
+ dev_vdbg(&spi->dev, "read-%d %02x\n",
word_len, *(rx - 1));
-#endif
}
} while (c);
} else if (word_len <= 16) {
@@ -542,10 +549,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_err(&spi->dev, "TXS timed out\n");
goto out;
}
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "write-%d %04x\n",
+ dev_vdbg(&spi->dev, "write-%d %04x\n",
word_len, *tx);
-#endif
__raw_writel(*tx++, tx_reg);
}
if (rx != NULL) {
@@ -559,10 +564,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
omap2_mcspi_set_enable(spi, 0);
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %04x\n",
+ dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
-#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev,
@@ -575,10 +578,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %04x\n",
+ dev_vdbg(&spi->dev, "read-%d %04x\n",
word_len, *(rx - 1));
-#endif
}
} while (c);
} else if (word_len <= 32) {
@@ -595,10 +596,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
dev_err(&spi->dev, "TXS timed out\n");
goto out;
}
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "write-%d %08x\n",
+ dev_vdbg(&spi->dev, "write-%d %08x\n",
word_len, *tx);
-#endif
__raw_writel(*tx++, tx_reg);
}
if (rx != NULL) {
@@ -612,10 +611,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
(l & OMAP2_MCSPI_CHCONF_TURBO)) {
omap2_mcspi_set_enable(spi, 0);
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %08x\n",
+ dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1));
-#endif
if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_RXS) < 0) {
dev_err(&spi->dev,
@@ -628,10 +625,8 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
}
*rx++ = __raw_readl(rx_reg);
-#ifdef VERBOSE
- dev_dbg(&spi->dev, "read-%d %08x\n",
+ dev_vdbg(&spi->dev, "read-%d %08x\n",
word_len, *(rx - 1));
-#endif
}
} while (c);
}
@@ -644,6 +639,12 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
} else if (mcspi_wait_for_reg_bit(chstat_reg,
OMAP2_MCSPI_CHSTAT_EOT) < 0)
dev_err(&spi->dev, "EOT timed out\n");
+
+ /* disable chan to purge rx datas received in TX_ONLY transfer,
+ * otherwise these rx datas will affect the direct following
+ * RX_ONLY transfer.
+ */
+ omap2_mcspi_set_enable(spi, 0);
}
out:
omap2_mcspi_set_enable(spi, 1);
diff --git a/drivers/spi/orion_spi.c b/drivers/spi/orion_spi.c
index 3aea50da7b2..0b677dc041a 100644
--- a/drivers/spi/orion_spi.c
+++ b/drivers/spi/orion_spi.c
@@ -404,7 +404,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
goto msg_rejected;
}
- if ((t != NULL) && t->bits_per_word)
+ if (t->bits_per_word)
bits_per_word = t->bits_per_word;
if ((bits_per_word != 8) && (bits_per_word != 16)) {
@@ -415,7 +415,7 @@ static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m)
goto msg_rejected;
}
/*make sure buffer length is even when working in 16 bit mode*/
- if ((t != NULL) && (t->bits_per_word == 16) && (t->len & 1)) {
+ if ((t->bits_per_word == 16) && (t->len & 1)) {
dev_err(&spi->dev,
"message rejected : "
"odd data length (%d) while in 16 bit mode\n",
diff --git a/drivers/spi/spi_bfin5xx.c b/drivers/spi/spi_bfin5xx.c
index 10a6dc3d37a..ab483a0ec6d 100644
--- a/drivers/spi/spi_bfin5xx.c
+++ b/drivers/spi/spi_bfin5xx.c
@@ -1,7 +1,7 @@
/*
* Blackfin On-Chip SPI Driver
*
- * Copyright 2004-2007 Analog Devices Inc.
+ * Copyright 2004-2010 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
@@ -41,13 +41,16 @@ MODULE_LICENSE("GPL");
#define RUNNING_STATE ((void *)1)
#define DONE_STATE ((void *)2)
#define ERROR_STATE ((void *)-1)
-#define QUEUE_RUNNING 0
-#define QUEUE_STOPPED 1
-/* Value to send if no TX value is supplied */
-#define SPI_IDLE_TXVAL 0x0000
+struct bfin_spi_master_data;
-struct driver_data {
+struct bfin_spi_transfer_ops {
+ void (*write) (struct bfin_spi_master_data *);
+ void (*read) (struct bfin_spi_master_data *);
+ void (*duplex) (struct bfin_spi_master_data *);
+};
+
+struct bfin_spi_master_data {
/* Driver model hookup */
struct platform_device *pdev;
@@ -69,7 +72,7 @@ struct driver_data {
spinlock_t lock;
struct list_head queue;
int busy;
- int run;
+ bool running;
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
@@ -77,7 +80,7 @@ struct driver_data {
/* Current message transfer state info */
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
- struct chip_data *cur_chip;
+ struct bfin_spi_slave_data *cur_chip;
size_t len_in_bytes;
size_t len;
void *tx;
@@ -92,38 +95,37 @@ struct driver_data {
dma_addr_t rx_dma;
dma_addr_t tx_dma;
+ int irq_requested;
+ int spi_irq;
+
size_t rx_map_len;
size_t tx_map_len;
u8 n_bytes;
+ u16 ctrl_reg;
+ u16 flag_reg;
+
int cs_change;
- void (*write) (struct driver_data *);
- void (*read) (struct driver_data *);
- void (*duplex) (struct driver_data *);
+ const struct bfin_spi_transfer_ops *ops;
};
-struct chip_data {
+struct bfin_spi_slave_data {
u16 ctl_reg;
u16 baud;
u16 flag;
u8 chip_select_num;
- u8 n_bytes;
- u8 width; /* 0 or 1 */
u8 enable_dma;
- u8 bits_per_word; /* 8 or 16 */
- u8 cs_change_per_word;
u16 cs_chg_udelay; /* Some devices require > 255usec delay */
u32 cs_gpio;
u16 idle_tx_val;
- void (*write) (struct driver_data *);
- void (*read) (struct driver_data *);
- void (*duplex) (struct driver_data *);
+ u8 pio_interrupt; /* use spi data irq */
+ const struct bfin_spi_transfer_ops *ops;
};
#define DEFINE_SPI_REG(reg, off) \
-static inline u16 read_##reg(struct driver_data *drv_data) \
+static inline u16 read_##reg(struct bfin_spi_master_data *drv_data) \
{ return bfin_read16(drv_data->regs_base + off); } \
-static inline void write_##reg(struct driver_data *drv_data, u16 v) \
+static inline void write_##reg(struct bfin_spi_master_data *drv_data, u16 v) \
{ bfin_write16(drv_data->regs_base + off, v); }
DEFINE_SPI_REG(CTRL, 0x00)
@@ -134,7 +136,7 @@ DEFINE_SPI_REG(RDBR, 0x10)
DEFINE_SPI_REG(BAUD, 0x14)
DEFINE_SPI_REG(SHAW, 0x18)
-static void bfin_spi_enable(struct driver_data *drv_data)
+static void bfin_spi_enable(struct bfin_spi_master_data *drv_data)
{
u16 cr;
@@ -142,7 +144,7 @@ static void bfin_spi_enable(struct driver_data *drv_data)
write_CTRL(drv_data, (cr | BIT_CTL_ENABLE));
}
-static void bfin_spi_disable(struct driver_data *drv_data)
+static void bfin_spi_disable(struct bfin_spi_master_data *drv_data)
{
u16 cr;
@@ -165,7 +167,7 @@ static u16 hz_to_spi_baud(u32 speed_hz)
return spi_baud;
}
-static int bfin_spi_flush(struct driver_data *drv_data)
+static int bfin_spi_flush(struct bfin_spi_master_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
@@ -179,13 +181,12 @@ static int bfin_spi_flush(struct driver_data *drv_data)
}
/* Chip select operation functions for cs_change flag */
-static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *chip)
+static void bfin_spi_cs_active(struct bfin_spi_master_data *drv_data, struct bfin_spi_slave_data *chip)
{
- if (likely(chip->chip_select_num)) {
+ if (likely(chip->chip_select_num < MAX_CTRL_CS)) {
u16 flag = read_FLAG(drv_data);
- flag |= chip->flag;
- flag &= ~(chip->flag << 8);
+ flag &= ~chip->flag;
write_FLAG(drv_data, flag);
} else {
@@ -193,13 +194,13 @@ static void bfin_spi_cs_active(struct driver_data *drv_data, struct chip_data *c
}
}
-static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data *chip)
+static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data,
+ struct bfin_spi_slave_data *chip)
{
- if (likely(chip->chip_select_num)) {
+ if (likely(chip->chip_select_num < MAX_CTRL_CS)) {
u16 flag = read_FLAG(drv_data);
- flag &= ~chip->flag;
- flag |= (chip->flag << 8);
+ flag |= chip->flag;
write_FLAG(drv_data, flag);
} else {
@@ -211,16 +212,43 @@ static void bfin_spi_cs_deactive(struct driver_data *drv_data, struct chip_data
udelay(chip->cs_chg_udelay);
}
+/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
+static inline void bfin_spi_cs_enable(struct bfin_spi_master_data *drv_data,
+ struct bfin_spi_slave_data *chip)
+{
+ if (chip->chip_select_num < MAX_CTRL_CS) {
+ u16 flag = read_FLAG(drv_data);
+
+ flag |= (chip->flag >> 8);
+
+ write_FLAG(drv_data, flag);
+ }
+}
+
+static inline void bfin_spi_cs_disable(struct bfin_spi_master_data *drv_data,
+ struct bfin_spi_slave_data *chip)
+{
+ if (chip->chip_select_num < MAX_CTRL_CS) {
+ u16 flag = read_FLAG(drv_data);
+
+ flag &= ~(chip->flag >> 8);
+
+ write_FLAG(drv_data, flag);
+ }
+}
+
/* stop controller and re-config current chip*/
-static void bfin_spi_restore_state(struct driver_data *drv_data)
+static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data)
{
- struct chip_data *chip = drv_data->cur_chip;
+ struct bfin_spi_slave_data *chip = drv_data->cur_chip;
/* Clear status and disable clock */
write_STAT(drv_data, BIT_STAT_CLR);
bfin_spi_disable(drv_data);
dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state\n");
+ SSYNC();
+
/* Load the registers */
write_CTRL(drv_data, chip->ctl_reg);
write_BAUD(drv_data, chip->baud);
@@ -230,49 +258,12 @@ static void bfin_spi_restore_state(struct driver_data *drv_data)
}
/* used to kick off transfer in rx mode and read unwanted RX data */
-static inline void bfin_spi_dummy_read(struct driver_data *drv_data)
+static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data)
{
(void) read_RDBR(drv_data);
}
-static void bfin_spi_null_writer(struct driver_data *drv_data)
-{
- u8 n_bytes = drv_data->n_bytes;
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- /* clear RXS (we check for RXS inside the loop) */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->tx < drv_data->tx_end) {
- write_TDBR(drv_data, tx_val);
- drv_data->tx += n_bytes;
- /* wait until transfer finished.
- checking SPIF or TXS may not guarantee transfer completion */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- /* discard RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
- }
-}
-
-static void bfin_spi_null_reader(struct driver_data *drv_data)
-{
- u8 n_bytes = drv_data->n_bytes;
- u16 tx_val = drv_data->cur_chip->idle_tx_val;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- write_TDBR(drv_data, tx_val);
- drv_data->rx += n_bytes;
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- bfin_spi_dummy_read(drv_data);
- }
-}
-
-static void bfin_spi_u8_writer(struct driver_data *drv_data)
+static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data)
{
/* clear RXS (we check for RXS inside the loop) */
bfin_spi_dummy_read(drv_data);
@@ -288,25 +279,7 @@ static void bfin_spi_u8_writer(struct driver_data *drv_data)
}
}
-static void bfin_spi_u8_cs_chg_writer(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
-
- /* clear RXS (we check for RXS inside the loop) */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
- /* make sure transfer finished before deactiving CS */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- bfin_spi_dummy_read(drv_data);
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
-
-static void bfin_spi_u8_reader(struct driver_data *drv_data)
+static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data)
{
u16 tx_val = drv_data->cur_chip->idle_tx_val;
@@ -321,25 +294,7 @@ static void bfin_spi_u8_reader(struct driver_data *drv_data)
}
}
-static void bfin_spi_u8_cs_chg_reader(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
- u16 tx_val = chip->idle_tx_val;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, tx_val);
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
-
-static void bfin_spi_u8_duplex(struct driver_data *drv_data)
+static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data)
{
/* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
@@ -352,24 +307,13 @@ static void bfin_spi_u8_duplex(struct driver_data *drv_data)
}
}
-static void bfin_spi_u8_cs_chg_duplex(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, (*(u8 *) (drv_data->tx++)));
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u8 *) (drv_data->rx++) = read_RDBR(drv_data);
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
+static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = {
+ .write = bfin_spi_u8_writer,
+ .read = bfin_spi_u8_reader,
+ .duplex = bfin_spi_u8_duplex,
+};
-static void bfin_spi_u16_writer(struct driver_data *drv_data)
+static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data)
{
/* clear RXS (we check for RXS inside the loop) */
bfin_spi_dummy_read(drv_data);
@@ -386,26 +330,7 @@ static void bfin_spi_u16_writer(struct driver_data *drv_data)
}
}
-static void bfin_spi_u16_cs_chg_writer(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
-
- /* clear RXS (we check for RXS inside the loop) */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->tx < drv_data->tx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- drv_data->tx += 2;
- /* make sure transfer finished before deactiving CS */
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- bfin_spi_dummy_read(drv_data);
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
-
-static void bfin_spi_u16_reader(struct driver_data *drv_data)
+static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data)
{
u16 tx_val = drv_data->cur_chip->idle_tx_val;
@@ -421,26 +346,7 @@ static void bfin_spi_u16_reader(struct driver_data *drv_data)
}
}
-static void bfin_spi_u16_cs_chg_reader(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
- u16 tx_val = chip->idle_tx_val;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, tx_val);
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
- drv_data->rx += 2;
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
-
-static void bfin_spi_u16_duplex(struct driver_data *drv_data)
+static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data)
{
/* discard old RX data and clear RXS */
bfin_spi_dummy_read(drv_data);
@@ -455,27 +361,14 @@ static void bfin_spi_u16_duplex(struct driver_data *drv_data)
}
}
-static void bfin_spi_u16_cs_chg_duplex(struct driver_data *drv_data)
-{
- struct chip_data *chip = drv_data->cur_chip;
-
- /* discard old RX data and clear RXS */
- bfin_spi_dummy_read(drv_data);
-
- while (drv_data->rx < drv_data->rx_end) {
- bfin_spi_cs_active(drv_data, chip);
- write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
- drv_data->tx += 2;
- while (!(read_STAT(drv_data) & BIT_STAT_RXS))
- cpu_relax();
- *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
- drv_data->rx += 2;
- bfin_spi_cs_deactive(drv_data, chip);
- }
-}
+static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = {
+ .write = bfin_spi_u16_writer,
+ .read = bfin_spi_u16_reader,
+ .duplex = bfin_spi_u16_duplex,
+};
-/* test if ther is more transfer to be done */
-static void *bfin_spi_next_transfer(struct driver_data *drv_data)
+/* test if there is more transfer to be done */
+static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
struct spi_transfer *trans = drv_data->cur_transfer;
@@ -494,9 +387,9 @@ static void *bfin_spi_next_transfer(struct driver_data *drv_data)
* caller already set message->status;
* dma and pio irqs are blocked give finished message back
*/
-static void bfin_spi_giveback(struct driver_data *drv_data)
+static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
{
- struct chip_data *chip = drv_data->cur_chip;
+ struct bfin_spi_slave_data *chip = drv_data->cur_chip;
struct spi_transfer *last_transfer;
unsigned long flags;
struct spi_message *msg;
@@ -525,10 +418,83 @@ static void bfin_spi_giveback(struct driver_data *drv_data)
msg->complete(msg->context);
}
+/* spi data irq handler */
+static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
+{
+ struct bfin_spi_master_data *drv_data = dev_id;
+ struct bfin_spi_slave_data *chip = drv_data->cur_chip;
+ struct spi_message *msg = drv_data->cur_msg;
+ int n_bytes = drv_data->n_bytes;
+
+ /* wait until transfer finished. */
+ while (!(read_STAT(drv_data) & BIT_STAT_RXS))
+ cpu_relax();
+
+ if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) ||
+ (drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) {
+ /* last read */
+ if (drv_data->rx) {
+ dev_dbg(&drv_data->pdev->dev, "last read\n");
+ if (n_bytes == 2)
+ *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+ else if (n_bytes == 1)
+ *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+ drv_data->rx += n_bytes;
+ }
+
+ msg->actual_length += drv_data->len_in_bytes;
+ if (drv_data->cs_change)
+ bfin_spi_cs_deactive(drv_data, chip);
+ /* Move to next transfer */
+ msg->state = bfin_spi_next_transfer(drv_data);
+
+ disable_irq_nosync(drv_data->spi_irq);
+
+ /* Schedule transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
+ return IRQ_HANDLED;
+ }
+
+ if (drv_data->rx && drv_data->tx) {
+ /* duplex */
+ dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR\n");
+ if (drv_data->n_bytes == 2) {
+ *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+ write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+ } else if (drv_data->n_bytes == 1) {
+ *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+ }
+ } else if (drv_data->rx) {
+ /* read */
+ dev_dbg(&drv_data->pdev->dev, "read: write_TDBR\n");
+ if (drv_data->n_bytes == 2)
+ *(u16 *) (drv_data->rx) = read_RDBR(drv_data);
+ else if (drv_data->n_bytes == 1)
+ *(u8 *) (drv_data->rx) = read_RDBR(drv_data);
+ write_TDBR(drv_data, chip->idle_tx_val);
+ } else if (drv_data->tx) {
+ /* write */
+ dev_dbg(&drv_data->pdev->dev, "write: write_TDBR\n");
+ bfin_spi_dummy_read(drv_data);
+ if (drv_data->n_bytes == 2)
+ write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+ else if (drv_data->n_bytes == 1)
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+ }
+
+ if (drv_data->tx)
+ drv_data->tx += n_bytes;
+ if (drv_data->rx)
+ drv_data->rx += n_bytes;
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
{
- struct driver_data *drv_data = dev_id;
- struct chip_data *chip = drv_data->cur_chip;
+ struct bfin_spi_master_data *drv_data = dev_id;
+ struct bfin_spi_slave_data *chip = drv_data->cur_chip;
struct spi_message *msg = drv_data->cur_msg;
unsigned long timeout;
unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
@@ -540,10 +506,6 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
clear_dma_irqstat(drv_data->dma_channel);
- /* Wait for DMA to complete */
- while (get_dma_curr_irqstat(drv_data->dma_channel) & DMA_RUN)
- cpu_relax();
-
/*
* wait for the last transaction shifted out. HRM states:
* at this point there may still be data in the SPI DMA FIFO waiting
@@ -551,8 +513,8 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
* register until it goes low for 2 successive reads
*/
if (drv_data->tx != NULL) {
- while ((read_STAT(drv_data) & TXS) ||
- (read_STAT(drv_data) & TXS))
+ while ((read_STAT(drv_data) & BIT_STAT_TXS) ||
+ (read_STAT(drv_data) & BIT_STAT_TXS))
cpu_relax();
}
@@ -561,14 +523,14 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
dmastat, read_STAT(drv_data));
timeout = jiffies + HZ;
- while (!(read_STAT(drv_data) & SPIF))
+ while (!(read_STAT(drv_data) & BIT_STAT_SPIF))
if (!time_before(jiffies, timeout)) {
dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF");
break;
} else
cpu_relax();
- if ((dmastat & DMA_ERR) && (spistat & RBSY)) {
+ if ((dmastat & DMA_ERR) && (spistat & BIT_STAT_RBSY)) {
msg->state = ERROR_STATE;
dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow\n");
} else {
@@ -588,20 +550,20 @@ static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
dev_dbg(&drv_data->pdev->dev,
"disable dma channel irq%d\n",
drv_data->dma_channel);
- dma_disable_irq(drv_data->dma_channel);
+ dma_disable_irq_nosync(drv_data->dma_channel);
return IRQ_HANDLED;
}
static void bfin_spi_pump_transfers(unsigned long data)
{
- struct driver_data *drv_data = (struct driver_data *)data;
+ struct bfin_spi_master_data *drv_data = (struct bfin_spi_master_data *)data;
struct spi_message *message = NULL;
struct spi_transfer *transfer = NULL;
struct spi_transfer *previous = NULL;
- struct chip_data *chip = NULL;
- u8 width;
- u16 cr, dma_width, dma_config;
+ struct bfin_spi_slave_data *chip = NULL;
+ unsigned int bits_per_word;
+ u16 cr, cr_width, dma_width, dma_config;
u32 tranf_success = 1;
u8 full_duplex = 0;
@@ -639,7 +601,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
udelay(previous->delay_usecs);
}
- /* Setup the transfer state based on the type of transfer */
+ /* Flush any existing transfers that may be sitting in the hardware */
if (bfin_spi_flush(drv_data) == 0) {
dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
message->status = -EIO;
@@ -679,52 +641,31 @@ static void bfin_spi_pump_transfers(unsigned long data)
drv_data->cs_change = transfer->cs_change;
/* Bits per word setup */
- switch (transfer->bits_per_word) {
- case 8:
+ bits_per_word = transfer->bits_per_word ? : message->spi->bits_per_word;
+ if (bits_per_word == 8) {
drv_data->n_bytes = 1;
- width = CFG_SPI_WORDSIZE8;
- drv_data->read = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_reader : bfin_spi_u8_reader;
- drv_data->write = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_writer : bfin_spi_u8_writer;
- drv_data->duplex = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_duplex : bfin_spi_u8_duplex;
- break;
-
- case 16:
+ drv_data->len = transfer->len;
+ cr_width = 0;
+ drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
+ } else if (bits_per_word == 16) {
drv_data->n_bytes = 2;
- width = CFG_SPI_WORDSIZE16;
- drv_data->read = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_reader : bfin_spi_u16_reader;
- drv_data->write = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_writer : bfin_spi_u16_writer;
- drv_data->duplex = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_duplex : bfin_spi_u16_duplex;
- break;
-
- default:
- /* No change, the same as default setting */
- drv_data->n_bytes = chip->n_bytes;
- width = chip->width;
- drv_data->write = drv_data->tx ? chip->write : bfin_spi_null_writer;
- drv_data->read = drv_data->rx ? chip->read : bfin_spi_null_reader;
- drv_data->duplex = chip->duplex ? chip->duplex : bfin_spi_null_writer;
- break;
- }
- cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD));
- cr |= (width << 8);
- write_CTRL(drv_data, cr);
-
- if (width == CFG_SPI_WORDSIZE16) {
drv_data->len = (transfer->len) >> 1;
+ cr_width = BIT_CTL_WORDSIZE;
+ drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
} else {
- drv_data->len = transfer->len;
+ dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n");
+ message->status = -EINVAL;
+ bfin_spi_giveback(drv_data);
+ return;
}
+ cr = read_CTRL(drv_data) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
+ cr |= cr_width;
+ write_CTRL(drv_data, cr);
+
dev_dbg(&drv_data->pdev->dev,
- "transfer: drv_data->write is %p, chip->write is %p, null_wr is %p\n",
- drv_data->write, chip->write, bfin_spi_null_writer);
+ "transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p\n",
+ drv_data->ops, chip->ops, &bfin_bfin_spi_transfer_ops_u8);
- /* speed and width has been set on per message */
message->state = RUNNING_STATE;
dma_config = 0;
@@ -735,13 +676,11 @@ static void bfin_spi_pump_transfers(unsigned long data)
write_BAUD(drv_data, chip->baud);
write_STAT(drv_data, BIT_STAT_CLR);
- cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD));
- if (drv_data->cs_change)
- bfin_spi_cs_active(drv_data, chip);
+ bfin_spi_cs_active(drv_data, chip);
dev_dbg(&drv_data->pdev->dev,
"now pumping a transfer: width is %d, len is %d\n",
- width, transfer->len);
+ cr_width, transfer->len);
/*
* Try to map dma buffer and do a dma transfer. If successful use,
@@ -760,7 +699,7 @@ static void bfin_spi_pump_transfers(unsigned long data)
/* config dma channel */
dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n");
set_dma_x_count(drv_data->dma_channel, drv_data->len);
- if (width == CFG_SPI_WORDSIZE16) {
+ if (cr_width == BIT_CTL_WORDSIZE) {
set_dma_x_modify(drv_data->dma_channel, 2);
dma_width = WDSIZE_16;
} else {
@@ -846,73 +785,100 @@ static void bfin_spi_pump_transfers(unsigned long data)
dma_enable_irq(drv_data->dma_channel);
local_irq_restore(flags);
- } else {
- /* IO mode write then read */
- dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
-
- /* we always use SPI_WRITE mode. SPI_READ mode
- seems to have problems with setting up the
- output value in TDBR prior to the transfer. */
- write_CTRL(drv_data, (cr | CFG_SPI_WRITE));
-
- if (full_duplex) {
- /* full duplex mode */
- BUG_ON((drv_data->tx_end - drv_data->tx) !=
- (drv_data->rx_end - drv_data->rx));
- dev_dbg(&drv_data->pdev->dev,
- "IO duplex: cr is 0x%x\n", cr);
-
- drv_data->duplex(drv_data);
+ return;
+ }
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->tx != NULL) {
- /* write only half duplex */
- dev_dbg(&drv_data->pdev->dev,
- "IO write: cr is 0x%x\n", cr);
+ /*
+ * We always use SPI_WRITE mode (transfer starts with TDBR write).
+ * SPI_READ mode (transfer starts with RDBR read) seems to have
+ * problems with setting up the output value in TDBR prior to the
+ * start of the transfer.
+ */
+ write_CTRL(drv_data, cr | BIT_CTL_TXMOD);
- drv_data->write(drv_data);
+ if (chip->pio_interrupt) {
+ /* SPI irq should have been disabled by now */
- if (drv_data->tx != drv_data->tx_end)
- tranf_success = 0;
- } else if (drv_data->rx != NULL) {
- /* read only half duplex */
- dev_dbg(&drv_data->pdev->dev,
- "IO read: cr is 0x%x\n", cr);
+ /* discard old RX data and clear RXS */
+ bfin_spi_dummy_read(drv_data);
- drv_data->read(drv_data);
- if (drv_data->rx != drv_data->rx_end)
- tranf_success = 0;
+ /* start transfer */
+ if (drv_data->tx == NULL)
+ write_TDBR(drv_data, chip->idle_tx_val);
+ else {
+ if (bits_per_word == 8)
+ write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));
+ else
+ write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));
+ drv_data->tx += drv_data->n_bytes;
}
- if (!tranf_success) {
- dev_dbg(&drv_data->pdev->dev,
- "IO write error!\n");
- message->state = ERROR_STATE;
- } else {
- /* Update total byte transfered */
- message->actual_length += drv_data->len_in_bytes;
- /* Move to next transfer of this msg */
- message->state = bfin_spi_next_transfer(drv_data);
- if (drv_data->cs_change)
- bfin_spi_cs_deactive(drv_data, chip);
- }
- /* Schedule next transfer tasklet */
- tasklet_schedule(&drv_data->pump_transfers);
+ /* once TDBR is empty, interrupt is triggered */
+ enable_irq(drv_data->spi_irq);
+ return;
+ }
+
+ /* IO mode */
+ dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n");
+
+ if (full_duplex) {
+ /* full duplex mode */
+ BUG_ON((drv_data->tx_end - drv_data->tx) !=
+ (drv_data->rx_end - drv_data->rx));
+ dev_dbg(&drv_data->pdev->dev,
+ "IO duplex: cr is 0x%x\n", cr);
+
+ drv_data->ops->duplex(drv_data);
+
+ if (drv_data->tx != drv_data->tx_end)
+ tranf_success = 0;
+ } else if (drv_data->tx != NULL) {
+ /* write only half duplex */
+ dev_dbg(&drv_data->pdev->dev,
+ "IO write: cr is 0x%x\n", cr);
+
+ drv_data->ops->write(drv_data);
+
+ if (drv_data->tx != drv_data->tx_end)
+ tranf_success = 0;
+ } else if (drv_data->rx != NULL) {
+ /* read only half duplex */
+ dev_dbg(&drv_data->pdev->dev,
+ "IO read: cr is 0x%x\n", cr);
+
+ drv_data->ops->read(drv_data);
+ if (drv_data->rx != drv_data->rx_end)
+ tranf_success = 0;
+ }
+
+ if (!tranf_success) {
+ dev_dbg(&drv_data->pdev->dev,
+ "IO write error!\n");
+ message->state = ERROR_STATE;
+ } else {
+ /* Update total byte transfered */
+ message->actual_length += drv_data->len_in_bytes;
+ /* Move to next transfer of this msg */
+ message->state = bfin_spi_next_transfer(drv_data);
+ if (drv_data->cs_change)
+ bfin_spi_cs_deactive(drv_data, chip);
}
+
+ /* Schedule next transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
}
/* pop a msg from queue and kick off real transfer */
static void bfin_spi_pump_messages(struct work_struct *work)
{
- struct driver_data *drv_data;
+ struct bfin_spi_master_data *drv_data;
unsigned long flags;
- drv_data = container_of(work, struct driver_data, pump_messages);
+ drv_data = container_of(work, struct bfin_spi_master_data, pump_messages);
/* Lock queue and check for queue work */
spin_lock_irqsave(&drv_data->lock, flags);
- if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
+ if (list_empty(&drv_data->queue) || !drv_data->running) {
/* pumper kicked off but no work to do */
drv_data->busy = 0;
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -962,12 +928,12 @@ static void bfin_spi_pump_messages(struct work_struct *work)
*/
static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
- struct driver_data *drv_data = spi_master_get_devdata(spi->master);
+ struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
unsigned long flags;
spin_lock_irqsave(&drv_data->lock, flags);
- if (drv_data->run == QUEUE_STOPPED) {
+ if (!drv_data->running) {
spin_unlock_irqrestore(&drv_data->lock, flags);
return -ESHUTDOWN;
}
@@ -979,7 +945,7 @@ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg)
dev_dbg(&spi->dev, "adding an msg in transfer() \n");
list_add_tail(&msg->queue, &drv_data->queue);
- if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
+ if (drv_data->running && !drv_data->busy)
queue_work(drv_data->workqueue, &drv_data->pump_messages);
spin_unlock_irqrestore(&drv_data->lock, flags);
@@ -1003,147 +969,184 @@ static u16 ssel[][MAX_SPI_SSEL] = {
P_SPI2_SSEL6, P_SPI2_SSEL7},
};
-/* first setup for new devices */
+/* setup for devices (may be called multiple times -- not just first setup) */
static int bfin_spi_setup(struct spi_device *spi)
{
- struct bfin5xx_spi_chip *chip_info = NULL;
- struct chip_data *chip;
- struct driver_data *drv_data = spi_master_get_devdata(spi->master);
- int ret;
-
- if (spi->bits_per_word != 8 && spi->bits_per_word != 16)
- return -EINVAL;
+ struct bfin5xx_spi_chip *chip_info;
+ struct bfin_spi_slave_data *chip = NULL;
+ struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
+ u16 bfin_ctl_reg;
+ int ret = -EINVAL;
/* Only alloc (or use chip_info) on first setup */
+ chip_info = NULL;
chip = spi_get_ctldata(spi);
if (chip == NULL) {
- chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spi->dev, "cannot allocate chip data\n");
+ ret = -ENOMEM;
+ goto error;
+ }
chip->enable_dma = 0;
chip_info = spi->controller_data;
}
+ /* Let people set non-standard bits directly */
+ bfin_ctl_reg = BIT_CTL_OPENDRAIN | BIT_CTL_EMISO |
+ BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ;
+
/* chip_info isn't always needed */
if (chip_info) {
/* Make sure people stop trying to set fields via ctl_reg
* when they should actually be using common SPI framework.
- * Currently we let through: WOM EMISO PSSE GM SZ TIMOD.
+ * Currently we let through: WOM EMISO PSSE GM SZ.
* Not sure if a user actually needs/uses any of these,
* but let's assume (for now) they do.
*/
- if (chip_info->ctl_reg & (SPE|MSTR|CPOL|CPHA|LSBF|SIZE)) {
+ if (chip_info->ctl_reg & ~bfin_ctl_reg) {
dev_err(&spi->dev, "do not set bits in ctl_reg "
"that the SPI framework manages\n");
- return -EINVAL;
+ goto error;
}
-
chip->enable_dma = chip_info->enable_dma != 0
&& drv_data->master_info->enable_dma;
chip->ctl_reg = chip_info->ctl_reg;
- chip->bits_per_word = chip_info->bits_per_word;
- chip->cs_change_per_word = chip_info->cs_change_per_word;
chip->cs_chg_udelay = chip_info->cs_chg_udelay;
- chip->cs_gpio = chip_info->cs_gpio;
chip->idle_tx_val = chip_info->idle_tx_val;
+ chip->pio_interrupt = chip_info->pio_interrupt;
+ spi->bits_per_word = chip_info->bits_per_word;
+ } else {
+ /* force a default base state */
+ chip->ctl_reg &= bfin_ctl_reg;
+ }
+
+ if (spi->bits_per_word != 8 && spi->bits_per_word != 16) {
+ dev_err(&spi->dev, "%d bits_per_word is not supported\n",
+ spi->bits_per_word);
+ goto error;
}
/* translate common spi framework into our register */
+ if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {
+ dev_err(&spi->dev, "unsupported spi modes detected\n");
+ goto error;
+ }
if (spi->mode & SPI_CPOL)
- chip->ctl_reg |= CPOL;
+ chip->ctl_reg |= BIT_CTL_CPOL;
if (spi->mode & SPI_CPHA)
- chip->ctl_reg |= CPHA;
+ chip->ctl_reg |= BIT_CTL_CPHA;
if (spi->mode & SPI_LSB_FIRST)
- chip->ctl_reg |= LSBF;
+ chip->ctl_reg |= BIT_CTL_LSBF;
/* we dont support running in slave mode (yet?) */
- chip->ctl_reg |= MSTR;
+ chip->ctl_reg |= BIT_CTL_MASTER;
/*
+ * Notice: for blackfin, the speed_hz is the value of register
+ * SPI_BAUD, not the real baudrate
+ */
+ chip->baud = hz_to_spi_baud(spi->max_speed_hz);
+ chip->chip_select_num = spi->chip_select;
+ if (chip->chip_select_num < MAX_CTRL_CS) {
+ if (!(spi->mode & SPI_CPHA))
+ dev_warn(&spi->dev, "Warning: SPI CPHA not set:"
+ " Slave Select not under software control!\n"
+ " See Documentation/blackfin/bfin-spi-notes.txt");
+
+ chip->flag = (1 << spi->chip_select) << 8;
+ } else
+ chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS;
+
+ if (chip->enable_dma && chip->pio_interrupt) {
+ dev_err(&spi->dev, "enable_dma is set, "
+ "do not set pio_interrupt\n");
+ goto error;
+ }
+ /*
* if any one SPI chip is registered and wants DMA, request the
* DMA channel for it
*/
if (chip->enable_dma && !drv_data->dma_requested) {
/* register dma irq handler */
- if (request_dma(drv_data->dma_channel, "BFIN_SPI_DMA") < 0) {
- dev_dbg(&spi->dev,
+ ret = request_dma(drv_data->dma_channel, "BFIN_SPI_DMA");
+ if (ret) {
+ dev_err(&spi->dev,
"Unable to request BlackFin SPI DMA channel\n");
- return -ENODEV;
+ goto error;
}
- if (set_dma_callback(drv_data->dma_channel,
- bfin_spi_dma_irq_handler, drv_data) < 0) {
- dev_dbg(&spi->dev, "Unable to set dma callback\n");
- return -EPERM;
+ drv_data->dma_requested = 1;
+
+ ret = set_dma_callback(drv_data->dma_channel,
+ bfin_spi_dma_irq_handler, drv_data);
+ if (ret) {
+ dev_err(&spi->dev, "Unable to set dma callback\n");
+ goto error;
}
dma_disable_irq(drv_data->dma_channel);
- drv_data->dma_requested = 1;
}
- /*
- * Notice: for blackfin, the speed_hz is the value of register
- * SPI_BAUD, not the real baudrate
- */
- chip->baud = hz_to_spi_baud(spi->max_speed_hz);
- chip->flag = 1 << (spi->chip_select);
- chip->chip_select_num = spi->chip_select;
+ if (chip->pio_interrupt && !drv_data->irq_requested) {
+ ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler,
+ IRQF_DISABLED, "BFIN_SPI", drv_data);
+ if (ret) {
+ dev_err(&spi->dev, "Unable to register spi IRQ\n");
+ goto error;
+ }
+ drv_data->irq_requested = 1;
+ /* we use write mode, spi irq has to be disabled here */
+ disable_irq(drv_data->spi_irq);
+ }
- if (chip->chip_select_num == 0) {
+ if (chip->chip_select_num >= MAX_CTRL_CS) {
ret = gpio_request(chip->cs_gpio, spi->modalias);
if (ret) {
- if (drv_data->dma_requested)
- free_dma(drv_data->dma_channel);
- return ret;
+ dev_err(&spi->dev, "gpio_request() error\n");
+ goto pin_error;
}
gpio_direction_output(chip->cs_gpio, 1);
}
- switch (chip->bits_per_word) {
- case 8:
- chip->n_bytes = 1;
- chip->width = CFG_SPI_WORDSIZE8;
- chip->read = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_reader : bfin_spi_u8_reader;
- chip->write = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_writer : bfin_spi_u8_writer;
- chip->duplex = chip->cs_change_per_word ?
- bfin_spi_u8_cs_chg_duplex : bfin_spi_u8_duplex;
- break;
-
- case 16:
- chip->n_bytes = 2;
- chip->width = CFG_SPI_WORDSIZE16;
- chip->read = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_reader : bfin_spi_u16_reader;
- chip->write = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_writer : bfin_spi_u16_writer;
- chip->duplex = chip->cs_change_per_word ?
- bfin_spi_u16_cs_chg_duplex : bfin_spi_u16_duplex;
- break;
-
- default:
- dev_err(&spi->dev, "%d bits_per_word is not supported\n",
- chip->bits_per_word);
- if (chip_info)
- kfree(chip);
- return -ENODEV;
- }
-
dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d\n",
- spi->modalias, chip->width, chip->enable_dma);
+ spi->modalias, spi->bits_per_word, chip->enable_dma);
dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x\n",
chip->ctl_reg, chip->flag);
spi_set_ctldata(spi, chip);
dev_dbg(&spi->dev, "chip select number is %d\n", chip->chip_select_num);
- if ((chip->chip_select_num > 0)
- && (chip->chip_select_num <= spi->master->num_chipselect))
- peripheral_request(ssel[spi->master->bus_num]
- [chip->chip_select_num-1], spi->modalias);
+ if (chip->chip_select_num < MAX_CTRL_CS) {
+ ret = peripheral_request(ssel[spi->master->bus_num]
+ [chip->chip_select_num-1], spi->modalias);
+ if (ret) {
+ dev_err(&spi->dev, "peripheral_request() error\n");
+ goto pin_error;
+ }
+ }
+ bfin_spi_cs_enable(drv_data, chip);
bfin_spi_cs_deactive(drv_data, chip);
return 0;
+
+ pin_error:
+ if (chip->chip_select_num >= MAX_CTRL_CS)
+ gpio_free(chip->cs_gpio);
+ else
+ peripheral_free(ssel[spi->master->bus_num]
+ [chip->chip_select_num - 1]);
+ error:
+ if (chip) {
+ if (drv_data->dma_requested)
+ free_dma(drv_data->dma_channel);
+ drv_data->dma_requested = 0;
+
+ kfree(chip);
+ /* prevent free 'chip' twice */
+ spi_set_ctldata(spi, NULL);
+ }
+
+ return ret;
}
/*
@@ -1152,28 +1155,30 @@ static int bfin_spi_setup(struct spi_device *spi)
*/
static void bfin_spi_cleanup(struct spi_device *spi)
{
- struct chip_data *chip = spi_get_ctldata(spi);
+ struct bfin_spi_slave_data *chip = spi_get_ctldata(spi);
+ struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
if (!chip)
return;
- if ((chip->chip_select_num > 0)
- && (chip->chip_select_num <= spi->master->num_chipselect))
+ if (chip->chip_select_num < MAX_CTRL_CS) {
peripheral_free(ssel[spi->master->bus_num]
[chip->chip_select_num-1]);
-
- if (chip->chip_select_num == 0)
+ bfin_spi_cs_disable(drv_data, chip);
+ } else
gpio_free(chip->cs_gpio);
kfree(chip);
+ /* prevent free 'chip' twice */
+ spi_set_ctldata(spi, NULL);
}
-static inline int bfin_spi_init_queue(struct driver_data *drv_data)
+static inline int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
{
INIT_LIST_HEAD(&drv_data->queue);
spin_lock_init(&drv_data->lock);
- drv_data->run = QUEUE_STOPPED;
+ drv_data->running = false;
drv_data->busy = 0;
/* init transfer tasklet */
@@ -1190,18 +1195,18 @@ static inline int bfin_spi_init_queue(struct driver_data *drv_data)
return 0;
}
-static inline int bfin_spi_start_queue(struct driver_data *drv_data)
+static inline int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
{
unsigned long flags;
spin_lock_irqsave(&drv_data->lock, flags);
- if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
+ if (drv_data->running || drv_data->busy) {
spin_unlock_irqrestore(&drv_data->lock, flags);
return -EBUSY;
}
- drv_data->run = QUEUE_RUNNING;
+ drv_data->running = true;
drv_data->cur_msg = NULL;
drv_data->cur_transfer = NULL;
drv_data->cur_chip = NULL;
@@ -1212,7 +1217,7 @@ static inline int bfin_spi_start_queue(struct driver_data *drv_data)
return 0;
}
-static inline int bfin_spi_stop_queue(struct driver_data *drv_data)
+static inline int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
{
unsigned long flags;
unsigned limit = 500;
@@ -1226,7 +1231,7 @@ static inline int bfin_spi_stop_queue(struct driver_data *drv_data)
* execution path (pump_messages) would be required to call wake_up or
* friends on every SPI message. Do this instead
*/
- drv_data->run = QUEUE_STOPPED;
+ drv_data->running = false;
while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
spin_unlock_irqrestore(&drv_data->lock, flags);
msleep(10);
@@ -1241,7 +1246,7 @@ static inline int bfin_spi_stop_queue(struct driver_data *drv_data)
return status;
}
-static inline int bfin_spi_destroy_queue(struct driver_data *drv_data)
+static inline int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data)
{
int status;
@@ -1259,14 +1264,14 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct bfin5xx_spi_master *platform_info;
struct spi_master *master;
- struct driver_data *drv_data = 0;
+ struct bfin_spi_master_data *drv_data;
struct resource *res;
int status = 0;
platform_info = dev->platform_data;
/* Allocate master with space for drv_data */
- master = spi_alloc_master(dev, sizeof(struct driver_data) + 16);
+ master = spi_alloc_master(dev, sizeof(*drv_data));
if (!master) {
dev_err(&pdev->dev, "can not alloc spi_master\n");
return -ENOMEM;
@@ -1302,11 +1307,19 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
goto out_error_ioremap;
}
- drv_data->dma_channel = platform_get_irq(pdev, 0);
- if (drv_data->dma_channel < 0) {
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (res == NULL) {
dev_err(dev, "No DMA channel specified\n");
status = -ENOENT;
- goto out_error_no_dma_ch;
+ goto out_error_free_io;
+ }
+ drv_data->dma_channel = res->start;
+
+ drv_data->spi_irq = platform_get_irq(pdev, 0);
+ if (drv_data->spi_irq < 0) {
+ dev_err(dev, "No spi pio irq specified\n");
+ status = -ENOENT;
+ goto out_error_free_io;
}
/* Initial and start queue */
@@ -1328,6 +1341,12 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
goto out_error_queue_alloc;
}
+ /* Reset SPI registers. If these registers were used by the boot loader,
+ * the sky may fall on your head if you enable the dma controller.
+ */
+ write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER);
+ write_FLAG(drv_data, 0xFF00);
+
/* Register with the SPI framework */
platform_set_drvdata(pdev, drv_data);
status = spi_register_master(master);
@@ -1343,7 +1362,7 @@ static int __init bfin_spi_probe(struct platform_device *pdev)
out_error_queue_alloc:
bfin_spi_destroy_queue(drv_data);
-out_error_no_dma_ch:
+out_error_free_io:
iounmap((void *) drv_data->regs_base);
out_error_ioremap:
out_error_get_res:
@@ -1355,7 +1374,7 @@ out_error_get_res:
/* stop hardware and remove the driver */
static int __devexit bfin_spi_remove(struct platform_device *pdev)
{
- struct driver_data *drv_data = platform_get_drvdata(pdev);
+ struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
if (!drv_data)
@@ -1375,6 +1394,11 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev)
free_dma(drv_data->dma_channel);
}
+ if (drv_data->irq_requested) {
+ free_irq(drv_data->spi_irq, drv_data);
+ drv_data->irq_requested = 0;
+ }
+
/* Disconnect from the SPI framework */
spi_unregister_master(drv_data->master);
@@ -1389,26 +1413,32 @@ static int __devexit bfin_spi_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct driver_data *drv_data = platform_get_drvdata(pdev);
+ struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
status = bfin_spi_stop_queue(drv_data);
if (status != 0)
return status;
- /* stop hardware */
- bfin_spi_disable(drv_data);
+ drv_data->ctrl_reg = read_CTRL(drv_data);
+ drv_data->flag_reg = read_FLAG(drv_data);
+
+ /*
+ * reset SPI_CTL and SPI_FLG registers
+ */
+ write_CTRL(drv_data, BIT_CTL_CPHA | BIT_CTL_MASTER);
+ write_FLAG(drv_data, 0xFF00);
return 0;
}
static int bfin_spi_resume(struct platform_device *pdev)
{
- struct driver_data *drv_data = platform_get_drvdata(pdev);
+ struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
int status = 0;
- /* Enable the SPI interface */
- bfin_spi_enable(drv_data);
+ write_CTRL(drv_data, drv_data->ctrl_reg);
+ write_FLAG(drv_data, drv_data->flag_reg);
/* Start the queue running */
status = bfin_spi_start_queue(drv_data);
@@ -1439,7 +1469,7 @@ static int __init bfin_spi_init(void)
{
return platform_driver_probe(&bfin_spi_driver, bfin_spi_probe);
}
-module_init(bfin_spi_init);
+subsys_initcall(bfin_spi_init);
static void __exit bfin_spi_exit(void)
{
diff --git a/drivers/spi/spi_fsl_espi.c b/drivers/spi/spi_fsl_espi.c
new file mode 100644
index 00000000000..e3b4f645196
--- /dev/null
+++ b/drivers/spi/spi_fsl_espi.c
@@ -0,0 +1,748 @@
+/*
+ * Freescale eSPI controller driver.
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <sysdev/fsl_soc.h>
+
+#include "spi_fsl_lib.h"
+
+/* eSPI Controller registers */
+struct fsl_espi_reg {
+ __be32 mode; /* 0x000 - eSPI mode register */
+ __be32 event; /* 0x004 - eSPI event register */
+ __be32 mask; /* 0x008 - eSPI mask register */
+ __be32 command; /* 0x00c - eSPI command register */
+ __be32 transmit; /* 0x010 - eSPI transmit FIFO access register*/
+ __be32 receive; /* 0x014 - eSPI receive FIFO access register*/
+ u8 res[8]; /* 0x018 - 0x01c reserved */
+ __be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */
+};
+
+struct fsl_espi_transfer {
+ const void *tx_buf;
+ void *rx_buf;
+ unsigned len;
+ unsigned n_tx;
+ unsigned n_rx;
+ unsigned actual_length;
+ int status;
+};
+
+/* eSPI Controller mode register definitions */
+#define SPMODE_ENABLE (1 << 31)
+#define SPMODE_LOOP (1 << 30)
+#define SPMODE_TXTHR(x) ((x) << 8)
+#define SPMODE_RXTHR(x) ((x) << 0)
+
+/* eSPI Controller CS mode register definitions */
+#define CSMODE_CI_INACTIVEHIGH (1 << 31)
+#define CSMODE_CP_BEGIN_EDGECLK (1 << 30)
+#define CSMODE_REV (1 << 29)
+#define CSMODE_DIV16 (1 << 28)
+#define CSMODE_PM(x) ((x) << 24)
+#define CSMODE_POL_1 (1 << 20)
+#define CSMODE_LEN(x) ((x) << 16)
+#define CSMODE_BEF(x) ((x) << 12)
+#define CSMODE_AFT(x) ((x) << 8)
+#define CSMODE_CG(x) ((x) << 3)
+
+/* Default mode/csmode for eSPI controller */
+#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3))
+#define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \
+ | CSMODE_AFT(0) | CSMODE_CG(1))
+
+/* SPIE register values */
+#define SPIE_NE 0x00000200 /* Not empty */
+#define SPIE_NF 0x00000100 /* Not full */
+
+/* SPIM register values */
+#define SPIM_NE 0x00000200 /* Not empty */
+#define SPIM_NF 0x00000100 /* Not full */
+#define SPIE_RXCNT(reg) ((reg >> 24) & 0x3F)
+#define SPIE_TXCNT(reg) ((reg >> 16) & 0x3F)
+
+/* SPCOM register values */
+#define SPCOM_CS(x) ((x) << 30)
+#define SPCOM_TRANLEN(x) ((x) << 0)
+#define SPCOM_TRANLEN_MAX 0xFFFF /* Max transaction length */
+
+static void fsl_espi_change_mode(struct spi_device *spi)
+{
+ struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+ struct fsl_espi_reg *reg_base = mspi->reg_base;
+ __be32 __iomem *mode = &reg_base->csmode[spi->chip_select];
+ __be32 __iomem *espi_mode = &reg_base->mode;
+ u32 tmp;
+ unsigned long flags;
+
+ /* Turn off IRQs locally to minimize time that SPI is disabled. */
+ local_irq_save(flags);
+
+ /* Turn off SPI unit prior changing mode */
+ tmp = mpc8xxx_spi_read_reg(espi_mode);
+ mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE);
+ mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+ mpc8xxx_spi_write_reg(espi_mode, tmp);
+
+ local_irq_restore(flags);
+}
+
+static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi)
+{
+ u32 data;
+ u16 data_h;
+ u16 data_l;
+ const u32 *tx = mpc8xxx_spi->tx;
+
+ if (!tx)
+ return 0;
+
+ data = *tx++ << mpc8xxx_spi->tx_shift;
+ data_l = data & 0xffff;
+ data_h = (data >> 16) & 0xffff;
+ swab16s(&data_l);
+ swab16s(&data_h);
+ data = data_h | data_l;
+
+ mpc8xxx_spi->tx = tx;
+ return data;
+}
+
+static int fsl_espi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ int bits_per_word = 0;
+ u8 pm;
+ u32 hz = 0;
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+
+ if (t) {
+ bits_per_word = t->bits_per_word;
+ hz = t->speed_hz;
+ }
+
+ /* spi_transfer level calls that work per-word */
+ if (!bits_per_word)
+ bits_per_word = spi->bits_per_word;
+
+ /* Make sure its a bit width we support [4..16] */
+ if ((bits_per_word < 4) || (bits_per_word > 16))
+ return -EINVAL;
+
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ cs->rx_shift = 0;
+ cs->tx_shift = 0;
+ cs->get_rx = mpc8xxx_spi_rx_buf_u32;
+ cs->get_tx = mpc8xxx_spi_tx_buf_u32;
+ if (bits_per_word <= 8) {
+ cs->rx_shift = 8 - bits_per_word;
+ } else if (bits_per_word <= 16) {
+ cs->rx_shift = 16 - bits_per_word;
+ if (spi->mode & SPI_LSB_FIRST)
+ cs->get_tx = fsl_espi_tx_buf_lsb;
+ } else {
+ return -EINVAL;
+ }
+
+ mpc8xxx_spi->rx_shift = cs->rx_shift;
+ mpc8xxx_spi->tx_shift = cs->tx_shift;
+ mpc8xxx_spi->get_rx = cs->get_rx;
+ mpc8xxx_spi->get_tx = cs->get_tx;
+
+ bits_per_word = bits_per_word - 1;
+
+ /* mask out bits we are going to set */
+ cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF));
+
+ cs->hw_mode |= CSMODE_LEN(bits_per_word);
+
+ if ((mpc8xxx_spi->spibrg / hz) > 64) {
+ cs->hw_mode |= CSMODE_DIV16;
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
+
+ WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
+ "Will use %d Hz instead.\n", dev_name(&spi->dev),
+ hz, mpc8xxx_spi->spibrg / 1024);
+ if (pm > 16)
+ pm = 16;
+ } else {
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+ }
+ if (pm)
+ pm--;
+
+ cs->hw_mode |= CSMODE_PM(pm);
+
+ fsl_espi_change_mode(spi);
+ return 0;
+}
+
+static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t,
+ unsigned int len)
+{
+ u32 word;
+ struct fsl_espi_reg *reg_base = mspi->reg_base;
+
+ mspi->count = len;
+
+ /* enable rx ints */
+ mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
+
+ /* transmit word */
+ word = mspi->get_tx(mspi);
+ mpc8xxx_spi_write_reg(&reg_base->transmit, word);
+
+ return 0;
+}
+
+static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base;
+ unsigned int len = t->len;
+ u8 bits_per_word;
+ int ret;
+
+ bits_per_word = spi->bits_per_word;
+ if (t->bits_per_word)
+ bits_per_word = t->bits_per_word;
+
+ mpc8xxx_spi->len = t->len;
+ len = roundup(len, 4) / 4;
+
+ mpc8xxx_spi->tx = t->tx_buf;
+ mpc8xxx_spi->rx = t->rx_buf;
+
+ INIT_COMPLETION(mpc8xxx_spi->done);
+
+ /* Set SPCOM[CS] and SPCOM[TRANLEN] field */
+ if ((t->len - 1) > SPCOM_TRANLEN_MAX) {
+ dev_err(mpc8xxx_spi->dev, "Transaction length (%d)"
+ " beyond the SPCOM[TRANLEN] field\n", t->len);
+ return -EINVAL;
+ }
+ mpc8xxx_spi_write_reg(&reg_base->command,
+ (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1)));
+
+ ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len);
+ if (ret)
+ return ret;
+
+ wait_for_completion(&mpc8xxx_spi->done);
+
+ /* disable rx ints */
+ mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+
+ return mpc8xxx_spi->count;
+}
+
+static void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd)
+{
+ if (cmd[1] && cmd[2] && cmd[3]) {
+ cmd[1] = (u8)(addr >> 16);
+ cmd[2] = (u8)(addr >> 8);
+ cmd[3] = (u8)(addr >> 0);
+ }
+}
+
+static unsigned int fsl_espi_cmd2addr(u8 *cmd)
+{
+ if (cmd[1] && cmd[2] && cmd[3])
+ return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0;
+
+ return 0;
+}
+
+static void fsl_espi_do_trans(struct spi_message *m,
+ struct fsl_espi_transfer *tr)
+{
+ struct spi_device *spi = m->spi;
+ struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+ struct fsl_espi_transfer *espi_trans = tr;
+ struct spi_message message;
+ struct spi_transfer *t, *first, trans;
+ int status = 0;
+
+ spi_message_init(&message);
+ memset(&trans, 0, sizeof(trans));
+
+ first = list_first_entry(&m->transfers, struct spi_transfer,
+ transfer_list);
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if ((first->bits_per_word != t->bits_per_word) ||
+ (first->speed_hz != t->speed_hz)) {
+ espi_trans->status = -EINVAL;
+ dev_err(mspi->dev, "bits_per_word/speed_hz should be"
+ " same for the same SPI transfer\n");
+ return;
+ }
+
+ trans.speed_hz = t->speed_hz;
+ trans.bits_per_word = t->bits_per_word;
+ trans.delay_usecs = max(first->delay_usecs, t->delay_usecs);
+ }
+
+ trans.len = espi_trans->len;
+ trans.tx_buf = espi_trans->tx_buf;
+ trans.rx_buf = espi_trans->rx_buf;
+ spi_message_add_tail(&trans, &message);
+
+ list_for_each_entry(t, &message.transfers, transfer_list) {
+ if (t->bits_per_word || t->speed_hz) {
+ status = -EINVAL;
+
+ status = fsl_espi_setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ }
+
+ if (t->len)
+ status = fsl_espi_bufs(spi, t);
+
+ if (status) {
+ status = -EMSGSIZE;
+ break;
+ }
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+ }
+
+ espi_trans->status = status;
+ fsl_espi_setup_transfer(spi, NULL);
+}
+
+static void fsl_espi_cmd_trans(struct spi_message *m,
+ struct fsl_espi_transfer *trans, u8 *rx_buff)
+{
+ struct spi_transfer *t;
+ u8 *local_buf;
+ int i = 0;
+ struct fsl_espi_transfer *espi_trans = trans;
+
+ local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
+ if (!local_buf) {
+ espi_trans->status = -ENOMEM;
+ return;
+ }
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->tx_buf) {
+ memcpy(local_buf + i, t->tx_buf, t->len);
+ i += t->len;
+ }
+ }
+
+ espi_trans->tx_buf = local_buf;
+ espi_trans->rx_buf = local_buf + espi_trans->n_tx;
+ fsl_espi_do_trans(m, espi_trans);
+
+ espi_trans->actual_length = espi_trans->len;
+ kfree(local_buf);
+}
+
+static void fsl_espi_rw_trans(struct spi_message *m,
+ struct fsl_espi_transfer *trans, u8 *rx_buff)
+{
+ struct fsl_espi_transfer *espi_trans = trans;
+ unsigned int n_tx = espi_trans->n_tx;
+ unsigned int n_rx = espi_trans->n_rx;
+ struct spi_transfer *t;
+ u8 *local_buf;
+ u8 *rx_buf = rx_buff;
+ unsigned int trans_len;
+ unsigned int addr;
+ int i, pos, loop;
+
+ local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL);
+ if (!local_buf) {
+ espi_trans->status = -ENOMEM;
+ return;
+ }
+
+ for (pos = 0, loop = 0; pos < n_rx; pos += trans_len, loop++) {
+ trans_len = n_rx - pos;
+ if (trans_len > SPCOM_TRANLEN_MAX - n_tx)
+ trans_len = SPCOM_TRANLEN_MAX - n_tx;
+
+ i = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->tx_buf) {
+ memcpy(local_buf + i, t->tx_buf, t->len);
+ i += t->len;
+ }
+ }
+
+ addr = fsl_espi_cmd2addr(local_buf);
+ addr += pos;
+ fsl_espi_addr2cmd(addr, local_buf);
+
+ espi_trans->n_tx = n_tx;
+ espi_trans->n_rx = trans_len;
+ espi_trans->len = trans_len + n_tx;
+ espi_trans->tx_buf = local_buf;
+ espi_trans->rx_buf = local_buf + n_tx;
+ fsl_espi_do_trans(m, espi_trans);
+
+ memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
+
+ if (loop > 0)
+ espi_trans->actual_length += espi_trans->len - n_tx;
+ else
+ espi_trans->actual_length += espi_trans->len;
+ }
+
+ kfree(local_buf);
+}
+
+static void fsl_espi_do_one_msg(struct spi_message *m)
+{
+ struct spi_transfer *t;
+ u8 *rx_buf = NULL;
+ unsigned int n_tx = 0;
+ unsigned int n_rx = 0;
+ struct fsl_espi_transfer espi_trans;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->tx_buf)
+ n_tx += t->len;
+ if (t->rx_buf) {
+ n_rx += t->len;
+ rx_buf = t->rx_buf;
+ }
+ }
+
+ espi_trans.n_tx = n_tx;
+ espi_trans.n_rx = n_rx;
+ espi_trans.len = n_tx + n_rx;
+ espi_trans.actual_length = 0;
+ espi_trans.status = 0;
+
+ if (!rx_buf)
+ fsl_espi_cmd_trans(m, &espi_trans, NULL);
+ else
+ fsl_espi_rw_trans(m, &espi_trans, rx_buf);
+
+ m->actual_length = espi_trans.actual_length;
+ m->status = espi_trans.status;
+ m->complete(m->context);
+}
+
+static int fsl_espi_setup(struct spi_device *spi)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_espi_reg *reg_base;
+ int retval;
+ u32 hw_mode;
+ u32 loop_mode;
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ if (!cs) {
+ cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+ spi->controller_state = cs;
+ }
+
+ mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ reg_base = mpc8xxx_spi->reg_base;
+
+ hw_mode = cs->hw_mode; /* Save orginal settings */
+ cs->hw_mode = mpc8xxx_spi_read_reg(
+ &reg_base->csmode[spi->chip_select]);
+ /* mask out bits we are going to set */
+ cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH
+ | CSMODE_REV);
+
+ if (spi->mode & SPI_CPHA)
+ cs->hw_mode |= CSMODE_CP_BEGIN_EDGECLK;
+ if (spi->mode & SPI_CPOL)
+ cs->hw_mode |= CSMODE_CI_INACTIVEHIGH;
+ if (!(spi->mode & SPI_LSB_FIRST))
+ cs->hw_mode |= CSMODE_REV;
+
+ /* Handle the loop mode */
+ loop_mode = mpc8xxx_spi_read_reg(&reg_base->mode);
+ loop_mode &= ~SPMODE_LOOP;
+ if (spi->mode & SPI_LOOP)
+ loop_mode |= SPMODE_LOOP;
+ mpc8xxx_spi_write_reg(&reg_base->mode, loop_mode);
+
+ retval = fsl_espi_setup_transfer(spi, NULL);
+ if (retval < 0) {
+ cs->hw_mode = hw_mode; /* Restore settings */
+ return retval;
+ }
+ return 0;
+}
+
+void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+ struct fsl_espi_reg *reg_base = mspi->reg_base;
+
+ /* We need handle RX first */
+ if (events & SPIE_NE) {
+ u32 rx_data;
+
+ /* Spin until RX is done */
+ while (SPIE_RXCNT(events) < min(4, mspi->len)) {
+ cpu_relax();
+ events = mpc8xxx_spi_read_reg(&reg_base->event);
+ }
+ mspi->len -= 4;
+
+ rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
+
+ if (mspi->rx)
+ mspi->get_rx(rx_data, mspi);
+ }
+
+ if (!(events & SPIE_NF)) {
+ int ret;
+
+ /* spin until TX is done */
+ ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
+ &reg_base->event)) & SPIE_NF) == 0, 1000, 0);
+ if (!ret) {
+ dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
+ return;
+ }
+ }
+
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&reg_base->event, events);
+
+ mspi->count -= 1;
+ if (mspi->count) {
+ u32 word = mspi->get_tx(mspi);
+
+ mpc8xxx_spi_write_reg(&reg_base->transmit, word);
+ } else {
+ complete(&mspi->done);
+ }
+}
+
+static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
+{
+ struct mpc8xxx_spi *mspi = context_data;
+ struct fsl_espi_reg *reg_base = mspi->reg_base;
+ irqreturn_t ret = IRQ_NONE;
+ u32 events;
+
+ /* Get interrupt events(tx/rx) */
+ events = mpc8xxx_spi_read_reg(&reg_base->event);
+ if (events)
+ ret = IRQ_HANDLED;
+
+ dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+ fsl_espi_cpu_irq(mspi, events);
+
+ return ret;
+}
+
+static void fsl_espi_remove(struct mpc8xxx_spi *mspi)
+{
+ iounmap(mspi->reg_base);
+}
+
+static struct spi_master * __devinit fsl_espi_probe(struct device *dev,
+ struct resource *mem, unsigned int irq)
+{
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct spi_master *master;
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_espi_reg *reg_base;
+ u32 regval;
+ int i, ret = 0;
+
+ master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
+ if (!master) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(dev, master);
+
+ ret = mpc8xxx_spi_probe(dev, mem, irq);
+ if (ret)
+ goto err_probe;
+
+ master->setup = fsl_espi_setup;
+
+ mpc8xxx_spi = spi_master_get_devdata(master);
+ mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg;
+ mpc8xxx_spi->spi_remove = fsl_espi_remove;
+
+ mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
+ if (!mpc8xxx_spi->reg_base) {
+ ret = -ENOMEM;
+ goto err_probe;
+ }
+
+ reg_base = mpc8xxx_spi->reg_base;
+
+ /* Register for SPI Interrupt */
+ ret = request_irq(mpc8xxx_spi->irq, fsl_espi_irq,
+ 0, "fsl_espi", mpc8xxx_spi);
+ if (ret)
+ goto free_irq;
+
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+ mpc8xxx_spi->rx_shift = 16;
+ mpc8xxx_spi->tx_shift = 24;
+ }
+
+ /* SPI controller initializations */
+ mpc8xxx_spi_write_reg(&reg_base->mode, 0);
+ mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+ mpc8xxx_spi_write_reg(&reg_base->command, 0);
+ mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
+
+ /* Init eSPI CS mode register */
+ for (i = 0; i < pdata->max_chipselect; i++)
+ mpc8xxx_spi_write_reg(&reg_base->csmode[i], CSMODE_INIT_VAL);
+
+ /* Enable SPI interface */
+ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
+
+ mpc8xxx_spi_write_reg(&reg_base->mode, regval);
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ goto unreg_master;
+
+ dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq);
+
+ return master;
+
+unreg_master:
+ free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+free_irq:
+ iounmap(mpc8xxx_spi->reg_base);
+err_probe:
+ spi_master_put(master);
+err:
+ return ERR_PTR(ret);
+}
+
+static int of_fsl_espi_get_chipselects(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ const u32 *prop;
+ int len;
+
+ prop = of_get_property(np, "fsl,espi-num-chipselects", &len);
+ if (!prop || len < sizeof(*prop)) {
+ dev_err(dev, "No 'fsl,espi-num-chipselects' property\n");
+ return -EINVAL;
+ }
+
+ pdata->max_chipselect = *prop;
+ pdata->cs_control = NULL;
+
+ return 0;
+}
+
+static int __devinit of_fsl_espi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct spi_master *master;
+ struct resource mem;
+ struct resource irq;
+ int ret = -ENOMEM;
+
+ ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+ if (ret)
+ return ret;
+
+ ret = of_fsl_espi_get_chipselects(dev);
+ if (ret)
+ goto err;
+
+ ret = of_address_to_resource(np, 0, &mem);
+ if (ret)
+ goto err;
+
+ ret = of_irq_to_resource(np, 0, &irq);
+ if (!ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ master = fsl_espi_probe(dev, &mem, irq.start);
+ if (IS_ERR(master)) {
+ ret = PTR_ERR(master);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int __devexit of_fsl_espi_remove(struct platform_device *dev)
+{
+ return mpc8xxx_spi_remove(&dev->dev);
+}
+
+static const struct of_device_id of_fsl_espi_match[] = {
+ { .compatible = "fsl,mpc8536-espi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_fsl_espi_match);
+
+static struct of_platform_driver fsl_espi_driver = {
+ .driver = {
+ .name = "fsl_espi",
+ .owner = THIS_MODULE,
+ .of_match_table = of_fsl_espi_match,
+ },
+ .probe = of_fsl_espi_probe,
+ .remove = __devexit_p(of_fsl_espi_remove),
+};
+
+static int __init fsl_espi_init(void)
+{
+ return of_register_platform_driver(&fsl_espi_driver);
+}
+module_init(fsl_espi_init);
+
+static void __exit fsl_espi_exit(void)
+{
+ of_unregister_platform_driver(&fsl_espi_driver);
+}
+module_exit(fsl_espi_exit);
+
+MODULE_AUTHOR("Mingkai Hu");
+MODULE_DESCRIPTION("Enhanced Freescale SPI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
new file mode 100644
index 00000000000..5cd741fdb5c
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.c
@@ -0,0 +1,237 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <sysdev/fsl_soc.h>
+
+#include "spi_fsl_lib.h"
+
+#define MPC8XXX_SPI_RX_BUF(type) \
+void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ type *rx = mpc8xxx_spi->rx; \
+ *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
+ mpc8xxx_spi->rx = rx; \
+}
+
+#define MPC8XXX_SPI_TX_BUF(type) \
+u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ u32 data; \
+ const type *tx = mpc8xxx_spi->tx; \
+ if (!tx) \
+ return 0; \
+ data = *tx++ << mpc8xxx_spi->tx_shift; \
+ mpc8xxx_spi->tx = tx; \
+ return data; \
+}
+
+MPC8XXX_SPI_RX_BUF(u8)
+MPC8XXX_SPI_RX_BUF(u16)
+MPC8XXX_SPI_RX_BUF(u32)
+MPC8XXX_SPI_TX_BUF(u8)
+MPC8XXX_SPI_TX_BUF(u16)
+MPC8XXX_SPI_TX_BUF(u32)
+
+struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata)
+{
+ return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
+}
+
+void mpc8xxx_spi_work(struct work_struct *work)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
+ work);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ while (!list_empty(&mpc8xxx_spi->queue)) {
+ struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
+ struct spi_message, queue);
+
+ list_del_init(&m->queue);
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+
+ if (mpc8xxx_spi->spi_do_one_msg)
+ mpc8xxx_spi->spi_do_one_msg(m);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ }
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+}
+
+int mpc8xxx_spi_transfer(struct spi_device *spi,
+ struct spi_message *m)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ m->actual_length = 0;
+ m->status = -EINPROGRESS;
+
+ spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
+ list_add_tail(&m->queue, &mpc8xxx_spi->queue);
+ queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
+ spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+
+ return 0;
+}
+
+void mpc8xxx_spi_cleanup(struct spi_device *spi)
+{
+ kfree(spi->controller_state);
+}
+
+const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+ if (flags & SPI_QE_CPU_MODE) {
+ return "QE CPU";
+ } else if (flags & SPI_CPM_MODE) {
+ if (flags & SPI_QE)
+ return "QE";
+ else if (flags & SPI_CPM2)
+ return "CPM2";
+ else
+ return "CPM1";
+ }
+ return "CPU";
+}
+
+int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+ unsigned int irq)
+{
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct spi_master *master;
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ int ret = 0;
+
+ master = dev_get_drvdata(dev);
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+ | SPI_LSB_FIRST | SPI_LOOP;
+
+ master->transfer = mpc8xxx_spi_transfer;
+ master->cleanup = mpc8xxx_spi_cleanup;
+ master->dev.of_node = dev->of_node;
+
+ mpc8xxx_spi = spi_master_get_devdata(master);
+ mpc8xxx_spi->dev = dev;
+ mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
+ mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+ mpc8xxx_spi->flags = pdata->flags;
+ mpc8xxx_spi->spibrg = pdata->sysclk;
+ mpc8xxx_spi->irq = irq;
+
+ mpc8xxx_spi->rx_shift = 0;
+ mpc8xxx_spi->tx_shift = 0;
+
+ init_completion(&mpc8xxx_spi->done);
+
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->max_chipselect;
+
+ spin_lock_init(&mpc8xxx_spi->lock);
+ init_completion(&mpc8xxx_spi->done);
+ INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
+ INIT_LIST_HEAD(&mpc8xxx_spi->queue);
+
+ mpc8xxx_spi->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (mpc8xxx_spi->workqueue == NULL) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+int __devexit mpc8xxx_spi_remove(struct device *dev)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ struct spi_master *master;
+
+ master = dev_get_drvdata(dev);
+ mpc8xxx_spi = spi_master_get_devdata(master);
+
+ flush_workqueue(mpc8xxx_spi->workqueue);
+ destroy_workqueue(mpc8xxx_spi->workqueue);
+ spi_unregister_master(master);
+
+ free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+
+ if (mpc8xxx_spi->spi_remove)
+ mpc8xxx_spi->spi_remove(mpc8xxx_spi);
+
+ return 0;
+}
+
+int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct mpc8xxx_spi_probe_info *pinfo;
+ struct fsl_spi_platform_data *pdata;
+ const void *prop;
+ int ret = -ENOMEM;
+
+ pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ pdata = &pinfo->pdata;
+ dev->platform_data = pdata;
+
+ /* Allocate bus num dynamically. */
+ pdata->bus_num = -1;
+
+ /* SPI controller is either clocked from QE or SoC clock. */
+ pdata->sysclk = get_brgfreq();
+ if (pdata->sysclk == -1) {
+ pdata->sysclk = fsl_get_sys_freq();
+ if (pdata->sysclk == -1) {
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ prop = of_get_property(np, "mode", NULL);
+ if (prop && !strcmp(prop, "cpu-qe"))
+ pdata->flags = SPI_QE_CPU_MODE;
+ else if (prop && !strcmp(prop, "qe"))
+ pdata->flags = SPI_CPM_MODE | SPI_QE;
+ else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+ else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM1;
+
+ return 0;
+
+err:
+ kfree(pinfo);
+ return ret;
+}
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
new file mode 100644
index 00000000000..281e060977c
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.h
@@ -0,0 +1,124 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.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.
+ */
+#ifndef __SPI_FSL_LIB_H__
+#define __SPI_FSL_LIB_H__
+
+#include <asm/io.h>
+
+/* SPI/eSPI Controller driver's private data. */
+struct mpc8xxx_spi {
+ struct device *dev;
+ void *reg_base;
+
+ /* rx & tx bufs from the spi_transfer */
+ const void *tx;
+ void *rx;
+#ifdef CONFIG_SPI_FSL_ESPI
+ int len;
+#endif
+
+ int subblock;
+ struct spi_pram __iomem *pram;
+ struct cpm_buf_desc __iomem *tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd;
+
+ struct spi_transfer *xfer_in_progress;
+
+ /* dma addresses for CPM transfers */
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+ bool map_tx_dma;
+ bool map_rx_dma;
+
+ dma_addr_t dma_dummy_tx;
+ dma_addr_t dma_dummy_rx;
+
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32(*get_tx) (struct mpc8xxx_spi *);
+
+ /* hooks for different controller driver */
+ void (*spi_do_one_msg) (struct spi_message *m);
+ void (*spi_remove) (struct mpc8xxx_spi *mspi);
+
+ unsigned int count;
+ unsigned int irq;
+
+ unsigned nsecs; /* (clock cycle time)/2 */
+
+ u32 spibrg; /* SPIBRG input clock */
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+
+ unsigned int flags;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct list_head queue;
+ spinlock_t lock;
+
+ struct completion done;
+};
+
+struct spi_mpc8xxx_cs {
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32 (*get_tx) (struct mpc8xxx_spi *);
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+ u32 hw_mode; /* Holds HW mode register settings */
+};
+
+static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
+{
+ out_be32(reg, val);
+}
+
+static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
+{
+ return in_be32(reg);
+}
+
+struct mpc8xxx_spi_probe_info {
+ struct fsl_spi_platform_data pdata;
+ int *gpios;
+ bool *alow_flags;
+};
+
+extern u32 mpc8xxx_spi_tx_buf_u8(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u16(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u32(struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u8(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u16(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u32(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+
+extern struct mpc8xxx_spi_probe_info *to_of_pinfo(
+ struct fsl_spi_platform_data *pdata);
+extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, unsigned int len);
+extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m);
+extern void mpc8xxx_spi_cleanup(struct spi_device *spi);
+extern const char *mpc8xxx_spi_strmode(unsigned int flags);
+extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+ unsigned int irq);
+extern int mpc8xxx_spi_remove(struct device *dev);
+extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid);
+
+#endif /* __SPI_FSL_LIB_H__ */
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_fsl_spi.c
index 1dd86b835cd..7ca52d3ae8f 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_fsl_spi.c
@@ -1,9 +1,10 @@
/*
- * MPC8xxx SPI controller driver.
+ * Freescale SPI controller driver.
*
* Maintainer: Kumar Gala
*
* Copyright (C) 2006 Polycom, Inc.
+ * Copyright 2010 Freescale Semiconductor, Inc.
*
* CPM SPI and QE buffer descriptors mode support:
* Copyright (c) 2009 MontaVista Software, Inc.
@@ -15,18 +16,11 @@
* option) any later version.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/irq.h>
-#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/platform_device.h>
@@ -38,12 +32,12 @@
#include <linux/of_platform.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
-#include <linux/slab.h>
#include <sysdev/fsl_soc.h>
#include <asm/cpm.h>
#include <asm/qe.h>
-#include <asm/irq.h>
+
+#include "spi_fsl_lib.h"
/* CPM1 and CPM2 are mutually exclusive. */
#ifdef CONFIG_CPM1
@@ -55,7 +49,7 @@
#endif
/* SPI Controller registers */
-struct mpc8xxx_spi_reg {
+struct fsl_spi_reg {
u8 res1[0x20];
__be32 mode;
__be32 event;
@@ -80,7 +74,7 @@ struct mpc8xxx_spi_reg {
/*
* Default for SPI Mode:
- * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
+ * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
*/
#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
@@ -102,112 +96,16 @@ struct mpc8xxx_spi_reg {
#define SPI_PRAM_SIZE 0x100
#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
-/* SPI Controller driver's private data. */
-struct mpc8xxx_spi {
- struct device *dev;
- struct mpc8xxx_spi_reg __iomem *base;
-
- /* rx & tx bufs from the spi_transfer */
- const void *tx;
- void *rx;
-
- int subblock;
- struct spi_pram __iomem *pram;
- struct cpm_buf_desc __iomem *tx_bd;
- struct cpm_buf_desc __iomem *rx_bd;
-
- struct spi_transfer *xfer_in_progress;
-
- /* dma addresses for CPM transfers */
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- bool map_tx_dma;
- bool map_rx_dma;
-
- dma_addr_t dma_dummy_tx;
- dma_addr_t dma_dummy_rx;
-
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32(*get_tx) (struct mpc8xxx_spi *);
-
- unsigned int count;
- unsigned int irq;
-
- unsigned nsecs; /* (clock cycle time)/2 */
-
- u32 spibrg; /* SPIBRG input clock */
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
-
- unsigned int flags;
-
- struct workqueue_struct *workqueue;
- struct work_struct work;
-
- struct list_head queue;
- spinlock_t lock;
-
- struct completion done;
-};
-
-static void *mpc8xxx_dummy_rx;
-static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
-static int mpc8xxx_dummy_rx_refcnt;
-
-struct spi_mpc8xxx_cs {
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32 (*get_tx) (struct mpc8xxx_spi *);
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
- u32 hw_mode; /* Holds HW mode register settings */
-};
-
-static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
-{
- out_be32(reg, val);
-}
-
-static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
-{
- return in_be32(reg);
-}
-
-#define MPC83XX_SPI_RX_BUF(type) \
-static \
-void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- type *rx = mpc8xxx_spi->rx; \
- *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
- mpc8xxx_spi->rx = rx; \
-}
-
-#define MPC83XX_SPI_TX_BUF(type) \
-static \
-u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- u32 data; \
- const type *tx = mpc8xxx_spi->tx; \
- if (!tx) \
- return 0; \
- data = *tx++ << mpc8xxx_spi->tx_shift; \
- mpc8xxx_spi->tx = tx; \
- return data; \
-}
+static void *fsl_dummy_rx;
+static DEFINE_MUTEX(fsl_dummy_rx_lock);
+static int fsl_dummy_rx_refcnt;
-MPC83XX_SPI_RX_BUF(u8)
-MPC83XX_SPI_RX_BUF(u16)
-MPC83XX_SPI_RX_BUF(u32)
-MPC83XX_SPI_TX_BUF(u8)
-MPC83XX_SPI_TX_BUF(u16)
-MPC83XX_SPI_TX_BUF(u32)
-
-static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+static void fsl_spi_change_mode(struct spi_device *spi)
{
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
struct spi_mpc8xxx_cs *cs = spi->controller_state;
- __be32 __iomem *mode = &mspi->base->mode;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
+ __be32 __iomem *mode = &reg_base->mode;
unsigned long flags;
if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
@@ -238,7 +136,7 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
local_irq_restore(flags);
}
-static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
+static void fsl_spi_chipselect(struct spi_device *spi, int value)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
@@ -256,18 +154,17 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
mpc8xxx_spi->get_rx = cs->get_rx;
mpc8xxx_spi->get_tx = cs->get_tx;
- mpc8xxx_spi_change_mode(spi);
+ fsl_spi_change_mode(spi);
if (pdata->cs_control)
pdata->cs_control(spi, pol);
}
}
-static int
-mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- struct mpc8xxx_spi *mpc8xxx_spi,
- int bits_per_word)
+static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ struct mpc8xxx_spi *mpc8xxx_spi,
+ int bits_per_word)
{
cs->rx_shift = 0;
cs->tx_shift = 0;
@@ -307,10 +204,9 @@ mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
return bits_per_word;
}
-static int
-mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- int bits_per_word)
+static int mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ int bits_per_word)
{
/* QE uses Little Endian for words > 8
* so transform all words > 8 into 8 bits
@@ -326,13 +222,13 @@ mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
return bits_per_word;
}
-static
-int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+static int fsl_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi;
- int bits_per_word;
+ int bits_per_word = 0;
u8 pm;
- u32 hz;
+ u32 hz = 0;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
mpc8xxx_spi = spi_master_get_devdata(spi->master);
@@ -340,9 +236,6 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
- } else {
- bits_per_word = 0;
- hz = 0;
}
/* spi_transfer level calls that work per-word */
@@ -388,23 +281,25 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
hz, mpc8xxx_spi->spibrg / 1024);
if (pm > 16)
pm = 16;
- } else
+ } else {
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+ }
if (pm)
pm--;
cs->hw_mode |= SPMODE_PM(pm);
- mpc8xxx_spi_change_mode(spi);
+ fsl_spi_change_mode(spi);
return 0;
}
-static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
{
struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
unsigned int xfer_ofs;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
@@ -424,13 +319,14 @@ static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
BD_SC_LAST);
/* start transfer */
- mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+ mpc8xxx_spi_write_reg(&reg_base->command, SPCOM_STR);
}
-static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
struct spi_transfer *t, bool is_dma_mapped)
{
struct device *dev = mspi->dev;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
if (is_dma_mapped) {
mspi->map_tx_dma = 0;
@@ -475,13 +371,13 @@ static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
}
/* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+ mpc8xxx_spi_write_reg(&reg_base->mask, SPIE_RXB);
mspi->xfer_in_progress = t;
mspi->count = t->len;
/* start CPM transfers */
- mpc8xxx_spi_cpm_bufs_start(mspi);
+ fsl_spi_cpm_bufs_start(mspi);
return 0;
@@ -491,7 +387,7 @@ err_rx_dma:
return -ENOMEM;
}
-static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct spi_transfer *t = mspi->xfer_in_progress;
@@ -503,31 +399,34 @@ static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
mspi->xfer_in_progress = NULL;
}
-static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
struct spi_transfer *t, unsigned int len)
{
u32 word;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
mspi->count = len;
/* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+ mpc8xxx_spi_write_reg(&reg_base->mask, SPIM_NE);
/* transmit word */
word = mspi->get_tx(mspi);
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+ mpc8xxx_spi_write_reg(&reg_base->transmit, word);
return 0;
}
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
bool is_dma_mapped)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ struct fsl_spi_reg *reg_base;
unsigned int len = t->len;
u8 bits_per_word;
int ret;
+ reg_base = mpc8xxx_spi->reg_base;
bits_per_word = spi->bits_per_word;
if (t->bits_per_word)
bits_per_word = t->bits_per_word;
@@ -551,24 +450,24 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
INIT_COMPLETION(mpc8xxx_spi->done);
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+ ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
else
- ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+ ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len);
if (ret)
return ret;
wait_for_completion(&mpc8xxx_spi->done);
/* disable rx ints */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+ mpc8xxx_spi_write_reg(&reg_base->mask, 0);
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+ fsl_spi_cpm_bufs_complete(mpc8xxx_spi);
return mpc8xxx_spi->count;
}
-static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
+static void fsl_spi_do_one_msg(struct spi_message *m)
{
struct spi_device *spi = m->spi;
struct spi_transfer *t;
@@ -584,18 +483,18 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
status = -EINVAL;
if (cs_change)
- status = mpc8xxx_spi_setup_transfer(spi, t);
+ status = fsl_spi_setup_transfer(spi, t);
if (status < 0)
break;
}
if (cs_change) {
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE);
ndelay(nsecs);
}
cs_change = t->cs_change;
if (t->len)
- status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
+ status = fsl_spi_bufs(spi, t, m->is_dma_mapped);
if (status) {
status = -EMSGSIZE;
break;
@@ -607,7 +506,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
if (cs_change) {
ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
}
@@ -617,35 +516,16 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
if (status || !cs_change) {
ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
}
- mpc8xxx_spi_setup_transfer(spi, NULL);
-}
-
-static void mpc8xxx_spi_work(struct work_struct *work)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
- work);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- while (!list_empty(&mpc8xxx_spi->queue)) {
- struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
- struct spi_message, queue);
-
- list_del_init(&m->queue);
- spin_unlock_irq(&mpc8xxx_spi->lock);
-
- mpc8xxx_spi_do_one_msg(m);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- }
- spin_unlock_irq(&mpc8xxx_spi->lock);
+ fsl_spi_setup_transfer(spi, NULL);
}
-static int mpc8xxx_spi_setup(struct spi_device *spi)
+static int fsl_spi_setup(struct spi_device *spi)
{
struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_spi_reg *reg_base;
int retval;
u32 hw_mode;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
@@ -661,8 +541,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
}
mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ reg_base = mpc8xxx_spi->reg_base;
+
hw_mode = cs->hw_mode; /* Save original settings */
- cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
+ cs->hw_mode = mpc8xxx_spi_read_reg(&reg_base->mode);
/* mask out bits we are going to set */
cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
| SPMODE_REV | SPMODE_LOOP);
@@ -676,7 +558,7 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
if (spi->mode & SPI_LOOP)
cs->hw_mode |= SPMODE_LOOP;
- retval = mpc8xxx_spi_setup_transfer(spi, NULL);
+ retval = fsl_spi_setup_transfer(spi, NULL);
if (retval < 0) {
cs->hw_mode = hw_mode; /* Restore settings */
return retval;
@@ -684,9 +566,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
return 0;
}
-static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
{
u16 len;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
@@ -698,20 +581,22 @@ static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
}
/* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
+ mpc8xxx_spi_write_reg(&reg_base->event, events);
mspi->count -= len;
if (mspi->count)
- mpc8xxx_spi_cpm_bufs_start(mspi);
+ fsl_spi_cpm_bufs_start(mspi);
else
complete(&mspi->done);
}
-static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
{
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
+
/* We need handle RX first */
if (events & SPIE_NE) {
- u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+ u32 rx_data = mpc8xxx_spi_read_reg(&reg_base->receive);
if (mspi->rx)
mspi->get_rx(rx_data, mspi);
@@ -720,102 +605,80 @@ static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
if ((events & SPIE_NF) == 0)
/* spin until TX is done */
while (((events =
- mpc8xxx_spi_read_reg(&mspi->base->event)) &
+ mpc8xxx_spi_read_reg(&reg_base->event)) &
SPIE_NF) == 0)
cpu_relax();
/* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
+ mpc8xxx_spi_write_reg(&reg_base->event, events);
mspi->count -= 1;
if (mspi->count) {
u32 word = mspi->get_tx(mspi);
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+ mpc8xxx_spi_write_reg(&reg_base->transmit, word);
} else {
complete(&mspi->done);
}
}
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
{
struct mpc8xxx_spi *mspi = context_data;
irqreturn_t ret = IRQ_NONE;
u32 events;
+ struct fsl_spi_reg *reg_base = mspi->reg_base;
/* Get interrupt events(tx/rx) */
- events = mpc8xxx_spi_read_reg(&mspi->base->event);
+ events = mpc8xxx_spi_read_reg(&reg_base->event);
if (events)
ret = IRQ_HANDLED;
dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
if (mspi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_irq(mspi, events);
+ fsl_spi_cpm_irq(mspi, events);
else
- mpc8xxx_spi_cpu_irq(mspi, events);
+ fsl_spi_cpu_irq(mspi, events);
return ret;
}
-static int mpc8xxx_spi_transfer(struct spi_device *spi,
- struct spi_message *m)
+static void *fsl_spi_alloc_dummy_rx(void)
{
- struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
- unsigned long flags;
+ mutex_lock(&fsl_dummy_rx_lock);
- m->actual_length = 0;
- m->status = -EINPROGRESS;
+ if (!fsl_dummy_rx)
+ fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+ if (fsl_dummy_rx)
+ fsl_dummy_rx_refcnt++;
- spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
- list_add_tail(&m->queue, &mpc8xxx_spi->queue);
- queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
- spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+ mutex_unlock(&fsl_dummy_rx_lock);
- return 0;
+ return fsl_dummy_rx;
}
-
-static void mpc8xxx_spi_cleanup(struct spi_device *spi)
+static void fsl_spi_free_dummy_rx(void)
{
- kfree(spi->controller_state);
-}
+ mutex_lock(&fsl_dummy_rx_lock);
-static void *mpc8xxx_spi_alloc_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- if (!mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
- if (mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx_refcnt++;
-
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
-
- return mpc8xxx_dummy_rx;
-}
-
-static void mpc8xxx_spi_free_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- switch (mpc8xxx_dummy_rx_refcnt) {
+ switch (fsl_dummy_rx_refcnt) {
case 0:
WARN_ON(1);
break;
case 1:
- kfree(mpc8xxx_dummy_rx);
- mpc8xxx_dummy_rx = NULL;
+ kfree(fsl_dummy_rx);
+ fsl_dummy_rx = NULL;
/* fall through */
default:
- mpc8xxx_dummy_rx_refcnt--;
+ fsl_dummy_rx_refcnt--;
break;
}
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
+ mutex_unlock(&fsl_dummy_rx_lock);
}
-static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct device_node *np = dev->of_node;
@@ -869,7 +732,7 @@ static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
return pram_ofs;
}
-static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct device_node *np = dev->of_node;
@@ -881,7 +744,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
if (!(mspi->flags & SPI_CPM_MODE))
return 0;
- if (!mpc8xxx_spi_alloc_dummy_rx())
+ if (!fsl_spi_alloc_dummy_rx())
return -ENOMEM;
if (mspi->flags & SPI_QE) {
@@ -902,7 +765,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
}
}
- pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+ pram_ofs = fsl_spi_cpm_get_pram(mspi);
if (IS_ERR_VALUE(pram_ofs)) {
dev_err(dev, "can't allocate spi parameter ram\n");
goto err_pram;
@@ -922,7 +785,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
goto err_dummy_tx;
}
- mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+ mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
dev_err(dev, "unable to map dummy rx buffer\n");
@@ -960,11 +823,11 @@ err_dummy_tx:
err_bds:
cpm_muram_free(pram_ofs);
err_pram:
- mpc8xxx_spi_free_dummy_rx();
+ fsl_spi_free_dummy_rx();
return -ENOMEM;
}
-static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
@@ -972,30 +835,22 @@ static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
cpm_muram_free(cpm_muram_offset(mspi->pram));
- mpc8xxx_spi_free_dummy_rx();
+ fsl_spi_free_dummy_rx();
}
-static const char *mpc8xxx_spi_strmode(unsigned int flags)
+static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
{
- if (flags & SPI_QE_CPU_MODE) {
- return "QE CPU";
- } else if (flags & SPI_CPM_MODE) {
- if (flags & SPI_QE)
- return "QE";
- else if (flags & SPI_CPM2)
- return "CPM2";
- else
- return "CPM1";
- }
- return "CPU";
+ iounmap(mspi->reg_base);
+ fsl_spi_cpm_free(mspi);
}
-static struct spi_master * __devinit
-mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
+static struct spi_master * __devinit fsl_spi_probe(struct device *dev,
+ struct resource *mem, unsigned int irq)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_spi_reg *reg_base;
u32 regval;
int ret = 0;
@@ -1007,132 +862,77 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
dev_set_drvdata(dev, master);
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
- | SPI_LSB_FIRST | SPI_LOOP;
+ ret = mpc8xxx_spi_probe(dev, mem, irq);
+ if (ret)
+ goto err_probe;
- master->setup = mpc8xxx_spi_setup;
- master->transfer = mpc8xxx_spi_transfer;
- master->cleanup = mpc8xxx_spi_cleanup;
- master->dev.of_node = dev->of_node;
+ master->setup = fsl_spi_setup;
mpc8xxx_spi = spi_master_get_devdata(master);
- mpc8xxx_spi->dev = dev;
- mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
- mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
- mpc8xxx_spi->flags = pdata->flags;
- mpc8xxx_spi->spibrg = pdata->sysclk;
+ mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg;
+ mpc8xxx_spi->spi_remove = fsl_spi_remove;
+
- ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+ ret = fsl_spi_cpm_init(mpc8xxx_spi);
if (ret)
goto err_cpm_init;
- mpc8xxx_spi->rx_shift = 0;
- mpc8xxx_spi->tx_shift = 0;
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
mpc8xxx_spi->rx_shift = 16;
mpc8xxx_spi->tx_shift = 24;
}
- init_completion(&mpc8xxx_spi->done);
-
- mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
- if (mpc8xxx_spi->base == NULL) {
+ mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
+ if (mpc8xxx_spi->reg_base == NULL) {
ret = -ENOMEM;
goto err_ioremap;
}
- mpc8xxx_spi->irq = irq;
-
/* Register for SPI Interrupt */
- ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
- 0, "mpc8xxx_spi", mpc8xxx_spi);
+ ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq,
+ 0, "fsl_spi", mpc8xxx_spi);
if (ret != 0)
- goto unmap_io;
+ goto free_irq;
- master->bus_num = pdata->bus_num;
- master->num_chipselect = pdata->max_chipselect;
+ reg_base = mpc8xxx_spi->reg_base;
/* SPI controller initializations */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
+ mpc8xxx_spi_write_reg(&reg_base->mode, 0);
+ mpc8xxx_spi_write_reg(&reg_base->mask, 0);
+ mpc8xxx_spi_write_reg(&reg_base->command, 0);
+ mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
regval |= SPMODE_OP;
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
- spin_lock_init(&mpc8xxx_spi->lock);
- init_completion(&mpc8xxx_spi->done);
- INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
- INIT_LIST_HEAD(&mpc8xxx_spi->queue);
-
- mpc8xxx_spi->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (mpc8xxx_spi->workqueue == NULL) {
- ret = -EBUSY;
- goto free_irq;
- }
+ mpc8xxx_spi_write_reg(&reg_base->mode, regval);
ret = spi_register_master(master);
if (ret < 0)
goto unreg_master;
- dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+ dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base,
mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
return master;
unreg_master:
- destroy_workqueue(mpc8xxx_spi->workqueue);
-free_irq:
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-unmap_io:
- iounmap(mpc8xxx_spi->base);
+free_irq:
+ iounmap(mpc8xxx_spi->reg_base);
err_ioremap:
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+ fsl_spi_cpm_free(mpc8xxx_spi);
err_cpm_init:
+err_probe:
spi_master_put(master);
err:
return ERR_PTR(ret);
}
-static int __devexit mpc8xxx_spi_remove(struct device *dev)
-{
- struct mpc8xxx_spi *mpc8xxx_spi;
- struct spi_master *master;
-
- master = dev_get_drvdata(dev);
- mpc8xxx_spi = spi_master_get_devdata(master);
-
- flush_workqueue(mpc8xxx_spi->workqueue);
- destroy_workqueue(mpc8xxx_spi->workqueue);
- spi_unregister_master(master);
-
- free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
- iounmap(mpc8xxx_spi->base);
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-
- return 0;
-}
-
-struct mpc8xxx_spi_probe_info {
- struct fsl_spi_platform_data pdata;
- int *gpios;
- bool *alow_flags;
-};
-
-static struct mpc8xxx_spi_probe_info *
-to_of_pinfo(struct fsl_spi_platform_data *pdata)
-{
- return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
-}
-
-static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
+static void fsl_spi_cs_control(struct spi_device *spi, bool on)
{
struct device *dev = spi->dev.parent;
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
@@ -1143,7 +943,7 @@ static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
gpio_set_value(gpio, on ^ alow);
}
-static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
+static int of_fsl_spi_get_chipselects(struct device *dev)
{
struct device_node *np = dev->of_node;
struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -1204,7 +1004,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
}
pdata->max_chipselect = ngpios;
- pdata->cs_control = mpc8xxx_spi_cs_control;
+ pdata->cs_control = fsl_spi_cs_control;
return 0;
@@ -1223,7 +1023,7 @@ err_alloc_flags:
return ret;
}
-static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
+static int of_fsl_spi_free_chipselects(struct device *dev)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
@@ -1242,50 +1042,21 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
- const struct of_device_id *ofid)
+static int __devinit of_fsl_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
- struct mpc8xxx_spi_probe_info *pinfo;
- struct fsl_spi_platform_data *pdata;
struct spi_master *master;
struct resource mem;
struct resource irq;
- const void *prop;
int ret = -ENOMEM;
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo)
- return -ENOMEM;
-
- pdata = &pinfo->pdata;
- dev->platform_data = pdata;
-
- /* Allocate bus num dynamically. */
- pdata->bus_num = -1;
-
- /* SPI controller is either clocked from QE or SoC clock. */
- pdata->sysclk = get_brgfreq();
- if (pdata->sysclk == -1) {
- pdata->sysclk = fsl_get_sys_freq();
- if (pdata->sysclk == -1) {
- ret = -ENODEV;
- goto err_clk;
- }
- }
+ ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+ if (ret)
+ return ret;
- prop = of_get_property(np, "mode", NULL);
- if (prop && !strcmp(prop, "cpu-qe"))
- pdata->flags = SPI_QE_CPU_MODE;
- else if (prop && !strcmp(prop, "qe"))
- pdata->flags = SPI_CPM_MODE | SPI_QE;
- else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM2;
- else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM1;
-
- ret = of_mpc8xxx_spi_get_chipselects(dev);
+ ret = of_fsl_spi_get_chipselects(dev);
if (ret)
goto err;
@@ -1299,7 +1070,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
goto err;
}
- master = mpc8xxx_spi_probe(dev, &mem, irq.start);
+ master = fsl_spi_probe(dev, &mem, irq.start);
if (IS_ERR(master)) {
ret = PTR_ERR(master);
goto err;
@@ -1308,42 +1079,40 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
return 0;
err:
- of_mpc8xxx_spi_free_chipselects(dev);
-err_clk:
- kfree(pinfo);
+ of_fsl_spi_free_chipselects(dev);
return ret;
}
-static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
+static int __devexit of_fsl_spi_remove(struct platform_device *ofdev)
{
int ret;
ret = mpc8xxx_spi_remove(&ofdev->dev);
if (ret)
return ret;
- of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
+ of_fsl_spi_free_chipselects(&ofdev->dev);
return 0;
}
-static const struct of_device_id of_mpc8xxx_spi_match[] = {
+static const struct of_device_id of_fsl_spi_match[] = {
{ .compatible = "fsl,spi" },
- {},
+ {}
};
-MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
+MODULE_DEVICE_TABLE(of, of_fsl_spi_match);
-static struct of_platform_driver of_mpc8xxx_spi_driver = {
+static struct of_platform_driver of_fsl_spi_driver = {
.driver = {
- .name = "mpc8xxx_spi",
+ .name = "fsl_spi",
.owner = THIS_MODULE,
- .of_match_table = of_mpc8xxx_spi_match,
+ .of_match_table = of_fsl_spi_match,
},
- .probe = of_mpc8xxx_spi_probe,
- .remove = __devexit_p(of_mpc8xxx_spi_remove),
+ .probe = of_fsl_spi_probe,
+ .remove = __devexit_p(of_fsl_spi_remove),
};
#ifdef CONFIG_MPC832x_RDB
/*
- * XXX XXX XXX
+ * XXX XXX XXX
* This is "legacy" platform driver, was used by the MPC8323E-RDB boards
* only. The driver should go away soon, since newer MPC8323E-RDB's device
* tree can work with OpenFirmware driver. But for now we support old trees
@@ -1366,7 +1135,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
if (irq <= 0)
return -EINVAL;
- master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
+ master = fsl_spi_probe(&pdev->dev, mem, irq);
if (IS_ERR(master))
return PTR_ERR(master);
return 0;
@@ -1405,21 +1174,20 @@ static void __init legacy_driver_register(void) {}
static void __exit legacy_driver_unregister(void) {}
#endif /* CONFIG_MPC832x_RDB */
-static int __init mpc8xxx_spi_init(void)
+static int __init fsl_spi_init(void)
{
legacy_driver_register();
- return of_register_platform_driver(&of_mpc8xxx_spi_driver);
+ return of_register_platform_driver(&of_fsl_spi_driver);
}
+module_init(fsl_spi_init);
-static void __exit mpc8xxx_spi_exit(void)
+static void __exit fsl_spi_exit(void)
{
- of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
+ of_unregister_platform_driver(&of_fsl_spi_driver);
legacy_driver_unregister();
}
-
-module_init(mpc8xxx_spi_init);
-module_exit(mpc8xxx_spi_exit);
+module_exit(fsl_spi_exit);
MODULE_AUTHOR("Kumar Gala");
-MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
+MODULE_DESCRIPTION("Simple Freescale SPI Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c
index c3038da2648..795828b90f4 100644
--- a/drivers/spi/spi_s3c64xx.c
+++ b/drivers/spi/spi_s3c64xx.c
@@ -261,15 +261,25 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
- s3c2410_dma_config(sdd->tx_dmach, 1);
+ s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8);
s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
xfer->tx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
} else {
- unsigned char *buf = (unsigned char *) xfer->tx_buf;
- int i = 0;
- while (i < xfer->len)
- writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);
+ switch (sdd->cur_bpw) {
+ case 32:
+ iowrite32_rep(regs + S3C64XX_SPI_TX_DATA,
+ xfer->tx_buf, xfer->len / 4);
+ break;
+ case 16:
+ iowrite16_rep(regs + S3C64XX_SPI_TX_DATA,
+ xfer->tx_buf, xfer->len / 2);
+ break;
+ default:
+ iowrite8_rep(regs + S3C64XX_SPI_TX_DATA,
+ xfer->tx_buf, xfer->len);
+ break;
+ }
}
}
@@ -286,7 +296,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
- s3c2410_dma_config(sdd->rx_dmach, 1);
+ s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8);
s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
xfer->rx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
@@ -366,20 +376,26 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
return -EIO;
}
} else {
- unsigned char *buf;
- int i;
-
/* If it was only Tx */
if (xfer->rx_buf == NULL) {
sdd->state &= ~TXBUSY;
return 0;
}
- i = 0;
- buf = xfer->rx_buf;
- while (i < xfer->len)
- buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA);
-
+ switch (sdd->cur_bpw) {
+ case 32:
+ ioread32_rep(regs + S3C64XX_SPI_RX_DATA,
+ xfer->rx_buf, xfer->len / 4);
+ break;
+ case 16:
+ ioread16_rep(regs + S3C64XX_SPI_RX_DATA,
+ xfer->rx_buf, xfer->len / 2);
+ break;
+ default:
+ ioread8_rep(regs + S3C64XX_SPI_RX_DATA,
+ xfer->rx_buf, xfer->len);
+ break;
+ }
sdd->state &= ~RXBUSY;
}
@@ -399,13 +415,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
{
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 val;
/* Disable Clock */
- val = readl(regs + S3C64XX_SPI_CLK_CFG);
- val &= ~S3C64XX_SPI_ENCLK_ENABLE;
- writel(val, regs + S3C64XX_SPI_CLK_CFG);
+ if (sci->clk_from_cmu) {
+ clk_disable(sdd->src_clk);
+ } else {
+ val = readl(regs + S3C64XX_SPI_CLK_CFG);
+ val &= ~S3C64XX_SPI_ENCLK_ENABLE;
+ writel(val, regs + S3C64XX_SPI_CLK_CFG);
+ }
/* Set Polarity and Phase */
val = readl(regs + S3C64XX_SPI_CH_CFG);
@@ -429,29 +450,39 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
switch (sdd->cur_bpw) {
case 32:
val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD;
+ val |= S3C64XX_SPI_MODE_CH_TSZ_WORD;
break;
case 16:
val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD;
+ val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD;
break;
default:
val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE;
+ val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE;
break;
}
- val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; /* Always 8bits wide */
writel(val, regs + S3C64XX_SPI_MODE_CFG);
- /* Configure Clock */
- val = readl(regs + S3C64XX_SPI_CLK_CFG);
- val &= ~S3C64XX_SPI_PSR_MASK;
- val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
- & S3C64XX_SPI_PSR_MASK);
- writel(val, regs + S3C64XX_SPI_CLK_CFG);
-
- /* Enable Clock */
- val = readl(regs + S3C64XX_SPI_CLK_CFG);
- val |= S3C64XX_SPI_ENCLK_ENABLE;
- writel(val, regs + S3C64XX_SPI_CLK_CFG);
+ if (sci->clk_from_cmu) {
+ /* Configure Clock */
+ /* There is half-multiplier before the SPI */
+ clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
+ /* Enable Clock */
+ clk_enable(sdd->src_clk);
+ } else {
+ /* Configure Clock */
+ val = readl(regs + S3C64XX_SPI_CLK_CFG);
+ val &= ~S3C64XX_SPI_PSR_MASK;
+ val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
+ & S3C64XX_SPI_PSR_MASK);
+ writel(val, regs + S3C64XX_SPI_CLK_CFG);
+
+ /* Enable Clock */
+ val = readl(regs + S3C64XX_SPI_CLK_CFG);
+ val |= S3C64XX_SPI_ENCLK_ENABLE;
+ writel(val, regs + S3C64XX_SPI_CLK_CFG);
+ }
}
static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
@@ -499,6 +530,7 @@ static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id,
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct device *dev = &sdd->pdev->dev;
struct spi_transfer *xfer;
@@ -514,6 +546,9 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
/* Map until end or first fail */
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+ continue;
+
if (xfer->tx_buf != NULL) {
xfer->tx_dma = dma_map_single(dev,
(void *)xfer->tx_buf, xfer->len,
@@ -545,6 +580,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
+ struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct device *dev = &sdd->pdev->dev;
struct spi_transfer *xfer;
@@ -553,6 +589,9 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+ continue;
+
if (xfer->rx_buf != NULL
&& xfer->rx_dma != XFER_DMAADDR_INVALID)
dma_unmap_single(dev, xfer->rx_dma,
@@ -608,6 +647,14 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
bpw = xfer->bits_per_word ? : spi->bits_per_word;
speed = xfer->speed_hz ? : spi->max_speed_hz;
+ if (xfer->len % (bpw / 8)) {
+ dev_err(&spi->dev,
+ "Xfer length(%u) not a multiple of word size(%u)\n",
+ xfer->len, bpw / 8);
+ status = -EIO;
+ goto out;
+ }
+
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
sdd->cur_bpw = bpw;
sdd->cur_speed = speed;
@@ -798,7 +845,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_message *msg;
- u32 psr, speed;
unsigned long flags;
int err = 0;
@@ -841,32 +887,37 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
/* Check if we can provide the requested rate */
- speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */
-
- if (spi->max_speed_hz > speed)
- spi->max_speed_hz = speed;
-
- psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
- psr &= S3C64XX_SPI_PSR_MASK;
- if (psr == S3C64XX_SPI_PSR_MASK)
- psr--;
+ if (!sci->clk_from_cmu) {
+ u32 psr, speed;
+
+ /* Max possible */
+ speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
+
+ if (spi->max_speed_hz > speed)
+ spi->max_speed_hz = speed;
+
+ psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
+ psr &= S3C64XX_SPI_PSR_MASK;
+ if (psr == S3C64XX_SPI_PSR_MASK)
+ psr--;
+
+ speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
+ if (spi->max_speed_hz < speed) {
+ if (psr+1 < S3C64XX_SPI_PSR_MASK) {
+ psr++;
+ } else {
+ err = -EINVAL;
+ goto setup_exit;
+ }
+ }
- speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
- if (spi->max_speed_hz < speed) {
- if (psr+1 < S3C64XX_SPI_PSR_MASK) {
- psr++;
- } else {
+ speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
+ if (spi->max_speed_hz >= speed)
+ spi->max_speed_hz = speed;
+ else
err = -EINVAL;
- goto setup_exit;
- }
}
- speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
- if (spi->max_speed_hz >= speed)
- spi->max_speed_hz = speed;
- else
- err = -EINVAL;
-
setup_exit:
/* setup() returns with device de-selected */
@@ -888,7 +939,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
/* Disable Interrupts - we use Polling if not DMA mode */
writel(0, regs + S3C64XX_SPI_INT_EN);
- writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
+ if (!sci->clk_from_cmu)
+ writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
regs + S3C64XX_SPI_CLK_CFG);
writel(0, regs + S3C64XX_SPI_MODE_CFG);
writel(0, regs + S3C64XX_SPI_PACKET_CNT);
diff --git a/drivers/spi/spi_topcliff_pch.c b/drivers/spi/spi_topcliff_pch.c
new file mode 100644
index 00000000000..58e187f45ec
--- /dev/null
+++ b/drivers/spi/spi_topcliff_pch.c
@@ -0,0 +1,1303 @@
+/*
+ * SPI bus driver for the Topcliff PCH used by Intel SoCs
+ *
+ * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD.
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/spi/spidev.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+/* Register offsets */
+#define PCH_SPCR 0x00 /* SPI control register */
+#define PCH_SPBRR 0x04 /* SPI baud rate register */
+#define PCH_SPSR 0x08 /* SPI status register */
+#define PCH_SPDWR 0x0C /* SPI write data register */
+#define PCH_SPDRR 0x10 /* SPI read data register */
+#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */
+#define PCH_SRST 0x1C /* SPI reset register */
+
+#define PCH_SPSR_TFD 0x000007C0
+#define PCH_SPSR_RFD 0x0000F800
+
+#define PCH_READABLE(x) (((x) & PCH_SPSR_RFD)>>11)
+#define PCH_WRITABLE(x) (((x) & PCH_SPSR_TFD)>>6)
+
+#define PCH_RX_THOLD 7
+#define PCH_RX_THOLD_MAX 15
+
+#define PCH_MAX_BAUDRATE 5000000
+#define PCH_MAX_FIFO_DEPTH 16
+
+#define STATUS_RUNNING 1
+#define STATUS_EXITING 2
+#define PCH_SLEEP_TIME 10
+
+#define PCH_ADDRESS_SIZE 0x20
+
+#define SSN_LOW 0x02U
+#define SSN_NO_CONTROL 0x00U
+#define PCH_MAX_CS 0xFF
+#define PCI_DEVICE_ID_GE_SPI 0x8816
+
+#define SPCR_SPE_BIT (1 << 0)
+#define SPCR_MSTR_BIT (1 << 1)
+#define SPCR_LSBF_BIT (1 << 4)
+#define SPCR_CPHA_BIT (1 << 5)
+#define SPCR_CPOL_BIT (1 << 6)
+#define SPCR_TFIE_BIT (1 << 8)
+#define SPCR_RFIE_BIT (1 << 9)
+#define SPCR_FIE_BIT (1 << 10)
+#define SPCR_ORIE_BIT (1 << 11)
+#define SPCR_MDFIE_BIT (1 << 12)
+#define SPCR_FICLR_BIT (1 << 24)
+#define SPSR_TFI_BIT (1 << 0)
+#define SPSR_RFI_BIT (1 << 1)
+#define SPSR_FI_BIT (1 << 2)
+#define SPBRR_SIZE_BIT (1 << 10)
+
+#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT)
+
+#define SPCR_RFIC_FIELD 20
+#define SPCR_TFIC_FIELD 16
+
+#define SPSR_INT_BITS 0x1F
+#define MASK_SPBRR_SPBR_BITS (~((1 << 10) - 1))
+#define MASK_RFIC_SPCR_BITS (~(0xf << 20))
+#define MASK_TFIC_SPCR_BITS (~(0xf000f << 12))
+
+#define PCH_CLOCK_HZ 50000000
+#define PCH_MAX_SPBR 1023
+
+
+/**
+ * struct pch_spi_data - Holds the SPI channel specific details
+ * @io_remap_addr: The remapped PCI base address
+ * @master: Pointer to the SPI master structure
+ * @work: Reference to work queue handler
+ * @wk: Workqueue for carrying out execution of the
+ * requests
+ * @wait: Wait queue for waking up upon receiving an
+ * interrupt.
+ * @transfer_complete: Status of SPI Transfer
+ * @bcurrent_msg_processing: Status flag for message processing
+ * @lock: Lock for protecting this structure
+ * @queue: SPI Message queue
+ * @status: Status of the SPI driver
+ * @bpw_len: Length of data to be transferred in bits per
+ * word
+ * @transfer_active: Flag showing active transfer
+ * @tx_index: Transmit data count; for bookkeeping during
+ * transfer
+ * @rx_index: Receive data count; for bookkeeping during
+ * transfer
+ * @tx_buff: Buffer for data to be transmitted
+ * @rx_index: Buffer for Received data
+ * @n_curnt_chip: The chip number that this SPI driver currently
+ * operates on
+ * @current_chip: Reference to the current chip that this SPI
+ * driver currently operates on
+ * @current_msg: The current message that this SPI driver is
+ * handling
+ * @cur_trans: The current transfer that this SPI driver is
+ * handling
+ * @board_dat: Reference to the SPI device data structure
+ */
+struct pch_spi_data {
+ void __iomem *io_remap_addr;
+ struct spi_master *master;
+ struct work_struct work;
+ struct workqueue_struct *wk;
+ wait_queue_head_t wait;
+ u8 transfer_complete;
+ u8 bcurrent_msg_processing;
+ spinlock_t lock;
+ struct list_head queue;
+ u8 status;
+ u32 bpw_len;
+ u8 transfer_active;
+ u32 tx_index;
+ u32 rx_index;
+ u16 *pkt_tx_buff;
+ u16 *pkt_rx_buff;
+ u8 n_curnt_chip;
+ struct spi_device *current_chip;
+ struct spi_message *current_msg;
+ struct spi_transfer *cur_trans;
+ struct pch_spi_board_data *board_dat;
+};
+
+/**
+ * struct pch_spi_board_data - Holds the SPI device specific details
+ * @pdev: Pointer to the PCI device
+ * @irq_reg_sts: Status of IRQ registration
+ * @pci_req_sts: Status of pci_request_regions
+ * @suspend_sts: Status of suspend
+ * @data: Pointer to SPI channel data structure
+ */
+struct pch_spi_board_data {
+ struct pci_dev *pdev;
+ u8 irq_reg_sts;
+ u8 pci_req_sts;
+ u8 suspend_sts;
+ struct pch_spi_data *data;
+};
+
+static struct pci_device_id pch_spi_pcidev_id[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)},
+ {0,}
+};
+
+/**
+ * pch_spi_writereg() - Performs register writes
+ * @master: Pointer to struct spi_master.
+ * @idx: Register offset.
+ * @val: Value to be written to register.
+ */
+static inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val)
+{
+ struct pch_spi_data *data = spi_master_get_devdata(master);
+ iowrite32(val, (data->io_remap_addr + idx));
+}
+
+/**
+ * pch_spi_readreg() - Performs register reads
+ * @master: Pointer to struct spi_master.
+ * @idx: Register offset.
+ */
+static inline u32 pch_spi_readreg(struct spi_master *master, int idx)
+{
+ struct pch_spi_data *data = spi_master_get_devdata(master);
+ return ioread32(data->io_remap_addr + idx);
+}
+
+static inline void pch_spi_setclr_reg(struct spi_master *master, int idx,
+ u32 set, u32 clr)
+{
+ u32 tmp = pch_spi_readreg(master, idx);
+ tmp = (tmp & ~clr) | set;
+ pch_spi_writereg(master, idx, tmp);
+}
+
+static void pch_spi_set_master_mode(struct spi_master *master)
+{
+ pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MSTR_BIT, 0);
+}
+
+/**
+ * pch_spi_clear_fifo() - Clears the Transmit and Receive FIFOs
+ * @master: Pointer to struct spi_master.
+ */
+static void pch_spi_clear_fifo(struct spi_master *master)
+{
+ pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FICLR_BIT, 0);
+ pch_spi_setclr_reg(master, PCH_SPCR, 0, SPCR_FICLR_BIT);
+}
+
+static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val,
+ void __iomem *io_remap_addr)
+{
+ u32 n_read, tx_index, rx_index, bpw_len;
+ u16 *pkt_rx_buffer, *pkt_tx_buff;
+ int read_cnt;
+ u32 reg_spcr_val;
+ void __iomem *spsr;
+ void __iomem *spdrr;
+ void __iomem *spdwr;
+
+ spsr = io_remap_addr + PCH_SPSR;
+ iowrite32(reg_spsr_val, spsr);
+
+ if (data->transfer_active) {
+ rx_index = data->rx_index;
+ tx_index = data->tx_index;
+ bpw_len = data->bpw_len;
+ pkt_rx_buffer = data->pkt_rx_buff;
+ pkt_tx_buff = data->pkt_tx_buff;
+
+ spdrr = io_remap_addr + PCH_SPDRR;
+ spdwr = io_remap_addr + PCH_SPDWR;
+
+ n_read = PCH_READABLE(reg_spsr_val);
+
+ for (read_cnt = 0; (read_cnt < n_read); read_cnt++) {
+ pkt_rx_buffer[rx_index++] = ioread32(spdrr);
+ if (tx_index < bpw_len)
+ iowrite32(pkt_tx_buff[tx_index++], spdwr);
+ }
+
+ /* disable RFI if not needed */
+ if ((bpw_len - rx_index) <= PCH_MAX_FIFO_DEPTH) {
+ reg_spcr_val = ioread32(io_remap_addr + PCH_SPCR);
+ reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */
+
+ /* reset rx threshold */
+ reg_spcr_val &= MASK_RFIC_SPCR_BITS;
+ reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD);
+ iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))),
+ (io_remap_addr + PCH_SPCR));
+ }
+
+ /* update counts */
+ data->tx_index = tx_index;
+ data->rx_index = rx_index;
+
+ }
+
+ /* if transfer complete interrupt */
+ if (reg_spsr_val & SPSR_FI_BIT) {
+ /* disable FI & RFI interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0,
+ SPCR_FIE_BIT | SPCR_TFIE_BIT);
+
+ /* transfer is completed;inform pch_spi_process_messages */
+ data->transfer_complete = true;
+ wake_up(&data->wait);
+ }
+}
+
+/**
+ * pch_spi_handler() - Interrupt handler
+ * @irq: The interrupt number.
+ * @dev_id: Pointer to struct pch_spi_board_data.
+ */
+static irqreturn_t pch_spi_handler(int irq, void *dev_id)
+{
+ u32 reg_spsr_val;
+ struct pch_spi_data *data;
+ void __iomem *spsr;
+ void __iomem *io_remap_addr;
+ irqreturn_t ret = IRQ_NONE;
+ struct pch_spi_board_data *board_dat = dev_id;
+
+ if (board_dat->suspend_sts) {
+ dev_dbg(&board_dat->pdev->dev,
+ "%s returning due to suspend\n", __func__);
+ return IRQ_NONE;
+ }
+
+ data = board_dat->data;
+ io_remap_addr = data->io_remap_addr;
+ spsr = io_remap_addr + PCH_SPSR;
+
+ reg_spsr_val = ioread32(spsr);
+
+ /* Check if the interrupt is for SPI device */
+ if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) {
+ pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr);
+ ret = IRQ_HANDLED;
+ }
+
+ dev_dbg(&board_dat->pdev->dev, "%s EXIT return value=%d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/**
+ * pch_spi_set_baud_rate() - Sets SPBR field in SPBRR
+ * @master: Pointer to struct spi_master.
+ * @speed_hz: Baud rate.
+ */
+static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz)
+{
+ u32 n_spbr = PCH_CLOCK_HZ / (speed_hz * 2);
+
+ /* if baud rate is less than we can support limit it */
+ if (n_spbr > PCH_MAX_SPBR)
+ n_spbr = PCH_MAX_SPBR;
+
+ pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS);
+}
+
+/**
+ * pch_spi_set_bits_per_word() - Sets SIZE field in SPBRR
+ * @master: Pointer to struct spi_master.
+ * @bits_per_word: Bits per word for SPI transfer.
+ */
+static void pch_spi_set_bits_per_word(struct spi_master *master,
+ u8 bits_per_word)
+{
+ if (bits_per_word == 8)
+ pch_spi_setclr_reg(master, PCH_SPBRR, 0, SPBRR_SIZE_BIT);
+ else
+ pch_spi_setclr_reg(master, PCH_SPBRR, SPBRR_SIZE_BIT, 0);
+}
+
+/**
+ * pch_spi_setup_transfer() - Configures the PCH SPI hardware for transfer
+ * @spi: Pointer to struct spi_device.
+ */
+static void pch_spi_setup_transfer(struct spi_device *spi)
+{
+ u32 flags = 0;
+
+ dev_dbg(&spi->dev, "%s SPBRR content =%x setting baud rate=%d\n",
+ __func__, pch_spi_readreg(spi->master, PCH_SPBRR),
+ spi->max_speed_hz);
+ pch_spi_set_baud_rate(spi->master, spi->max_speed_hz);
+
+ /* set bits per word */
+ pch_spi_set_bits_per_word(spi->master, spi->bits_per_word);
+
+ if (!(spi->mode & SPI_LSB_FIRST))
+ flags |= SPCR_LSBF_BIT;
+ if (spi->mode & SPI_CPOL)
+ flags |= SPCR_CPOL_BIT;
+ if (spi->mode & SPI_CPHA)
+ flags |= SPCR_CPHA_BIT;
+ pch_spi_setclr_reg(spi->master, PCH_SPCR, flags,
+ (SPCR_LSBF_BIT | SPCR_CPOL_BIT | SPCR_CPHA_BIT));
+
+ /* Clear the FIFO by toggling FICLR to 1 and back to 0 */
+ pch_spi_clear_fifo(spi->master);
+}
+
+/**
+ * pch_spi_reset() - Clears SPI registers
+ * @master: Pointer to struct spi_master.
+ */
+static void pch_spi_reset(struct spi_master *master)
+{
+ /* write 1 to reset SPI */
+ pch_spi_writereg(master, PCH_SRST, 0x1);
+
+ /* clear reset */
+ pch_spi_writereg(master, PCH_SRST, 0x0);
+}
+
+static int pch_spi_setup(struct spi_device *pspi)
+{
+ /* check bits per word */
+ if (pspi->bits_per_word == 0) {
+ pspi->bits_per_word = 8;
+ dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__);
+ }
+
+ if ((pspi->bits_per_word != 8) && (pspi->bits_per_word != 16)) {
+ dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Check baud rate setting */
+ /* if baud rate of chip is greater than
+ max we can support,return error */
+ if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE)
+ pspi->max_speed_hz = PCH_MAX_BAUDRATE;
+
+ dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__,
+ (pspi->mode) & (SPI_CPOL | SPI_CPHA));
+
+ return 0;
+}
+
+static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
+{
+
+ struct spi_transfer *transfer;
+ struct pch_spi_data *data = spi_master_get_devdata(pspi->master);
+ int retval;
+ unsigned long flags;
+
+ /* validate spi message and baud rate */
+ if (unlikely(list_empty(&pmsg->transfers) == 1)) {
+ dev_err(&pspi->dev, "%s list empty\n", __func__);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ if (unlikely(pspi->max_speed_hz == 0)) {
+ dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n",
+ __func__, pspi->max_speed_hz);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ dev_dbg(&pspi->dev, "%s Transfer List not empty. "
+ "Transfer Speed is set.\n", __func__);
+
+ /* validate Tx/Rx buffers and Transfer length */
+ list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
+ if (!transfer->tx_buf && !transfer->rx_buf) {
+ dev_err(&pspi->dev,
+ "%s Tx and Rx buffer NULL\n", __func__);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ if (!transfer->len) {
+ dev_err(&pspi->dev, "%s Transfer length invalid\n",
+ __func__);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length"
+ " valid\n", __func__);
+
+ /* if baud rate hs been specified validate the same */
+ if (transfer->speed_hz > PCH_MAX_BAUDRATE)
+ transfer->speed_hz = PCH_MAX_BAUDRATE;
+
+ /* if bits per word has been specified validate the same */
+ if (transfer->bits_per_word) {
+ if ((transfer->bits_per_word != 8)
+ && (transfer->bits_per_word != 16)) {
+ retval = -EINVAL;
+ dev_err(&pspi->dev,
+ "%s Invalid bits per word\n", __func__);
+ goto err_out;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* We won't process any messages if we have been asked to terminate */
+ if (data->status == STATUS_EXITING) {
+ dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
+ retval = -ESHUTDOWN;
+ goto err_return_spinlock;
+ }
+
+ /* If suspended ,return -EINVAL */
+ if (data->board_dat->suspend_sts) {
+ dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__);
+ retval = -EINVAL;
+ goto err_return_spinlock;
+ }
+
+ /* set status of message */
+ pmsg->actual_length = 0;
+ dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status);
+
+ pmsg->status = -EINPROGRESS;
+
+ /* add message to queue */
+ list_add_tail(&pmsg->queue, &data->queue);
+ dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__);
+
+ /* schedule work queue to run */
+ queue_work(data->wk, &data->work);
+ dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__);
+
+ retval = 0;
+
+err_return_spinlock:
+ spin_unlock_irqrestore(&data->lock, flags);
+err_out:
+ dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
+ return retval;
+}
+
+static inline void pch_spi_select_chip(struct pch_spi_data *data,
+ struct spi_device *pspi)
+{
+ if (data->current_chip != NULL) {
+ if (pspi->chip_select != data->n_curnt_chip) {
+ dev_dbg(&pspi->dev, "%s : different slave\n", __func__);
+ data->current_chip = NULL;
+ }
+ }
+
+ data->current_chip = pspi;
+
+ data->n_curnt_chip = data->current_chip->chip_select;
+
+ dev_dbg(&pspi->dev, "%s :Invoking pch_spi_setup_transfer\n", __func__);
+ pch_spi_setup_transfer(pspi);
+}
+
+static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw,
+ struct spi_message **ppmsg)
+{
+ int size;
+ u32 n_writes;
+ int j;
+ struct spi_message *pmsg;
+ const u8 *tx_buf;
+ const u16 *tx_sbuf;
+
+ pmsg = *ppmsg;
+
+ /* set baud rate if needed */
+ if (data->cur_trans->speed_hz) {
+ dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__);
+ pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz);
+ }
+
+ /* set bits per word if needed */
+ if (data->cur_trans->bits_per_word &&
+ (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) {
+ dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__);
+ pch_spi_set_bits_per_word(data->master,
+ data->cur_trans->bits_per_word);
+ *bpw = data->cur_trans->bits_per_word;
+ } else {
+ *bpw = data->current_msg->spi->bits_per_word;
+ }
+
+ /* reset Tx/Rx index */
+ data->tx_index = 0;
+ data->rx_index = 0;
+
+ data->bpw_len = data->cur_trans->len / (*bpw / 8);
+
+ /* find alloc size */
+ size = data->cur_trans->len * sizeof(*data->pkt_tx_buff);
+
+ /* allocate memory for pkt_tx_buff & pkt_rx_buffer */
+ data->pkt_tx_buff = kzalloc(size, GFP_KERNEL);
+ if (data->pkt_tx_buff != NULL) {
+ data->pkt_rx_buff = kzalloc(size, GFP_KERNEL);
+ if (!data->pkt_rx_buff)
+ kfree(data->pkt_tx_buff);
+ }
+
+ if (!data->pkt_rx_buff) {
+ /* flush queue and set status of all transfers to -ENOMEM */
+ dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__);
+ list_for_each_entry(pmsg, data->queue.next, queue) {
+ pmsg->status = -ENOMEM;
+
+ if (pmsg->complete != 0)
+ pmsg->complete(pmsg->context);
+
+ /* delete from queue */
+ list_del_init(&pmsg->queue);
+ }
+ return;
+ }
+
+ /* copy Tx Data */
+ if (data->cur_trans->tx_buf != NULL) {
+ if (*bpw == 8) {
+ tx_buf = data->cur_trans->tx_buf;
+ for (j = 0; j < data->bpw_len; j++)
+ data->pkt_tx_buff[j] = *tx_buf++;
+ } else {
+ tx_sbuf = data->cur_trans->tx_buf;
+ for (j = 0; j < data->bpw_len; j++)
+ data->pkt_tx_buff[j] = *tx_sbuf++;
+ }
+ }
+
+ /* if len greater than PCH_MAX_FIFO_DEPTH, write 16,else len bytes */
+ n_writes = data->bpw_len;
+ if (n_writes > PCH_MAX_FIFO_DEPTH)
+ n_writes = PCH_MAX_FIFO_DEPTH;
+
+ dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing "
+ "0x2 to SSNXCR\n", __func__);
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW);
+
+ for (j = 0; j < n_writes; j++)
+ pch_spi_writereg(data->master, PCH_SPDWR, data->pkt_tx_buff[j]);
+
+ /* update tx_index */
+ data->tx_index = j;
+
+ /* reset transfer complete flag */
+ data->transfer_complete = false;
+ data->transfer_active = true;
+}
+
+
+static void pch_spi_nomore_transfer(struct pch_spi_data *data,
+ struct spi_message *pmsg)
+{
+ dev_dbg(&data->master->dev, "%s called\n", __func__);
+ /* Invoke complete callback
+ * [To the spi core..indicating end of transfer] */
+ data->current_msg->status = 0;
+
+ if (data->current_msg->complete != 0) {
+ dev_dbg(&data->master->dev,
+ "%s:Invoking callback of SPI core\n", __func__);
+ data->current_msg->complete(data->current_msg->context);
+ }
+
+ /* update status in global variable */
+ data->bcurrent_msg_processing = false;
+
+ dev_dbg(&data->master->dev,
+ "%s:data->bcurrent_msg_processing = false\n", __func__);
+
+ data->current_msg = NULL;
+ data->cur_trans = NULL;
+
+ /* check if we have items in list and not suspending
+ * return 1 if list empty */
+ if ((list_empty(&data->queue) == 0) &&
+ (!data->board_dat->suspend_sts) &&
+ (data->status != STATUS_EXITING)) {
+ /* We have some more work to do (either there is more tranint
+ * bpw;sfer requests in the current message or there are
+ *more messages)
+ */
+ dev_dbg(&data->master->dev, "%s:Invoke queue_work\n", __func__);
+ queue_work(data->wk, &data->work);
+ } else if (data->board_dat->suspend_sts ||
+ data->status == STATUS_EXITING) {
+ dev_dbg(&data->master->dev,
+ "%s suspend/remove initiated, flushing queue\n",
+ __func__);
+ list_for_each_entry(pmsg, data->queue.next, queue) {
+ pmsg->status = -EIO;
+
+ if (pmsg->complete)
+ pmsg->complete(pmsg->context);
+
+ /* delete from queue */
+ list_del_init(&pmsg->queue);
+ }
+ }
+}
+
+static void pch_spi_set_ir(struct pch_spi_data *data)
+{
+ /* enable interrupts */
+ if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) {
+ /* set receive threhold to PCH_RX_THOLD */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ PCH_RX_THOLD << SPCR_TFIC_FIELD,
+ ~MASK_TFIC_SPCR_BITS);
+ /* enable FI and RFI interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ SPCR_RFIE_BIT | SPCR_TFIE_BIT, 0);
+ } else {
+ /* set receive threhold to maximum */
+ pch_spi_setclr_reg(data->master, PCH_SPCR,
+ PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD,
+ ~MASK_TFIC_SPCR_BITS);
+ /* enable FI interrupt */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0);
+ }
+
+ dev_dbg(&data->master->dev,
+ "%s:invoking pch_spi_set_enable to enable SPI\n", __func__);
+
+ /* SPI set enable */
+ pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0);
+
+ /* Wait until the transfer completes; go to sleep after
+ initiating the transfer. */
+ dev_dbg(&data->master->dev,
+ "%s:waiting for transfer to get over\n", __func__);
+
+ wait_event_interruptible(data->wait, data->transfer_complete);
+
+ pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL);
+ dev_dbg(&data->master->dev,
+ "%s:no more control over SSN-writing 0 to SSNXCR.", __func__);
+
+ data->transfer_active = false;
+ dev_dbg(&data->master->dev,
+ "%s set data->transfer_active = false\n", __func__);
+
+ /* clear all interrupts */
+ pch_spi_writereg(data->master, PCH_SPSR,
+ pch_spi_readreg(data->master, PCH_SPSR));
+ /* disable interrupts */
+ pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL);
+}
+
+static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw)
+{
+ int j;
+ u8 *rx_buf;
+ u16 *rx_sbuf;
+
+ /* copy Rx Data */
+ if (!data->cur_trans->rx_buf)
+ return;
+
+ if (bpw == 8) {
+ rx_buf = data->cur_trans->rx_buf;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_buf++ = data->pkt_rx_buff[j] & 0xFF;
+ } else {
+ rx_sbuf = data->cur_trans->rx_buf;
+ for (j = 0; j < data->bpw_len; j++)
+ *rx_sbuf++ = data->pkt_rx_buff[j];
+ }
+}
+
+
+static void pch_spi_process_messages(struct work_struct *pwork)
+{
+ struct spi_message *pmsg;
+ struct pch_spi_data *data;
+ int bpw;
+
+ data = container_of(pwork, struct pch_spi_data, work);
+ dev_dbg(&data->master->dev, "%s data initialized\n", __func__);
+
+ spin_lock(&data->lock);
+
+ /* check if suspend has been initiated;if yes flush queue */
+ if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
+ dev_dbg(&data->master->dev,
+ "%s suspend/remove initiated,flushing queue\n",
+ __func__);
+
+ list_for_each_entry(pmsg, data->queue.next, queue) {
+ pmsg->status = -EIO;
+
+ if (pmsg->complete != 0) {
+ spin_unlock(&data->lock);
+ pmsg->complete(pmsg->context);
+ spin_lock(&data->lock);
+ }
+
+ /* delete from queue */
+ list_del_init(&pmsg->queue);
+ }
+
+ spin_unlock(&data->lock);
+ return;
+ }
+
+ data->bcurrent_msg_processing = true;
+ dev_dbg(&data->master->dev,
+ "%s Set data->bcurrent_msg_processing= true\n", __func__);
+
+ /* Get the message from the queue and delete it from there. */
+ data->current_msg = list_entry(data->queue.next, struct spi_message,
+ queue);
+
+ list_del_init(&data->current_msg->queue);
+
+ data->current_msg->status = 0;
+
+ pch_spi_select_chip(data, data->current_msg->spi);
+
+ spin_unlock(&data->lock);
+
+ do {
+ /* If we are already processing a message get the next
+ transfer structure from the message otherwise retrieve
+ the 1st transfer request from the message. */
+ spin_lock(&data->lock);
+
+ if (data->cur_trans == NULL) {
+ data->cur_trans =
+ list_entry(data->current_msg->transfers.
+ next, struct spi_transfer,
+ transfer_list);
+ dev_dbg(&data->master->dev,
+ "%s :Getting 1st transfer message\n", __func__);
+ } else {
+ data->cur_trans =
+ list_entry(data->cur_trans->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dev_dbg(&data->master->dev,
+ "%s :Getting next transfer message\n",
+ __func__);
+ }
+
+ spin_unlock(&data->lock);
+
+ pch_spi_set_tx(data, &bpw, &pmsg);
+
+ /* Control interrupt*/
+ pch_spi_set_ir(data);
+
+ /* Disable SPI transfer */
+ pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0,
+ SPCR_SPE_BIT);
+
+ /* clear FIFO */
+ pch_spi_clear_fifo(data->master);
+
+ /* copy Rx Data */
+ pch_spi_copy_rx_data(data, bpw);
+
+ /* free memory */
+ kfree(data->pkt_rx_buff);
+ data->pkt_rx_buff = NULL;
+
+ kfree(data->pkt_tx_buff);
+ data->pkt_tx_buff = NULL;
+
+ /* increment message count */
+ data->current_msg->actual_length += data->cur_trans->len;
+
+ dev_dbg(&data->master->dev,
+ "%s:data->current_msg->actual_length=%d\n",
+ __func__, data->current_msg->actual_length);
+
+ /* check for delay */
+ if (data->cur_trans->delay_usecs) {
+ dev_dbg(&data->master->dev, "%s:"
+ "delay in usec=%d\n", __func__,
+ data->cur_trans->delay_usecs);
+ udelay(data->cur_trans->delay_usecs);
+ }
+
+ spin_lock(&data->lock);
+
+ /* No more transfer in this message. */
+ if ((data->cur_trans->transfer_list.next) ==
+ &(data->current_msg->transfers)) {
+ pch_spi_nomore_transfer(data, pmsg);
+ }
+
+ spin_unlock(&data->lock);
+
+ } while (data->cur_trans != NULL);
+}
+
+static void pch_spi_free_resources(struct pch_spi_board_data *board_dat)
+{
+ dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
+
+ /* free workqueue */
+ if (board_dat->data->wk != NULL) {
+ destroy_workqueue(board_dat->data->wk);
+ board_dat->data->wk = NULL;
+ dev_dbg(&board_dat->pdev->dev,
+ "%s destroy_workqueue invoked successfully\n",
+ __func__);
+ }
+
+ /* disable interrupts & free IRQ */
+ if (board_dat->irq_reg_sts) {
+ /* disable interrupts */
+ pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
+ PCH_ALL);
+
+ /* free IRQ */
+ free_irq(board_dat->pdev->irq, board_dat);
+
+ dev_dbg(&board_dat->pdev->dev,
+ "%s free_irq invoked successfully\n", __func__);
+
+ board_dat->irq_reg_sts = false;
+ }
+
+ /* unmap PCI base address */
+ if (board_dat->data->io_remap_addr != 0) {
+ pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr);
+
+ board_dat->data->io_remap_addr = 0;
+
+ dev_dbg(&board_dat->pdev->dev,
+ "%s pci_iounmap invoked successfully\n", __func__);
+ }
+
+ /* release PCI region */
+ if (board_dat->pci_req_sts) {
+ pci_release_regions(board_dat->pdev);
+ dev_dbg(&board_dat->pdev->dev,
+ "%s pci_release_regions invoked successfully\n",
+ __func__);
+ board_dat->pci_req_sts = false;
+ }
+}
+
+static int pch_spi_get_resources(struct pch_spi_board_data *board_dat)
+{
+ void __iomem *io_remap_addr;
+ int retval;
+ dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__);
+
+ /* create workqueue */
+ board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME);
+ if (!board_dat->data->wk) {
+ dev_err(&board_dat->pdev->dev,
+ "%s create_singlet hread_workqueue failed\n", __func__);
+ retval = -EBUSY;
+ goto err_return;
+ }
+
+ dev_dbg(&board_dat->pdev->dev,
+ "%s create_singlethread_workqueue success\n", __func__);
+
+ retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME);
+ if (retval != 0) {
+ dev_err(&board_dat->pdev->dev,
+ "%s request_region failed\n", __func__);
+ goto err_return;
+ }
+
+ board_dat->pci_req_sts = true;
+
+ io_remap_addr = pci_iomap(board_dat->pdev, 1, 0);
+ if (io_remap_addr == 0) {
+ dev_err(&board_dat->pdev->dev,
+ "%s pci_iomap failed\n", __func__);
+ retval = -ENOMEM;
+ goto err_return;
+ }
+
+ /* calculate base address for all channels */
+ board_dat->data->io_remap_addr = io_remap_addr;
+
+ /* reset PCH SPI h/w */
+ pch_spi_reset(board_dat->data->master);
+ dev_dbg(&board_dat->pdev->dev,
+ "%s pch_spi_reset invoked successfully\n", __func__);
+
+ /* register IRQ */
+ retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME, board_dat);
+ if (retval != 0) {
+ dev_err(&board_dat->pdev->dev,
+ "%s request_irq failed\n", __func__);
+ goto err_return;
+ }
+
+ dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n",
+ __func__, retval);
+
+ board_dat->irq_reg_sts = true;
+ dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__);
+
+err_return:
+ if (retval != 0) {
+ dev_err(&board_dat->pdev->dev,
+ "%s FAIL:invoking pch_spi_free_resources\n", __func__);
+ pch_spi_free_resources(board_dat);
+ }
+
+ dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval);
+
+ return retval;
+}
+
+static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+
+ struct spi_master *master;
+
+ struct pch_spi_board_data *board_dat;
+ int retval;
+
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ /* allocate memory for private data */
+ board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL);
+ if (board_dat == NULL) {
+ dev_err(&pdev->dev,
+ " %s memory allocation for private data failed\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_kmalloc;
+ }
+
+ dev_dbg(&pdev->dev,
+ "%s memory allocation for private data success\n", __func__);
+
+ /* enable PCI device */
+ retval = pci_enable_device(pdev);
+ if (retval != 0) {
+ dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__);
+
+ goto err_pci_en_device;
+ }
+
+ dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n",
+ __func__, retval);
+
+ board_dat->pdev = pdev;
+
+ /* alllocate memory for SPI master */
+ master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data));
+ if (master == NULL) {
+ retval = -ENOMEM;
+ dev_err(&pdev->dev, "%s Fail.\n", __func__);
+ goto err_spi_alloc_master;
+ }
+
+ dev_dbg(&pdev->dev,
+ "%s spi_alloc_master returned non NULL\n", __func__);
+
+ /* initialize members of SPI master */
+ master->bus_num = -1;
+ master->num_chipselect = PCH_MAX_CS;
+ master->setup = pch_spi_setup;
+ master->transfer = pch_spi_transfer;
+ dev_dbg(&pdev->dev,
+ "%s transfer member of SPI master initialized\n", __func__);
+
+ board_dat->data = spi_master_get_devdata(master);
+
+ board_dat->data->master = master;
+ board_dat->data->n_curnt_chip = 255;
+ board_dat->data->board_dat = board_dat;
+ board_dat->data->status = STATUS_RUNNING;
+
+ INIT_LIST_HEAD(&board_dat->data->queue);
+ spin_lock_init(&board_dat->data->lock);
+ INIT_WORK(&board_dat->data->work, pch_spi_process_messages);
+ init_waitqueue_head(&board_dat->data->wait);
+
+ /* allocate resources for PCH SPI */
+ retval = pch_spi_get_resources(board_dat);
+ if (retval) {
+ dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval);
+ goto err_spi_get_resources;
+ }
+
+ dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n",
+ __func__, retval);
+
+ /* save private data in dev */
+ pci_set_drvdata(pdev, board_dat);
+ dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__);
+
+ /* set master mode */
+ pch_spi_set_master_mode(master);
+ dev_dbg(&pdev->dev,
+ "%s invoked pch_spi_set_master_mode\n", __func__);
+
+ /* Register the controller with the SPI core. */
+ retval = spi_register_master(master);
+ if (retval != 0) {
+ dev_err(&pdev->dev,
+ "%s spi_register_master FAILED\n", __func__);
+ goto err_spi_reg_master;
+ }
+
+ dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n",
+ __func__, retval);
+
+
+ return 0;
+
+err_spi_reg_master:
+ spi_unregister_master(master);
+err_spi_get_resources:
+err_spi_alloc_master:
+ spi_master_put(master);
+ pci_disable_device(pdev);
+err_pci_en_device:
+ kfree(board_dat);
+err_kmalloc:
+ return retval;
+}
+
+static void pch_spi_remove(struct pci_dev *pdev)
+{
+ struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+ int count;
+
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ if (!board_dat) {
+ dev_err(&pdev->dev,
+ "%s pci_get_drvdata returned NULL\n", __func__);
+ return;
+ }
+
+ /* check for any pending messages; no action is taken if the queue
+ * is still full; but at least we tried. Unload anyway */
+ count = 500;
+ spin_lock(&board_dat->data->lock);
+ board_dat->data->status = STATUS_EXITING;
+ while ((list_empty(&board_dat->data->queue) == 0) && --count) {
+ dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n",
+ __func__);
+ spin_unlock(&board_dat->data->lock);
+ msleep(PCH_SLEEP_TIME);
+ spin_lock(&board_dat->data->lock);
+ }
+ spin_unlock(&board_dat->data->lock);
+
+ /* Free resources allocated for PCH SPI */
+ pch_spi_free_resources(board_dat);
+
+ spi_unregister_master(board_dat->data->master);
+
+ /* free memory for private data */
+ kfree(board_dat);
+
+ pci_set_drvdata(pdev, NULL);
+
+ /* disable PCI device */
+ pci_disable_device(pdev);
+
+ dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ u8 count;
+ int retval;
+
+ struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ if (!board_dat) {
+ dev_err(&pdev->dev,
+ "%s pci_get_drvdata returned NULL\n", __func__);
+ return -EFAULT;
+ }
+
+ retval = 0;
+ board_dat->suspend_sts = true;
+
+ /* check if the current message is processed:
+ Only after thats done the transfer will be suspended */
+ count = 255;
+ while ((--count) > 0) {
+ if (!(board_dat->data->bcurrent_msg_processing)) {
+ dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_"
+ "msg_processing = false\n", __func__);
+ break;
+ } else {
+ dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_"
+ "processing = true\n", __func__);
+ }
+ msleep(PCH_SLEEP_TIME);
+ }
+
+ /* Free IRQ */
+ if (board_dat->irq_reg_sts) {
+ /* disable all interrupts */
+ pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0,
+ PCH_ALL);
+ pch_spi_reset(board_dat->data->master);
+
+ free_irq(board_dat->pdev->irq, board_dat);
+
+ board_dat->irq_reg_sts = false;
+ dev_dbg(&pdev->dev,
+ "%s free_irq invoked successfully.\n", __func__);
+ }
+
+ /* save config space */
+ retval = pci_save_state(pdev);
+
+ if (retval == 0) {
+ dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n",
+ __func__, retval);
+ /* disable PM notifications */
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ dev_dbg(&pdev->dev,
+ "%s pci_enable_wake invoked successfully\n", __func__);
+ /* disable PCI device */
+ pci_disable_device(pdev);
+ dev_dbg(&pdev->dev,
+ "%s pci_disable_device invoked successfully\n",
+ __func__);
+ /* move device to D3hot state */
+ pci_set_power_state(pdev, PCI_D3hot);
+ dev_dbg(&pdev->dev,
+ "%s pci_set_power_state invoked successfully\n",
+ __func__);
+ } else {
+ dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__);
+ }
+
+ dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval);
+
+ return retval;
+}
+
+static int pch_spi_resume(struct pci_dev *pdev)
+{
+ int retval;
+
+ struct pch_spi_board_data *board = pci_get_drvdata(pdev);
+ dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
+
+ if (!board) {
+ dev_err(&pdev->dev,
+ "%s pci_get_drvdata returned NULL\n", __func__);
+ return -EFAULT;
+ }
+
+ /* move device to DO power state */
+ pci_set_power_state(pdev, PCI_D0);
+
+ /* restore state */
+ pci_restore_state(pdev);
+
+ retval = pci_enable_device(pdev);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s pci_enable_device failed\n", __func__);
+ } else {
+ /* disable PM notifications */
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+
+ /* register IRQ handler */
+ if (!board->irq_reg_sts) {
+ /* register IRQ */
+ retval = request_irq(board->pdev->irq, pch_spi_handler,
+ IRQF_SHARED, KBUILD_MODNAME,
+ board);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s request_irq failed\n", __func__);
+ return retval;
+ }
+ board->irq_reg_sts = true;
+
+ /* reset PCH SPI h/w */
+ pch_spi_reset(board->data->master);
+ pch_spi_set_master_mode(board->data->master);
+
+ /* set suspend status to false */
+ board->suspend_sts = false;
+
+ }
+ }
+
+ dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval);
+
+ return retval;
+}
+#else
+#define pch_spi_suspend NULL
+#define pch_spi_resume NULL
+
+#endif
+
+static struct pci_driver pch_spi_pcidev = {
+ .name = "pch_spi",
+ .id_table = pch_spi_pcidev_id,
+ .probe = pch_spi_probe,
+ .remove = pch_spi_remove,
+ .suspend = pch_spi_suspend,
+ .resume = pch_spi_resume,
+};
+
+static int __init pch_spi_init(void)
+{
+ return pci_register_driver(&pch_spi_pcidev);
+}
+module_init(pch_spi_init);
+
+static void __exit pch_spi_exit(void)
+{
+ pci_unregister_driver(&pch_spi_pcidev);
+}
+module_exit(pch_spi_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver");