add spi driver support
authorluowei <lw@rock-chips.com>
Fri, 7 Mar 2014 11:01:09 +0000 (19:01 +0800)
committerluowei <lw@rock-chips.com>
Fri, 7 Mar 2014 11:01:09 +0000 (19:01 +0800)
drivers/spi/Kconfig [changed mode: 0644->0755]
drivers/spi/Makefile [changed mode: 0644->0755]
drivers/spi/spi-rockchip-core.c [new file with mode: 0755]
drivers/spi/spi-rockchip-core.h [new file with mode: 0755]
drivers/spi/spi-rockchip-dma.c [new file with mode: 0755]
drivers/spi/spi-rockchip-test.c [new file with mode: 0755]
drivers/spi/spi-rockchip.c [new file with mode: 0755]
include/linux/platform_data/spi-rockchip.h [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index 92a9345..1eea3cf
@@ -508,6 +508,23 @@ config SPI_DW_MMIO
        tristate "Memory-mapped io interface driver for DW SPI core"
        depends on SPI_DESIGNWARE && HAVE_CLK
 
+config SPI_ROCKCHIP_CORE
+       tristate "ROCKCHIP SPI controller core support"
+       help
+         general driver for SPI controller core from ROCKCHIP
+
+config SPI_ROCKCHIP
+       tristate "ROCKCHIP SPI interface driver"
+       depends on SPI_ROCKCHIP_CORE
+
+config SPI_ROCKCHIP_DMA
+       bool "DMA support for ROCKCHIP SPI"
+       depends on SPI_ROCKCHIP
+
+config SPI_ROCKCHIP_TEST
+       bool "ROCKCHIP spi test code"
+       depends on SPI_ROCKCHIP
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
old mode 100644 (file)
new mode 100755 (executable)
index 33f9c09..c4cbe7e
@@ -74,3 +74,7 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH)                += spi-topcliff-pch.o
 obj-$(CONFIG_SPI_TXX9)                 += spi-txx9.o
 obj-$(CONFIG_SPI_XCOMM)                += spi-xcomm.o
 obj-$(CONFIG_SPI_XILINX)               += spi-xilinx.o
+obj-$(CONFIG_SPI_ROCKCHIP_CORE)        += spi-rockchip-core.o
+obj-$(CONFIG_SPI_ROCKCHIP_DMA) += spi-rockchip-dma.o
+obj-$(CONFIG_SPI_ROCKCHIP)             += spi-rockchip.o
+obj-$(CONFIG_SPI_ROCKCHIP_TEST)        += spi-rockchip-test.o
\ No newline at end of file
diff --git a/drivers/spi/spi-rockchip-core.c b/drivers/spi/spi-rockchip-core.c
new file mode 100755 (executable)
index 0000000..05051a1
--- /dev/null
@@ -0,0 +1,1106 @@
+/*
+ * Designware SPI core controller driver (refer spi_dw.c)
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/clk.h>
+
+
+#include "spi-rockchip-core.h"
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#define START_STATE    ((void *)0)
+#define RUNNING_STATE  ((void *)1)
+#define DONE_STATE     ((void *)2)
+#define ERROR_STATE    ((void *)-1)
+
+#define QUEUE_RUNNING  0
+#define QUEUE_STOPPED  1
+
+#define MRST_SPI_DEASSERT      0
+#define MRST_SPI_ASSERT                1
+
+
+/* Slave spi_dev related */
+struct chip_data {
+       u16 cr0;
+       u8 cs;                  /* chip select pin */
+       u8 n_bytes;             /* current is a 1/2/4 byte op */
+       u8 tmode;               /* TR/TO/RO/EEPROM */
+       u8 type;                /* SPI/SSP/MicroWire */
+
+       u8 poll_mode;           /* 1 means use poll mode */
+       
+       u8 slave_enable;
+       u32 dma_width;
+       u32 rx_threshold;
+       u32 tx_threshold;
+       u8 enable_dma;
+       u8 bits_per_word;
+       u16 clk_div;            /* baud rate divider */
+       u32 speed_hz;           /* baud rate */
+       void (*cs_control)(struct dw_spi *dws, u32 cs, u8 flag);
+};
+
+#ifdef CONFIG_DEBUG_FS
+#define SPI_REGS_BUFSIZE       1024
+
+static ssize_t spi_write_proc_data(struct file *file, const char __user *buffer,
+                          size_t count, loff_t *data)
+{      
+       struct dw_spi *dws;
+       char *buf;
+       u32 len = 0;
+       ssize_t ret;
+       int reg = 0,value = 0;
+       
+       dws = file->private_data;
+
+       buf = kzalloc(32, GFP_KERNEL);
+       if (!buf)
+       return 0;
+       
+       ret = copy_from_user(buf, buffer, count);
+       if (ret)
+       {
+               return ret; 
+       }
+
+       if((strstr(buf, "debug") != NULL) || (strstr(buf, "DEBUG") != NULL))
+       {               
+               atomic_set(&dws->debug_flag, 1);                
+               kfree(buf);
+               printk("%s:open debug\n",__func__);
+               return count;
+       }
+       else if((strstr(buf, "stop") != NULL) || (strstr(buf, "STOP") != NULL))
+       {               
+               atomic_set(&dws->debug_flag, 0);
+               printk("%s:close debug\n",__func__);
+       }
+       else if((strstr(buf, "=") != NULL))
+       {
+               printk("%s:invalid command\n",__func__);        
+               return count;
+       }
+
+       sscanf(buf, "0x%x=0x%x", &reg, &value);
+
+       if((reg >= SPIM_CTRLR0) && (reg <= SPIM_DMARDLR))       
+       {
+               dw_writew(dws, reg, value);
+               printk("%s:write data[0x%x] to reg[0x%x] succesfully\n",__func__, value, reg);
+       }
+       else
+       {
+               printk("%s:data[0x%x] or reg[0x%x] is out of range\n",__func__, value, reg);
+       }
+       
+       kfree(buf);
+               
+       return count; 
+}
+
+static ssize_t  spi_show_regs(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct dw_spi *dws;
+       char *buf;
+       u32 len = 0;
+       ssize_t ret;
+
+       dws = file->private_data;
+
+       buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL);
+       if (!buf)
+               return 0;
+
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "MRST SPI0 registers:\n");
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "=================================\n");
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL0: \t\t0x%08x\n", dw_readl(dws, SPIM_CTRLR0));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL1: \t\t0x%08x\n", dw_readl(dws, SPIM_CTRLR1));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SSIENR: \t0x%08x\n", dw_readl(dws, SPIM_SSIENR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SER: \t\t0x%08x\n", dw_readl(dws, SPIM_SER));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "BAUDR: \t\t0x%08x\n", dw_readl(dws, SPIM_BAUDR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFTLR: \t0x%08x\n", dw_readl(dws, SPIM_TXFTLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFTLR: \t0x%08x\n", dw_readl(dws, SPIM_RXFTLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFLR: \t\t0x%08x\n", dw_readl(dws, SPIM_TXFLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFLR: \t\t0x%08x\n", dw_readl(dws, SPIM_RXFLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SR: \t\t0x%08x\n", dw_readl(dws, SPIM_SR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "IMR: \t\t0x%08x\n", dw_readl(dws, SPIM_IMR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "ISR: \t\t0x%08x\n", dw_readl(dws, SPIM_ISR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMACR: \t\t0x%08x\n", dw_readl(dws, SPIM_DMACR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMATDLR: \t0x%08x\n", dw_readl(dws, SPIM_DMATDLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMARDLR: \t0x%08x\n", dw_readl(dws, SPIM_DMARDLR));
+       len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
+                       "=================================\n");
+
+       ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations spi_regs_ops = {
+       .owner          = THIS_MODULE,
+       .open           = simple_open,
+       .read           = spi_show_regs,
+       .write          = spi_write_proc_data,
+       .llseek         = default_llseek,
+};
+
+static int spi_debugfs_init(struct dw_spi *dws)
+{
+       dws->debugfs = debugfs_create_dir("spi", NULL);
+       if (!dws->debugfs)
+               return -ENOMEM;
+
+       debugfs_create_file("registers", S_IFREG | S_IRUGO,
+               dws->debugfs, (void *)dws, &spi_regs_ops);
+       return 0;
+}
+
+static void spi_debugfs_remove(struct dw_spi *dws)
+{
+       if (dws->debugfs)
+               debugfs_remove_recursive(dws->debugfs);
+}
+
+#else
+static inline int spi_debugfs_init(struct dw_spi *dws)
+{
+       return 0;
+}
+
+static inline void spi_debugfs_remove(struct dw_spi *dws)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void wait_till_not_busy(struct dw_spi *dws)
+{
+       unsigned long end = jiffies + 1 + usecs_to_jiffies(1000);
+       //if spi was slave, it is SR_BUSY always.  
+       if(dws->cur_chip) {
+               if(dws->cur_chip->slave_enable == 1)
+                       return;
+       }
+       
+       while (time_before(jiffies, end)) {
+               if (!(dw_readw(dws, SPIM_SR) & SR_BUSY))
+                       return;
+       }
+       dev_err(&dws->master->dev,
+               "DW SPI: Status keeps busy for 1000us after a read/write!\n");
+}
+
+
+static void flush(struct dw_spi *dws)
+{
+       while (!(dw_readw(dws, SPIM_SR) & SR_RF_EMPT))
+               dw_readw(dws, SPIM_RXDR);
+
+       wait_till_not_busy(dws);
+}
+
+
+/* Return the max entries we can fill into tx fifo */
+static inline u32 tx_max(struct dw_spi *dws)
+{
+       u32 tx_left, tx_room, rxtx_gap;
+
+       tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
+       tx_room = dws->fifo_len - dw_readw(dws, SPIM_TXFLR);
+
+       /*
+        * Another concern is about the tx/rx mismatch, we
+        * though to use (dws->fifo_len - rxflr - txflr) as
+        * one maximum value for tx, but it doesn't cover the
+        * data which is out of tx/rx fifo and inside the
+        * shift registers. So a control from sw point of
+        * view is taken.
+        */
+       //rxtx_gap =  ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx))
+       //              / dws->n_bytes;
+
+       return min(tx_left, tx_room);
+}
+
+/* Return the max entries we should read out of rx fifo */
+static inline u32 rx_max(struct dw_spi *dws)
+{
+       u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
+
+       return min(rx_left, (u32)dw_readw(dws, SPIM_RXFLR));
+}
+
+static void dw_writer(struct dw_spi *dws)
+{
+       u32 max = tx_max(dws);
+       u16 txw = 0;    
+       
+       DBG_SPI("%dbyte tx:",dws->n_bytes);
+       while (max--) {
+               /* Set the tx word if the transfer's original "tx" is not null */
+               if (dws->tx_end - dws->len) {
+                       if (dws->n_bytes == 1)
+                       {
+                               txw = *(u8 *)(dws->tx); 
+                               DBG_SPI("0x%02x,", *(u8 *)(dws->tx));
+                       }
+                       else
+                       {
+                               txw = *(u16 *)(dws->tx);
+                               DBG_SPI("0x%02x,", *(u16 *)(dws->tx));
+                       }
+               }
+               dw_writew(dws, SPIM_TXDR, txw);
+               dws->tx += dws->n_bytes;
+       }
+       
+       //it is neccessary
+       wait_till_not_busy(dws);
+       
+       DBG_SPI("\n");
+}
+
+static void dw_reader(struct dw_spi *dws)
+{
+       u32 max = rx_max(dws);
+       u16 rxw;
+       
+       DBG_SPI("%dbyte rx:",dws->n_bytes);
+
+       while (max--) {
+               rxw = dw_readw(dws, SPIM_RXDR);
+               /* Care rx only if the transfer's original "rx" is not null */
+               if (dws->rx_end - dws->len) {
+                       if (dws->n_bytes == 1)
+                       {
+                               *(u8 *)(dws->rx) = rxw;
+                               DBG_SPI("0x%02x,", *(u8 *)(dws->rx));
+                       }
+                       else
+                       {
+                               *(u16 *)(dws->rx) = rxw;
+                               DBG_SPI("0x%02x,", *(u16 *)(dws->rx));
+                       }
+               }
+               
+               dws->rx += dws->n_bytes;
+       }
+       
+       DBG_SPI("\n");
+}
+
+static int reader_all(struct dw_spi *dws)
+{
+       u16 rxw;
+       while (!(dw_readw(dws, SPIM_SR) & SR_RF_EMPT)
+               && (dws->rx < dws->rx_end)) {
+                       dw_reader(dws);         
+                       wait_till_not_busy(dws);
+               }
+
+       return dws->rx == dws->rx_end;
+}
+
+
+static void *next_transfer(struct dw_spi *dws)
+{
+       struct spi_message *msg = dws->cur_msg;
+       struct spi_transfer *trans = dws->cur_transfer;
+
+       /* Move to next transfer */
+       if (trans->transfer_list.next != &msg->transfers) {
+               dws->cur_transfer =
+                       list_entry(trans->transfer_list.next,
+                                       struct spi_transfer,
+                                       transfer_list);
+               return RUNNING_STATE;
+       } else
+               return DONE_STATE;
+}
+
+/*
+ * Note: first step is the protocol driver prepares
+ * a dma-capable memory, and this func just need translate
+ * the virt addr to physical
+ */
+static int map_dma_buffers(struct dw_spi *dws)
+{
+       if (!dws->cur_msg->is_dma_mapped
+               || !dws->dma_inited
+               || !dws->cur_chip->enable_dma
+               || !dws->dma_ops)
+               return 0;
+
+       if (dws->cur_transfer->tx_dma)
+               dws->tx_dma = dws->cur_transfer->tx_dma;
+
+       if (dws->cur_transfer->rx_dma)
+               dws->rx_dma = dws->cur_transfer->rx_dma;
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+       return 1;
+}
+
+/* Caller already set message->status; dma and pio irqs are blocked */
+static void giveback(struct dw_spi *dws)
+{
+       struct spi_transfer *last_transfer;
+       unsigned long flags;
+       struct spi_message *msg;        
+       struct spi_message *next_msg;
+       
+       spin_lock_irqsave(&dws->lock, flags);
+       msg = dws->cur_msg;
+       dws->cur_msg = NULL;
+       dws->cur_transfer = NULL;
+       dws->prev_chip = dws->cur_chip;
+       dws->cur_chip = NULL;
+       dws->dma_mapped = 0;
+       //queue_work(dws->workqueue, &dws->pump_messages);
+
+       /*it is important to close intterrupt*/
+       spi_mask_intr(dws, 0xff);
+       //rk29xx_writew(dws, SPIM_DMACR, 0);
+       
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       last_transfer = list_entry(msg->transfers.prev,
+                                       struct spi_transfer,
+                                       transfer_list);
+
+       if (!last_transfer->cs_change && dws->cs_control)
+               dws->cs_control(dws, msg->spi->chip_select, MRST_SPI_DEASSERT);
+
+       msg->state = NULL;
+
+       /* get a pointer to the next message, if any */
+       next_msg = spi_get_next_queued_message(dws->master);
+
+       /* see if the next and current messages point
+       * to the same chip
+       */
+       if (next_msg && next_msg->spi != msg->spi)
+       next_msg = NULL;
+       
+       spi_finalize_current_message(dws->master);
+       dws->cur_chip = NULL;
+
+       
+       DBG_SPI("%s:line=%d,tx_left=%d\n",__func__,__LINE__, (dws->tx_end - dws->tx) / dws->n_bytes);
+}
+
+
+static void int_error_stop(struct dw_spi *dws, const char *msg)
+{
+       /* Stop the hw */
+       flush(dws);
+       spi_enable_chip(dws, 0);
+
+       dev_err(&dws->master->dev, "%s\n", msg);
+       dws->cur_msg->state = ERROR_STATE;
+       tasklet_schedule(&dws->pump_transfers);
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+}
+
+void dw_spi_xfer_done(struct dw_spi *dws)
+{
+       /* Update total byte transferred return count actual bytes read */
+       dws->cur_msg->actual_length += dws->len;
+
+       /* Move to next transfer */
+       dws->cur_msg->state = next_transfer(dws);
+
+       /* Handle end of message */
+       if (dws->cur_msg->state == DONE_STATE) {
+               dws->cur_msg->status = 0;
+               giveback(dws);
+       } else
+               tasklet_schedule(&dws->pump_transfers);
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+}
+EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
+
+static irqreturn_t interrupt_transfer(struct dw_spi *dws)
+{
+       u16 irq_status;
+       u32 int_level = dws->fifo_len / 2;
+       u32 left;
+
+
+       irq_status = dw_readw(dws, SPIM_ISR) & 0x1f;
+       
+       DBG_SPI("%s:line=%d,irq_status=0x%x\n",__func__,__LINE__,irq_status);
+       
+       /* Error handling */
+       if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
+               dw_writew(dws, SPIM_ICR, SPI_CLEAR_INT_TXOI | SPI_CLEAR_INT_RXOI | SPI_CLEAR_INT_RXUI);
+               printk("%s:irq_status=0x%x\n",__func__,irq_status);
+               int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
+               return IRQ_HANDLED;
+       }
+
+       if (irq_status & SPI_INT_TXEI) 
+       {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+               dw_writer(dws);
+
+               if (dws->rx) {
+                   reader_all(dws);
+               }
+
+               /* Re-enable the IRQ if there is still data left to tx */
+               if (dws->tx_end > dws->tx)
+                       spi_umask_intr(dws, SPI_INT_TXEI);
+               else
+                       dw_spi_xfer_done(dws);
+       }
+
+       if (irq_status & SPI_INT_RXFI) {
+               spi_mask_intr(dws, SPI_INT_RXFI);
+               
+               reader_all(dws);
+
+               /* Re-enable the IRQ if there is still data left to rx */
+               if (dws->rx_end > dws->rx) {
+                       left = ((dws->rx_end - dws->rx) / dws->n_bytes) - 1;
+                   left = (left > int_level) ? int_level : left;
+
+                       dw_writew(dws, SPIM_RXFTLR, left);
+                       spi_umask_intr(dws, SPI_INT_RXFI);
+               }
+               else {
+                       dw_spi_xfer_done(dws);
+               }
+               
+       }
+
+       return IRQ_HANDLED;
+}
+
+
+static irqreturn_t dw_spi_irq(int irq, void *dev_id)
+{
+       struct dw_spi *dws = dev_id;
+       u16 irq_status = dw_readw(dws, SPIM_ISR)&0x3f;
+
+       if (!irq_status)
+               return IRQ_NONE;
+
+       if (!dws->cur_msg) {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+               return IRQ_HANDLED;
+       }
+
+       return dws->transfer_handler(dws);
+}
+
+/* Must be called inside pump_transfers() */
+static void poll_transfer(struct dw_spi *dws)
+{      
+       DBG_SPI("%s:len=%d\n",__func__, dws->len);
+       
+       do {
+               dw_writer(dws);
+               dw_reader(dws);
+               cpu_relax();
+       } while (dws->rx_end > dws->rx);
+
+       dw_spi_xfer_done(dws);
+       
+}
+
+static void pump_transfers(unsigned long data)
+{
+       struct dw_spi *dws = (struct dw_spi *)data;
+       struct spi_message *message = NULL;
+       struct spi_transfer *transfer = NULL;
+       struct spi_transfer *previous = NULL;
+       struct spi_device *spi = NULL;
+       struct chip_data *chip = NULL;
+       u8 bits = 0;
+       u8 imask = 0;
+       u8 cs_change = 0;
+       u16 txint_level = 0;    
+       u16 rxint_level = 0;
+       u16 clk_div = 0;
+       u32 speed = 0;
+       u32 cr0 = 0;
+
+
+       /* Get current state information */
+       message = dws->cur_msg;
+       transfer = dws->cur_transfer;
+       chip = dws->cur_chip;
+       spi = message->spi;
+
+       if (unlikely(!chip->clk_div))
+               chip->clk_div = dws->max_freq / chip->speed_hz;
+
+       if (message->state == ERROR_STATE) {
+               message->status = -EIO;
+               goto early_exit;
+       }
+
+       /* Handle end of message */
+       if (message->state == DONE_STATE) {
+               message->status = 0;
+               goto early_exit;
+       }
+
+       /* Delay if requested at end of transfer*/
+       if (message->state == RUNNING_STATE) {
+               previous = list_entry(transfer->transfer_list.prev,
+                                       struct spi_transfer,
+                                       transfer_list);
+               if (previous->delay_usecs)
+                       udelay(previous->delay_usecs);
+       }
+
+       dws->n_bytes = chip->n_bytes;
+       dws->dma_width = chip->dma_width;
+       dws->cs_control = chip->cs_control;
+
+       dws->rx_dma = transfer->rx_dma;
+       dws->tx_dma = transfer->tx_dma;
+       dws->tx = (void *)transfer->tx_buf;
+       dws->tx_end = dws->tx + transfer->len;
+       dws->rx = transfer->rx_buf;
+       dws->rx_end = dws->rx + transfer->len;
+       dws->cs_change = transfer->cs_change;
+       dws->len = dws->cur_transfer->len;
+       if (chip != dws->prev_chip)
+               cs_change = 1;
+
+       cr0 = chip->cr0;
+
+       
+       DBG_SPI("%s:len=%d\n",__func__,dws->len);
+
+       /* Handle per transfer options for bpw and speed */
+       if (transfer->speed_hz) {
+               speed = chip->speed_hz;
+
+               if (transfer->speed_hz != speed) {
+                       speed = transfer->speed_hz;
+                       if (speed > dws->max_freq) {
+                               printk(KERN_ERR "MRST SPI0: unsupported"
+                                       "freq: %dHz\n", speed);
+                               message->status = -EIO;
+                               goto early_exit;
+                       }
+
+                       /* clk_div doesn't support odd number */
+                       clk_div = dws->max_freq / speed;
+                       clk_div = (clk_div + 1) & 0xfffe;
+
+                       chip->speed_hz = speed;
+                       chip->clk_div = clk_div;
+               }
+       }
+       if (transfer->bits_per_word) {
+               bits = transfer->bits_per_word;
+
+               switch (bits) {
+               case 8:
+               case 16:
+                       dws->n_bytes = dws->dma_width = bits >> 3;
+                       break;
+               default:
+                       printk(KERN_ERR "MRST SPI0: unsupported bits:"
+                               "%db\n", bits);
+                       message->status = -EIO;
+                       goto early_exit;
+               }
+
+               cr0 =((dws->n_bytes) << SPI_DFS_OFFSET)
+                       | (SPI_HALF_WORLD_OFF << SPI_HALF_WORLD_TX_OFFSET)
+                       | (SPI_SSN_DELAY_ONE << SPI_SSN_DELAY_OFFSET)
+                       | (chip->type << SPI_FRF_OFFSET)
+                       | (spi->mode << SPI_MODE_OFFSET)
+                       | (chip->tmode << SPI_TMOD_OFFSET);
+       }
+       message->state = RUNNING_STATE;
+
+       /*
+        * Adjust transfer mode if necessary. Requires platform dependent
+        * chipselect mechanism.
+        */
+       if (dws->cs_control) {
+               if (dws->rx && dws->tx)
+                       chip->tmode = SPI_TMOD_TR;
+               else if (dws->rx)
+                       chip->tmode = SPI_TMOD_RO;
+               else
+                       chip->tmode = SPI_TMOD_TO;
+
+               //cr0 &= ~SPI_TMOD_MASK;
+               //cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
+
+               cr0 &= ~(0x3 << SPI_MODE_OFFSET);               
+               cr0 &= ~(0x3 << SPI_TMOD_OFFSET);
+               cr0 &= ~(0x1 << SPI_OPMOD_OFFSET);      
+               cr0 |= (spi->mode << SPI_MODE_OFFSET);
+               cr0 |= (chip->tmode << SPI_TMOD_OFFSET);
+               cr0 |= ((chip->slave_enable & 1) << SPI_OPMOD_OFFSET);
+       }
+
+       /* Check if current transfer is a DMA transaction */
+       dws->dma_mapped = map_dma_buffers(dws);
+
+       /*
+        * Interrupt mode
+        * we only need set the TXEI IRQ, as TX/RX always happen syncronizely
+        */
+       if (!dws->dma_mapped && !chip->poll_mode) {     
+               int templen ;
+               
+               if (chip->tmode == SPI_TMOD_RO) {
+                       templen = dws->len / dws->n_bytes - 1;
+                       rxint_level = dws->fifo_len / 2;
+                       rxint_level = (templen > rxint_level) ? rxint_level : templen;
+                       imask |= SPI_INT_RXFI;
+               }
+               else {  
+                       templen = dws->len / dws->n_bytes;
+                       txint_level = dws->fifo_len / 2;
+                       txint_level = (templen > txint_level) ? txint_level : templen;
+                       imask |= SPI_INT_TXEI | SPI_INT_TXOI;
+               }
+               dws->transfer_handler = interrupt_transfer;
+       }
+
+       /*
+        * Reprogram registers only if
+        *      1. chip select changes
+        *      2. clk_div is changed
+        *      3. control value changes
+        */
+       if (dw_readw(dws, SPIM_CTRLR0) != cr0 || cs_change || clk_div || imask) {
+               spi_enable_chip(dws, 0);
+               if (dw_readl(dws, SPIM_CTRLR0) != cr0)
+                       dw_writel(dws, SPIM_CTRLR0, cr0);
+
+               spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);            
+               spi_chip_sel(dws, spi->chip_select);
+
+        dw_writew(dws, SPIM_CTRLR1, dws->len-1);
+               
+               if (txint_level)
+                       dw_writew(dws, SPIM_TXFTLR, txint_level);
+                       
+               if (rxint_level)
+               {
+                       dw_writew(dws, SPIM_RXFTLR, rxint_level);
+                       DBG_SPI("%s:rxint_level=%d\n",__func__,rxint_level);
+               }
+               /* Set the interrupt mask, for poll mode just diable all int */
+               spi_mask_intr(dws, 0xff);
+               if (imask)
+                       spi_umask_intr(dws, imask);
+       
+               spi_enable_chip(dws, 1);        
+               
+               if (cs_change)
+                       dws->prev_chip = chip;
+       }
+
+       if (dws->dma_mapped)
+               dws->dma_ops->dma_transfer(dws, cs_change);
+
+       if (chip->poll_mode)
+               poll_transfer(dws);
+
+       return;
+
+early_exit:
+       giveback(dws);
+       return;
+}
+
+static void pump_messages(struct work_struct *work)
+{
+       struct dw_spi *dws =
+               container_of(work, struct dw_spi, pump_messages);
+       unsigned long flags;
+
+       /* Lock queue and check for queue work */
+       spin_lock_irqsave(&dws->lock, flags);
+       if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
+               dws->busy = 0;
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return;
+       }
+
+       /* Make sure we are not already running a message */
+       if (dws->cur_msg) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return;
+       }
+
+       /* Extract head of queue */
+       dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue);
+       list_del_init(&dws->cur_msg->queue);
+
+       /* Initial message state*/
+       dws->cur_msg->state = START_STATE;
+       dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
+                                               struct spi_transfer,
+                                               transfer_list);
+       dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
+
+       /* Mark as busy and launch transfers */
+       tasklet_schedule(&dws->pump_transfers);
+
+       dws->busy = 1;
+       spin_unlock_irqrestore(&dws->lock, flags);
+}
+
+
+static int dw_spi_transfer_one_message(struct spi_master *master,
+                                          struct spi_message *msg)
+{
+       struct dw_spi *dws = spi_master_get_devdata(master);
+
+       dws->cur_msg = msg;
+       /* Initial message state*/
+       dws->cur_msg->state = START_STATE;
+       dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
+                                               struct spi_transfer,
+                                               transfer_list);
+
+       /* prepare to setup the SSP, in pump_transfers, using the per
+        * chip configuration */
+       dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
+
+       /* Mark as busy and launch transfers */
+       tasklet_schedule(&dws->pump_transfers);
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+       return 0;
+}
+
+static int dw_spi_prepare_transfer(struct spi_master *master)
+{
+       struct dw_spi *dws = spi_master_get_devdata(master);
+
+       //pm_runtime_get_sync(&dws->pdev->dev);
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+       return 0;
+}
+
+static int dw_spi_unprepare_transfer(struct spi_master *master)
+{
+       struct dw_spi *dws = spi_master_get_devdata(master);
+
+       /* Disable the SSP now */
+       //write_SSCR0(read_SSCR0(dws->ioaddr) & ~SSCR0_SSE,
+       //          dws->ioaddr);
+
+       //pm_runtime_mark_last_busy(&dws->pdev->dev);
+       //pm_runtime_put_autosuspend(&dws->pdev->dev);
+       
+       DBG_SPI("%s:line=%d\n",__func__,__LINE__);
+       return 0;
+}
+
+/* This may be called twice for each spi dev */
+static int dw_spi_setup(struct spi_device *spi)
+{
+       struct dw_spi_chip *chip_info = NULL;
+       struct chip_data *chip;
+
+       if (spi->bits_per_word != 8 && spi->bits_per_word != 16)
+               return -EINVAL;
+
+       /* Only alloc on first setup */
+       chip = spi_get_ctldata(spi);
+       if (!chip) {
+               chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
+               if (!chip)
+                       return -ENOMEM;
+
+               chip->cs_control = spi_cs_control;
+               chip->enable_dma = 0; 
+       }
+
+       /*
+        * Protocol drivers may change the chip settings, so...
+        * if chip_info exists, use it
+        */
+       chip_info = spi->controller_data;
+
+       /* chip_info doesn't always exist */
+       if (chip_info) {
+               if (chip_info->cs_control)
+                       chip->cs_control = chip_info->cs_control;
+
+               chip->poll_mode = chip_info->poll_mode;
+               chip->type = chip_info->type;
+
+               chip->rx_threshold = 0;
+               chip->tx_threshold = 0;
+
+               chip->enable_dma = chip_info->enable_dma;
+       }
+
+       if (spi->bits_per_word <= 8) {
+               chip->n_bytes = 1;
+               chip->dma_width = 1;
+       } else if (spi->bits_per_word <= 16) {
+               chip->n_bytes = 2;
+               chip->dma_width = 2;
+       } else {
+               /* Never take >16b case for MRST SPIC */
+               dev_err(&spi->dev, "invalid wordsize\n");
+               return -EINVAL;
+       }
+       chip->bits_per_word = spi->bits_per_word;
+
+       if (!spi->max_speed_hz) {
+               dev_err(&spi->dev, "No max speed HZ parameter\n");
+               return -EINVAL;
+       }
+       chip->speed_hz = spi->max_speed_hz;
+
+       chip->tmode = 0; /* Tx & Rx */
+       /* Default SPI mode is SCPOL = 0, SCPH = 0 */
+       chip->cr0 = ((chip->n_bytes) << SPI_DFS_OFFSET)
+                   | (SPI_HALF_WORLD_OFF << SPI_HALF_WORLD_TX_OFFSET)
+                       | (SPI_SSN_DELAY_ONE << SPI_SSN_DELAY_OFFSET)
+                       | (chip->type << SPI_FRF_OFFSET)
+                       | (spi->mode  << SPI_MODE_OFFSET)
+                       | (chip->tmode << SPI_TMOD_OFFSET);
+
+       spi_set_ctldata(spi, chip);
+       
+       //printk("%s:line=%d\n",__func__,__LINE__);
+       return 0;
+}
+
+static void dw_spi_cleanup(struct spi_device *spi)
+{
+       struct chip_data *chip = spi_get_ctldata(spi);
+       kfree(chip);
+}
+
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static void spi_hw_init(struct dw_spi *dws)
+{
+       spi_enable_chip(dws, 0);
+       spi_mask_intr(dws, 0xff);
+
+       /*
+        * Try to detect the FIFO depth if not set by interface driver,
+        * the depth could be from 2 to 32 from HW spec
+        */
+       if (!dws->fifo_len) {
+               u32 fifo;
+               for (fifo = 2; fifo <= 31; fifo++) {
+                       dw_writew(dws, SPIM_TXFTLR, fifo);
+                       if (fifo != dw_readw(dws, SPIM_TXFTLR))
+                               break;
+               }
+
+               dws->fifo_len = (fifo == 31) ? 0 : fifo;
+               dw_writew(dws, SPIM_TXFTLR, 0);
+       }
+       
+       spi_enable_chip(dws, 1);
+       flush(dws);
+       DBG_SPI("%s:fifo_len=%d\n",__func__, dws->fifo_len);
+}
+
+int dw_spi_add_host(struct dw_spi *dws)
+{
+       struct spi_master *master;
+       int ret;
+
+       BUG_ON(dws == NULL);
+
+       master = spi_alloc_master(dws->parent_dev, 0);
+       if (!master) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       dws->master = master;
+       dws->type = SSI_MOTO_SPI;
+       dws->prev_chip = NULL;
+       dws->dma_inited = 0;
+       dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
+       snprintf(dws->name, sizeof(dws->name), "dw_spi%d",
+                       dws->bus_num);
+
+       ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED,
+                       dws->name, dws);
+       if (ret < 0) {
+               dev_err(&master->dev, "can not get IRQ\n");
+               goto err_free_master;
+       }
+
+       master->mode_bits = SPI_CPOL | SPI_CPHA;
+       master->bus_num = dws->bus_num;
+       master->num_chipselect = dws->num_cs;
+       master->cleanup = dw_spi_cleanup;
+       master->setup = dw_spi_setup;
+       master->transfer_one_message = dw_spi_transfer_one_message;
+       master->prepare_transfer_hardware = dw_spi_prepare_transfer;
+       master->unprepare_transfer_hardware = dw_spi_unprepare_transfer;
+       
+       spin_lock_init(&dws->lock);
+       tasklet_init(&dws->pump_transfers,
+                       pump_transfers, (unsigned long)dws);
+
+
+       /* Basic HW init */
+       spi_hw_init(dws);
+
+       if (dws->dma_ops && dws->dma_ops->dma_init) {
+               ret = dws->dma_ops->dma_init(dws);
+               if (ret) {
+                       dev_warn(&master->dev, "DMA init failed\n");
+                       dws->dma_inited = 0;
+               }
+       }
+       
+       spi_master_set_devdata(master, dws);
+       ret = spi_register_master(master);
+       if (ret) {
+               dev_err(&master->dev, "problem registering spi master\n");
+               goto err_queue_alloc;
+       }
+
+       spi_debugfs_init(dws);
+
+       
+       DBG_SPI("%s:bus_num=%d\n",__func__, dws->bus_num);
+       return 0;
+
+err_queue_alloc:
+       if (dws->dma_ops && dws->dma_ops->dma_exit)
+               dws->dma_ops->dma_exit(dws);
+err_diable_hw:
+       spi_enable_chip(dws, 0);
+       free_irq(dws->irq, dws);
+err_free_master:
+       spi_master_put(master);
+exit:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_spi_add_host);
+
+void dw_spi_remove_host(struct dw_spi *dws)
+{
+       int status = 0;
+
+       if (!dws)
+               return;
+       
+       spi_debugfs_remove(dws);
+
+       if (dws->dma_ops && dws->dma_ops->dma_exit)
+               dws->dma_ops->dma_exit(dws);
+       
+       spi_enable_chip(dws, 0);
+       /* Disable clk */
+       spi_set_clk(dws, 0);
+       free_irq(dws->irq, dws);
+
+       /* Disconnect from the SPI framework */
+       spi_unregister_master(dws->master);
+
+       
+       DBG_SPI("%s:bus_num=%d\n",__func__, dws->bus_num);
+}
+EXPORT_SYMBOL_GPL(dw_spi_remove_host);
+
+int dw_spi_suspend_host(struct dw_spi *dws)
+{
+       int ret = 0;
+       
+       ret = spi_master_suspend(dws->master);
+       if (ret != 0)
+       return ret;
+       
+       spi_enable_chip(dws, 0);
+       spi_set_clk(dws, 0);
+       
+       clk_disable_unprepare(dws->clk_spi);
+       
+       DBG_SPI("%s:bus_num=%d\n",__func__, dws->bus_num);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_spi_suspend_host);
+
+int dw_spi_resume_host(struct dw_spi *dws)
+{
+       int ret;
+
+       /* Enable the SPI clock */
+       clk_prepare_enable(dws->clk_spi);
+       
+       spi_hw_init(dws);
+
+       /* Start the queue running */
+       ret = spi_master_resume(dws->master);
+       if (ret != 0) {
+               printk("%s:problem starting queue (%d)\n", __func__, ret);
+               return ret;
+       }
+       
+       DBG_SPI("%s:bus_num=%d\n",__func__, dws->bus_num);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_spi_resume_host);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("Driver for DesignWare SPI controller core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-rockchip-core.h b/drivers/spi/spi-rockchip-core.h
new file mode 100755 (executable)
index 0000000..3225a98
--- /dev/null
@@ -0,0 +1,355 @@
+#ifndef DW_SPI_HEADER_H
+#define DW_SPI_HEADER_H
+
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+
+#if 1
+#define DBG_SPI(x...)  if(atomic_read(&dws->debug_flag) == 1) printk(x)
+#else
+#define DBG_SPI(x...)
+#endif
+
+/* SPI register offsets */
+#define SPIM_CTRLR0                            0x0000
+#define SPIM_CTRLR1                            0x0004
+#define SPIM_SSIENR                            0x0008
+#define SPIM_SER                               0x000c
+#define SPIM_BAUDR                             0x0010
+#define SPIM_TXFTLR                            0x0014
+#define SPIM_RXFTLR                            0x0018
+#define SPIM_TXFLR                             0x001c
+#define SPIM_RXFLR                             0x0020
+#define SPIM_SR                                        0x0024
+#define SPIM_IPR                0x0028
+#define SPIM_IMR                               0x002c
+#define SPIM_ISR                               0x0030
+#define SPIM_RISR                              0x0034
+#define SPIM_ICR                               0x0038
+#define SPIM_DMACR                             0x003c
+#define SPIM_DMATDLR                   0x0040
+#define SPIM_DMARDLR                   0x0044
+#define SPIM_TXDR                              0x0400
+#define SPIM_RXDR               0x0800
+
+/* --------Bit fields in CTRLR0--------begin */
+
+#define SPI_DFS_OFFSET                 0                  /* Data Frame Size */
+#define SPI_DFS_4BIT            0x00
+#define SPI_DFS_8BIT            0x01
+#define SPI_DFS_16BIT           0x02
+#define SPI_DFS_RESV            0x03
+
+#define SPI_FRF_OFFSET                 16                 /* Frame Format */
+#define SPI_FRF_SPI                        0x00               /* motorola spi */
+#define SPI_FRF_SSP                        0x01               /* Texas Instruments SSP*/
+#define SPI_FRF_MICROWIRE              0x02               /*  National Semiconductors Microwire */
+#define SPI_FRF_RESV                   0x03
+
+#define SPI_MODE_OFFSET                    6                 /* SCPH & SCOL */
+
+#define SPI_SCPH_OFFSET                        6                  /* Serial Clock Phase */
+#define SPI_SCPH_TOGMID         0                  /* Serial clock toggles in middle of first data bit */
+#define SPI_SCPH_TOGSTA         1                  /* Serial clock toggles at start of first data bit */
+
+#define SPI_SCOL_OFFSET                        7                  /* Serial Clock Polarity */
+
+#define SPI_OPMOD_OFFSET           20
+#define SPI_OPMOD_MASTER        0
+#define SPI_OPMOD_SLAVE         1
+
+#define SPI_TMOD_OFFSET                        18                 /* Transfer Mode */
+//#define SPI_TMOD_MASK                        (0x3 << SPI_TMOD_OFFSET)
+#define        SPI_TMOD_TR                         0x00                       /* xmit & recv */
+#define SPI_TMOD_TO                        0x01                       /* xmit only */
+#define SPI_TMOD_RO                        0x02                       /* recv only */
+#define SPI_TMOD_RESV              0x03
+
+#define SPI_CFS_OFFSET                 2                  /* Control Frame Size */
+
+#define SPI_CSM_OFFSET          8                  /* Chip Select Mode */
+#define SPI_CSM_KEEP            0x00               /* ss_n keep low after every frame data is transferred */
+#define SPI_CSM_HALF            0x01               /* ss_n be high for half sclk_out cycles after every frame data is transferred */
+#define SPI_CSM_ONE             0x02               /* ss_n be high for one sclk_out cycle after every frame data is transferred */
+
+#define SPI_SSN_DELAY_OFFSET    10
+#define SPI_SSN_DELAY_HALF      0x00
+#define SPI_SSN_DELAY_ONE       0x01
+
+#define SPI_HALF_WORLD_TX_OFFSET       13
+#define SPI_HALF_WORLD_ON       0x00
+#define SPI_HALF_WORLD_OFF      0x01
+
+
+/* --------Bit fields in CTRLR0--------end */
+
+
+/* Bit fields in SR, 7 bits */
+#define SR_MASK                                0x7f            /* cover 7 bits */
+#define SR_BUSY                                (1 << 0)
+#define SR_TF_FULL                 (1 << 1)
+#define SR_TF_EMPT                     (1 << 2)
+#define SR_RF_EMPT                 (1 << 3)
+#define SR_RF_FULL                     (1 << 4)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define SPI_INT_TXEI                   (1 << 0)
+#define SPI_INT_TXOI                   (1 << 1)
+#define SPI_INT_RXUI                   (1 << 2)
+#define SPI_INT_RXOI                   (1 << 3)
+#define SPI_INT_RXFI                   (1 << 4)
+
+/* Bit fields in DMACR */
+#define SPI_DMACR_TX_ENABLE     (1 << 1)
+#define SPI_DMACR_RX_ENABLE     (1 << 0)
+
+/* Bit fields in ICR */
+#define SPI_CLEAR_INT_ALL       (1<< 0)
+#define SPI_CLEAR_INT_RXUI      (1 << 1)
+#define SPI_CLEAR_INT_RXOI      (1 << 2)
+#define SPI_CLEAR_INT_TXOI      (1 << 3)
+
+
+#if 0
+
+
+/* Bit fields in CTRLR0 */
+#define SPI_DFS_OFFSET                 0
+
+#define SPI_FRF_OFFSET                 4
+#define SPI_FRF_SPI                    0x0
+#define SPI_FRF_SSP                    0x1
+#define SPI_FRF_MICROWIRE              0x2
+#define SPI_FRF_RESV                   0x3
+
+#define SPI_MODE_OFFSET                        6
+#define SPI_SCPH_OFFSET                        6
+#define SPI_SCOL_OFFSET                        7
+
+#define SPI_TMOD_OFFSET                        8
+#define SPI_TMOD_MASK                  (0x3 << SPI_TMOD_OFFSET)
+#define        SPI_TMOD_TR                     0x0             /* xmit & recv */
+#define SPI_TMOD_TO                    0x1             /* xmit only */
+#define SPI_TMOD_RO                    0x2             /* recv only */
+#define SPI_TMOD_EPROMREAD             0x3             /* eeprom read mode */
+
+#define SPI_SLVOE_OFFSET               10
+#define SPI_SRL_OFFSET                 11
+#define SPI_CFS_OFFSET                 12
+
+/* Bit fields in SR, 7 bits */
+#define SR_MASK                                0x7f            /* cover 7 bits */
+#define SR_BUSY                                (1 << 0)
+#define SR_TF_NOT_FULL                 (1 << 1)
+#define SR_TF_EMPT                     (1 << 2)
+#define SR_RF_NOT_EMPT                 (1 << 3)
+#define SR_RF_FULL                     (1 << 4)
+#define SR_TX_ERR                      (1 << 5)
+#define SR_DCOL                                (1 << 6)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define SPI_INT_TXEI                   (1 << 0)
+#define SPI_INT_TXOI                   (1 << 1)
+#define SPI_INT_RXUI                   (1 << 2)
+#define SPI_INT_RXOI                   (1 << 3)
+#define SPI_INT_RXFI                   (1 << 4)
+#define SPI_INT_MSTI                   (1 << 5)
+
+/* Bit fields in DMACR */
+#define SPI_DMACR_TX_ENABLE     (1 << 1)
+#define SPI_DMACR_RX_ENABLE     (1 << 0)
+
+/* Bit fields in ICR */
+#define SPI_CLEAR_INT_ALL       (1<< 0)
+#define SPI_CLEAR_INT_RXUI      (1 << 1)
+#define SPI_CLEAR_INT_RXOI      (1 << 2)
+#define SPI_CLEAR_INT_TXOI      (1 << 3)
+
+
+/* TX RX interrupt level threshold, max can be 256 */
+#define SPI_INT_THRESHOLD              16
+#endif
+
+enum dw_ssi_type {
+       SSI_MOTO_SPI = 0,
+       SSI_TI_SSP,
+       SSI_NS_MICROWIRE,
+};
+
+struct dw_spi;
+struct dw_spi_dma_ops {
+       int (*dma_init)(struct dw_spi *dws);
+       void (*dma_exit)(struct dw_spi *dws);
+       int (*dma_transfer)(struct dw_spi *dws, int cs_change);
+};
+
+struct dw_spi {
+       struct spi_master       *master;
+       struct spi_device       *cur_dev;
+       struct device           *parent_dev;
+       enum dw_ssi_type        type;
+       char                    name[16];
+
+       struct clk          *clk_spi;
+       struct clk          *pclk_spi;
+
+       void __iomem            *regs;
+       unsigned long           paddr;
+       u32                     iolen;
+       int                     irq;
+       u32                     fifo_len;       /* depth of the FIFO buffer */
+       u32                     max_freq;       /* max bus freq supported */
+
+       u16                     bus_num;
+       u16                     num_cs;         /* supported slave numbers */
+
+       /* Driver message queue */
+       struct workqueue_struct *workqueue;
+       struct work_struct      pump_messages;
+       spinlock_t              lock;
+       struct list_head        queue;
+       int                     busy;
+       int                     run;
+
+       /* Message Transfer pump */
+       struct tasklet_struct   pump_transfers;
+
+       /* Current message transfer state info */
+       struct spi_message      *cur_msg;
+       struct spi_transfer     *cur_transfer;
+       struct chip_data        *cur_chip;
+       struct chip_data        *prev_chip;
+       size_t                  len;
+       void                    *tx;
+       void                    *tx_end;
+       void                    *rx;
+       void                    *rx_end;
+       int                     dma_mapped;
+       dma_addr_t              rx_dma;
+       dma_addr_t              tx_dma;
+       size_t                  rx_map_len;
+       size_t                  tx_map_len;
+       u8                      n_bytes;        /* current is a 1/2 bytes op */
+       u8                      max_bits_per_word;      /* maxim is 16b */
+       u32                     dma_width;
+       int                     cs_change;
+       irqreturn_t             (*transfer_handler)(struct dw_spi *dws);
+       void                    (*cs_control)(struct dw_spi *dws, u32 cs, u8 flag);
+
+       /* Dma info */
+       int                     dma_inited;
+       struct dma_chan         *txchan;
+       struct scatterlist      tx_sgl;
+       struct dma_chan         *rxchan;
+       struct scatterlist      rx_sgl;
+       int                     dma_chan_done;
+       struct device           *dma_dev;
+       dma_addr_t              dma_addr; /* phy address of the Data register */
+       struct dw_spi_dma_ops   *dma_ops;
+       void                    *dma_priv; /* platform relate info */
+       
+       //struct pci_dev                *dmac;
+       atomic_t                debug_flag;
+
+       /* Bus interface info */
+       void                    *priv;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
+};
+
+static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
+{
+       return __raw_readl(dws->regs + offset);
+}
+
+static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
+{
+       __raw_writel(val, dws->regs + offset);
+}
+
+static inline u16 dw_readw(struct dw_spi *dws, u32 offset)
+{
+       return __raw_readw(dws->regs + offset);
+}
+
+static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val)
+{
+       __raw_writew(val, dws->regs + offset);
+}
+
+static inline void spi_enable_chip(struct dw_spi *dws, int enable)
+{
+       dw_writel(dws, SPIM_SSIENR, (enable ? 1 : 0));
+}
+
+static inline void spi_set_clk(struct dw_spi *dws, u16 div)
+{
+       dw_writel(dws, SPIM_BAUDR, div);
+}
+
+static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
+{
+       if (cs > dws->num_cs)
+               return;
+
+       if (dws->cs_control)
+               dws->cs_control(dws, cs, 1);
+
+       dw_writel(dws, SPIM_SER, 1 << cs);
+
+       DBG_SPI("%s:cs=%d\n",__func__,cs);
+}
+
+static  inline void spi_cs_control(struct dw_spi *dws, u32 cs, u8 flag)
+{
+       if (flag)
+               dw_writel(dws, SPIM_SER, 1 << cs);
+       else            
+               dw_writel(dws, SPIM_SER, 0);
+       
+       return;
+}
+
+
+/* Disable IRQ bits */
+static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = dw_readl(dws, SPIM_IMR) & ~mask;
+       dw_writel(dws, SPIM_IMR, new_mask);
+}
+
+/* Enable IRQ bits */
+static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = dw_readl(dws, SPIM_IMR) | mask;
+       dw_writel(dws, SPIM_IMR, new_mask);
+}
+
+/*
+ * Each SPI slave device to work with dw_api controller should
+ * has such a structure claiming its working mode (PIO/DMA etc),
+ * which can be save in the "controller_data" member of the
+ * struct spi_device
+ */
+struct dw_spi_chip {
+       u8 poll_mode;   /* 0 for contoller polling mode */
+       u8 type;        /* SPI/SSP/Micrwire */
+       u8 enable_dma;
+       void (*cs_control)(u32 command);
+};
+
+extern int dw_spi_add_host(struct dw_spi *dws);
+extern void dw_spi_remove_host(struct dw_spi *dws);
+extern int dw_spi_suspend_host(struct dw_spi *dws);
+extern int dw_spi_resume_host(struct dw_spi *dws);
+extern void dw_spi_xfer_done(struct dw_spi *dws);
+
+/* platform related setup */
+extern int dw_spi_dma_init(struct dw_spi *dws); /* Intel MID platforms */
+#endif /* SPIM_HEADER_H */
diff --git a/drivers/spi/spi-rockchip-dma.c b/drivers/spi/spi-rockchip-dma.c
new file mode 100755 (executable)
index 0000000..653396a
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Special handling for DW core on Intel MID platform
+ *
+ * Copyright (c) 2009, Intel Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/spi-rockchip.h>
+
+
+#include "spi-rockchip-core.h"
+
+#ifdef CONFIG_SPI_ROCKCHIP_DMA
+
+struct spi_dma_slave {
+       struct dma_chan *ch;
+       enum dma_transfer_direction direction;
+       unsigned int dmach;
+};
+
+
+struct spi_dma {
+       struct spi_dma_slave    dmas_tx;
+       struct spi_dma_slave    dmas_rx;
+};
+
+static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
+{
+       struct dw_spi *dws = param;
+       int ret = 0;
+       ret = dws->parent_dev && (&dws->parent_dev == chan->device->dev);
+
+       printk("%s:ret=%d\n",__func__, ret);
+       return ret;
+}
+
+
+static int mid_spi_dma_init(struct dw_spi *dws)
+{
+       struct spi_dma *dw_dma = dws->dma_priv;
+       struct spi_dma_slave *rxs, *txs;
+       dma_cap_mask_t mask;
+
+       /*
+        * Get pci device for DMA controller, currently it could only
+        * be the DMA controller of either Moorestown or Medfield
+        */
+       //dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL);
+       //if (!dws->dmac)
+       //      dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       /* 1. Init rx channel */
+       dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
+       if (!dws->rxchan)
+               goto err_exit;
+       rxs = &dw_dma->dmas_rx;
+       //rxs->hs_mode = LNW_DMA_HW_HS;
+       //rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
+       dws->rxchan->private = rxs;
+
+       /* 2. Init tx channel */
+       dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
+       if (!dws->txchan)
+               goto free_rxchan;
+       txs = &dw_dma->dmas_tx;
+       //txs->hs_mode = LNW_DMA_HW_HS;
+       //txs->cfg_mode = LNW_DMA_MEM_TO_PER;
+       dws->txchan->private = txs;
+
+       dws->dma_inited = 1;
+       return 0;
+
+       free_rxchan:
+       dma_release_channel(dws->rxchan);
+       err_exit:
+       return -1;
+
+}
+
+static void mid_spi_dma_exit(struct dw_spi *dws)
+{
+       dma_release_channel(dws->txchan);
+       dma_release_channel(dws->rxchan);
+}
+
+/*
+ * dws->dma_chan_done is cleared before the dma transfer starts,
+ * callback for rx/tx channel will each increment it by 1.
+ * Reaching 2 means the whole spi transaction is done.
+ */
+static void dw_spi_dma_done(void *arg)
+{
+       struct dw_spi *dws = arg;
+
+       if (++dws->dma_chan_done != 2)
+               return;
+       dw_spi_xfer_done(dws);
+}
+
+static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
+{
+       struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
+       struct dma_chan *txchan, *rxchan;
+       struct dma_slave_config txconf, rxconf;
+       u16 dma_ctrl = 0;
+
+       /* 1. setup DMA related registers */
+       if (cs_change) {
+               spi_enable_chip(dws, 0);
+               dw_writew(dws, SPIM_DMARDLR, 0xf);
+               dw_writew(dws, SPIM_DMATDLR, 0x10);
+               if (dws->tx_dma)
+                       dma_ctrl |= 0x2;
+               if (dws->rx_dma)
+                       dma_ctrl |= 0x1;
+               dw_writew(dws, SPIM_DMACR, dma_ctrl);
+               spi_enable_chip(dws, 1);
+       }
+
+       dws->dma_chan_done = 0;
+       txchan = dws->txchan;
+       rxchan = dws->rxchan;
+
+       /* 2. Prepare the TX dma transfer */
+       txconf.direction = DMA_MEM_TO_DEV;
+       txconf.dst_addr = dws->dma_addr;
+       txconf.dst_maxburst = 0x03;//LNW_DMA_MSIZE_16;
+       txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       txconf.device_fc = false;
+
+       txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
+                                      (unsigned long) &txconf);
+
+       memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
+       dws->tx_sgl.dma_address = dws->tx_dma;
+       dws->tx_sgl.length = dws->len;
+
+       txdesc = dmaengine_prep_slave_sg(txchan,
+                               &dws->tx_sgl,
+                               1,
+                               DMA_MEM_TO_DEV,
+                               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+       txdesc->callback = dw_spi_dma_done;
+       txdesc->callback_param = dws;
+
+       /* 3. Prepare the RX dma transfer */
+       rxconf.direction = DMA_DEV_TO_MEM;
+       rxconf.src_addr = dws->dma_addr;
+       rxconf.src_maxburst = 0x03; //LNW_DMA_MSIZE_16;
+       rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       rxconf.device_fc = false;
+
+       rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
+                                      (unsigned long) &rxconf);
+
+       memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
+       dws->rx_sgl.dma_address = dws->rx_dma;
+       dws->rx_sgl.length = dws->len;
+
+       rxdesc = dmaengine_prep_slave_sg(rxchan,
+                               &dws->rx_sgl,
+                               1,
+                               DMA_DEV_TO_MEM,
+                               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
+       rxdesc->callback = dw_spi_dma_done;
+       rxdesc->callback_param = dws;
+
+       /* rx must be started before tx due to spi instinct */
+       rxdesc->tx_submit(rxdesc);
+       txdesc->tx_submit(txdesc);
+       return 0;
+}
+
+static struct dw_spi_dma_ops spi_dma_ops = {
+       .dma_init       = mid_spi_dma_init,
+       .dma_exit       = mid_spi_dma_exit,
+       .dma_transfer   = mid_spi_dma_transfer,
+};
+
+int dw_spi_dma_init(struct dw_spi *dws)
+{
+
+       dws->dma_priv = kzalloc(sizeof(struct spi_dma), GFP_KERNEL);
+       if (!dws->dma_priv)
+               return -ENOMEM;
+       dws->dma_ops = &spi_dma_ops;
+       return 0;
+}
+#endif
+
diff --git a/drivers/spi/spi-rockchip-test.c b/drivers/spi/spi-rockchip-test.c
new file mode 100755 (executable)
index 0000000..a242e45
--- /dev/null
@@ -0,0 +1,256 @@
+/*drivers/serial/spi_test.c -spi test driver\r
+ *\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ */\r
+#include <linux/interrupt.h>\r
+#include <linux/slab.h>\r
+#include <linux/init.h>\r
+#include <linux/module.h>\r
+#include <linux/workqueue.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/delay.h>\r
+#include <linux/clk.h>\r
+#include <linux/fs.h>\r
+#include <linux/dma-mapping.h>\r
+#include <linux/dmaengine.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/pm_runtime.h>\r
+#include <linux/spi/spi.h>\r
+#include <linux/gpio.h>\r
+#include <linux/of.h>\r
+#include <linux/of_gpio.h>\r
+#include <linux/miscdevice.h>\r
+#include <linux/platform_data/spi-rockchip.h>\r
+#include <asm/uaccess.h>\r
+\r
+#include "spi-rockchip-core.h"\r
+\r
+\r
+#define MAX_SPI_BUS_NUM 2\r
+\r
+struct spi_test_data {\r
+       struct device   *dev;\r
+       struct spi_device       *spi;   \r
+       char *rx_buf;\r
+       int rx_len; \r
+       char *tx_buf;\r
+       int tx_len; \r
+};\r
+static struct spi_test_data *g_spi_test_data[MAX_SPI_BUS_NUM];\r
+\r
+\r
+static struct dw_spi_chip spi_test_chip[] = {\r
+{\r
+       //.poll_mode = 1,\r
+       //.enable_dma = 1,\r
+},\r
+{\r
+       //.poll_mode = 1,\r
+       //.enable_dma = 1,\r
+},\r
+\r
+};\r
+\r
+       \r
+static struct spi_board_info board_spi_test_devices[] = {      \r
+//#if defined(CONFIG_SPIM0_RK29)\r
+       {\r
+               .modalias  = "spi_test_bus0",\r
+               .bus_num = 0,   //0 or 1\r
+               .max_speed_hz  = 12*1000*1000,\r
+               .chip_select   = 0,             \r
+               .mode   = SPI_MODE_0,\r
+               .controller_data = &spi_test_chip[0],\r
+       },\r
+//#endif\r
+//#if defined(CONFIG_SPIM1_RK29)\r
+       {\r
+               .modalias  = "spi_test_bus1",\r
+               .bus_num = 1,   //0 or 1\r
+               .max_speed_hz  = 12*1000*1000,\r
+               .chip_select   = 0,             \r
+               .mode   = SPI_MODE_0,\r
+               .controller_data = &spi_test_chip[1],\r
+       }\r
+//#endif\r
+};\r
+\r
+static ssize_t spi_test_write(struct file *file, \r
+                       const char __user *buf, size_t count, loff_t *offset)\r
+{\r
+       u8 nr_buf[8];\r
+       int nr = 0, ret;\r
+       int i = 0;\r
+       struct spi_device *spi = NULL;\r
+       char txbuf[256],rxbuf[256];\r
+\r
+       printk("%s:0:bus=0,cs=0; 1:bus=0,cs=1; 2:bus=1,cs=0; 3:bus=1,cs=1\n",__func__);\r
+\r
+       if(count > 3)\r
+           return -EFAULT;\r
+       \r
+       ret = copy_from_user(nr_buf, buf, count);\r
+       if(ret < 0)\r
+           return -EFAULT;\r
+\r
+       sscanf(nr_buf, "%d", &nr);\r
+       if(nr >= 4 || nr < 0)\r
+       {\r
+               printk("%s:cmd is error\n",__func__);\r
+           return -EFAULT;\r
+       }\r
+       \r
+       for(i=0; i<256; i++)\r
+       txbuf[i] = i;\r
+\r
+\r
+/*\r
+       if((nr == 0) || (nr == 1))\r
+       {\r
+               printk("%s:error SPIM0 need selected\n",__func__);      \r
+               return -EFAULT;\r
+       }\r
+\r
+       if((nr == 2) || (nr == 3))\r
+       {\r
+               printk("%s:error SPIM1 need selected\n",__func__);      \r
+               return -EFAULT;\r
+       }\r
+*/\r
+\r
+       switch(nr)\r
+       {\r
+               case 0: \r
+                       if(!g_spi_test_data[0]->spi)            \r
+                       return -EFAULT;\r
+                       spi = g_spi_test_data[0]->spi;\r
+                       spi->chip_select = 0;\r
+                       break;\r
+               case 1: \r
+                       if(!g_spi_test_data[0]->spi)            \r
+                       return -EFAULT;\r
+                       spi = g_spi_test_data[0]->spi;\r
+                       spi->chip_select = 1;\r
+                       break;\r
+               case 2: \r
+                       if(!g_spi_test_data[1]->spi)            \r
+                       return -EFAULT;\r
+                       spi = g_spi_test_data[1]->spi;\r
+                       spi->chip_select = 0;\r
+                       break;\r
+               case 3: \r
+                       if(!g_spi_test_data[1]->spi)            \r
+                       return -EFAULT;\r
+                       spi = g_spi_test_data[1]->spi;\r
+                       spi->chip_select = 1;\r
+                       break;\r
+               \r
+               default:\r
+                       break;\r
+       }\r
+\r
+       for(i=0; i<1; i++)\r
+       {\r
+               ret = spi_write(spi, txbuf, 256);\r
+               ret = spi_read(spi, rxbuf, 256);\r
+               ret = spi_write_then_read(spi,txbuf,256,rxbuf,256);             \r
+               ret = spi_write_and_read(spi,txbuf,rxbuf,256);\r
+               printk("%s:test %d times\n\n",__func__,i+1);\r
+       }\r
+       \r
+       if(!ret)\r
+       printk("%s:bus_num=%d,chip_select=%d,ok\n",__func__,spi->master->bus_num, spi->chip_select);\r
+       else\r
+       printk("%s:bus_num=%d,chip_select=%d,error\n",__func__,spi->master->bus_num, spi->chip_select);\r
+       \r
+       return count;\r
+}\r
+\r
+\r
+static const struct file_operations spi_test_fops = {\r
+       .write = spi_test_write,\r
+};\r
+\r
+static struct miscdevice spi_test_misc = {\r
+       .minor = MISC_DYNAMIC_MINOR,\r
+       .name = "spi_misc_test",\r
+       .fops = &spi_test_fops,\r
+};\r
+\r
+static int spi_test_probe(struct spi_device *spi)\r
+{      \r
+       struct spi_test_data *spi_test_data;\r
+       int ret;\r
+       int i =0;\r
+       char txbuf[256],rxbuf[256];\r
+       \r
+       if(!spi)        \r
+       return -ENOMEM;\r
+\r
+       if((spi->master->bus_num >= MAX_SPI_BUS_NUM) || (spi->master->bus_num < 0))\r
+       {\r
+               printk("%s:error:bus_num=%d\n",__func__, spi->master->bus_num); \r
+               return -ENOMEM;\r
+       }\r
+       \r
+       spi_test_data = (struct spi_test_data *)kzalloc(sizeof(struct spi_test_data), GFP_KERNEL);\r
+       if(!spi_test_data){\r
+               dev_err(&spi->dev, "ERR: no memory for spi_test_data\n");\r
+               return -ENOMEM;
+       }\r
+\r
+       spi->bits_per_word = 8;\r
+       \r
+       spi_test_data->spi = spi;\r
+       spi_test_data->dev = &spi->dev;\r
+       ret = spi_setup(spi);\r
+       if (ret < 0){\r
+               dev_err(spi_test_data->dev, "ERR: fail to setup spi\n");\r
+               return -1;\r
+       }       \r
+\r
+       g_spi_test_data[spi->master->bus_num] = spi_test_data;\r
+\r
+       printk("%s:bus_num=%d,ok\n",__func__,spi->master->bus_num);\r
+       return ret;\r
+\r
+}\r
+\r
+static const struct spi_device_id spi_test_id[] = {            \r
+       {"spi_test_bus0", 0},\r
+       {"spi_test_bus1", 1},\r
+       {},\r
+};\r
+\r
+\r
+static struct spi_driver spi_test_driver = {\r
+       .driver = {\r
+               .name           = "spi_test",\r
+               .bus            = &spi_bus_type,\r
+               .owner          = THIS_MODULE,\r
+       },\r
+       .id_table = spi_test_id,\r
+\r
+       .probe          = spi_test_probe,\r
+};\r
+\r
+static int __init spi_test_init(void)\r
+{      \r
+       printk("%s\n",__func__);\r
+       spi_register_board_info(board_spi_test_devices, ARRAY_SIZE(board_spi_test_devices));\r
+       misc_register(&spi_test_misc);\r
+       return spi_register_driver(&spi_test_driver);\r
+}\r
+\r
+static void __exit spi_test_exit(void)\r
+{\r
+        misc_deregister(&spi_test_misc);\r
+       return spi_unregister_driver(&spi_test_driver);\r
+}\r
+module_init(spi_test_init);\r
+module_exit(spi_test_exit);\r
+\r
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
new file mode 100755 (executable)
index 0000000..95dec8e
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * rockchip spi interface driver for DW SPI Core
+ *
+ * Copyright (c) 2014, ROCKCHIP Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/spi-rockchip.h>
+
+#include "spi-rockchip-core.h"
+
+
+#define DRIVER_NAME "rockchip_spi_driver_data"
+#define SPI_MAX_FREQUENCY      24000000
+
+struct rockchip_spi_driver_data {
+       struct platform_device *pdev;
+       struct dw_spi   dws;
+       struct rockchip_spi_info *info;
+       struct clk                      *clk_spi;
+       struct clk                      *pclk_spi;
+};
+
+#ifdef CONFIG_OF
+static struct rockchip_spi_info *rockchip_spi_parse_dt(struct device *dev)
+{
+       struct rockchip_spi_info *info;
+       u32 temp;
+
+       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               dev_err(dev, "memory allocation for spi_info failed\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (of_property_read_u32(dev->of_node, "rockchip,spi-src-clk", &temp)) {
+               dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n");
+               info->src_clk_nr = 0;
+       } else {
+               info->src_clk_nr = temp;
+       }
+#if 0
+       if (of_property_read_u32(dev->of_node, "bus-num", &temp)) {
+               dev_warn(dev, "number of bus not specified, assuming bus 0\n");
+               info->bus_num= 0;
+       } else {
+               info->bus_num = temp;
+       }
+#endif
+       if (of_property_read_u32(dev->of_node, "num-cs", &temp)) {
+               dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n");
+               info->num_cs = 1;
+       } else {
+               info->num_cs = temp;
+       }
+
+       if (of_property_read_u32(dev->of_node, "max-freq", &temp)) {
+               dev_warn(dev, "fail to get max-freq\n");
+               info->spi_freq = SPI_MAX_FREQUENCY;
+       } else {
+               info->spi_freq = temp;
+       }
+       
+       //printk("%s:line=%d,src_clk_nr=%d,bus_num=%d,num_cs=%d\n",__func__, __LINE__,info->src_clk_nr,info->bus_num,info->num_cs);
+       
+       return info;
+}
+#else
+static struct rockchip_spi_info *rockchip_spi_parse_dt(struct device *dev)
+{
+       return dev->platform_data;
+}
+#endif
+
+
+static int rockchip_spi_probe(struct platform_device *pdev)
+{
+       struct resource *mem_res;
+       struct resource *res;
+       struct rockchip_spi_driver_data *sdd;
+       struct rockchip_spi_info *info = pdev->dev.platform_data;
+       struct dw_spi *dws;
+       struct spi_master *master;
+       int ret, irq;
+       char clk_name[16];
+
+       if (!info && pdev->dev.of_node) {
+               info = rockchip_spi_parse_dt(&pdev->dev);
+               if (IS_ERR(info))
+                       return PTR_ERR(info);
+       }
+
+       if (!info) {
+               dev_err(&pdev->dev, "platform_data missing!\n");
+               return -ENODEV;
+       }       
+
+       sdd = kzalloc(sizeof(struct rockchip_spi_driver_data), GFP_KERNEL);
+       if (!sdd) {
+               ret = -ENOMEM;
+               goto err_kfree;
+       }
+
+       
+       sdd->pdev = pdev;
+       sdd->info = info;
+       dws = &sdd->dws;
+
+       atomic_set(&dws->debug_flag, 0);//debug flag
+
+       /* Get basic io resource and map it */
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+               return irq;
+       }
+       
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (mem_res == NULL) {
+               dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
+               ret =  -ENXIO;
+               goto err_unmap;
+       }
+       
+       dws->regs = ioremap(mem_res->start, (mem_res->end - mem_res->start) + 1);
+       if (!dws->regs){
+               ret = -EBUSY;
+               goto err_unmap;
+       }
+
+       dws->paddr = mem_res->start;
+       dws->iolen = (mem_res->end - mem_res->start) + 1;
+       
+       printk(KERN_INFO "dws->regs: %p\n", dws->regs);
+
+       //get bus num
+       if (pdev->dev.of_node) {
+               ret = of_alias_get_id(pdev->dev.of_node, "spi");
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
+                               ret);
+                       goto err_release_mem;
+               }
+               info->bus_num = ret;
+       } else {
+               info->bus_num = pdev->id;
+       }
+
+       /* Setup clocks */
+       sdd->clk_spi = devm_clk_get(&pdev->dev, "spi");
+       if (IS_ERR(sdd->clk_spi)) {
+               dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
+               ret = PTR_ERR(sdd->clk_spi);
+               goto err_clk;
+       }
+
+       if (clk_prepare_enable(sdd->clk_spi)) {
+               dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
+               ret = -EBUSY;
+               goto err_clk;
+       }
+       
+       sprintf(clk_name, "pclk_spi%d", info->src_clk_nr);
+       sdd->pclk_spi = devm_clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(sdd->pclk_spi)) {
+               dev_err(&pdev->dev,
+                       "Unable to acquire clock '%s'\n", clk_name);
+               ret = PTR_ERR(sdd->pclk_spi);
+               goto err_pclk;
+       }
+
+       if (clk_prepare_enable(sdd->pclk_spi)) {
+               dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
+               ret = -EBUSY;
+               goto err_pclk;
+       }
+
+       clk_set_rate(sdd->clk_spi, info->spi_freq);
+       
+       dws->max_freq = clk_get_rate(sdd->clk_spi);
+       dws->parent_dev = &pdev->dev;
+       dws->bus_num = info->bus_num;
+       dws->num_cs = info->num_cs;
+       dws->irq = irq;
+       dws->clk_spi = sdd->clk_spi;    
+       dws->pclk_spi = sdd->pclk_spi;
+
+       /*
+        * handling for rockchip paltforms, like dma setup,
+        * clock rate, FIFO depth.
+        */
+       
+#ifdef CONFIG_SPI_ROCKCHIP_DMA
+       ret = dw_spi_dma_init(dws);
+       if (ret)
+               goto err_release_mem;
+#endif
+
+       ret = dw_spi_add_host(dws);
+       if (ret)
+               goto err_release_mem;
+
+       platform_set_drvdata(pdev, sdd);
+
+       printk("%s:num_cs=%d,irq=%d,freq=%d ok\n",__func__, info->num_cs, irq, dws->max_freq);
+       
+       return 0;
+err_release_mem:
+    release_mem_region(mem_res->start, (mem_res->end - mem_res->start) + 1);
+err_pclk:
+       clk_disable_unprepare(sdd->pclk_spi);
+err_clk:
+       clk_disable_unprepare(sdd->clk_spi);
+err_unmap:
+       iounmap(dws->regs);
+err_kfree:
+       kfree(sdd);
+       return ret;
+}
+
+static int rockchip_spi_remove(struct platform_device *pdev)
+{
+       struct rockchip_spi_driver_data *sdd = platform_get_drvdata(pdev);
+       
+       platform_set_drvdata(pdev, NULL);
+       dw_spi_remove_host(&sdd->dws);
+       iounmap(sdd->dws.regs);
+       kfree(sdd);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_spi_suspend(struct device *dev)
+{
+       struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
+       int ret = 0;
+       
+       ret = dw_spi_suspend_host(&sdd->dws);
+       
+       return ret;
+}
+
+static int rockchip_spi_resume(struct device *dev)
+{
+       struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
+       int ret = 0;
+       
+       ret = dw_spi_resume_host(&sdd->dws);    
+
+       return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static int rockchip_spi_runtime_suspend(struct device *dev)
+{
+       struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
+       struct dw_spi *dws = &sdd->dws;
+       
+       clk_disable_unprepare(sdd->clk_spi);
+       clk_disable_unprepare(sdd->pclk_spi);
+
+       
+       DBG_SPI("%s\n",__func__);
+       
+       return 0;
+}
+
+static int rockchip_spi_runtime_resume(struct device *dev)
+{
+       struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
+       struct dw_spi *dws = &sdd->dws;
+
+       clk_prepare_enable(sdd->pclk_spi);
+       clk_prepare_enable(sdd->clk_spi);
+       
+       DBG_SPI("%s\n",__func__);
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops rockchip_spi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
+       SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
+                          rockchip_spi_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rockchip_spi_dt_match[] = {
+       { .compatible = "rockchip,rockchip-spi",
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver rockchip_spi_driver = {
+       .driver = {
+               .name   = "rockchip-spi",
+               .owner = THIS_MODULE,
+               .pm = &rockchip_spi_pm,
+               .of_match_table = of_match_ptr(rockchip_spi_dt_match),
+       },
+       .remove = rockchip_spi_remove,
+};
+MODULE_ALIAS("platform:rockchip-spi");
+
+static int __init rockchip_spi_init(void)
+{
+       return platform_driver_probe(&rockchip_spi_driver, rockchip_spi_probe);
+}
+subsys_initcall(rockchip_spi_init);
+
+static void __exit rockchip_spi_exit(void)
+{
+       platform_driver_unregister(&rockchip_spi_driver);
+}
+module_exit(rockchip_spi_exit);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP SPI Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/spi-rockchip.h b/include/linux/platform_data/spi-rockchip.h
new file mode 100755 (executable)
index 0000000..4b58873
--- /dev/null
@@ -0,0 +1,79 @@
+/* include/linux/platform_data/spi-rockchip.h
+ *
+ * Copyright (C) 2014 Rockchip Electronics Ltd.
+ *     luowei <lw@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ROCKCHIP_PLAT_SPI_H
+#define __ROCKCHIP_PLAT_SPI_H
+
+#include <linux/dmaengine.h>
+
+struct platform_device;
+
+/**
+ * struct rockchip_spi_csinfo - ChipSelect description
+ * @fb_delay: Slave specific feedback delay.
+ *            Refer to FB_CLK_SEL register definition in SPI chapter.
+ * @line: Custom 'identity' of the CS line.
+ *
+ * This is per SPI-Slave Chipselect information.
+ * Allocate and initialize one in machine init code and make the
+ * spi_board_info.controller_data point to it.
+ */
+struct rockchip_spi_csinfo {
+       u8 fb_delay;
+       unsigned line;
+};
+
+/**
+ * struct rockchip_spi_info - SPI Controller defining structure
+ * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.
+ * @num_cs: Number of CS this controller emulates.
+ * @cfg_gpio: Configure pins for this SPI controller.
+ */
+struct rockchip_spi_info {
+       int src_clk_nr;
+       int spi_freq;
+       int num_cs;
+       int bus_num;
+       int (*cfg_gpio)(void);
+       dma_filter_fn filter;
+
+       u8 transfer_mode;/*full or half duplex*/
+       u8 poll_mode;   /* 0 for contoller polling mode */
+       u8 type;        /* SPI/SSP/Micrwire */
+       u8 enable_dma;
+       u8 slave_enable;
+};
+
+/**
+ * rockchip_spi_set_platdata - SPI Controller configure callback by the board
+ *                             initialization code.
+ * @cfg_gpio: Pointer to gpio setup function.
+ * @src_clk_nr: Clock the SPI controller is to use to generate SPI clocks.
+ * @num_cs: Number of elements in the 'cs' array.
+ *
+ * Call this from machine init code for each SPI Controller that
+ * has some chips attached to it.
+ */
+extern void rockchip_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
+                                               int num_cs);
+extern void rockchip_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
+                                               int num_cs);
+extern void rockchip_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
+                                               int num_cs);
+
+/* defined by architecture to configure gpio */
+extern int rockchip_spi0_cfg_gpio(void);
+extern int rockchip_spi1_cfg_gpio(void);
+extern int rockchip_spi2_cfg_gpio(void);
+
+extern struct rockchip_spi_info rockchip_spi0_pdata;
+extern struct rockchip_spi_info rockchip_spi1_pdata;
+extern struct rockchip_spi_info rockchip_spi2_pdata;
+#endif /* __ROCKCHIP_PLAT_SPI_H */