lkml.org 
[lkml]   [2013]   [Oct]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 1/2] spi: add Intel Mid SSP driver
    Date
    From: Fei Yang <fei.yang@intel.com>

    This patch adds driver for ssp spi interface on Intel Mid platform.

    Signed-off-by: David Cohen <david.a.cohen@linux.intel.com>
    Signed-off-by: Fei Yang <fei.yang@intel.com>
    Reviewed-by: Ning Li <ning.li@intel.com>
    ---
    drivers/spi/Kconfig | 12 +
    drivers/spi/Makefile | 1 +
    drivers/spi/spi-intel-mid-ssp.c | 1506 +++++++++++++++++++++++++++++++++
    include/linux/spi/intel_mid_ssp_spi.h | 330 ++++++++
    4 files changed, 1849 insertions(+)
    create mode 100644 drivers/spi/spi-intel-mid-ssp.c
    create mode 100644 include/linux/spi/intel_mid_ssp_spi.h

    diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
    index b9c53cc..e211829 100644
    --- a/drivers/spi/Kconfig
    +++ b/drivers/spi/Kconfig
    @@ -211,6 +211,18 @@ config SPI_IMX
    This enables using the Freescale i.MX SPI controllers in master
    mode.

    +config SPI_INTEL_MID_SSP
    + tristate "SSP SPI controller driver for Intel MID platforms (EXPERIMENTAL)"
    + depends on X86_INTEL_MID && SPI_MASTER && INTEL_MID_DMAC
    + help
    + Select Y or M to build the unified SSP SPI slave controller driver
    + for the Intel MID platforms (Medfield, Clovertrail and Merrifield),
    + primarily used to implement a SPI host controller driver over a SSP
    + host controller.
    +
    + If you choose to build this driver as module it will be called
    + spi-intel-mid-ssp.ko
    +
    config SPI_LM70_LLP
    tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
    depends on PARPORT
    diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
    index ab8d864..60c8a1e 100644
    --- a/drivers/spi/Makefile
    +++ b/drivers/spi/Makefile
    @@ -38,6 +38,7 @@ obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
    obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
    obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
    obj-$(CONFIG_SPI_IMX) += spi-imx.o
    +obj-$(CONFIG_SPI_INTEL_MID_SSP) += spi-intel-mid-ssp.o
    obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
    obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
    obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
    diff --git a/drivers/spi/spi-intel-mid-ssp.c b/drivers/spi/spi-intel-mid-ssp.c
    new file mode 100644
    index 0000000..b3b9fe8
    --- /dev/null
    +++ b/drivers/spi/spi-intel-mid-ssp.c
    @@ -0,0 +1,1506 @@
    +/*
    + * spi-intel-mid-ssp.c
    + * This driver supports Bulverde SSP core used on Intel MID platforms
    + * It supports SSP of Moorestown & Medfield platforms and handles clock
    + * slave & master modes.
    + *
    + * Copyright (c) 2013, Intel Corporation.
    + * Contacts: Ning Li <ning.li@intel.com>
    + * David Cohen <david.a.cohen@linux.intel.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms and conditions of the GNU General Public License,
    + * version 2, as published by the Free Software Foundation.
    + *
    + * This program is distributed in the hope it will be useful, but WITHOUT
    + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    + * more details.
    + *
    + * You should have received a copy of the GNU General Public License along with
    + * this program; if not, write to the Free Software Foundation, Inc.,
    + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + */
    +
    +/*
    + * Note:
    + *
    + * Supports DMA and non-interrupt polled transfers.
    + *
    + */
    +#include <linux/kernel.h>
    +#include <linux/module.h>
    +#include <linux/delay.h>
    +#include <linux/interrupt.h>
    +#include <linux/highmem.h>
    +#include <linux/pci.h>
    +#include <linux/init.h>
    +#include <linux/interrupt.h>
    +#include <linux/dma-mapping.h>
    +#include <linux/intel_mid_dma.h>
    +#include <linux/pm_qos.h>
    +#include <linux/pm_runtime.h>
    +#include <linux/completion.h>
    +#include <asm/intel-mid.h>
    +
    +#include <linux/spi/spi.h>
    +#include <linux/spi/intel_mid_ssp_spi.h>
    +
    +#define DRIVER_NAME "intel_mid_ssp_spi_unified"
    +
    +static int ssp_timing_wr;
    +
    +#ifdef DUMP_RX
    +static void dump_trailer(const struct device *dev, char *buf, int len, int sz)
    +{
    + int tlen1 = (len < sz ? len : sz);
    + int tlen2 = ((len - sz) > sz) ? sz : (len - sz);
    + unsigned char *p;
    + static char msg[MAX_SPI_TRANSFER_SIZE];
    +
    + memset(msg, '\0', sizeof(msg));
    + p = buf;
    + while (p < buf + tlen1)
    + sprintf(msg, "%s%02x", msg, (unsigned int)*p++);
    +
    + if (tlen2 > 0) {
    + sprintf(msg, "%s .....", msg);
    + p = (buf+len) - tlen2;
    + while (p < buf + len)
    + sprintf(msg, "%s%02x", msg, (unsigned int)*p++);
    + }
    +
    + dev_info(dev, "DUMP: %p[0:%d ... %d:%d]:%s", buf, tlen1 - 1,
    + len-tlen2, len - 1, msg);
    +}
    +#endif
    +
    +static inline u8 ssp_cfg_get_mode(u8 ssp_cfg)
    +{
    + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
    + return (ssp_cfg) & 0x03;
    + else
    + return (ssp_cfg) & 0x07;
    +}
    +
    +static inline u8 ssp_cfg_get_spi_bus_nb(u8 ssp_cfg)
    +{
    + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
    + return ((ssp_cfg) >> 2) & 0x07;
    + else
    + return ((ssp_cfg) >> 3) & 0x07;
    +}
    +
    +static inline u8 ssp_cfg_is_spi_slave(u8 ssp_cfg)
    +{
    + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
    + return (ssp_cfg) & 0x20;
    + else
    + return (ssp_cfg) & 0x40;
    +}
    +
    +static inline u32 is_tx_fifo_empty(struct ssp_drv_context *sspc)
    +{
    + u32 sssr;
    + sssr = read_SSSR(sspc->ioaddr);
    + if ((sssr & SSSR_TFL_MASK) || (sssr & SSSR_TNF) == 0)
    + return 0;
    + else
    + return 1;
    +}
    +
    +static inline u32 is_rx_fifo_empty(struct ssp_drv_context *sspc)
    +{
    + return ((read_SSSR(sspc->ioaddr) & SSSR_RNE) == 0);
    +}
    +
    +static inline void disable_interface(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
    +}
    +
    +static inline void disable_triggers(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + write_SSCR1(read_SSCR1(reg) & ~sspc->cr1_sig, reg);
    +}
    +
    +
    +static void flush(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + u32 i = 0;
    +
    + /* If the transmit fifo is not empty, reset the interface. */
    + if (!is_tx_fifo_empty(sspc)) {
    + dev_err(&sspc->pdev->dev, "TX FIFO not empty. Reset of SPI IF");
    + disable_interface(sspc);
    + return;
    + }
    +
    + dev_dbg(&sspc->pdev->dev, " SSSR=%x\r\n", read_SSSR(reg));
    + while (!is_rx_fifo_empty(sspc) && (i < SPI_FIFO_SIZE + 1)) {
    + read_SSDR(reg);
    + i++;
    + }
    + WARN(i > 0, "%d words flush occured\n", i);
    +
    + return;
    +}
    +
    +static int null_writer(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + u8 n_bytes = sspc->n_bytes;
    +
    + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
    + || (sspc->tx == sspc->tx_end))
    + return 0;
    +
    + write_SSDR(0, reg);
    + sspc->tx += n_bytes;
    +
    + return 1;
    +}
    +
    +static int null_reader(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + u8 n_bytes = sspc->n_bytes;
    +
    + while ((read_SSSR(reg) & SSSR_RNE)
    + && (sspc->rx < sspc->rx_end)) {
    + read_SSDR(reg);
    + sspc->rx += n_bytes;
    + }
    +
    + return sspc->rx == sspc->rx_end;
    +}
    +
    +static int u8_writer(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
    + || (sspc->tx == sspc->tx_end))
    + return 0;
    +
    + write_SSDR(*(u8 *)(sspc->tx), reg);
    + ++sspc->tx;
    +
    + return 1;
    +}
    +
    +static int u8_reader(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + while ((read_SSSR(reg) & SSSR_RNE)
    + && (sspc->rx < sspc->rx_end)) {
    + *(u8 *)(sspc->rx) = read_SSDR(reg);
    + ++sspc->rx;
    + }
    +
    + return sspc->rx == sspc->rx_end;
    +}
    +
    +static int u16_writer(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
    + || (sspc->tx == sspc->tx_end))
    + return 0;
    +
    + write_SSDR(*(u16 *)(sspc->tx), reg);
    + sspc->tx += 2;
    +
    + return 1;
    +}
    +
    +static int u16_reader(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + while ((read_SSSR(reg) & SSSR_RNE) && (sspc->rx < sspc->rx_end)) {
    + *(u16 *)(sspc->rx) = read_SSDR(reg);
    + sspc->rx += 2;
    + }
    +
    + return sspc->rx == sspc->rx_end;
    +}
    +
    +static int u32_writer(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + if (((read_SSSR(reg) & SSSR_TFL_MASK) == SSSR_TFL_MASK)
    + || (sspc->tx == sspc->tx_end))
    + return 0;
    +
    + write_SSDR(*(u32 *)(sspc->tx), reg);
    + sspc->tx += 4;
    +
    + return 1;
    +}
    +
    +static int u32_reader(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + while ((read_SSSR(reg) & SSSR_RNE) && (sspc->rx < sspc->rx_end)) {
    + *(u32 *)(sspc->rx) = read_SSDR(reg);
    + sspc->rx += 4;
    + }
    +
    + return sspc->rx == sspc->rx_end;
    +}
    +
    +static bool chan_filter(struct dma_chan *chan, void *param)
    +{
    + struct ssp_drv_context *sspc = param;
    +
    + if (sspc->dmac1 && chan->device->dev == &sspc->dmac1->dev)
    + return true;
    +
    + return false;
    +}
    +
    +/**
    + * unmap_dma_buffers() - Unmap the DMA buffers used during the last transfer.
    + * @sspc: Pointer to the private driver context
    + */
    +static void unmap_dma_buffers(struct ssp_drv_context *sspc)
    +{
    + struct device *dev = &sspc->pdev->dev;
    +
    + if (!sspc->dma_mapped)
    + return;
    + dma_unmap_single(dev, sspc->rx_dma, sspc->len, PCI_DMA_FROMDEVICE);
    + dma_unmap_single(dev, sspc->tx_dma, sspc->len, PCI_DMA_TODEVICE);
    + sspc->dma_mapped = 0;
    +}
    +
    +/**
    + * ssp_spi_dma_done() - End of DMA transfer callback
    + * @arg: Pointer to the data provided at callback registration
    + *
    + * This function is set as callback for both RX and TX DMA transfers. The
    + * RX or TX 'done' flag is set acording to the direction of the ended
    + * transfer. Then, if both RX and TX flags are set, it means that the
    + * transfer job is completed.
    + */
    +static void ssp_spi_dma_done(void *arg)
    +{
    + struct callback_param *cb_param = (struct callback_param *)arg;
    + struct ssp_drv_context *sspc = cb_param->drv_context;
    + struct device *dev = &sspc->pdev->dev;
    + void *reg = sspc->ioaddr;
    +
    + if (cb_param->direction == TX_DIRECTION)
    + sspc->txdma_done = 1;
    + else
    + sspc->rxdma_done = 1;
    +
    + dev_dbg(dev, "DMA callback for direction %d [RX done:%d] [TX done:%d]\n",
    + cb_param->direction, sspc->rxdma_done,
    + sspc->txdma_done);
    +
    + if (sspc->txdma_done && sspc->rxdma_done) {
    + /* Clear Status Register */
    + write_SSSR(sspc->clear_sr, reg);
    + dev_dbg(dev, "DMA done\n");
    + /* Disable Triggers to DMA or to CPU*/
    + disable_triggers(sspc);
    + unmap_dma_buffers(sspc);
    +
    + queue_work(sspc->dma_wq, &sspc->complete_work);
    + }
    +}
    +
    +/**
    + * ssp_spi_dma_init() - Initialize DMA
    + * @sspc: Pointer to the private driver context
    + *
    + * This function is called at driver setup phase to allocate DMA
    + * ressources.
    + */
    +static void ssp_spi_dma_init(struct ssp_drv_context *sspc)
    +{
    + struct intel_mid_dma_slave *rxs, *txs;
    + struct dma_slave_config *ds;
    + dma_cap_mask_t mask;
    + struct device *dev = &sspc->pdev->dev;
    + unsigned int device_id;
    +
    + /* Configure RX channel parameters */
    + rxs = &sspc->dmas_rx;
    + ds = &rxs->dma_slave;
    +
    + ds->direction = DMA_FROM_DEVICE;
    + rxs->hs_mode = LNW_DMA_HW_HS;
    + rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
    + ds->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    + ds->src_addr_width = sspc->n_bytes;
    +
    + /* Use a DMA burst according to the FIFO thresholds */
    + if (sspc->rx_fifo_threshold == 8) {
    + ds->src_maxburst = 8;
    + ds->dst_maxburst = 8;
    + } else if (sspc->rx_fifo_threshold == 4) {
    + ds->src_maxburst = 4;
    + ds->dst_maxburst = 4;
    + } else {
    + ds->src_maxburst = 1;
    + ds->dst_maxburst = 1;
    + }
    +
    + /* Configure TX channel parameters */
    + txs = &sspc->dmas_tx;
    + ds = &txs->dma_slave;
    +
    + ds->direction = DMA_TO_DEVICE;
    + txs->hs_mode = LNW_DMA_HW_HS;
    + txs->cfg_mode = LNW_DMA_MEM_TO_PER;
    + ds->src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    + ds->dst_addr_width = sspc->n_bytes;
    +
    + /* Use a DMA burst according to the FIFO thresholds */
    + if (sspc->rx_fifo_threshold == 8) {
    + ds->src_maxburst = 8;
    + ds->dst_maxburst = 8;
    + } else if (sspc->rx_fifo_threshold == 4) {
    + ds->src_maxburst = 4;
    + ds->dst_maxburst = 4;
    + } else {
    + ds->src_maxburst = 1;
    + ds->dst_maxburst = 1;
    + }
    +
    + /* Nothing more to do if already initialized */
    + if (sspc->dma_initialized)
    + return;
    +
    + /* Use DMAC1 */
    + if (sspc->quirks & QUIRKS_PLATFORM_MRST)
    + device_id = PCI_MRST_DMAC1_ID;
    + else
    + device_id = PCI_MDFL_DMAC1_ID;
    +
    + sspc->dmac1 = pci_get_device(PCI_VENDOR_ID_INTEL, device_id, NULL);
    + if (!sspc->dmac1) {
    + dev_err(dev, "Can't find DMAC1");
    + return;
    + }
    +
    + if (sspc->quirks & QUIRKS_SRAM_ADDITIONAL_CPY) {
    + sspc->virt_addr_sram_rx = ioremap_nocache(SRAM_BASE_ADDR,
    + 2 * MAX_SPI_TRANSFER_SIZE);
    + if (sspc->virt_addr_sram_rx)
    + sspc->virt_addr_sram_tx = sspc->virt_addr_sram_rx +
    + MAX_SPI_TRANSFER_SIZE;
    + else
    + dev_err(dev, "Virt_addr_sram_rx is null\n");
    + }
    +
    + /* 1. Allocate rx channel */
    + dma_cap_zero(mask);
    + dma_cap_set(DMA_MEMCPY, mask);
    + dma_cap_set(DMA_SLAVE, mask);
    +
    + sspc->rxchan = dma_request_channel(mask, chan_filter, sspc);
    + if (!sspc->rxchan)
    + goto err_exit;
    +
    + sspc->rxchan->private = rxs;
    +
    + /* 2. Allocate tx channel */
    + dma_cap_set(DMA_SLAVE, mask);
    + dma_cap_set(DMA_MEMCPY, mask);
    +
    + sspc->txchan = dma_request_channel(mask, chan_filter, sspc);
    + if (!sspc->txchan)
    + goto free_rxchan;
    + else
    + sspc->txchan->private = txs;
    +
    + /* set the dma done bit to 1 */
    + sspc->txdma_done = 1;
    + sspc->rxdma_done = 1;
    +
    + sspc->tx_param.drv_context = sspc;
    + sspc->tx_param.direction = TX_DIRECTION;
    + sspc->rx_param.drv_context = sspc;
    + sspc->rx_param.direction = RX_DIRECTION;
    +
    + sspc->dma_initialized = 1;
    + return;
    +
    +free_rxchan:
    + dma_release_channel(sspc->rxchan);
    +err_exit:
    + dev_err(dev, "Error : DMA Channel Not available\n");
    +
    + if (sspc->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)
    + iounmap(sspc->virt_addr_sram_rx);
    +
    + pci_dev_put(sspc->dmac1);
    + return;
    +}
    +
    +/**
    + * ssp_spi_dma_exit() - Release DMA ressources
    + * @sspc: Pointer to the private driver context
    + */
    +static void ssp_spi_dma_exit(struct ssp_drv_context *sspc)
    +{
    + dma_release_channel(sspc->txchan);
    + dma_release_channel(sspc->rxchan);
    +
    + if (sspc->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)
    + iounmap(sspc->virt_addr_sram_rx);
    +
    + pci_dev_put(sspc->dmac1);
    +}
    +
    +/**
    + * dma_transfer() - Initiate a DMA transfer
    + * @sspc: Pointer to the private driver context
    + */
    +static void dma_transfer(struct ssp_drv_context *sspc)
    +{
    + dma_addr_t ssdr_addr;
    + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
    + struct dma_chan *txchan, *rxchan;
    + enum dma_ctrl_flags flag;
    + struct device *dev = &sspc->pdev->dev;
    +
    + /* get Data Read/Write address */
    + ssdr_addr = (dma_addr_t)(sspc->paddr + 0x10);
    +
    + if (sspc->tx_dma)
    + sspc->txdma_done = 0;
    +
    + if (sspc->rx_dma)
    + sspc->rxdma_done = 0;
    +
    + /* 2. prepare the RX dma transfer */
    + txchan = sspc->txchan;
    + rxchan = sspc->rxchan;
    +
    + flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
    +
    + if (likely(sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL)) {
    + /* Since the DMA is configured to do 32bits access */
    + /* to/from the DDR, the DMA transfer size must be */
    + /* a multiple of 4 bytes */
    + sspc->len_dma_rx = sspc->len & ~(4 - 1);
    + sspc->len_dma_tx = sspc->len_dma_rx;
    +
    + /* In Rx direction, TRAIL Bytes are handled by memcpy */
    + if (sspc->rx_dma &&
    + (sspc->len_dma_rx >
    + sspc->rx_fifo_threshold * sspc->n_bytes))
    + sspc->len_dma_rx = TRUNCATE(sspc->len_dma_rx,
    + sspc->rx_fifo_threshold * sspc->n_bytes);
    + else if (!sspc->rx_dma)
    + dev_err(dev, "ERROR : rx_dma is null\r\n");
    + } else {
    + /* TRAIL Bytes are handled by DMA */
    + if (sspc->rx_dma) {
    + sspc->len_dma_rx = sspc->len;
    + sspc->len_dma_tx = sspc->len;
    + } else
    + dev_err(dev, "ERROR : sspc->rx_dma is null!\n");
    + }
    +
    + rxdesc = rxchan->device->device_prep_dma_memcpy
    + (rxchan, /* DMA Channel */
    + sspc->rx_dma, /* DAR */
    + ssdr_addr, /* SAR */
    + sspc->len_dma_rx, /* Data Length */
    + flag); /* Flag */
    +
    + if (rxdesc) {
    + rxdesc->callback = ssp_spi_dma_done;
    + rxdesc->callback_param = &sspc->rx_param;
    + } else {
    + dev_dbg(dev, "rxdesc is null! (len_dma_rx:%d)\n",
    + sspc->len_dma_rx);
    + sspc->rxdma_done = 1;
    + }
    +
    + /* 3. prepare the TX dma transfer */
    + if (sspc->tx_dma) {
    + txdesc = txchan->device->device_prep_dma_memcpy
    + (txchan, /* DMA Channel */
    + ssdr_addr, /* DAR */
    + sspc->tx_dma, /* SAR */
    + sspc->len_dma_tx, /* Data Length */
    + flag); /* Flag */
    + if (txdesc) {
    + txdesc->callback = ssp_spi_dma_done;
    + txdesc->callback_param = &sspc->tx_param;
    + } else {
    + dev_dbg(dev, "txdesc is null! (len_dma_tx:%d)\n",
    + sspc->len_dma_tx);
    + sspc->txdma_done = 1;
    + }
    + } else {
    + dev_err(dev, "ERROR : sspc->tx_dma is null!\n");
    + return;
    + }
    +
    + dev_info(dev, "DMA transfer len:%d len_dma_tx:%d len_dma_rx:%d\n",
    + sspc->len, sspc->len_dma_tx, sspc->len_dma_rx);
    +
    + if (rxdesc || txdesc) {
    + if (rxdesc) {
    + dev_dbg(dev, "Firing DMA RX channel\n");
    + rxdesc->tx_submit(rxdesc);
    + }
    + if (txdesc) {
    + dev_dbg(dev, "Firing DMA TX channel\n");
    + txdesc->tx_submit(txdesc);
    + }
    + } else {
    + struct callback_param cb_param;
    + cb_param.drv_context = sspc;
    + dev_dbg(dev, "Bypassing DMA transfer\n");
    + ssp_spi_dma_done(&cb_param);
    + }
    +}
    +
    +/**
    + * map_dma_buffers() - Map DMA buffer before a transfer
    + * @sspc: Pointer to the private drivzer context
    + */
    +static int map_dma_buffers(struct ssp_drv_context *sspc)
    +{
    + struct device *dev = &sspc->pdev->dev;
    +
    + if (unlikely(sspc->dma_mapped)) {
    + dev_err(dev, "ERROR : DMA buffers already mapped\n");
    + return 0;
    + }
    + if (unlikely(sspc->quirks & QUIRKS_SRAM_ADDITIONAL_CPY)) {
    + /* Copy sspc->tx into sram_tx */
    + memcpy_toio(sspc->virt_addr_sram_tx, sspc->tx, sspc->len);
    +#ifdef DUMP_RX
    + dump_trailer(&sspc->pdev->dev, sspc->tx, sspc->len, 16);
    +#endif
    + sspc->rx_dma = SRAM_RX_ADDR;
    + sspc->tx_dma = SRAM_TX_ADDR;
    + } else {
    + /* no QUIRKS_SRAM_ADDITIONAL_CPY */
    + if (unlikely(sspc->dma_mapped))
    + return 1;
    +
    + sspc->tx_dma = dma_map_single(dev, sspc->tx, sspc->len,
    + PCI_DMA_TODEVICE);
    + if (unlikely(dma_mapping_error(dev, sspc->tx_dma))) {
    + dev_err(dev, "ERROR : tx dma mapping failed\n");
    + return 0;
    + }
    +
    + sspc->rx_dma = dma_map_single(dev, sspc->rx, sspc->len,
    + PCI_DMA_FROMDEVICE);
    + if (unlikely(dma_mapping_error(dev, sspc->rx_dma))) {
    + dma_unmap_single(dev, sspc->tx_dma,
    + sspc->len, DMA_TO_DEVICE);
    + dev_err(dev, "ERROR : rx dma mapping failed\n");
    + return 0;
    + }
    + }
    + return 1;
    +}
    +
    +/**
    + * drain_trail() - Handle trailing bytes of a transfer
    + * @sspc: Pointer to the private driver context
    + *
    + * This function handles the trailing bytes of a transfer for the case
    + * they are not handled by the DMA.
    + */
    +static void drain_trail(struct ssp_drv_context *sspc)
    +{
    + struct device *dev = &sspc->pdev->dev;
    + void *reg = sspc->ioaddr;
    +
    + if (sspc->len != sspc->len_dma_rx) {
    + dev_dbg(dev, "Handling trailing bytes. SSSR:%08x\n",
    + read_SSSR(reg));
    + sspc->rx += sspc->len_dma_rx;
    + sspc->tx += sspc->len_dma_tx;
    +
    + while ((sspc->tx != sspc->tx_end) ||
    + (sspc->rx != sspc->rx_end)) {
    + sspc->read(sspc);
    + sspc->write(sspc);
    + }
    + }
    +}
    +
    +/**
    + * sram_to_ddr_cpy() - Copy data from Langwell SDRAM to DDR
    + * @sspc: Pointer to the private driver context
    + */
    +static void sram_to_ddr_cpy(struct ssp_drv_context *sspc)
    +{
    + u32 length = sspc->len;
    +
    + if ((sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL)
    + && (sspc->len > sspc->rx_fifo_threshold * sspc->n_bytes))
    + length = TRUNCATE(sspc->len,
    + sspc->rx_fifo_threshold * sspc->n_bytes);
    +
    + memcpy_fromio(sspc->rx, sspc->virt_addr_sram_rx, length);
    +}
    +
    +static void int_transfer_complete(struct ssp_drv_context *sspc)
    +{
    + void *reg = sspc->ioaddr;
    + struct spi_message *msg;
    + struct device *dev = &sspc->pdev->dev;
    +
    + if (unlikely(sspc->quirks & QUIRKS_USE_PM_QOS))
    + pm_qos_update_request(&sspc->pm_qos_req,
    + PM_QOS_DEFAULT_VALUE);
    +
    + if (unlikely(sspc->quirks & QUIRKS_SRAM_ADDITIONAL_CPY))
    + sram_to_ddr_cpy(sspc);
    +
    + if (likely(sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL))
    + drain_trail(sspc);
    + else
    + /* Stop getting Time Outs */
    + write_SSTO(0, reg);
    +
    + sspc->cur_msg->status = 0;
    + sspc->cur_msg->actual_length = sspc->len;
    +
    +#ifdef DUMP_RX
    + dump_trailer(dev, sspc->rx, sspc->len, 16);
    +#endif
    +
    + dev_dbg(dev, "End of transfer. SSSR:%08X\n", read_SSSR(reg));
    + msg = sspc->cur_msg;
    + if (likely(msg->complete))
    + msg->complete(msg->context);
    + complete(&sspc->msg_done);
    +}
    +
    +static void int_transfer_complete_work(struct work_struct *work)
    +{
    + struct ssp_drv_context *sspc = container_of(work,
    + struct ssp_drv_context, complete_work);
    +
    + int_transfer_complete(sspc);
    +}
    +
    +static void poll_transfer_complete(struct ssp_drv_context *sspc)
    +{
    + struct spi_message *msg;
    +
    + /* Update total byte transfered return count actual bytes read */
    + sspc->cur_msg->actual_length += sspc->len - (sspc->rx_end - sspc->rx);
    +
    + sspc->cur_msg->status = 0;
    + msg = sspc->cur_msg;
    + if (likely(msg->complete))
    + msg->complete(msg->context);
    + complete(&sspc->msg_done);
    +}
    +
    +/**
    + * ssp_int() - Interrupt handler
    + * @irq
    + * @dev_id
    + *
    + * The SSP interrupt is not used for transfer which are handled by
    + * DMA or polling: only under/over run are catched to detect
    + * broken transfers.
    + */
    +static irqreturn_t ssp_int(int irq, void *dev_id)
    +{
    + struct ssp_drv_context *sspc = dev_id;
    + void *reg = sspc->ioaddr;
    + struct device *dev = &sspc->pdev->dev;
    + u32 status = read_SSSR(reg);
    +
    + /* It should never be our interrupt since SSP will */
    + /* only trigs interrupt for under/over run. */
    + if (likely(!(status & sspc->mask_sr)))
    + return IRQ_NONE;
    +
    + if (status & SSSR_ROR || status & SSSR_TUR) {
    + dev_err(dev, "--- SPI ROR or TUR occurred : SSSR=%x\n", status);
    + WARN_ON(1);
    + if (status & SSSR_ROR)
    + dev_err(dev, "we have Overrun\n");
    + if (status & SSSR_TUR)
    + dev_err(dev, "we have Underrun\n");
    + }
    +
    + /* We can fall here when not using DMA mode */
    + if (!sspc->cur_msg) {
    + disable_interface(sspc);
    + disable_triggers(sspc);
    + }
    + /* clear status register */
    + write_SSSR(sspc->clear_sr, reg);
    + return IRQ_HANDLED;
    +}
    +
    +static void poll_transfer(unsigned long data)
    +{
    + struct ssp_drv_context *sspc = (void *)data;
    +
    + if (sspc->tx) {
    + while (sspc->tx != sspc->tx_end) {
    + if (ssp_timing_wr) {
    + int timeout = 100;
    + /* It is used as debug UART on Tangier. Since
    + baud rate = 115200, it needs at least 312us
    + for one word transferring. Becuase of silicon
    + issue, it MUST check SFIFOL here instead of
    + TNF. It is the workaround for A0 stepping*/
    + while (--timeout &&
    + ((read_SFIFOL(sspc->ioaddr)) & 0xFFFF))
    + udelay(10);
    + }
    + sspc->write(sspc);
    + sspc->read(sspc);
    + }
    + }
    +
    + while (!sspc->read(sspc))
    + cpu_relax();
    +
    + poll_transfer_complete(sspc);
    +}
    +
    +/**
    + * start_bitbanging() - Clock synchronization by bit banging
    + * @sspc: Pointer to private driver context
    + *
    + * This clock synchronization will be removed as soon as it is
    + * handled by the SCU.
    + */
    +static void start_bitbanging(struct ssp_drv_context *sspc)
    +{
    + u32 sssr;
    + u32 count = 0;
    + u32 cr0;
    + void *i2c_reg = sspc->I2C_ioaddr;
    + struct device *dev = &sspc->pdev->dev;
    + void *reg = sspc->ioaddr;
    + struct chip_data *chip = spi_get_ctldata(sspc->cur_msg->spi);
    + cr0 = chip->cr0;
    +
    + dev_warn(dev, "In %s : Starting bit banging\n",
    + __func__);
    + if (read_SSSR(reg) & SSP_NOT_SYNC)
    + dev_warn(dev, "SSP clock desynchronized.\n");
    + if (!(read_SSCR0(reg) & SSCR0_SSE))
    + dev_warn(dev, "in SSCR0, SSP disabled.\n");
    +
    + dev_dbg(dev, "SSP not ready, start CLK sync\n");
    +
    + write_SSCR0(cr0 & ~SSCR0_SSE, reg);
    + write_SSPSP(0x02010007, reg);
    +
    + write_SSTO(chip->timeout, reg);
    + write_SSCR0(cr0, reg);
    +
    + /*
    + * This routine uses the DFx block to override the SSP inputs
    + * and outputs allowing us to bit bang SSPSCLK. On Langwell,
    + * we have to generate the clock to clear busy.
    + */
    + write_I2CDATA(0x3, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CCTRL(0x01070034, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CDATA(0x00000099, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CCTRL(0x01070038, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + sssr = read_SSSR(reg);
    +
    + /* Bit bang the clock until CSS clears */
    + while ((sssr & 0x400000) && (count < MAX_BITBANGING_LOOP)) {
    + write_I2CDATA(0x2, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CCTRL(0x01070034, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CDATA(0x3, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CCTRL(0x01070034, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + sssr = read_SSSR(reg);
    + count++;
    + }
    + if (count >= MAX_BITBANGING_LOOP)
    + dev_err(dev, "ERROR in %s : infinite loop on bit banging. Aborting\n",
    + __func__);
    +
    + dev_dbg(dev, "---Bit bang count=%d\n", count);
    +
    + write_I2CDATA(0x0, i2c_reg);
    + udelay(I2C_ACCESS_USDELAY);
    + write_I2CCTRL(0x01070038, i2c_reg);
    +}
    +
    +static unsigned int ssp_get_clk_div(int speed)
    +{
    + return max(100000000 / speed, 4) - 1;
    +}
    +
    +/**
    + * transfer() - Start a SPI transfer
    + * @spi: Pointer to the spi_device struct
    + * @msg: Pointer to the spi_message struct
    + */
    +static int transfer(struct spi_device *spi, struct spi_message *msg)
    +{
    + struct ssp_drv_context *sspc = spi_master_get_devdata(spi->master);
    + unsigned long flags;
    +
    + msg->actual_length = 0;
    + msg->status = -EINPROGRESS;
    + spin_lock_irqsave(&sspc->lock, flags);
    + list_add_tail(&msg->queue, &sspc->queue);
    + if (!sspc->suspended)
    + queue_work(sspc->workqueue, &sspc->pump_messages);
    + spin_unlock_irqrestore(&sspc->lock, flags);
    +
    + return 0;
    +}
    +
    +static int handle_message(struct ssp_drv_context *sspc)
    +{
    + struct chip_data *chip = NULL;
    + struct spi_transfer *transfer = NULL;
    + void *reg = sspc->ioaddr;
    + u32 cr1;
    + struct device *dev = &sspc->pdev->dev;
    + struct spi_message *msg = sspc->cur_msg;
    +
    + chip = spi_get_ctldata(msg->spi);
    +
    + /* We handle only one transfer message since the protocol module has to
    + control the out of band signaling. */
    + transfer = list_entry(msg->transfers.next, struct spi_transfer,
    + transfer_list);
    +
    + /* Check transfer length */
    + if (unlikely((transfer->len > MAX_SPI_TRANSFER_SIZE) ||
    + (transfer->len == 0))) {
    + dev_warn(dev, "transfer length null or greater than %d\n",
    + MAX_SPI_TRANSFER_SIZE);
    + dev_warn(dev, "length = %d\n", transfer->len);
    + msg->status = -EINVAL;
    +
    + if (msg->complete)
    + msg->complete(msg->context);
    + complete(&sspc->msg_done);
    + return 0;
    + }
    +
    + /* Flush any remaining data (in case of failed previous transfer) */
    + flush(sspc);
    +
    + sspc->tx = (void *)transfer->tx_buf;
    + sspc->rx = (void *)transfer->rx_buf;
    + sspc->len = transfer->len;
    + sspc->write = chip->write;
    + sspc->read = chip->read;
    +
    + if (likely(chip->dma_enabled)) {
    + sspc->dma_mapped = map_dma_buffers(sspc);
    + if (unlikely(!sspc->dma_mapped))
    + return 0;
    + } else {
    + sspc->write = sspc->tx ? chip->write : null_writer;
    + sspc->read = sspc->rx ? chip->read : null_reader;
    + }
    + sspc->tx_end = sspc->tx + transfer->len;
    + sspc->rx_end = sspc->rx + transfer->len;
    + write_SSSR(sspc->clear_sr, reg);
    +
    + /* setup the CR1 control register */
    + cr1 = chip->cr1 | sspc->cr1_sig;
    +
    + if (likely(sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL)) {
    + /* in case of len smaller than burst size, adjust the RX */
    + /* threshold. All other cases will use the default threshold */
    + /* value. The RX fifo threshold must be aligned with the DMA */
    + /* RX transfer size, which may be limited to a multiple of 4 */
    + /* bytes due to 32bits DDR access. */
    + if (sspc->len / sspc->n_bytes <= sspc->rx_fifo_threshold) {
    + u32 rx_fifo_threshold;
    +
    + rx_fifo_threshold = (sspc->len & ~(4 - 1)) /
    + sspc->n_bytes;
    + cr1 &= ~(SSCR1_RFT);
    + cr1 |= SSCR1_RxTresh(rx_fifo_threshold) & SSCR1_RFT;
    + } else
    + write_SSTO(chip->timeout, reg);
    + }
    +
    + dev_dbg(dev, "transfer len:%d n_bytes:%d cr0:%x cr1:%x",
    + sspc->len, sspc->n_bytes, chip->cr0, cr1);
    +
    + /* first set CR1 */
    + write_SSCR1(cr1, reg);
    +
    + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
    + write_SSFS((1 << chip->chip_select), reg);
    +
    + /* Do bitbanging only if SSP not-enabled or not-synchronized */
    + if (unlikely(((read_SSSR(reg) & SSP_NOT_SYNC) ||
    + (!(read_SSCR0(reg) & SSCR0_SSE))) &&
    + (sspc->quirks & QUIRKS_BIT_BANGING))) {
    + start_bitbanging(sspc);
    + } else {
    + /* (re)start the SSP */
    + if (ssp_timing_wr) {
    + chip->cr0 = 0x00c0000f;
    + write_SSCR0(chip->cr0, reg);
    + chip->cr0 = 0x00c12c0f;
    + write_SSCR0(chip->cr0, reg);
    + chip->cr0 = 0x00c12c8f;
    + write_SSCR0(chip->cr0, reg);
    + } else {
    + write_SSCR0(chip->cr0, reg);
    + }
    + }
    +
    + if (likely(chip->dma_enabled)) {
    + if (unlikely(sspc->quirks & QUIRKS_USE_PM_QOS))
    + pm_qos_update_request(&sspc->pm_qos_req,
    + MIN_EXIT_LATENCY);
    + dma_transfer(sspc);
    + } else
    + tasklet_schedule(&sspc->poll_transfer);
    +
    + return 0;
    +}
    +
    +static void pump_messages(struct work_struct *work)
    +{
    + struct ssp_drv_context *sspc =
    + container_of(work, struct ssp_drv_context, pump_messages);
    + struct device *dev = &sspc->pdev->dev;
    + unsigned long flags;
    + struct spi_message *msg;
    +
    + pm_runtime_get_sync(dev);
    + spin_lock_irqsave(&sspc->lock, flags);
    + while (!list_empty(&sspc->queue)) {
    + if (sspc->suspended)
    + break;
    + msg = list_entry(sspc->queue.next, struct spi_message, queue);
    + list_del_init(&msg->queue);
    + sspc->cur_msg = msg;
    + spin_unlock_irqrestore(&sspc->lock, flags);
    + INIT_COMPLETION(sspc->msg_done);
    + handle_message(sspc);
    + wait_for_completion(&sspc->msg_done);
    + spin_lock_irqsave(&sspc->lock, flags);
    + sspc->cur_msg = NULL;
    + }
    + spin_unlock_irqrestore(&sspc->lock, flags);
    + pm_runtime_put(dev);
    +}
    +
    +/**
    + * setup() - Driver setup procedure
    + * @spi: Pointeur to the spi_device struct
    + */
    +static int setup(struct spi_device *spi)
    +{
    + struct intel_mid_ssp_spi_chip *chip_info = NULL;
    + struct chip_data *chip;
    + struct ssp_drv_context *sspc =
    + spi_master_get_devdata(spi->master);
    + u32 tx_fifo_threshold;
    + u32 burst_size;
    + u32 clk_div;
    +
    + if (!spi->bits_per_word)
    + spi->bits_per_word = DFLT_BITS_PER_WORD;
    +
    + if ((spi->bits_per_word < MIN_BITS_PER_WORD
    + || spi->bits_per_word > MAX_BITS_PER_WORD))
    + return -EINVAL;
    +
    + chip = spi_get_ctldata(spi);
    + if (!chip) {
    + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
    + if (!chip) {
    + dev_err(&spi->dev,
    + "failed setup: can't allocate chip data\n");
    + return -ENOMEM;
    + }
    + }
    + chip->cr0 = SSCR0_Motorola | SSCR0_DataSize(spi->bits_per_word > 16 ?
    + spi->bits_per_word - 16 : spi->bits_per_word)
    + | SSCR0_SSE
    + | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0);
    +
    + /* protocol drivers may change the chip settings, so... */
    + /* if chip_info exists, use it */
    + chip_info = spi->controller_data;
    +
    + /* chip_info isn't always needed */
    + chip->cr1 = 0;
    + if (chip_info) {
    + burst_size = chip_info->burst_size;
    + if (burst_size > IMSS_FIFO_BURST_8)
    + burst_size = DFLT_FIFO_BURST_SIZE;
    +
    + chip->timeout = chip_info->timeout;
    +
    + if (chip_info->enable_loopback)
    + chip->cr1 |= SSCR1_LBM;
    +
    + chip->dma_enabled = chip_info->dma_enabled;
    +
    + } else {
    + /* if no chip_info provided by protocol driver, */
    + /* set default values */
    + dev_info(&spi->dev, "setting default chip values\n");
    +
    + burst_size = DFLT_FIFO_BURST_SIZE;
    + chip->dma_enabled = 1;
    + if (sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL)
    + chip->timeout = 0;
    + else
    + chip->timeout = DFLT_TIMEOUT_VAL;
    + }
    + /* Set FIFO thresholds according to burst_size */
    + if (burst_size == IMSS_FIFO_BURST_8)
    + sspc->rx_fifo_threshold = 8;
    + else if (burst_size == IMSS_FIFO_BURST_4)
    + sspc->rx_fifo_threshold = 4;
    + else
    + sspc->rx_fifo_threshold = 1;
    + tx_fifo_threshold = SPI_FIFO_SIZE - sspc->rx_fifo_threshold;
    + chip->cr1 |= (SSCR1_RxTresh(sspc->rx_fifo_threshold) &
    + SSCR1_RFT) | (SSCR1_TxTresh(tx_fifo_threshold) & SSCR1_TFT);
    +
    + sspc->dma_mapped = 0;
    +
    + /* setting phase and polarity. spi->mode comes from boardinfo */
    + if ((spi->mode & SPI_CPHA) != 0)
    + chip->cr1 |= SSCR1_SPH;
    + if ((spi->mode & SPI_CPOL) != 0)
    + chip->cr1 |= SSCR1_SPO;
    +
    + if (sspc->quirks & QUIRKS_SPI_SLAVE_CLOCK_MODE)
    + /* set slave mode */
    + chip->cr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
    + chip->cr1 |= SSCR1_SCFR; /* clock is not free running */
    +
    + dev_dbg(&spi->dev, "%d bits/word, mode %d\n",
    + spi->bits_per_word, spi->mode & 0x3);
    + if (spi->bits_per_word <= 8) {
    + chip->n_bytes = 1;
    + chip->read = u8_reader;
    + chip->write = u8_writer;
    + } else if (spi->bits_per_word <= 16) {
    + chip->n_bytes = 2;
    + chip->read = u16_reader;
    + chip->write = u16_writer;
    + } else if (spi->bits_per_word <= 32) {
    + if (!ssp_timing_wr)
    + chip->cr0 |= SSCR0_EDSS;
    + chip->n_bytes = 4;
    + chip->read = u32_reader;
    + chip->write = u32_writer;
    + } else {
    + dev_err(&spi->dev, "invalid wordsize\n");
    + return -EINVAL;
    + }
    +
    + if ((sspc->quirks & QUIRKS_SPI_SLAVE_CLOCK_MODE) == 0) {
    + chip->speed_hz = spi->max_speed_hz;
    + clk_div = ssp_get_clk_div(chip->speed_hz);
    + if (!ssp_timing_wr)
    + chip->cr0 |= clk_div << 8;
    + }
    + chip->bits_per_word = spi->bits_per_word;
    + chip->chip_select = spi->chip_select;
    +
    + spi_set_ctldata(spi, chip);
    +
    + /* setup of sspc members that will not change across transfers */
    + sspc->n_bytes = chip->n_bytes;
    +
    + if (chip->dma_enabled) {
    + ssp_spi_dma_init(sspc);
    + sspc->cr1_sig = SSCR1_TSRE | SSCR1_RSRE;
    + sspc->mask_sr = SSSR_ROR | SSSR_TUR;
    + if (sspc->quirks & QUIRKS_DMA_USE_NO_TRAIL)
    + sspc->cr1_sig |= SSCR1_TRAIL;
    + } else {
    + sspc->cr1_sig = SSCR1_RIE | SSCR1_TIE | SSCR1_TINTE;
    + sspc->mask_sr = SSSR_RFS | SSSR_TFS |
    + SSSR_ROR | SSSR_TUR | SSSR_TINT;
    + }
    + sspc->clear_sr = SSSR_TUR | SSSR_ROR | SSSR_TINT;
    +
    + return 0;
    +}
    +
    +/**
    + * cleanup() - Driver cleanup procedure
    + * @spi: Pointer to the spi_device struct
    + */
    +static void cleanup(struct spi_device *spi)
    +{
    + struct chip_data *chip = spi_get_ctldata(spi);
    + struct ssp_drv_context *sspc =
    + spi_master_get_devdata(spi->master);
    +
    + if (sspc->dma_initialized)
    + ssp_spi_dma_exit(sspc);
    +
    + /* Remove the PM_QOS request */
    + if (sspc->quirks & QUIRKS_USE_PM_QOS)
    + pm_qos_remove_request(&sspc->pm_qos_req);
    +
    + kfree(chip);
    + spi_set_ctldata(spi, NULL);
    +}
    +
    +/**
    + * ssp_spi_probe() - Driver probe procedure
    + * @pdev: Pointer to the pci_dev struct
    + * @ent: Pointer to the pci_device_id struct
    + */
    +static int ssp_spi_probe(struct pci_dev *pdev,
    + const struct pci_device_id *ent)
    +{
    + struct device *dev = &pdev->dev;
    + struct spi_master *master;
    + struct ssp_drv_context *sspc = 0;
    + int status;
    + u32 iolen = 0;
    + u8 ssp_cfg;
    + int pos;
    + void __iomem *syscfg_ioaddr;
    + unsigned long syscfg;
    +
    + /* Check if the SSP we are probed for has been allocated */
    + /* to operate as SPI. This information is retreived from */
    + /* the field adid of the Vendor-Specific PCI capability */
    + /* which is used as a configuration register. */
    + pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
    + if (pos > 0) {
    + pci_read_config_byte(pdev,
    + pos + VNDR_CAPABILITY_ADID_OFFSET,
    + &ssp_cfg);
    + } else {
    + dev_info(dev, "No Vendor Specific PCI capability\n");
    + goto err_abort_probe;
    + }
    +
    + if (ssp_cfg_get_mode(ssp_cfg) != SSP_CFG_SPI_MODE_ID) {
    + dev_info(dev, "Unsupported SSP mode (%02xh)\n", ssp_cfg);
    + goto err_abort_probe;
    + }
    +
    + dev_info(dev, "found PCI SSP controller (ID: %04xh:%04xh cfg: %02xh)\n",
    + pdev->vendor, pdev->device, ssp_cfg);
    +
    + status = pci_enable_device(pdev);
    + if (status)
    + return status;
    +
    + /* Allocate Slave with space for sspc and null dma buffer */
    + master = spi_alloc_master(dev, sizeof(struct ssp_drv_context));
    +
    + if (!master) {
    + dev_err(dev, "cannot alloc spi_slave\n");
    + status = -ENOMEM;
    + goto err_free_0;
    + }
    +
    + sspc = spi_master_get_devdata(master);
    + sspc->master = master;
    +
    + sspc->pdev = pdev;
    + sspc->quirks = ent->driver_data;
    +
    + /* Set platform & configuration quirks */
    + if (sspc->quirks & QUIRKS_PLATFORM_MRST) {
    + /* Apply bit banging workarround on MRST */
    + sspc->quirks |= QUIRKS_BIT_BANGING;
    + /* MRST slave mode workarrounds */
    + if (ssp_cfg_is_spi_slave(ssp_cfg))
    + sspc->quirks |= QUIRKS_USE_PM_QOS |
    + QUIRKS_SRAM_ADDITIONAL_CPY;
    + }
    + sspc->quirks |= QUIRKS_DMA_USE_NO_TRAIL;
    + if (ssp_cfg_is_spi_slave(ssp_cfg))
    + sspc->quirks |= QUIRKS_SPI_SLAVE_CLOCK_MODE;
    +
    + master->mode_bits = SPI_CPOL | SPI_CPHA;
    + master->bus_num = ssp_cfg_get_spi_bus_nb(ssp_cfg);
    + master->num_chipselect = 4;
    + master->cleanup = cleanup;
    + master->setup = setup;
    + master->transfer = transfer;
    + sspc->dma_wq = create_workqueue("spi-intel-mid-ssp");
    + INIT_WORK(&sspc->complete_work, int_transfer_complete_work);
    +
    + sspc->dma_initialized = 0;
    + sspc->suspended = 0;
    + sspc->cur_msg = NULL;
    +
    + /* get basic io resource and map it */
    + sspc->paddr = pci_resource_start(pdev, 0);
    + iolen = pci_resource_len(pdev, 0);
    +
    + status = pci_request_region(pdev, 0, dev_name(&pdev->dev));
    + if (status)
    + goto err_free_1;
    +
    + sspc->ioaddr = ioremap_nocache(sspc->paddr, iolen);
    + if (!sspc->ioaddr) {
    + status = -ENOMEM;
    + goto err_free_2;
    + }
    + dev_dbg(dev, "paddr = : %08lx", sspc->paddr);
    + dev_dbg(dev, "ioaddr = : %p\n", sspc->ioaddr);
    + dev_dbg(dev, "attaching to IRQ: %04x\n", pdev->irq);
    + dev_dbg(dev, "quirks = : %08lx\n", sspc->quirks);
    +
    + if (sspc->quirks & QUIRKS_BIT_BANGING) {
    + /* Bit banging on the clock is done through */
    + /* DFT which is available through I2C. */
    + /* get base address of I2C_Serbus registers */
    + sspc->I2C_paddr = 0xff12b000;
    + sspc->I2C_ioaddr = ioremap_nocache(sspc->I2C_paddr, 0x10);
    + if (!sspc->I2C_ioaddr) {
    + status = -ENOMEM;
    + goto err_free_3;
    + }
    + }
    +
    + /* Attach to IRQ */
    + sspc->irq = pdev->irq;
    + status = request_irq(sspc->irq, ssp_int, IRQF_SHARED,
    + "spi-intel-mid-ssp", sspc);
    +
    + if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER)
    + ssp_timing_wr = 1;
    +
    + if (status < 0) {
    + dev_err(&pdev->dev, "can not get IRQ\n");
    + goto err_free_4;
    + }
    +
    + if (sspc->quirks & QUIRKS_PLATFORM_MDFL) {
    + /* get base address of DMA selector. */
    + syscfg = sspc->paddr - SYSCFG;
    + syscfg_ioaddr = ioremap_nocache(syscfg, 0x10);
    + if (!syscfg_ioaddr) {
    + status = -ENOMEM;
    + goto err_free_5;
    + }
    + iowrite32(ioread32(syscfg_ioaddr) | 2, syscfg_ioaddr);
    + }
    +
    + INIT_LIST_HEAD(&sspc->queue);
    + init_completion(&sspc->msg_done);
    + spin_lock_init(&sspc->lock);
    + tasklet_init(&sspc->poll_transfer, poll_transfer, (unsigned long)sspc);
    + INIT_WORK(&sspc->pump_messages, pump_messages);
    + sspc->workqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
    +
    + /* Register with the SPI framework */
    + dev_info(dev, "register with SPI framework (bus spi%d)\n",
    + master->bus_num);
    +
    + status = spi_register_master(master);
    + if (status) {
    + dev_err(dev, "problem registering spi\n");
    + goto err_free_5;
    + }
    +
    + pci_set_drvdata(pdev, sspc);
    +
    + /* Create the PM_QOS request */
    + if (sspc->quirks & QUIRKS_USE_PM_QOS)
    + pm_qos_add_request(&sspc->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
    + PM_QOS_DEFAULT_VALUE);
    +
    + pm_runtime_put_noidle(&pdev->dev);
    + pm_runtime_allow(&pdev->dev);
    +
    + return status;
    +
    +err_free_5:
    + free_irq(sspc->irq, sspc);
    +err_free_4:
    + iounmap(sspc->I2C_ioaddr);
    +err_free_3:
    + iounmap(sspc->ioaddr);
    +err_free_2:
    + pci_release_region(pdev, 0);
    +err_free_1:
    + spi_master_put(master);
    +err_free_0:
    + pci_disable_device(pdev);
    +
    + return status;
    +err_abort_probe:
    + dev_info(dev, "Abort probe for SSP %04xh:%04xh\n",
    + pdev->vendor, pdev->device);
    + return -ENODEV;
    +}
    +
    +/**
    + * ssp_spi_remove() - driver remove procedure
    + * @pdev: Pointer to the pci_dev struct
    + */
    +static void ssp_spi_remove(struct pci_dev *pdev)
    +{
    + struct ssp_drv_context *sspc = pci_get_drvdata(pdev);
    +
    + if (!sspc)
    + return;
    +
    + pm_runtime_forbid(&pdev->dev);
    + pm_runtime_get_noresume(&pdev->dev);
    + /* Release IRQ */
    + free_irq(sspc->irq, sspc);
    +
    + iounmap(sspc->ioaddr);
    + if (sspc->quirks & QUIRKS_BIT_BANGING)
    + iounmap(sspc->I2C_ioaddr);
    +
    + /* disconnect from the SPI framework */
    + spi_unregister_master(sspc->master);
    +
    + pci_set_drvdata(pdev, NULL);
    + pci_release_region(pdev, 0);
    + pci_disable_device(pdev);
    +
    + return;
    +}
    +
    +static int ssp_spi_suspend(struct device *dev)
    +{
    + struct pci_dev *pdev = to_pci_dev(dev);
    + struct ssp_drv_context *sspc = pci_get_drvdata(pdev);
    + unsigned long flags;
    + int loop = 26;
    +
    + spin_lock_irqsave(&sspc->lock, flags);
    + sspc->suspended = 1;
    + /*
    + * If there is one msg being handled, wait 500ms at most,
    + * if still not done, return busy
    + */
    + while (sspc->cur_msg && --loop) {
    + spin_unlock_irqrestore(&sspc->lock, flags);
    + msleep(20);
    + spin_lock_irqsave(&sspc->lock, flags);
    + if (!loop)
    + sspc->suspended = 0;
    + }
    + spin_unlock_irqrestore(&sspc->lock, flags);
    +
    + if (loop)
    + return 0;
    + else
    + return -EBUSY;
    +}
    +
    +static int ssp_spi_resume(struct device *dev)
    +{
    + struct pci_dev *pdev = to_pci_dev(dev);
    + struct ssp_drv_context *sspc = pci_get_drvdata(pdev);
    +
    + spin_lock(&sspc->lock);
    + sspc->suspended = 0;
    + if (!list_empty(&sspc->queue))
    + queue_work(sspc->workqueue, &sspc->pump_messages);
    + spin_unlock(&sspc->lock);
    + return 0;
    +}
    +
    +static int ssp_spi_runtime_suspend(struct device *dev)
    +{
    + return 0;
    +}
    +
    +static int ssp_spi_runtime_resume(struct device *dev)
    +{
    + return 0;
    +}
    +
    +static int ssp_spi_runtime_idle(struct device *dev)
    +{
    + int err;
    +
    + dev_dbg(dev, "runtime idle called\n");
    + if (system_state == SYSTEM_BOOTING)
    + /* if SSP SPI UART is set as default console and earlyprintk
    + * is enabled, it cannot shutdown SSP controller during booting.
    + */
    + err = pm_schedule_suspend(dev, 30000);
    + else
    + err = pm_schedule_suspend(dev, 500);
    +
    + return err;
    +}
    +
    +static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
    + /* MRST SSP0 */
    + { PCI_VDEVICE(INTEL, 0x0815), QUIRKS_PLATFORM_MRST},
    + /* MDFL SSP0 */
    + { PCI_VDEVICE(INTEL, 0x0832), QUIRKS_PLATFORM_MDFL},
    + /* MDFL SSP1 */
    + { PCI_VDEVICE(INTEL, 0x0825), QUIRKS_PLATFORM_MDFL},
    + /* MDFL SSP3 */
    + { PCI_VDEVICE(INTEL, 0x0816), QUIRKS_PLATFORM_MDFL},
    + /* MRFL SSP5 */
    + { PCI_VDEVICE(INTEL, 0x1194), 0},
    + { 0 },
    +};
    +
    +static const struct dev_pm_ops ssp_spi_pm_ops = {
    + SET_SYSTEM_SLEEP_PM_OPS(ssp_spi_suspend, ssp_spi_resume)
    + SET_RUNTIME_PM_OPS(ssp_spi_runtime_suspend, ssp_spi_runtime_resume,
    + ssp_spi_runtime_idle)
    +};
    +
    +static struct pci_driver ssp_spi_driver = {
    + .name = DRIVER_NAME,
    + .id_table = pci_ids,
    + .probe = ssp_spi_probe,
    + .remove = ssp_spi_remove,
    + .driver = {
    + .pm = &ssp_spi_pm_ops,
    + },
    +};
    +
    +static int __init ssp_spi_init(void)
    +{
    + return pci_register_driver(&ssp_spi_driver);
    +}
    +
    +late_initcall(ssp_spi_init);
    +
    +static void __exit ssp_spi_exit(void)
    +{
    + pci_unregister_driver(&ssp_spi_driver);
    +}
    +
    +module_exit(ssp_spi_exit);
    +
    +MODULE_AUTHOR("Ken Mills");
    +MODULE_DESCRIPTION("Bulverde SSP core SPI contoller");
    +MODULE_LICENSE("GPL");
    diff --git a/include/linux/spi/intel_mid_ssp_spi.h b/include/linux/spi/intel_mid_ssp_spi.h
    new file mode 100644
    index 0000000..a3bb973
    --- /dev/null
    +++ b/include/linux/spi/intel_mid_ssp_spi.h
    @@ -0,0 +1,330 @@
    +/*
    + * Copyright (C) Intel 2013
    + *
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    + *
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + *
    + */
    +#ifndef INTEL_MID_SSP_SPI_H_
    +#define INTEL_MID_SSP_SPI_H_
    +
    +#include <linux/completion.h>
    +#include <linux/intel_mid_dma.h>
    +#include <linux/interrupt.h>
    +#include <linux/pm_qos.h>
    +#include <linux/spi/spi.h>
    +
    +#define PCI_MRST_DMAC1_ID 0x0814
    +#define PCI_MDFL_DMAC1_ID 0x0827
    +
    +#define SSP_NOT_SYNC 0x400000
    +#define MAX_SPI_TRANSFER_SIZE 8192
    +#define MAX_BITBANGING_LOOP 10000
    +#define SPI_FIFO_SIZE 16
    +
    +/* PM QoS define */
    +#define MIN_EXIT_LATENCY 20
    +
    +/* SSP assignement configuration from PCI config */
    +
    +#define SSP_CFG_SPI_MODE_ID 1
    +/* adid field offset is 6 inside the vendor specific capability */
    +#define VNDR_CAPABILITY_ADID_OFFSET 6
    +
    +/* Driver's quirk flags */
    +/* This workarround bufferizes data in the audio fabric SDRAM from */
    +/* where the DMA transfers will operate. Should be enabled only for */
    +/* SPI slave mode. */
    +#define QUIRKS_SRAM_ADDITIONAL_CPY 1
    +/* If set the trailing bytes won't be handled by the DMA. */
    +/* Trailing byte feature not fully available. */
    +#define QUIRKS_DMA_USE_NO_TRAIL 2
    +/* If set, the driver will use PM_QOS to reduce the latency */
    +/* introduced by the deeper C-states which may produce over/under */
    +/* run issues. Must be used in slave mode. In master mode, the */
    +/* latency is not critical, but setting this workarround may */
    +/* improve the SPI throughput. */
    +#define QUIRKS_USE_PM_QOS 4
    +/* This quirks is set on Moorestown */
    +#define QUIRKS_PLATFORM_MRST 8
    +/* This quirks is set on Medfield */
    +#define QUIRKS_PLATFORM_MDFL 16
    +/* If set, the driver will apply the bitbanging workarround needed */
    +/* to enable defective Langwell stepping A SSP. The defective SSP */
    +/* can be enabled only once, and should never be disabled. */
    +#define QUIRKS_BIT_BANGING 32
    +/* If set, SPI is in slave clock mode */
    +#define QUIRKS_SPI_SLAVE_CLOCK_MODE 64
    +
    +/* Uncomment to get RX and TX short dumps after each transfer */
    +/* #define DUMP_RX 1 */
    +#define MAX_TRAILING_BYTE_RETRY 16
    +#define MAX_TRAILING_BYTE_LOOP 100
    +#define DELAY_TO_GET_A_WORD 3
    +#define DFLT_TIMEOUT_VAL 500
    +
    +#define DEFINE_SSP_REG(reg, off) \
    +static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \
    +static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }
    +
    +#define RX_DIRECTION 0
    +#define TX_DIRECTION 1
    +
    +#define I2C_ACCESS_USDELAY 10
    +
    +#define DFLT_BITS_PER_WORD 16
    +#define MIN_BITS_PER_WORD 4
    +#define MAX_BITS_PER_WORD 32
    +#define DFLT_FIFO_BURST_SIZE IMSS_FIFO_BURST_8
    +
    +#define TRUNCATE(x, a) ((x) & ~((a)-1))
    +
    +DEFINE_SSP_REG(SSCR0, 0x00)
    +DEFINE_SSP_REG(SSCR1, 0x04)
    +DEFINE_SSP_REG(SSSR, 0x08)
    +DEFINE_SSP_REG(SSITR, 0x0c)
    +DEFINE_SSP_REG(SSDR, 0x10)
    +DEFINE_SSP_REG(SSTO, 0x28)
    +DEFINE_SSP_REG(SSPSP, 0x2c)
    +DEFINE_SSP_REG(SSFS, 0x44)
    +DEFINE_SSP_REG(SFIFOL, 0x68)
    +
    +DEFINE_SSP_REG(I2CCTRL, 0x00);
    +DEFINE_SSP_REG(I2CDATA, 0x04);
    +
    +DEFINE_SSP_REG(GPLR1, 0x04);
    +DEFINE_SSP_REG(GPDR1, 0x0c);
    +DEFINE_SSP_REG(GPSR1, 0x14);
    +DEFINE_SSP_REG(GPCR1, 0x1c);
    +DEFINE_SSP_REG(GAFR1_U, 0x44);
    +
    +#define SYSCFG 0x20bc0
    +
    +#define SRAM_BASE_ADDR 0xfffdc000
    +#define SRAM_RX_ADDR SRAM_BASE_ADDR
    +#define SRAM_TX_ADDR (SRAM_BASE_ADDR + MAX_SPI_TRANSFER_SIZE)
    +
    +#define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */
    +#define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */
    +#define SSCR0_FRF (0x00000030) /* FRame Format (mask) */
    +#define SSCR0_Motorola (0x0 << 4) /* Motorola's SPI mode */
    +#define SSCR0_ECS (1 << 6) /* External clock select */
    +#define SSCR0_SSE (1 << 7) /* Synchronous Serial Port Enable */
    +
    +#define SSCR0_SCR (0x000fff00) /* Serial Clock Rate (mask) */
    +#define SSCR0_SerClkDiv(x) (((x) - 1) << 8) /* Divisor [1..4096] */
    +#define SSCR0_EDSS (1 << 20) /* Extended data size select */
    +#define SSCR0_NCS (1 << 21) /* Network clock select */
    +#define SSCR0_RIM (1 << 22) /* Receive FIFO overrrun int mask */
    +#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun int mask */
    +#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */
    +#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame */
    +#define SSCR0_ADC (1 << 30) /* Audio clock select */
    +#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */
    +
    +#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */
    +#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */
    +#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */
    +#define SSCR1_SPO (1 << 3) /* SSPSCLK polarity setting */
    +#define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */
    +#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */
    +#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */
    +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */
    +#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
    +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */
    +
    +#define SSSR_TNF (1 << 2) /* Tx FIFO Not Full */
    +#define SSSR_RNE (1 << 3) /* Rx FIFO Not Empty */
    +#define SSSR_BSY (1 << 4) /* SSP Busy */
    +#define SSSR_TFS (1 << 5) /* Tx FIFO Service Request */
    +#define SSSR_RFS (1 << 6) /* Rx FIFO Service Request */
    +#define SSSR_ROR (1 << 7) /* Rx FIFO Overrun */
    +#define SSSR_TFL_MASK (0x0f << 8) /* Tx FIFO level field mask */
    +
    +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Int Mask */
    +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run int Mask */
    +#define SSCR0_NCS (1 << 21) /* Network Clock Select */
    +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */
    +
    +#define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */
    +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */
    +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */
    +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */
    +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */
    +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */
    +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */
    +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */
    +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */
    +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */
    +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */
    +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */
    +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */
    +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */
    +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */
    +#define SSCR1_PINTE (1 << 18) /* Trailing Byte Interupt Enable */
    +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */
    +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */
    +#define SSCR1_IFS (1 << 16) /* Invert Frame Signal */
    +
    +#define SSSR_BCE (1 << 23) /* Bit Count Error */
    +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */
    +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */
    +#define SSSR_EOC (1 << 20) /* End Of Chain */
    +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */
    +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */
    +
    +#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */
    +#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */
    +#define SSPSP_SFRMWDTH(x)((x) << 16) /* Serial Frame Width */
    +#define SSPSP_SFRMDLY(x) ((x) << 9) /* Serial Frame Delay */
    +#define SSPSP_DMYSTRT(x) ((x) << 7) /* Dummy Start */
    +#define SSPSP_STRTDLY(x) ((x) << 4) /* Start Delay */
    +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */
    +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */
    +#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */
    +
    +/*
    + * For testing SSCR1 changes that require SSP restart, basically
    + * everything except the service and interrupt enables
    + */
    +
    +#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_SCFR \
    + | SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \
    + | SSCR1_SFRMDIR | SSCR1_RWOT | SSCR1_TRAIL \
    + | SSCR1_IFS | SSCR1_STRF | SSCR1_EFWR \
    + | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \
    + | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
    +
    +struct callback_param {
    + void *drv_context;
    + u32 direction;
    +};
    +
    +struct ssp_drv_context {
    + /* Driver model hookup */
    + struct pci_dev *pdev;
    +
    + /* SPI framework hookup */
    + struct spi_master *master;
    +
    + /* SSP register addresses */
    + unsigned long paddr;
    + void *ioaddr;
    + int irq;
    +
    + /* I2C registers */
    + dma_addr_t I2C_paddr;
    + void *I2C_ioaddr;
    +
    + /* SSP masks*/
    + u32 cr1_sig;
    + u32 cr1;
    + u32 clear_sr;
    + u32 mask_sr;
    +
    + /* PM_QOS request */
    + struct pm_qos_request pm_qos_req;
    +
    + struct tasklet_struct poll_transfer;
    +
    + spinlock_t lock;
    + struct workqueue_struct *workqueue;
    + struct work_struct pump_messages;
    + struct list_head queue;
    + struct completion msg_done;
    +
    + int suspended;
    +
    + /* Current message transfer state info */
    + struct spi_message *cur_msg;
    + size_t len;
    + size_t len_dma_rx;
    + size_t len_dma_tx;
    + void *tx;
    + void *tx_end;
    + void *rx;
    + void *rx_end;
    + bool dma_initialized;
    + int dma_mapped;
    + dma_addr_t rx_dma;
    + dma_addr_t tx_dma;
    + u8 n_bytes;
    + int (*write)(struct ssp_drv_context *sspc);
    + int (*read)(struct ssp_drv_context *sspc);
    +
    + struct intel_mid_dma_slave dmas_tx;
    + struct intel_mid_dma_slave dmas_rx;
    + struct dma_chan *txchan;
    + struct dma_chan *rxchan;
    + struct workqueue_struct *dma_wq;
    + struct work_struct complete_work;
    +
    + u8 __iomem *virt_addr_sram_tx;
    + u8 __iomem *virt_addr_sram_rx;
    +
    + int txdma_done;
    + int rxdma_done;
    + struct callback_param tx_param;
    + struct callback_param rx_param;
    + struct pci_dev *dmac1;
    +
    + unsigned long quirks;
    + u32 rx_fifo_threshold;
    +};
    +
    +struct chip_data {
    + u32 cr0;
    + u32 cr1;
    + u32 timeout;
    + u8 chip_select;
    + u8 n_bytes;
    + u8 dma_enabled;
    + u8 bits_per_word;
    + u32 speed_hz;
    + int (*write)(struct ssp_drv_context *sspc);
    + int (*read)(struct ssp_drv_context *sspc);
    +};
    +
    +enum intel_mid_ssp_spi_fifo_burst {
    + IMSS_FIFO_BURST_1,
    + IMSS_FIFO_BURST_4,
    + IMSS_FIFO_BURST_8
    +};
    +
    +/* spi_board_info.controller_data for SPI slave devices,
    + * copied to spi_device.platform_data ... mostly for dma tuning
    + */
    +struct intel_mid_ssp_spi_chip {
    + enum intel_mid_ssp_spi_fifo_burst burst_size;
    + u32 timeout;
    + u8 enable_loopback;
    + u8 dma_enabled;
    +};
    +
    +#define SPI_DIB_NAME_LEN 16
    +#define SPI_DIB_SPEC_INFO_LEN 10
    +
    +struct spi_dib_header {
    + u32 signature;
    + u32 length;
    + u8 rev;
    + u8 checksum;
    + u8 dib[0];
    +} __packed;
    +
    +#endif /*INTEL_MID_SSP_SPI_H_*/
    --
    1.8.4.rc3


    \
     
     \ /
      Last update: 2013-10-29 19:41    [W:5.238 / U:0.432 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site