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