Logo Search packages:      
Sourcecode: linux version File versions  Download package

spi_imx.c

/*
 * drivers/spi/spi_imx.c
 *
 * Copyright (C) 2006 SWAPP
 *    Andrea Paterniani <a.paterniani@swapp-eng.it>
 *
 * Initial version inspired by:
 *    linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/spi/spi.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/delay.h>

#include <asm/arch/hardware.h>
#include <asm/arch/imx-dma.h>
#include <asm/arch/spi_imx.h>

/*-------------------------------------------------------------------------*/
/* SPI Registers offsets from peripheral base address */
#define SPI_RXDATA            (0x00)
#define SPI_TXDATA            (0x04)
#define SPI_CONTROL           (0x08)
#define SPI_INT_STATUS        (0x0C)
#define SPI_TEST        (0x10)
#define SPI_PERIOD            (0x14)
#define SPI_DMA               (0x18)
#define SPI_RESET       (0x1C)

/* SPI Control Register Bit Fields & Masks */
#define SPI_CONTROL_BITCOUNT_MASK   (0xF)       /* Bit Count Mask */
#define SPI_CONTROL_BITCOUNT(n)           (((n) - 1) & SPI_CONTROL_BITCOUNT_MASK)
#define SPI_CONTROL_POL             (0x1 << 4)      /* Clock Polarity Mask */
#define SPI_CONTROL_POL_ACT_HIGH    (0x0 << 4)      /* Active high pol. (0=idle) */
#define SPI_CONTROL_POL_ACT_LOW           (0x1 << 4)      /* Active low pol. (1=idle) */
#define SPI_CONTROL_PHA             (0x1 << 5)      /* Clock Phase Mask */
#define SPI_CONTROL_PHA_0           (0x0 << 5)      /* Clock Phase 0 */
#define SPI_CONTROL_PHA_1           (0x1 << 5)      /* Clock Phase 1 */
#define SPI_CONTROL_SSCTL           (0x1 << 6)      /* /SS Waveform Select Mask */
#define SPI_CONTROL_SSCTL_0         (0x0 << 6)      /* Master: /SS stays low between SPI burst
                                             Slave: RXFIFO advanced by BIT_COUNT */
#define SPI_CONTROL_SSCTL_1         (0x1 << 6)      /* Master: /SS insert pulse between SPI burst
                                             Slave: RXFIFO advanced by /SS rising edge */
#define SPI_CONTROL_SSPOL           (0x1 << 7)      /* /SS Polarity Select Mask */
#define SPI_CONTROL_SSPOL_ACT_LOW   (0x0 << 7)      /* /SS Active low */
#define SPI_CONTROL_SSPOL_ACT_HIGH  (0x1 << 7)      /* /SS Active high */
#define SPI_CONTROL_XCH             (0x1 << 8)      /* Exchange */
#define SPI_CONTROL_SPIEN           (0x1 << 9)      /* SPI Module Enable */
#define SPI_CONTROL_MODE            (0x1 << 10)     /* SPI Mode Select Mask */
#define SPI_CONTROL_MODE_SLAVE            (0x0 << 10)     /* SPI Mode Slave */
#define SPI_CONTROL_MODE_MASTER           (0x1 << 10)     /* SPI Mode Master */
#define SPI_CONTROL_DRCTL           (0x3 << 11)     /* /SPI_RDY Control Mask */
#define SPI_CONTROL_DRCTL_0         (0x0 << 11)     /* Ignore /SPI_RDY */
#define SPI_CONTROL_DRCTL_1         (0x1 << 11)     /* /SPI_RDY falling edge triggers input */
#define SPI_CONTROL_DRCTL_2         (0x2 << 11)     /* /SPI_RDY active low level triggers input */
#define SPI_CONTROL_DATARATE        (0x7 << 13)     /* Data Rate Mask */
#define SPI_PERCLK2_DIV_MIN         (0)         /* PERCLK2:4 */
#define SPI_PERCLK2_DIV_MAX         (7)         /* PERCLK2:512 */
#define SPI_CONTROL_DATARATE_MIN    (SPI_PERCLK2_DIV_MAX << 13)
#define SPI_CONTROL_DATARATE_MAX    (SPI_PERCLK2_DIV_MIN << 13)
#define SPI_CONTROL_DATARATE_BAD    (SPI_CONTROL_DATARATE_MIN + 1)

/* SPI Interrupt/Status Register Bit Fields & Masks */
#define SPI_STATUS_TE   (0x1 << 0)  /* TXFIFO Empty Status */
#define SPI_STATUS_TH   (0x1 << 1)      /* TXFIFO Half Status */
#define SPI_STATUS_TF   (0x1 << 2)      /* TXFIFO Full Status */
#define SPI_STATUS_RR   (0x1 << 3)      /* RXFIFO Data Ready Status */
#define SPI_STATUS_RH   (0x1 << 4)      /* RXFIFO Half Status */
#define SPI_STATUS_RF   (0x1 << 5)      /* RXFIFO Full Status */
#define SPI_STATUS_RO   (0x1 << 6)      /* RXFIFO Overflow */
#define SPI_STATUS_BO   (0x1 << 7)      /* Bit Count Overflow */
#define SPI_STATUS      (0xFF)            /* SPI Status Mask */
#define SPI_INTEN_TE    (0x1 << 8)      /* TXFIFO Empty Interrupt Enable */
#define SPI_INTEN_TH    (0x1 << 9)      /* TXFIFO Half Interrupt Enable */
#define SPI_INTEN_TF    (0x1 << 10)     /* TXFIFO Full Interrupt Enable */
#define SPI_INTEN_RE    (0x1 << 11)     /* RXFIFO Data Ready Interrupt Enable */
#define SPI_INTEN_RH    (0x1 << 12)     /* RXFIFO Half Interrupt Enable */
#define SPI_INTEN_RF    (0x1 << 13)     /* RXFIFO Full Interrupt Enable */
#define SPI_INTEN_RO    (0x1 << 14)     /* RXFIFO Overflow Interrupt Enable */
#define SPI_INTEN_BO    (0x1 << 15)     /* Bit Count Overflow Interrupt Enable */
#define SPI_INTEN (0xFF << 8) /* SPI Interrupt Enable Mask */

/* SPI Test Register Bit Fields & Masks */
#define SPI_TEST_TXCNT        (0xF << 0)  /* TXFIFO Counter */
#define SPI_TEST_RXCNT_LSB    (4)         /* RXFIFO Counter LSB */
#define SPI_TEST_RXCNT        (0xF << 4)  /* RXFIFO Counter */
#define SPI_TEST_SSTATUS      (0xF << 8)  /* State Machine Status */
#define SPI_TEST_LBC          (0x1 << 14) /* Loop Back Control */

/* SPI Period Register Bit Fields & Masks */
#define SPI_PERIOD_WAIT       (0x7FFF << 0)     /* Wait Between Transactions */
#define SPI_PERIOD_MAX_WAIT   (0x7FFF)    /* Max Wait Between
                                          Transactions */
#define SPI_PERIOD_CSRC       (0x1 << 15) /* Period Clock Source Mask */
#define SPI_PERIOD_CSRC_BCLK  (0x0 << 15) /* Period Clock Source is
                                          Bit Clock */
#define SPI_PERIOD_CSRC_32768 (0x1 << 15) /* Period Clock Source is
                                          32.768 KHz Clock */

/* SPI DMA Register Bit Fields & Masks */
#define SPI_DMA_RHDMA   (0x1 << 4)  /* RXFIFO Half Status */
#define SPI_DMA_RFDMA   (0x1 << 5)      /* RXFIFO Full Status */
#define SPI_DMA_TEDMA   (0x1 << 6)      /* TXFIFO Empty Status */
#define SPI_DMA_THDMA   (0x1 << 7)      /* TXFIFO Half Status */
#define SPI_DMA_RHDEN   (0x1 << 12) /* RXFIFO Half DMA Request Enable */
#define SPI_DMA_RFDEN   (0x1 << 13)     /* RXFIFO Full DMA Request Enable */
#define SPI_DMA_TEDEN   (0x1 << 14)     /* TXFIFO Empty DMA Request Enable */
#define SPI_DMA_THDEN   (0x1 << 15)     /* TXFIFO Half DMA Request Enable */

/* SPI Soft Reset Register Bit Fields & Masks */
#define SPI_RESET_START (0x1)       /* Start */

/* Default SPI configuration values */
#define SPI_DEFAULT_CONTROL         \
(                             \
      SPI_CONTROL_BITCOUNT(16) |    \
      SPI_CONTROL_POL_ACT_HIGH |    \
      SPI_CONTROL_PHA_0 |           \
      SPI_CONTROL_SPIEN |           \
      SPI_CONTROL_SSCTL_1 |         \
      SPI_CONTROL_MODE_MASTER |     \
      SPI_CONTROL_DRCTL_0 |         \
      SPI_CONTROL_DATARATE_MIN      \
)
#define SPI_DEFAULT_ENABLE_LOOPBACK (0)
#define SPI_DEFAULT_ENABLE_DMA            (0)
#define SPI_DEFAULT_PERIOD_WAIT           (8)
/*-------------------------------------------------------------------------*/


/*-------------------------------------------------------------------------*/
/* TX/RX SPI FIFO size */
#define SPI_FIFO_DEPTH              (8)
#define SPI_FIFO_BYTE_WIDTH         (2)
#define SPI_FIFO_OVERFLOW_MARGIN    (2)

/* DMA burst lenght for half full/empty request trigger */
#define SPI_DMA_BLR                 (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2)

/* Dummy char output to achieve reads.
   Choosing something different from all zeroes may help pattern recogition
   for oscilloscope analysis, but may break some drivers. */
#define SPI_DUMMY_u8                0
#define SPI_DUMMY_u16               ((SPI_DUMMY_u8 << 8) | SPI_DUMMY_u8)
#define SPI_DUMMY_u32               ((SPI_DUMMY_u16 << 16) | SPI_DUMMY_u16)

/**
 * Macro to change a u32 field:
 * @r : register to edit
 * @m : bit mask
 * @v : new value for the field correctly bit-alligned
*/
#define u32_EDIT(r, m, v)           r = (r & ~(m)) | (v)

/* Message state */
#define START_STATE                 ((void*)0)
#define RUNNING_STATE               ((void*)1)
#define DONE_STATE                  ((void*)2)
#define ERROR_STATE                 ((void*)-1)

/* Queue state */
#define QUEUE_RUNNING               (0)
#define QUEUE_STOPPED               (1)

#define IS_DMA_ALIGNED(x)           (((u32)(x) & 0x03) == 0)
/*-------------------------------------------------------------------------*/


/*-------------------------------------------------------------------------*/
/* Driver data structs */

/* Context */
struct driver_data {
      /* Driver model hookup */
      struct platform_device *pdev;

      /* SPI framework hookup */
      struct spi_master *master;

      /* IMX hookup */
      struct spi_imx_master *master_info;

      /* Memory resources and SPI regs virtual address */
      struct resource *ioarea;
      void __iomem *regs;

      /* SPI RX_DATA physical address */
      dma_addr_t rd_data_phys;

      /* Driver message queue */
      struct workqueue_struct *workqueue;
      struct work_struct work;
      spinlock_t lock;
      struct list_head queue;
      int busy;
      int run;

      /* Message Transfer pump */
      struct tasklet_struct pump_transfers;

      /* Current message, transfer and state */
      struct spi_message *cur_msg;
      struct spi_transfer *cur_transfer;
      struct chip_data *cur_chip;

      /* Rd / Wr buffers pointers */
      size_t len;
      void *tx;
      void *tx_end;
      void *rx;
      void *rx_end;

      u8 rd_only;
      u8 n_bytes;
      int cs_change;

      /* Function pointers */
      irqreturn_t (*transfer_handler)(struct driver_data *drv_data);
      void (*cs_control)(u32 command);

      /* DMA setup */
      int rx_channel;
      int tx_channel;
      dma_addr_t rx_dma;
      dma_addr_t tx_dma;
      int rx_dma_needs_unmap;
      int tx_dma_needs_unmap;
      size_t tx_map_len;
      u32 dummy_dma_buf ____cacheline_aligned;
};

/* Runtime state */
struct chip_data {
      u32 control;
      u32 period;
      u32 test;

      u8 enable_dma:1;
      u8 bits_per_word;
      u8 n_bytes;
      u32 max_speed_hz;

      void (*cs_control)(u32 command);
};
/*-------------------------------------------------------------------------*/


static void pump_messages(struct work_struct *work);

static int flush(struct driver_data *drv_data)
{
      unsigned long limit = loops_per_jiffy << 1;
      void __iomem *regs = drv_data->regs;
      volatile u32 d;

      dev_dbg(&drv_data->pdev->dev, "flush\n");
      do {
            while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR)
                  d = readl(regs + SPI_RXDATA);
      } while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--);

      return limit;
}

static void restore_state(struct driver_data *drv_data)
{
      void __iomem *regs = drv_data->regs;
      struct chip_data *chip = drv_data->cur_chip;

      /* Load chip registers */
      dev_dbg(&drv_data->pdev->dev,
            "restore_state\n"
            "    test    = 0x%08X\n"
            "    control = 0x%08X\n",
            chip->test,
            chip->control);
      writel(chip->test, regs + SPI_TEST);
      writel(chip->period, regs + SPI_PERIOD);
      writel(0, regs + SPI_INT_STATUS);
      writel(chip->control, regs + SPI_CONTROL);
}

static void null_cs_control(u32 command)
{
}

static inline u32 data_to_write(struct driver_data *drv_data)
{
      return ((u32)(drv_data->tx_end - drv_data->tx)) / drv_data->n_bytes;
}

static inline u32 data_to_read(struct driver_data *drv_data)
{
      return ((u32)(drv_data->rx_end - drv_data->rx)) / drv_data->n_bytes;
}

static int write(struct driver_data *drv_data)
{
      void __iomem *regs = drv_data->regs;
      void *tx = drv_data->tx;
      void *tx_end = drv_data->tx_end;
      u8 n_bytes = drv_data->n_bytes;
      u32 remaining_writes;
      u32 fifo_avail_space;
      u32 n;
      u16 d;

      /* Compute how many fifo writes to do */
      remaining_writes = (u32)(tx_end - tx) / n_bytes;
      fifo_avail_space = SPI_FIFO_DEPTH -
                        (readl(regs + SPI_TEST) & SPI_TEST_TXCNT);
      if (drv_data->rx && (fifo_avail_space > SPI_FIFO_OVERFLOW_MARGIN))
            /* Fix misunderstood receive overflow */
            fifo_avail_space -= SPI_FIFO_OVERFLOW_MARGIN;
      n = min(remaining_writes, fifo_avail_space);

      dev_dbg(&drv_data->pdev->dev,
            "write type %s\n"
            "    remaining writes = %d\n"
            "    fifo avail space = %d\n"
            "    fifo writes      = %d\n",
            (n_bytes == 1) ? "u8" : "u16",
            remaining_writes,
            fifo_avail_space,
            n);

      if (n > 0) {
            /* Fill SPI TXFIFO */
            if (drv_data->rd_only) {
                  tx += n * n_bytes;
                  while (n--)
                        writel(SPI_DUMMY_u16, regs + SPI_TXDATA);
            } else {
                  if (n_bytes == 1) {
                        while (n--) {
                              d = *(u8*)tx;
                              writel(d, regs + SPI_TXDATA);
                              tx += 1;
                        }
                  } else {
                        while (n--) {
                              d = *(u16*)tx;
                              writel(d, regs + SPI_TXDATA);
                              tx += 2;
                        }
                  }
            }

            /* Trigger transfer */
            writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH,
                  regs + SPI_CONTROL);

            /* Update tx pointer */
            drv_data->tx = tx;
      }

      return (tx >= tx_end);
}

static int read(struct driver_data *drv_data)
{
      void __iomem *regs = drv_data->regs;
      void *rx = drv_data->rx;
      void *rx_end = drv_data->rx_end;
      u8 n_bytes = drv_data->n_bytes;
      u32 remaining_reads;
      u32 fifo_rxcnt;
      u32 n;
      u16 d;

      /* Compute how many fifo reads to do */
      remaining_reads = (u32)(rx_end - rx) / n_bytes;
      fifo_rxcnt = (readl(regs + SPI_TEST) & SPI_TEST_RXCNT) >>
                  SPI_TEST_RXCNT_LSB;
      n = min(remaining_reads, fifo_rxcnt);

      dev_dbg(&drv_data->pdev->dev,
            "read type %s\n"
            "    remaining reads = %d\n"
            "    fifo rx count   = %d\n"
            "    fifo reads      = %d\n",
            (n_bytes == 1) ? "u8" : "u16",
            remaining_reads,
            fifo_rxcnt,
            n);

      if (n > 0) {
            /* Read SPI RXFIFO */
            if (n_bytes == 1) {
                  while (n--) {
                        d = readl(regs + SPI_RXDATA);
                        *((u8*)rx) = d;
                        rx += 1;
                  }
            } else {
                  while (n--) {
                        d = readl(regs + SPI_RXDATA);
                        *((u16*)rx) = d;
                        rx += 2;
                  }
            }

            /* Update rx pointer */
            drv_data->rx = rx;
      }

      return (rx >= rx_end);
}

static void *next_transfer(struct driver_data *drv_data)
{
      struct spi_message *msg = drv_data->cur_msg;
      struct spi_transfer *trans = drv_data->cur_transfer;

      /* Move to next transfer */
      if (trans->transfer_list.next != &msg->transfers) {
            drv_data->cur_transfer =
                  list_entry(trans->transfer_list.next,
                              struct spi_transfer,
                              transfer_list);
            return RUNNING_STATE;
      }

      return DONE_STATE;
}

static int map_dma_buffers(struct driver_data *drv_data)
{
      struct spi_message *msg;
      struct device *dev;
      void *buf;

      drv_data->rx_dma_needs_unmap = 0;
      drv_data->tx_dma_needs_unmap = 0;

      if (!drv_data->master_info->enable_dma ||
            !drv_data->cur_chip->enable_dma)
                  return -1;

      msg = drv_data->cur_msg;
      dev = &msg->spi->dev;
      if (msg->is_dma_mapped) {
            if (drv_data->tx_dma)
                  /* The caller provided at least dma and cpu virtual
                     address for write; pump_transfers() will consider the
                     transfer as write only if cpu rx virtual address is
                     NULL */
                  return 0;

            if (drv_data->rx_dma) {
                  /* The caller provided dma and cpu virtual address to
                     performe read only transfer -->
                     use drv_data->dummy_dma_buf for dummy writes to
                     achive reads */
                  buf = &drv_data->dummy_dma_buf;
                  drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf);
                  drv_data->tx_dma = dma_map_single(dev,
                                          buf,
                                          drv_data->tx_map_len,
                                          DMA_TO_DEVICE);
                  if (dma_mapping_error(drv_data->tx_dma))
                        return -1;

                  drv_data->tx_dma_needs_unmap = 1;

                  /* Flags transfer as rd_only for pump_transfers() DMA
                     regs programming (should be redundant) */
                  drv_data->tx = NULL;

                  return 0;
            }
      }

      if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))
            return -1;

      /* NULL rx means write-only transfer and no map needed
         since rx DMA will not be used */
      if (drv_data->rx) {
            buf = drv_data->rx;
            drv_data->rx_dma = dma_map_single(
                              dev,
                              buf,
                              drv_data->len,
                              DMA_FROM_DEVICE);
            if (dma_mapping_error(drv_data->rx_dma))
                  return -1;
            drv_data->rx_dma_needs_unmap = 1;
      }

      if (drv_data->tx == NULL) {
            /* Read only message --> use drv_data->dummy_dma_buf for dummy
               writes to achive reads */
            buf = &drv_data->dummy_dma_buf;
            drv_data->tx_map_len = sizeof(drv_data->dummy_dma_buf);
      } else {
            buf = drv_data->tx;
            drv_data->tx_map_len = drv_data->len;
      }
      drv_data->tx_dma = dma_map_single(dev,
                              buf,
                              drv_data->tx_map_len,
                              DMA_TO_DEVICE);
      if (dma_mapping_error(drv_data->tx_dma)) {
            if (drv_data->rx_dma) {
                  dma_unmap_single(dev,
                              drv_data->rx_dma,
                              drv_data->len,
                              DMA_FROM_DEVICE);
                  drv_data->rx_dma_needs_unmap = 0;
            }
            return -1;
      }
      drv_data->tx_dma_needs_unmap = 1;

      return 0;
}

static void unmap_dma_buffers(struct driver_data *drv_data)
{
      struct spi_message *msg = drv_data->cur_msg;
      struct device *dev = &msg->spi->dev;

      if (drv_data->rx_dma_needs_unmap) {
            dma_unmap_single(dev,
                        drv_data->rx_dma,
                        drv_data->len,
                        DMA_FROM_DEVICE);
            drv_data->rx_dma_needs_unmap = 0;
      }
      if (drv_data->tx_dma_needs_unmap) {
            dma_unmap_single(dev,
                        drv_data->tx_dma,
                        drv_data->tx_map_len,
                        DMA_TO_DEVICE);
            drv_data->tx_dma_needs_unmap = 0;
      }
}

/* Caller already set message->status (dma is already blocked) */
static void giveback(struct spi_message *message, struct driver_data *drv_data)
{
      void __iomem *regs = drv_data->regs;

      /* Bring SPI to sleep; restore_state() and pump_transfer()
         will do new setup */
      writel(0, regs + SPI_INT_STATUS);
      writel(0, regs + SPI_DMA);

      drv_data->cs_control(SPI_CS_DEASSERT);

      message->state = NULL;
      if (message->complete)
            message->complete(message->context);

      drv_data->cur_msg = NULL;
      drv_data->cur_transfer = NULL;
      drv_data->cur_chip = NULL;
      queue_work(drv_data->workqueue, &drv_data->work);
}

static void dma_err_handler(int channel, void *data, int errcode)
{
      struct driver_data *drv_data = data;
      struct spi_message *msg = drv_data->cur_msg;

      dev_dbg(&drv_data->pdev->dev, "dma_err_handler\n");

      /* Disable both rx and tx dma channels */
      imx_dma_disable(drv_data->rx_channel);
      imx_dma_disable(drv_data->tx_channel);

      if (flush(drv_data) == 0)
            dev_err(&drv_data->pdev->dev,
                        "dma_err_handler - flush failed\n");

      unmap_dma_buffers(drv_data);

      msg->state = ERROR_STATE;
      tasklet_schedule(&drv_data->pump_transfers);
}

static void dma_tx_handler(int channel, void *data)
{
      struct driver_data *drv_data = data;

      dev_dbg(&drv_data->pdev->dev, "dma_tx_handler\n");

      imx_dma_disable(channel);

      /* Now waits for TX FIFO empty */
      writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE,
                  drv_data->regs + SPI_INT_STATUS);
}

static irqreturn_t dma_transfer(struct driver_data *drv_data)
{
      u32 status;
      struct spi_message *msg = drv_data->cur_msg;
      void __iomem *regs = drv_data->regs;
      unsigned long limit;

      status = readl(regs + SPI_INT_STATUS);

      if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) {
            writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);

            imx_dma_disable(drv_data->rx_channel);
            unmap_dma_buffers(drv_data);

            if (flush(drv_data) == 0)
                  dev_err(&drv_data->pdev->dev,
                        "dma_transfer - flush failed\n");

            dev_warn(&drv_data->pdev->dev,
                        "dma_transfer - fifo overun\n");

            msg->state = ERROR_STATE;
            tasklet_schedule(&drv_data->pump_transfers);

            return IRQ_HANDLED;
      }

      if (status & SPI_STATUS_TE) {
            writel(status & ~SPI_INTEN_TE, regs + SPI_INT_STATUS);

            if (drv_data->rx) {
                  /* Wait end of transfer before read trailing data */
                  limit = loops_per_jiffy << 1;
                  while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) &&
                              limit--);

                  if (limit == 0)
                        dev_err(&drv_data->pdev->dev,
                              "dma_transfer - end of tx failed\n");
                  else
                        dev_dbg(&drv_data->pdev->dev,
                              "dma_transfer - end of tx\n");

                  imx_dma_disable(drv_data->rx_channel);
                  unmap_dma_buffers(drv_data);

                  /* Calculate number of trailing data and read them */
                  dev_dbg(&drv_data->pdev->dev,
                        "dma_transfer - test = 0x%08X\n",
                        readl(regs + SPI_TEST));
                  drv_data->rx = drv_data->rx_end -
                              ((readl(regs + SPI_TEST) &
                              SPI_TEST_RXCNT) >>
                              SPI_TEST_RXCNT_LSB)*drv_data->n_bytes;
                  read(drv_data);
            } else {
                  /* Write only transfer */
                  unmap_dma_buffers(drv_data);

                  if (flush(drv_data) == 0)
                        dev_err(&drv_data->pdev->dev,
                              "dma_transfer - flush failed\n");
            }

            /* End of transfer, update total byte transfered */
            msg->actual_length += drv_data->len;

            /* Release chip select if requested, transfer delays are
               handled in pump_transfers() */
            if (drv_data->cs_change)
                  drv_data->cs_control(SPI_CS_DEASSERT);

            /* Move to next transfer */
            msg->state = next_transfer(drv_data);

            /* Schedule transfer tasklet */
            tasklet_schedule(&drv_data->pump_transfers);

            return IRQ_HANDLED;
      }

      /* Opps problem detected */
      return IRQ_NONE;
}

static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data)
{
      struct spi_message *msg = drv_data->cur_msg;
      void __iomem *regs = drv_data->regs;
      u32 status;
      irqreturn_t handled = IRQ_NONE;

      status = readl(regs + SPI_INT_STATUS);

      while (status & SPI_STATUS_TH) {
            dev_dbg(&drv_data->pdev->dev,
                  "interrupt_wronly_transfer - status = 0x%08X\n", status);

            /* Pump data */
            if (write(drv_data)) {
                  writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
                        regs + SPI_INT_STATUS);

                  dev_dbg(&drv_data->pdev->dev,
                        "interrupt_wronly_transfer - end of tx\n");

                  if (flush(drv_data) == 0)
                        dev_err(&drv_data->pdev->dev,
                              "interrupt_wronly_transfer - "
                              "flush failed\n");

                  /* End of transfer, update total byte transfered */
                  msg->actual_length += drv_data->len;

                  /* Release chip select if requested, transfer delays are
                     handled in pump_transfers */
                  if (drv_data->cs_change)
                        drv_data->cs_control(SPI_CS_DEASSERT);

                  /* Move to next transfer */
                  msg->state = next_transfer(drv_data);

                  /* Schedule transfer tasklet */
                  tasklet_schedule(&drv_data->pump_transfers);

                  return IRQ_HANDLED;
            }

            status = readl(regs + SPI_INT_STATUS);

            /* We did something */
            handled = IRQ_HANDLED;
      }

      return handled;
}

static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
{
      struct spi_message *msg = drv_data->cur_msg;
      void __iomem *regs = drv_data->regs;
      u32 status;
      irqreturn_t handled = IRQ_NONE;
      unsigned long limit;

      status = readl(regs + SPI_INT_STATUS);

      while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
            dev_dbg(&drv_data->pdev->dev,
                  "interrupt_transfer - status = 0x%08X\n", status);

            if (status & SPI_STATUS_RO) {
                  writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
                        regs + SPI_INT_STATUS);

                  dev_warn(&drv_data->pdev->dev,
                        "interrupt_transfer - fifo overun\n"
                        "    data not yet written = %d\n"
                        "    data not yet read    = %d\n",
                        data_to_write(drv_data),
                        data_to_read(drv_data));

                  if (flush(drv_data) == 0)
                        dev_err(&drv_data->pdev->dev,
                              "interrupt_transfer - flush failed\n");

                  msg->state = ERROR_STATE;
                  tasklet_schedule(&drv_data->pump_transfers);

                  return IRQ_HANDLED;
            }

            /* Pump data */
            read(drv_data);
            if (write(drv_data)) {
                  writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
                        regs + SPI_INT_STATUS);

                  dev_dbg(&drv_data->pdev->dev,
                        "interrupt_transfer - end of tx\n");

                  /* Read trailing bytes */
                  limit = loops_per_jiffy << 1;
                  while ((read(drv_data) == 0) && limit--);

                  if (limit == 0)
                        dev_err(&drv_data->pdev->dev,
                              "interrupt_transfer - "
                              "trailing byte read failed\n");
                  else
                        dev_dbg(&drv_data->pdev->dev,
                              "interrupt_transfer - end of rx\n");

                  /* End of transfer, update total byte transfered */
                  msg->actual_length += drv_data->len;

                  /* Release chip select if requested, transfer delays are
                     handled in pump_transfers */
                  if (drv_data->cs_change)
                        drv_data->cs_control(SPI_CS_DEASSERT);

                  /* Move to next transfer */
                  msg->state = next_transfer(drv_data);

                  /* Schedule transfer tasklet */
                  tasklet_schedule(&drv_data->pump_transfers);

                  return IRQ_HANDLED;
            }

            status = readl(regs + SPI_INT_STATUS);

            /* We did something */
            handled = IRQ_HANDLED;
      }

      return handled;
}

static irqreturn_t spi_int(int irq, void *dev_id)
{
      struct driver_data *drv_data = (struct driver_data *)dev_id;

      if (!drv_data->cur_msg) {
            dev_err(&drv_data->pdev->dev,
                  "spi_int - bad message state\n");
            /* Never fail */
            return IRQ_HANDLED;
      }

      return drv_data->transfer_handler(drv_data);
}

static inline u32 spi_speed_hz(u32 data_rate)
{
      return imx_get_perclk2() / (4 << ((data_rate) >> 13));
}

static u32 spi_data_rate(u32 speed_hz)
{
      u32 div;
      u32 quantized_hz = imx_get_perclk2() >> 2;

      for (div = SPI_PERCLK2_DIV_MIN;
            div <= SPI_PERCLK2_DIV_MAX;
            div++, quantized_hz >>= 1) {
                  if (quantized_hz <= speed_hz)
                        /* Max available speed LEQ required speed */
                        return div << 13;
      }
      return SPI_CONTROL_DATARATE_BAD;
}

static void pump_transfers(unsigned long data)
{
      struct driver_data *drv_data = (struct driver_data *)data;
      struct spi_message *message;
      struct spi_transfer *transfer, *previous;
      struct chip_data *chip;
      void __iomem *regs;
      u32 tmp, control;

      dev_dbg(&drv_data->pdev->dev, "pump_transfer\n");

      message = drv_data->cur_msg;

      /* Handle for abort */
      if (message->state == ERROR_STATE) {
            message->status = -EIO;
            giveback(message, drv_data);
            return;
      }

      /* Handle end of message */
      if (message->state == DONE_STATE) {
            message->status = 0;
            giveback(message, drv_data);
            return;
      }

      chip = drv_data->cur_chip;

      /* Delay if requested at end of transfer*/
      transfer = drv_data->cur_transfer;
      if (message->state == RUNNING_STATE) {
            previous = list_entry(transfer->transfer_list.prev,
                              struct spi_transfer,
                              transfer_list);
            if (previous->delay_usecs)
                  udelay(previous->delay_usecs);
      } else {
            /* START_STATE */
            message->state = RUNNING_STATE;
            drv_data->cs_control = chip->cs_control;
      }

      transfer = drv_data->cur_transfer;
      drv_data->tx = (void *)transfer->tx_buf;
      drv_data->tx_end = drv_data->tx + transfer->len;
      drv_data->rx = transfer->rx_buf;
      drv_data->rx_end = drv_data->rx + transfer->len;
      drv_data->rx_dma = transfer->rx_dma;
      drv_data->tx_dma = transfer->tx_dma;
      drv_data->len = transfer->len;
      drv_data->cs_change = transfer->cs_change;
      drv_data->rd_only = (drv_data->tx == NULL);

      regs = drv_data->regs;
      control = readl(regs + SPI_CONTROL);

      /* Bits per word setup */
      tmp = transfer->bits_per_word;
      if (tmp == 0) {
            /* Use device setup */
            tmp = chip->bits_per_word;
            drv_data->n_bytes = chip->n_bytes;
      } else
            /* Use per-transfer setup */
            drv_data->n_bytes = (tmp <= 8) ? 1 : 2;
      u32_EDIT(control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1);

      /* Speed setup (surely valid because already checked) */
      tmp = transfer->speed_hz;
      if (tmp == 0)
            tmp = chip->max_speed_hz;
      tmp = spi_data_rate(tmp);
      u32_EDIT(control, SPI_CONTROL_DATARATE, tmp);

      writel(control, regs + SPI_CONTROL);

      /* Assert device chip-select */
      drv_data->cs_control(SPI_CS_ASSERT);

      /* DMA cannot read/write SPI FIFOs other than 16 bits at a time; hence
         if bits_per_word is less or equal 8 PIO transfers are performed.
         Moreover DMA is convinient for transfer length bigger than FIFOs
         byte size. */
      if ((drv_data->n_bytes == 2) &&
            (drv_data->len > SPI_FIFO_DEPTH*SPI_FIFO_BYTE_WIDTH) &&
            (map_dma_buffers(drv_data) == 0)) {
            dev_dbg(&drv_data->pdev->dev,
                  "pump dma transfer\n"
                  "    tx      = %p\n"
                  "    tx_dma  = %08X\n"
                  "    rx      = %p\n"
                  "    rx_dma  = %08X\n"
                  "    len     = %d\n",
                  drv_data->tx,
                  (unsigned int)drv_data->tx_dma,
                  drv_data->rx,
                  (unsigned int)drv_data->rx_dma,
                  drv_data->len);

            /* Ensure we have the correct interrupt handler */
            drv_data->transfer_handler = dma_transfer;

            /* Trigger transfer */
            writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH,
                  regs + SPI_CONTROL);

            /* Setup tx DMA */
            if (drv_data->tx)
                  /* Linear source address */
                  CCR(drv_data->tx_channel) =
                        CCR_DMOD_FIFO |
                        CCR_SMOD_LINEAR |
                        CCR_SSIZ_32 | CCR_DSIZ_16 |
                        CCR_REN;
            else
                  /* Read only transfer -> fixed source address for
                     dummy write to achive read */
                  CCR(drv_data->tx_channel) =
                        CCR_DMOD_FIFO |
                        CCR_SMOD_FIFO |
                        CCR_SSIZ_32 | CCR_DSIZ_16 |
                        CCR_REN;

            imx_dma_setup_single(
                  drv_data->tx_channel,
                  drv_data->tx_dma,
                  drv_data->len,
                  drv_data->rd_data_phys + 4,
                  DMA_MODE_WRITE);

            if (drv_data->rx) {
                  /* Setup rx DMA for linear destination address */
                  CCR(drv_data->rx_channel) =
                        CCR_DMOD_LINEAR |
                        CCR_SMOD_FIFO |
                        CCR_DSIZ_32 | CCR_SSIZ_16 |
                        CCR_REN;
                  imx_dma_setup_single(
                        drv_data->rx_channel,
                        drv_data->rx_dma,
                        drv_data->len,
                        drv_data->rd_data_phys,
                        DMA_MODE_READ);
                  imx_dma_enable(drv_data->rx_channel);

                  /* Enable SPI interrupt */
                  writel(SPI_INTEN_RO, regs + SPI_INT_STATUS);

                  /* Set SPI to request DMA service on both
                     Rx and Tx half fifo watermark */
                  writel(SPI_DMA_RHDEN | SPI_DMA_THDEN, regs + SPI_DMA);
            } else
                  /* Write only access -> set SPI to request DMA
                     service on Tx half fifo watermark */
                  writel(SPI_DMA_THDEN, regs + SPI_DMA);

            imx_dma_enable(drv_data->tx_channel);
      } else {
            dev_dbg(&drv_data->pdev->dev,
                  "pump pio transfer\n"
                  "    tx      = %p\n"
                  "    rx      = %p\n"
                  "    len     = %d\n",
                  drv_data->tx,
                  drv_data->rx,
                  drv_data->len);

            /* Ensure we have the correct interrupt handler */
            if (drv_data->rx)
                  drv_data->transfer_handler = interrupt_transfer;
            else
                  drv_data->transfer_handler = interrupt_wronly_transfer;

            /* Enable SPI interrupt */
            if (drv_data->rx)
                  writel(SPI_INTEN_TH | SPI_INTEN_RO,
                        regs + SPI_INT_STATUS);
            else
                  writel(SPI_INTEN_TH, regs + SPI_INT_STATUS);
      }
}

static void pump_messages(struct work_struct *work)
{
      struct driver_data *drv_data =
                        container_of(work, struct driver_data, work);
      unsigned long flags;

      /* Lock queue and check for queue work */
      spin_lock_irqsave(&drv_data->lock, flags);
      if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
            drv_data->busy = 0;
            spin_unlock_irqrestore(&drv_data->lock, flags);
            return;
      }

      /* Make sure we are not already running a message */
      if (drv_data->cur_msg) {
            spin_unlock_irqrestore(&drv_data->lock, flags);
            return;
      }

      /* Extract head of queue */
      drv_data->cur_msg = list_entry(drv_data->queue.next,
                              struct spi_message, queue);
      list_del_init(&drv_data->cur_msg->queue);
      drv_data->busy = 1;
      spin_unlock_irqrestore(&drv_data->lock, flags);

      /* Initial message state */
      drv_data->cur_msg->state = START_STATE;
      drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
                                    struct spi_transfer,
                                    transfer_list);

      /* Setup the SPI using the per chip configuration */
      drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
      restore_state(drv_data);

      /* Mark as busy and launch transfers */
      tasklet_schedule(&drv_data->pump_transfers);
}

static int transfer(struct spi_device *spi, struct spi_message *msg)
{
      struct driver_data *drv_data = spi_master_get_devdata(spi->master);
      u32 min_speed_hz, max_speed_hz, tmp;
      struct spi_transfer *trans;
      unsigned long flags;

      msg->actual_length = 0;

      /* Per transfer setup check */
      min_speed_hz = spi_speed_hz(SPI_CONTROL_DATARATE_MIN);
      max_speed_hz = spi->max_speed_hz;
      list_for_each_entry(trans, &msg->transfers, transfer_list) {
            tmp = trans->bits_per_word;
            if (tmp > 16) {
                  dev_err(&drv_data->pdev->dev,
                        "message rejected : "
                        "invalid transfer bits_per_word (%d bits)\n",
                        tmp);
                  goto msg_rejected;
            }
            tmp = trans->speed_hz;
            if (tmp) {
                  if (tmp < min_speed_hz) {
                        dev_err(&drv_data->pdev->dev,
                              "message rejected : "
                              "device min speed (%d Hz) exceeds "
                              "required transfer speed (%d Hz)\n",
                              min_speed_hz,
                              tmp);
                        goto msg_rejected;
                  } else if (tmp > max_speed_hz) {
                        dev_err(&drv_data->pdev->dev,
                              "message rejected : "
                              "transfer speed (%d Hz) exceeds "
                              "device max speed (%d Hz)\n",
                              tmp,
                              max_speed_hz);
                        goto msg_rejected;
                  }
            }
      }

      /* Message accepted */
      msg->status = -EINPROGRESS;
      msg->state = START_STATE;

      spin_lock_irqsave(&drv_data->lock, flags);
      if (drv_data->run == QUEUE_STOPPED) {
            spin_unlock_irqrestore(&drv_data->lock, flags);
            return -ESHUTDOWN;
      }

      list_add_tail(&msg->queue, &drv_data->queue);
      if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
            queue_work(drv_data->workqueue, &drv_data->work);

      spin_unlock_irqrestore(&drv_data->lock, flags);
      return 0;

msg_rejected:
      /* Message rejected and not queued */
      msg->status = -EINVAL;
      msg->state = ERROR_STATE;
      if (msg->complete)
            msg->complete(msg->context);
      return -EINVAL;
}

/* the spi->mode bits understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)

/* On first setup bad values must free chip_data memory since will cause
   spi_new_device to fail. Bad value setup from protocol driver are simply not
   applied and notified to the calling driver. */
static int setup(struct spi_device *spi)
{
      struct spi_imx_chip *chip_info;
      struct chip_data *chip;
      int first_setup = 0;
      u32 tmp;
      int status = 0;

      if (spi->mode & ~MODEBITS) {
            dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
                  spi->mode & ~MODEBITS);
            return -EINVAL;
      }

      /* Get controller data */
      chip_info = spi->controller_data;

      /* Get controller_state */
      chip = spi_get_ctldata(spi);
      if (chip == NULL) {
            first_setup = 1;

            chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
            if (!chip) {
                  dev_err(&spi->dev,
                        "setup - cannot allocate controller state\n");
                  return -ENOMEM;
            }
            chip->control = SPI_DEFAULT_CONTROL;

            if (chip_info == NULL) {
                  /* spi_board_info.controller_data not is supplied */
                  chip_info = kzalloc(sizeof(struct spi_imx_chip),
                                    GFP_KERNEL);
                  if (!chip_info) {
                        dev_err(&spi->dev,
                              "setup - "
                              "cannot allocate controller data\n");
                        status = -ENOMEM;
                        goto err_first_setup;
                  }
                  /* Set controller data default value */
                  chip_info->enable_loopback =
                                    SPI_DEFAULT_ENABLE_LOOPBACK;
                  chip_info->enable_dma = SPI_DEFAULT_ENABLE_DMA;
                  chip_info->ins_ss_pulse = 1;
                  chip_info->bclk_wait = SPI_DEFAULT_PERIOD_WAIT;
                  chip_info->cs_control = null_cs_control;
            }
      }

      /* Now set controller state based on controller data */

      if (first_setup) {
            /* SPI loopback */
            if (chip_info->enable_loopback)
                  chip->test = SPI_TEST_LBC;
            else
                  chip->test = 0;

            /* SPI dma driven */
            chip->enable_dma = chip_info->enable_dma;

            /* SPI /SS pulse between spi burst */
            if (chip_info->ins_ss_pulse)
                  u32_EDIT(chip->control,
                        SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_1);
            else
                  u32_EDIT(chip->control,
                        SPI_CONTROL_SSCTL, SPI_CONTROL_SSCTL_0);

            /* SPI bclk waits between each bits_per_word spi burst */
            if (chip_info->bclk_wait > SPI_PERIOD_MAX_WAIT) {
                  dev_err(&spi->dev,
                        "setup - "
                        "bclk_wait exceeds max allowed (%d)\n",
                        SPI_PERIOD_MAX_WAIT);
                  goto err_first_setup;
            }
            chip->period = SPI_PERIOD_CSRC_BCLK |
                        (chip_info->bclk_wait & SPI_PERIOD_WAIT);
      }

      /* SPI mode */
      tmp = spi->mode;
      if (tmp & SPI_CS_HIGH) {
            u32_EDIT(chip->control,
                        SPI_CONTROL_SSPOL, SPI_CONTROL_SSPOL_ACT_HIGH);
      }
      switch (tmp & SPI_MODE_3) {
      case SPI_MODE_0:
            tmp = 0;
            break;
      case SPI_MODE_1:
            tmp = SPI_CONTROL_PHA_1;
            break;
      case SPI_MODE_2:
            tmp = SPI_CONTROL_POL_ACT_LOW;
            break;
      default:
            /* SPI_MODE_3 */
            tmp = SPI_CONTROL_PHA_1 | SPI_CONTROL_POL_ACT_LOW;
            break;
      }
      u32_EDIT(chip->control, SPI_CONTROL_POL | SPI_CONTROL_PHA, tmp);

      /* SPI word width */
      tmp = spi->bits_per_word;
      if (tmp == 0) {
            tmp = 8;
            spi->bits_per_word = 8;
      } else if (tmp > 16) {
            status = -EINVAL;
            dev_err(&spi->dev,
                  "setup - "
                  "invalid bits_per_word (%d)\n",
                  tmp);
            if (first_setup)
                  goto err_first_setup;
            else {
                  /* Undo setup using chip as backup copy */
                  tmp = chip->bits_per_word;
                  spi->bits_per_word = tmp;
            }
      }
      chip->bits_per_word = tmp;
      u32_EDIT(chip->control, SPI_CONTROL_BITCOUNT_MASK, tmp - 1);
      chip->n_bytes = (tmp <= 8) ? 1 : 2;

      /* SPI datarate */
      tmp = spi_data_rate(spi->max_speed_hz);
      if (tmp == SPI_CONTROL_DATARATE_BAD) {
            status = -EINVAL;
            dev_err(&spi->dev,
                  "setup - "
                  "HW min speed (%d Hz) exceeds required "
                  "max speed (%d Hz)\n",
                  spi_speed_hz(SPI_CONTROL_DATARATE_MIN),
                  spi->max_speed_hz);
            if (first_setup)
                  goto err_first_setup;
            else
                  /* Undo setup using chip as backup copy */
                  spi->max_speed_hz = chip->max_speed_hz;
      } else {
            u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp);
            /* Actual rounded max_speed_hz */
            tmp = spi_speed_hz(tmp);
            spi->max_speed_hz = tmp;
            chip->max_speed_hz = tmp;
      }

      /* SPI chip-select management */
      if (chip_info->cs_control)
            chip->cs_control = chip_info->cs_control;
      else
            chip->cs_control = null_cs_control;

      /* Save controller_state */
      spi_set_ctldata(spi, chip);

      /* Summary */
      dev_dbg(&spi->dev,
            "setup succeded\n"
            "    loopback enable   = %s\n"
            "    dma enable        = %s\n"
            "    insert /ss pulse  = %s\n"
            "    period wait       = %d\n"
            "    mode              = %d\n"
            "    bits per word     = %d\n"
            "    min speed         = %d Hz\n"
            "    rounded max speed = %d Hz\n",
            chip->test & SPI_TEST_LBC ? "Yes" : "No",
            chip->enable_dma ? "Yes" : "No",
            chip->control & SPI_CONTROL_SSCTL ? "Yes" : "No",
            chip->period & SPI_PERIOD_WAIT,
            spi->mode,
            spi->bits_per_word,
            spi_speed_hz(SPI_CONTROL_DATARATE_MIN),
            spi->max_speed_hz);
      return status;

err_first_setup:
      kfree(chip);
      return status;
}

static void cleanup(struct spi_device *spi)
{
      kfree(spi_get_ctldata(spi));
}

static int __init init_queue(struct driver_data *drv_data)
{
      INIT_LIST_HEAD(&drv_data->queue);
      spin_lock_init(&drv_data->lock);

      drv_data->run = QUEUE_STOPPED;
      drv_data->busy = 0;

      tasklet_init(&drv_data->pump_transfers,
                  pump_transfers,   (unsigned long)drv_data);

      INIT_WORK(&drv_data->work, pump_messages);
      drv_data->workqueue = create_singlethread_workqueue(
                              drv_data->master->dev.parent->bus_id);
      if (drv_data->workqueue == NULL)
            return -EBUSY;

      return 0;
}

static int start_queue(struct driver_data *drv_data)
{
      unsigned long flags;

      spin_lock_irqsave(&drv_data->lock, flags);

      if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
            spin_unlock_irqrestore(&drv_data->lock, flags);
            return -EBUSY;
      }

      drv_data->run = QUEUE_RUNNING;
      drv_data->cur_msg = NULL;
      drv_data->cur_transfer = NULL;
      drv_data->cur_chip = NULL;
      spin_unlock_irqrestore(&drv_data->lock, flags);

      queue_work(drv_data->workqueue, &drv_data->work);

      return 0;
}

static int stop_queue(struct driver_data *drv_data)
{
      unsigned long flags;
      unsigned limit = 500;
      int status = 0;

      spin_lock_irqsave(&drv_data->lock, flags);

      /* This is a bit lame, but is optimized for the common execution path.
       * A wait_queue on the drv_data->busy could be used, but then the common
       * execution path (pump_messages) would be required to call wake_up or
       * friends on every SPI message. Do this instead */
      drv_data->run = QUEUE_STOPPED;
      while (!list_empty(&drv_data->queue) && drv_data->busy && limit--) {
            spin_unlock_irqrestore(&drv_data->lock, flags);
            msleep(10);
            spin_lock_irqsave(&drv_data->lock, flags);
      }

      if (!list_empty(&drv_data->queue) || drv_data->busy)
            status = -EBUSY;

      spin_unlock_irqrestore(&drv_data->lock, flags);

      return status;
}

static int destroy_queue(struct driver_data *drv_data)
{
      int status;

      status = stop_queue(drv_data);
      if (status != 0)
            return status;

      if (drv_data->workqueue)
            destroy_workqueue(drv_data->workqueue);

      return 0;
}

static int __init spi_imx_probe(struct platform_device *pdev)
{
      struct device *dev = &pdev->dev;
      struct spi_imx_master *platform_info;
      struct spi_master *master;
      struct driver_data *drv_data = NULL;
      struct resource *res;
      int irq, status = 0;

      platform_info = dev->platform_data;
      if (platform_info == NULL) {
            dev_err(&pdev->dev, "probe - no platform data supplied\n");
            status = -ENODEV;
            goto err_no_pdata;
      }

      /* Allocate master with space for drv_data */
      master = spi_alloc_master(dev, sizeof(struct driver_data));
      if (!master) {
            dev_err(&pdev->dev, "probe - cannot alloc spi_master\n");
            status = -ENOMEM;
            goto err_no_mem;
      }
      drv_data = spi_master_get_devdata(master);
      drv_data->master = master;
      drv_data->master_info = platform_info;
      drv_data->pdev = pdev;

      master->bus_num = pdev->id;
      master->num_chipselect = platform_info->num_chipselect;
      master->cleanup = cleanup;
      master->setup = setup;
      master->transfer = transfer;

      drv_data->dummy_dma_buf = SPI_DUMMY_u32;

      /* Find and map resources */
      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
      if (!res) {
            dev_err(&pdev->dev, "probe - MEM resources not defined\n");
            status = -ENODEV;
            goto err_no_iores;
      }
      drv_data->ioarea = request_mem_region(res->start,
                                    res->end - res->start + 1,
                                    pdev->name);
      if (drv_data->ioarea == NULL) {
            dev_err(&pdev->dev, "probe - cannot reserve region\n");
            status = -ENXIO;
            goto err_no_iores;
      }
      drv_data->regs = ioremap(res->start, res->end - res->start + 1);
      if (drv_data->regs == NULL) {
            dev_err(&pdev->dev, "probe - cannot map IO\n");
            status = -ENXIO;
            goto err_no_iomap;
      }
      drv_data->rd_data_phys = (dma_addr_t)res->start;

      /* Attach to IRQ */
      irq = platform_get_irq(pdev, 0);
      if (irq < 0) {
            dev_err(&pdev->dev, "probe - IRQ resource not defined\n");
            status = -ENODEV;
            goto err_no_irqres;
      }
      status = request_irq(irq, spi_int, IRQF_DISABLED, dev->bus_id, drv_data);
      if (status < 0) {
            dev_err(&pdev->dev, "probe - cannot get IRQ (%d)\n", status);
            goto err_no_irqres;
      }

      /* Setup DMA if requested */
      drv_data->tx_channel = -1;
      drv_data->rx_channel = -1;
      if (platform_info->enable_dma) {
            /* Get rx DMA channel */
            status = imx_dma_request_by_prio(&drv_data->rx_channel,
                  "spi_imx_rx", DMA_PRIO_HIGH);
            if (status < 0) {
                  dev_err(dev,
                        "probe - problem (%d) requesting rx channel\n",
                        status);
                  goto err_no_rxdma;
            } else
                  imx_dma_setup_handlers(drv_data->rx_channel, NULL,
                                    dma_err_handler, drv_data);

            /* Get tx DMA channel */
            status = imx_dma_request_by_prio(&drv_data->tx_channel,
                                    "spi_imx_tx", DMA_PRIO_MEDIUM);
            if (status < 0) {
                  dev_err(dev,
                        "probe - problem (%d) requesting tx channel\n",
                        status);
                  imx_dma_free(drv_data->rx_channel);
                  goto err_no_txdma;
            } else
                  imx_dma_setup_handlers(drv_data->tx_channel,
                                    dma_tx_handler, dma_err_handler,
                                    drv_data);

            /* Set request source and burst length for allocated channels */
            switch (drv_data->pdev->id) {
            case 1:
                  /* Using SPI1 */
                  RSSR(drv_data->rx_channel) = DMA_REQ_SPI1_R;
                  RSSR(drv_data->tx_channel) = DMA_REQ_SPI1_T;
                  break;
            case 2:
                  /* Using SPI2 */
                  RSSR(drv_data->rx_channel) = DMA_REQ_SPI2_R;
                  RSSR(drv_data->tx_channel) = DMA_REQ_SPI2_T;
                  break;
            default:
                  dev_err(dev, "probe - bad SPI Id\n");
                  imx_dma_free(drv_data->rx_channel);
                  imx_dma_free(drv_data->tx_channel);
                  status = -ENODEV;
                  goto err_no_devid;
            }
            BLR(drv_data->rx_channel) = SPI_DMA_BLR;
            BLR(drv_data->tx_channel) = SPI_DMA_BLR;
      }

      /* Load default SPI configuration */
      writel(SPI_RESET_START, drv_data->regs + SPI_RESET);
      writel(0, drv_data->regs + SPI_RESET);
      writel(SPI_DEFAULT_CONTROL, drv_data->regs + SPI_CONTROL);

      /* Initial and start queue */
      status = init_queue(drv_data);
      if (status != 0) {
            dev_err(&pdev->dev, "probe - problem initializing queue\n");
            goto err_init_queue;
      }
      status = start_queue(drv_data);
      if (status != 0) {
            dev_err(&pdev->dev, "probe - problem starting queue\n");
            goto err_start_queue;
      }

      /* Register with the SPI framework */
      platform_set_drvdata(pdev, drv_data);
      status = spi_register_master(master);
      if (status != 0) {
            dev_err(&pdev->dev, "probe - problem registering spi master\n");
            goto err_spi_register;
      }

      dev_dbg(dev, "probe succeded\n");
      return 0;

err_init_queue:
err_start_queue:
err_spi_register:
      destroy_queue(drv_data);

err_no_rxdma:
err_no_txdma:
err_no_devid:
      free_irq(irq, drv_data);

err_no_irqres:
      iounmap(drv_data->regs);

err_no_iomap:
      release_resource(drv_data->ioarea);
      kfree(drv_data->ioarea);

err_no_iores:
      spi_master_put(master);

err_no_pdata:
err_no_mem:
      return status;
}

static int __exit spi_imx_remove(struct platform_device *pdev)
{
      struct driver_data *drv_data = platform_get_drvdata(pdev);
      int irq;
      int status = 0;

      if (!drv_data)
            return 0;

      tasklet_kill(&drv_data->pump_transfers);

      /* Remove the queue */
      status = destroy_queue(drv_data);
      if (status != 0) {
            dev_err(&pdev->dev, "queue remove failed (%d)\n", status);
            return status;
      }

      /* Reset SPI */
      writel(SPI_RESET_START, drv_data->regs + SPI_RESET);
      writel(0, drv_data->regs + SPI_RESET);

      /* Release DMA */
      if (drv_data->master_info->enable_dma) {
            RSSR(drv_data->rx_channel) = 0;
            RSSR(drv_data->tx_channel) = 0;
            imx_dma_free(drv_data->tx_channel);
            imx_dma_free(drv_data->rx_channel);
      }

      /* Release IRQ */
      irq = platform_get_irq(pdev, 0);
      if (irq >= 0)
            free_irq(irq, drv_data);

      /* Release map resources */
      iounmap(drv_data->regs);
      release_resource(drv_data->ioarea);
      kfree(drv_data->ioarea);

      /* Disconnect from the SPI framework */
      spi_unregister_master(drv_data->master);
      spi_master_put(drv_data->master);

      /* Prevent double remove */
      platform_set_drvdata(pdev, NULL);

      dev_dbg(&pdev->dev, "remove succeded\n");

      return 0;
}

static void spi_imx_shutdown(struct platform_device *pdev)
{
      struct driver_data *drv_data = platform_get_drvdata(pdev);

      /* Reset SPI */
      writel(SPI_RESET_START, drv_data->regs + SPI_RESET);
      writel(0, drv_data->regs + SPI_RESET);

      dev_dbg(&pdev->dev, "shutdown succeded\n");
}

#ifdef CONFIG_PM
static int suspend_devices(struct device *dev, void *pm_message)
{
      pm_message_t *state = pm_message;

      if (dev->power.power_state.event != state->event) {
            dev_warn(dev, "pm state does not match request\n");
            return -1;
      }

      return 0;
}

static int spi_imx_suspend(struct platform_device *pdev, pm_message_t state)
{
      struct driver_data *drv_data = platform_get_drvdata(pdev);
      int status = 0;

      status = stop_queue(drv_data);
      if (status != 0) {
            dev_warn(&pdev->dev, "suspend cannot stop queue\n");
            return status;
      }

      dev_dbg(&pdev->dev, "suspended\n");

      return 0;
}

static int spi_imx_resume(struct platform_device *pdev)
{
      struct driver_data *drv_data = platform_get_drvdata(pdev);
      int status = 0;

      /* Start the queue running */
      status = start_queue(drv_data);
      if (status != 0)
            dev_err(&pdev->dev, "problem starting queue (%d)\n", status);
      else
            dev_dbg(&pdev->dev, "resumed\n");

      return status;
}
#else
#define spi_imx_suspend NULL
#define spi_imx_resume NULL
#endif /* CONFIG_PM */

static struct platform_driver driver = {
      .driver = {
            .name = "spi_imx",
            .bus = &platform_bus_type,
            .owner = THIS_MODULE,
      },
      .remove = __exit_p(spi_imx_remove),
      .shutdown = spi_imx_shutdown,
      .suspend = spi_imx_suspend,
      .resume = spi_imx_resume,
};

static int __init spi_imx_init(void)
{
      return platform_driver_probe(&driver, spi_imx_probe);
}
module_init(spi_imx_init);

static void __exit spi_imx_exit(void)
{
      platform_driver_unregister(&driver);
}
module_exit(spi_imx_exit);

MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("iMX SPI Controller Driver");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index