add spi driver
author林辉辉 <lhh@rock-chips.com>
Tue, 11 May 2010 07:34:46 +0000 (07:34 +0000)
committer黄涛 <huangtao@rock-chips.com>
Mon, 21 Jun 2010 05:34:50 +0000 (13:34 +0800)
arch/arm/mach-rk2818/board-midsdk.c
arch/arm/mach-rk2818/clock.c
arch/arm/mach-rk2818/devices.c
arch/arm/mach-rk2818/devices.h
arch/arm/mach-rk2818/include/mach/dma.h
arch/arm/mach-rk2818/include/mach/iomux.h
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/rk2818_spim.c [new file with mode: 0644]
drivers/spi/rk2818_spim.h [new file with mode: 0644]

index 49104bb40c3403e7dfa7c380df291bf922739195..f2707735db3d20749828e9c8de294872f7de8d1b 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/input.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/spi/spi.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -136,11 +137,34 @@ static struct map_desc rk2818_io_desc[] __initdata = {
                .length         = 0xa0000,                      ///apb bus i2s i2c spi no map in this
                .type           = MT_DEVICE
        },
+};
+
+/*****************************************************************************************
+ * SPI devices
+ *author: lhh
+ *****************************************************************************************/
+static struct spi_board_info board_spi_devices[] = {
+       {       /* net chip */
+               .modalias       = "enc28j60",
+               .chip_select    = 1,
+               .max_speed_hz   = 12 * 1000 * 1000,
+               .bus_num        = 0,
+               .mode   = SPI_MODE_0,
+       },
+
+       {       
+               .modalias       = "xpt2046_ts",
+               .chip_select    = 0,
+               .max_speed_hz   = 1000000,
+               .bus_num        = 0,
+               .mode   = SPI_MODE_0,
+       },
 
 };
 
 static struct platform_device *devices[] __initdata = {
        &rk2818_device_uart1,
+       &rk2818_device_spim,
 };
 
 extern struct sys_timer rk2818_timer;
@@ -155,7 +179,10 @@ static void __init machine_rk2818_init_irq(void)
 static void __init machine_rk2818_board_init(void)
 {
        platform_add_devices(devices, ARRAY_SIZE(devices));
-
+       spi_register_board_info(board_spi_devices, ARRAY_SIZE(board_spi_devices));
+       rk2818_mux_api_set(GPIOB4_SPI0CS0_MMC0D4_NAME,IOMUXA_GPIO0_B4); //IOMUXA_SPI0_CSN0);//use for gpio SPI CS0
+       rk2818_mux_api_set(GPIOB0_SPI0CSN1_MMC1PCA_NAME,IOMUXA_GPIO0_B0); //IOMUXA_SPI0_CSN1);//use for gpio SPI CS1
+       rk2818_mux_api_set(GPIOB_SPI0_MMC0_NAME,IOMUXA_SPI0);//use for SPI CLK SDI SDO
 }
 
 static void __init machine_rk2818_mapio(void)
index 228c52ef5af34e71da154de64dd113007421fc82..d7cd503d821f75232d07797ee6db561692fd9fc0 100644 (file)
@@ -915,7 +915,7 @@ static struct clk_lookup clks[] = {
        CLK("rk2818_serial.1", "uart", &uart1_clk),
        CLK1(i2c0),
        CLK1(i2c1),
-       CLK1(spi0),
+       CLK("rk2818_spim.0", "spi", &spi0_clk),
        CLK1(spi1),
        CLK1(pwm),
        CLK1(timer),
index 95359a4e70c1d42a4cf34f95ae70e91a584cdd88..fc429d92595d853e34e895520332cbe5055428b5 100644 (file)
@@ -100,3 +100,25 @@ struct platform_device rk2818_device_uart3 = {
        .resource       = resources_uart3,
 };
 
+/*
+ * rk2818 spi master device
+ */
+static struct resource resources_spim[] = {
+       {
+               .start  = IRQ_NR_SPIM,
+               .end    = IRQ_NR_SPIM,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .start  = RK2818_SPIMASTER_PHYS,
+               .end    = RK2818_SPIMASTER_PHYS + SZ_4K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+struct platform_device rk2818_device_spim = {
+       .name   = "rk2818_spim",
+       .id     = 0,
+       .num_resources  = ARRAY_SIZE(resources_spim),
+       .resource       = resources_spim,
+};
+
index 9228e5398aaaa532e9003c1cf677af0cacb780b8..15766a01d5e64460680565d744301f1e1f1d7732 100644 (file)
 #ifndef __ARCH_ARM_MACH_RK2818_DEVICES_H
 #define __ARCH_ARM_MACH_RK2818_DEVICES_H
 
+extern struct platform_device rk2818_device_uart0;
 extern struct platform_device rk2818_device_uart1;
-
+extern struct platform_device rk2818_device_uart2;
+extern struct platform_device rk2818_device_uart3;
+extern struct platform_device rk2818_device_spim;
 
 #endif
index 22416d0b7abca4ad6eb4536aa119180bbeb6c8de..0ac2ea8c6f06f7ff84dfb6641abad047d1e58b0e 100644 (file)
@@ -1,4 +1,5 @@
-/* arch/arm/mach-rk2818/dma.h
+/*
+ * arch/arm/mach-rk2818/include/mach/dma.h
  *
  * Copyright (C) 2010 ROCKCHIP, Inc.
  *
index d64a2e5de64169b5eec48cfb28f508e5eff3d9d0..f37c9e1a67931895c3bf2a8397e37906e633f9cc 100644 (file)
@@ -37,7 +37,7 @@
 #define  IOMUXA_FLASH_CS67                  (2)
 #define  IOMUXA_GPIO1_A12                   (0)
 #define  IOMUXA_CLKIN_SSINN                 (1)
-#define  IOMUXA_FLASH_CS45                  (2)                        (1)                      
+#define  IOMUXA_FLASH_CS45                  (2)                     
 #define  IOMUXA_GPIO0_B0                    (0)
 #define  IOMUXA_SPI0_CSN1                   (1)
 #define  IOMUXA_SDMMC1_PWR_EN               (2)
index 4b6f7cba3b3dc536d6e8480d5c2b1e14523bb329..60f6a186e640c5531d7866fa1efd3fa4d4e9bdeb 100644 (file)
@@ -248,6 +248,13 @@ config SPI_XILINX
 # Add new SPI master controllers in alphabetical order above this line
 #
 
+config SPIM_RK2818
+       tristate "RK2818 SPI master controller core support"
+       depends on ARCH_RK2818 && SPI_MASTER
+       help
+         general driver for SPI controller core from DesignWare
+
+
 #
 # There are lots of SPI device types, with sensors and memory
 # being probably the most widely used ones.
index 21a118269cac1feb030b23977eb6dd454740345b..ad604874029c0a448b4f3fc578af4e203180e95c 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN)                        += spi_bfin5xx.o
 obj-$(CONFIG_SPI_BITBANG)              += spi_bitbang.o
 obj-$(CONFIG_SPI_AU1550)               += au1550_spi.o
 obj-$(CONFIG_SPI_BUTTERFLY)            += spi_butterfly.o
+obj-$(CONFIG_SPIM_RK2818)              += rk2818_spim.o
 obj-$(CONFIG_SPI_GPIO)                 += spi_gpio.o
 obj-$(CONFIG_SPI_IMX)                  += spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)             += spi_lm70llp.o
diff --git a/drivers/spi/rk2818_spim.c b/drivers/spi/rk2818_spim.c
new file mode 100644 (file)
index 0000000..cd6a2fb
--- /dev/null
@@ -0,0 +1,1039 @@
+/*drivers/serial/rk2818_spim.c - driver for rk2818 spim device 
+ *
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/gpio.h>
+#include <linux/dma-mapping.h>
+#include <asm/dma.h>
+
+#include "rk2818_spim.h"
+#include <linux/spi/spi.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  ///CS0
+#define MRST_SPI_ASSERT1       2  ///CS1
+
+/* 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 */
+
+       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 */
+       int (*write)(struct rk2818_spi *dws);
+       int (*read)(struct rk2818_spi *dws);
+       void (*cs_control)(u32 command);
+};
+
+#ifdef CONFIG_DEBUG_FS
+static int spi_show_regs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+#define SPI_REGS_BUFSIZE       1024
+static ssize_t  spi_show_regs(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct rk2818_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 += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "MRST SPI0 registers:\n");
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "=================================\n");
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL0: \t\t0x%08x\n", rk2818_readl(dws, SPIM_CTRLR0));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "CTRL1: \t\t0x%08x\n", rk2818_readl(dws, SPIM_CTRLR1));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SSIENR: \t0x%08x\n", rk2818_readl(dws, SPIM_SPIENR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SER: \t\t0x%08x\n", rk2818_readl(dws, SPIM_SER));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "BAUDR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_BAUDR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFTLR: \t0x%08x\n", rk2818_readl(dws, SPIM_TXFTLR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFTLR: \t0x%08x\n", rk2818_readl(dws, SPIM_RXFTLR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "TXFLR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_TXFLR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "RXFLR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_RXFLR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "SR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_SR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "IMR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_IMR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "ISR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_ISR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMACR: \t\t0x%08x\n", rk2818_readl(dws, SPIM_DMACR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMATDLR: \t0x%08x\n", rk2818_readl(dws, SPIM_DMATDLR));
+       len += printk(buf + len, SPI_REGS_BUFSIZE - len,
+                       "DMARDLR: \t0x%08x\n", rk2818_readl(dws, SPIM_DMARDLR));
+       len += printk(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 mrst_spi_regs_ops = {
+       .owner          = THIS_MODULE,
+       .open           = spi_show_regs_open,
+       .read           = spi_show_regs,
+};
+
+static int mrst_spi_debugfs_init(struct rk2818_spi *dws)
+{
+       dws->debugfs = debugfs_create_dir("mrst_spi", NULL);
+       if (!dws->debugfs)
+               return -ENOMEM;
+
+       debugfs_create_file("registers", S_IFREG | S_IRUGO,
+               dws->debugfs, (void *)dws, &mrst_spi_regs_ops);
+       return 0;
+}
+
+static void mrst_spi_debugfs_remove(struct rk2818_spi *dws)
+{
+       if (dws->debugfs)
+               debugfs_remove_recursive(dws->debugfs);
+}
+
+#else
+static inline int mrst_spi_debugfs_init(struct rk2818_spi *dws)
+{
+       return 0;
+}
+
+static inline void mrst_spi_debugfs_remove(struct rk2818_spi *dws)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void wait_till_not_busy(struct rk2818_spi *dws)
+{
+       unsigned long end = jiffies + 1 + usecs_to_jiffies(1000);
+
+       while (time_before(jiffies, end)) {
+               if (!(rk2818_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 rk2818_spi *dws)
+{
+       while (rk2818_readw(dws, SPIM_SR) & SR_RF_NOT_EMPT)
+               rk2818_readw(dws, SPIM_DR0);
+
+       wait_till_not_busy(dws);
+}
+
+static void null_cs_control(u32 command)
+{
+               if(command == 2)        
+                       gpio_direction_output(RK2818_PIN_PB0,GPIO_LOW);
+               else if(command == 1)
+                       gpio_direction_output(RK2818_PIN_PB4,GPIO_LOW);
+               else{
+                       gpio_direction_output(RK2818_PIN_PB0,GPIO_HIGH);
+                       gpio_direction_output(RK2818_PIN_PB4,GPIO_HIGH);
+               }
+}
+
+static int null_writer(struct rk2818_spi *dws)
+{
+       u8 n_bytes = dws->n_bytes;
+
+       if (!(rk2818_readw(dws, SPIM_SR) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+       rk2818_writew(dws, SPIM_DR0, 0);
+       dws->tx += n_bytes;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int null_reader(struct rk2818_spi *dws)
+{
+       u8 n_bytes = dws->n_bytes;
+       while ((rk2818_readw(dws, SPIM_SR) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               rk2818_readw(dws, SPIM_DR0);
+               dws->rx += n_bytes;
+       }
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static int u8_writer(struct rk2818_spi *dws)
+{      
+       if (!(rk2818_readw(dws, SPIM_SR) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+       rk2818_writew(dws, SPIM_DR0, *(u8 *)(dws->tx));
+       ++dws->tx;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int u8_reader(struct rk2818_spi *dws)
+{
+       while ((rk2818_readw(dws, SPIM_SR) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               *(u8 *)(dws->rx) = rk2818_readw(dws, SPIM_DR0);
+               ++dws->rx;
+       }
+
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static int u16_writer(struct rk2818_spi *dws)
+{
+       if (!(rk2818_readw(dws, SPIM_SR) & SR_TF_NOT_FULL)
+               || (dws->tx == dws->tx_end))
+               return 0;
+
+       rk2818_writew(dws, SPIM_DR0, *(u16 *)(dws->tx));
+       dws->tx += 2;
+
+       wait_till_not_busy(dws);
+       return 1;
+}
+
+static int u16_reader(struct rk2818_spi *dws)
+{
+       u16 temp;
+
+       while ((rk2818_readw(dws, SPIM_SR) & SR_RF_NOT_EMPT)
+               && (dws->rx < dws->rx_end)) {
+               temp = rk2818_readw(dws, SPIM_DR0);
+               *(u16 *)(dws->rx) = temp;
+               dws->rx += 2;
+       }
+
+       wait_till_not_busy(dws);
+       return dws->rx == dws->rx_end;
+}
+
+static void *next_transfer(struct rk2818_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 rk2818_spi *dws)
+{              
+       if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited
+               || !dws->cur_chip->enable_dma)
+               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;
+
+       return 1;
+}
+
+/* Caller already set message->status; dma and pio irqs are blocked */
+static void giveback(struct rk2818_spi *dws)
+{
+       struct spi_transfer *last_transfer;
+       unsigned long flags;
+       struct spi_message *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);
+       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(MRST_SPI_DEASSERT);
+
+       msg->state = NULL;
+       if (msg->complete)
+               msg->complete(msg->context);
+}
+
+static void int_error_stop(struct rk2818_spi *dws, const char *msg)
+{
+       /* Stop and reset 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);
+}
+
+static void transfer_complete(struct rk2818_spi *dws)
+{
+       /* Update total byte transfered 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);
+}
+
+static irqreturn_t interrupt_transfer(struct rk2818_spi *dws)
+{
+       u16 irq_status, irq_mask = 0x3f;
+       u32 int_level = dws->fifo_len / 2;
+       u32 left;
+       
+       irq_status = rk2818_readw(dws, SPIM_ISR) & irq_mask;
+       /* Error handling */
+       if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
+               rk2818_readw(dws, SPIM_TXOICR);
+               rk2818_readw(dws, SPIM_RXOICR);
+               rk2818_readw(dws, SPIM_RXUICR);
+               int_error_stop(dws, "interrupt_transfer: fifo overrun");
+               return IRQ_HANDLED;
+       }
+
+       if (irq_status & SPI_INT_TXEI) {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+
+               left = (dws->tx_end - dws->tx) / dws->n_bytes;
+               left = (left > int_level) ? int_level : left;
+
+               while (left--)
+                       dws->write(dws);
+               dws->read(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
+                       transfer_complete(dws);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t rk2818_spi_irq(int irq, void *dev_id)
+{
+       struct rk2818_spi *dws = dev_id;
+
+       if (!dws->cur_msg) {
+               spi_mask_intr(dws, SPI_INT_TXEI);
+               /* Never fail */
+               return IRQ_HANDLED;
+       }
+
+       return dws->transfer_handler(dws);
+}
+
+/* Must be called inside pump_transfers() */
+static void poll_transfer(struct rk2818_spi *dws)
+{
+       while (dws->write(dws))
+               dws->read(dws);
+
+       transfer_complete(dws);
+}
+
+static void dma_transfer(struct rk2818_spi *dws, struct spi_transfer *xfer) //int cs_change)
+{
+       
+}
+
+static void pump_transfers(unsigned long data)
+{
+       struct rk2818_spi *dws = (struct rk2818_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 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 = clk_get_rate(dws->clock_spim) / 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->write = dws->tx ? chip->write : null_writer;
+       dws->read = dws->rx ? chip->read : null_reader;
+       dws->cs_change = transfer->cs_change;
+       dws->len = dws->cur_transfer->len;
+       if (chip != dws->prev_chip)
+               cs_change = 1;
+
+       cr0 = chip->cr0;
+
+       /* 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 > clk_get_rate(dws->clock_spim)) {
+                               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 = clk_get_rate(dws->clock_spim) / 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:
+                       dws->n_bytes = 1;
+                       dws->dma_width = 1;
+                       dws->read = (dws->read != null_reader) ?
+                                       u8_reader : null_reader;
+                       dws->write = (dws->write != null_writer) ?
+                                       u8_writer : null_writer;
+                       break;
+               case 16:
+                       dws->n_bytes = 2;
+                       dws->dma_width = 2;
+                       dws->read = (dws->read != null_reader) ?
+                                       u16_reader : null_reader;
+                       dws->write = (dws->write != null_writer) ?
+                                       u16_writer : null_writer;
+                       break;
+               default:
+                       printk(KERN_ERR "MRST SPI0: unsupported bits:"
+                               "%db\n", bits);
+                       message->status = -EIO;
+                       goto early_exit;
+               }
+
+               cr0 = (bits - 1)
+                       | (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 = 0x00;
+               else if (dws->rx)
+                       chip->tmode = 0x02;
+               else
+                       chip->tmode = 0x01;
+
+               cr0 &= ~(0x3 << SPI_MODE_OFFSET);
+               cr0 |= (chip->tmode << SPI_TMOD_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 = dws->len / dws->n_bytes;
+               txint_level = dws->fifo_len / 2;
+               txint_level = (templen > txint_level) ? txint_level : templen;
+
+               imask |= SPI_INT_TXEI;
+               dws->transfer_handler = interrupt_transfer;
+       }
+
+       /*
+        * Reprogram registers only if
+        *      1. chip select changes
+        *      2. clk_div is changed
+        *      3. control value changes
+        */
+       if (rk2818_readw(dws, SPIM_CTRLR0) != cr0 || cs_change || clk_div || imask) {
+               spi_enable_chip(dws, 0);
+               if (rk2818_readw(dws, SPIM_CTRLR0) != cr0)
+                       rk2818_writew(dws, SPIM_CTRLR0, cr0);
+
+               spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);            
+               spi_chip_sel(dws, spi->chip_select);
+               /* Set the interrupt mask, for poll mode just diable all int */
+               spi_mask_intr(dws, 0xff);
+               if (imask)
+                       spi_umask_intr(dws, imask);
+               if (txint_level)
+                       rk2818_writew(dws, SPIM_TXFTLR, txint_level);
+
+               spi_enable_chip(dws, 1);
+               if (cs_change)
+                       dws->prev_chip = chip;
+       }
+
+       if (dws->dma_mapped)
+               dma_transfer(dws, transfer); ///cs_change);
+
+       if (chip->poll_mode)
+               poll_transfer(dws);
+
+       return;
+
+early_exit:
+       giveback(dws);
+       return;
+}
+
+static void pump_messages(struct work_struct *work)
+{
+       struct rk2818_spi *dws =
+               container_of(work, struct rk2818_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);
+}
+
+/* spi_device use this to queue in their spi_msg */
+static int rk2818_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+       struct rk2818_spi *dws = spi_master_get_devdata(spi->master);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dws->lock, flags);
+
+       if (dws->run == QUEUE_STOPPED) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return -ESHUTDOWN;
+       }
+
+       msg->actual_length = 0;
+       msg->status = -EINPROGRESS;
+       msg->state = START_STATE;
+
+       list_add_tail(&msg->queue, &dws->queue);
+
+       if (dws->run == QUEUE_RUNNING && !dws->busy) {
+
+               if (dws->cur_transfer || dws->cur_msg)
+                       queue_work(dws->workqueue,
+                                       &dws->pump_messages);
+               else {
+                       /* If no other data transaction in air, just go */
+                       spin_unlock_irqrestore(&dws->lock, flags);
+                       pump_messages(&dws->pump_messages);
+                       return 0;
+               }
+       }
+
+       spin_unlock_irqrestore(&dws->lock, flags);
+       
+       return 0;
+}
+
+/* This may be called twice for each spi dev */
+static int rk2818_spi_setup(struct spi_device *spi)
+{
+       struct rk2818_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 = null_cs_control;
+               chip->enable_dma = 1;  //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;
+               chip->read = u8_reader;
+               chip->write = u8_writer;
+       } else if (spi->bits_per_word <= 16) {
+               chip->n_bytes = 2;
+               chip->dma_width = 2;
+               chip->read = u16_reader;
+               chip->write = u16_writer;
+       } 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->bits_per_word - 1)
+                       | (chip->type << SPI_FRF_OFFSET)
+                       | (spi->mode  << SPI_MODE_OFFSET)
+                       | (chip->tmode << SPI_TMOD_OFFSET);
+
+       spi_set_ctldata(spi, chip);
+       return 0;
+}
+
+static void rk2818_spi_cleanup(struct spi_device *spi)
+{
+       struct chip_data *chip = spi_get_ctldata(spi);
+       kfree(chip);
+}
+
+static int __devinit init_queue(struct rk2818_spi *dws)
+{
+       INIT_LIST_HEAD(&dws->queue);
+       spin_lock_init(&dws->lock);
+
+       dws->run = QUEUE_STOPPED;
+       dws->busy = 0;
+
+       tasklet_init(&dws->pump_transfers,
+                       pump_transfers, (unsigned long)dws);
+
+       INIT_WORK(&dws->pump_messages, pump_messages);
+       dws->workqueue = create_singlethread_workqueue(
+                                       dev_name(dws->master->dev.parent));
+       if (dws->workqueue == NULL)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int start_queue(struct rk2818_spi *dws)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dws->lock, flags);
+
+       if (dws->run == QUEUE_RUNNING || dws->busy) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               return -EBUSY;
+       }
+
+       dws->run = QUEUE_RUNNING;
+       dws->cur_msg = NULL;
+       dws->cur_transfer = NULL;
+       dws->cur_chip = NULL;
+       dws->prev_chip = NULL;
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       queue_work(dws->workqueue, &dws->pump_messages);
+
+       return 0;
+}
+
+static int stop_queue(struct rk2818_spi *dws)
+{
+       unsigned long flags;
+       unsigned limit = 50;
+       int status = 0;
+
+       spin_lock_irqsave(&dws->lock, flags);
+       dws->run = QUEUE_STOPPED;
+       while (!list_empty(&dws->queue) && dws->busy && limit--) {
+               spin_unlock_irqrestore(&dws->lock, flags);
+               msleep(10);
+               spin_lock_irqsave(&dws->lock, flags);
+       }
+
+       if (!list_empty(&dws->queue) || dws->busy)
+               status = -EBUSY;
+       spin_unlock_irqrestore(&dws->lock, flags);
+
+       return status;
+}
+
+static int destroy_queue(struct rk2818_spi *dws)
+{
+       int status;
+
+       status = stop_queue(dws);
+       if (status != 0)
+               return status;
+       destroy_workqueue(dws->workqueue);
+       return 0;
+}
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+static void spi_hw_init(struct rk2818_spi *dws)
+{
+       spi_enable_chip(dws, 0);
+       spi_mask_intr(dws, 0xff);
+       spi_enable_chip(dws, 1);
+       flush(dws);
+
+       /*
+        * 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++) {
+                       rk2818_writew(dws, SPIM_TXFTLR, fifo);
+                       if (fifo != rk2818_readw(dws, SPIM_TXFTLR))
+                               break;
+               }
+
+               dws->fifo_len = (fifo == 31) ? 0 : fifo;
+               rk2818_writew(dws, SPIM_TXFTLR, 0);
+       }
+}
+
+static int __init rk2818_spim_probe(struct platform_device *pdev)
+{
+       struct resource         *regs;
+       struct rk2818_spi   *dws;
+       struct spi_master   *master;
+       int                     irq;
+       int         ret;
+               
+       gpio_request(RK2818_PIN_PB0, "rk2818_spim");    
+       gpio_request(RK2818_PIN_PB4, "rk2818_spim");
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!regs)
+               return -ENXIO;  
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;                     
+       /* setup spi core then atmel-specific driver state */
+       ret = -ENOMEM;  
+       master = spi_alloc_master(&pdev->dev, sizeof *dws);
+       if (!master) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+       platform_set_drvdata(pdev, master);
+       dws = spi_master_get_devdata(master);
+       dws->clock_spim = clk_get(&pdev->dev, "spi");
+       clk_enable(dws->clock_spim);    
+       if (IS_ERR(dws->clock_spim))
+               return PTR_ERR(dws->clock_spim);
+       dws->regs = ioremap(regs->start, (regs->end - regs->start) + 1);
+       if (!dws->regs)
+         goto exit;
+    dws->irq = irq;      
+       dws->master = master;
+       dws->type = SSI_MOTO_SPI;
+       dws->prev_chip = NULL;
+       dws->dma_inited = 1;  ///0;     
+       ///dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
+       ret = request_irq(dws->irq, rk2818_spi_irq, 0,
+                       "rk2818_spim", 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 = pdev->id;
+       master->num_chipselect = 2;
+       master->cleanup = rk2818_spi_cleanup;
+       master->setup = rk2818_spi_setup;
+       master->transfer = rk2818_spi_transfer;
+       dws->pdev = pdev;
+       /* Basic HW init */
+       spi_hw_init(dws);
+       /* Initial and start queue */
+       ret = init_queue(dws);
+       if (ret) {
+               dev_err(&master->dev, "problem initializing queue\n");
+               goto err_diable_hw;
+       }
+       ret = start_queue(dws);
+       if (ret) {
+               dev_err(&master->dev, "problem starting queue\n");
+               goto err_diable_hw;
+       }
+       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;
+       }
+       printk(KERN_INFO "rk2818_spim: driver initialized\n");
+       mrst_spi_debugfs_init(dws);
+       return 0;
+
+err_queue_alloc:
+       destroy_queue(dws);
+err_diable_hw:
+       spi_enable_chip(dws, 0);
+       free_irq(dws->irq, dws);
+err_free_master:
+       spi_master_put(master);
+       iounmap(dws->regs);
+exit:
+       return ret;
+}
+
+static void __exit rk2818_spim_remove(struct platform_device *pdev)
+{
+       struct rk2818_spi *dws = platform_get_drvdata(pdev);
+       int status = 0;
+
+       if (!dws)
+               return;
+       mrst_spi_debugfs_remove(dws);
+
+       /* Remove the queue */
+       status = destroy_queue(dws);
+       if (status != 0)
+               dev_err(&dws->master->dev, "rk2818_spi_remove: workqueue will not "
+                       "complete, message memory not freed\n");
+       clk_put(dws->clock_spim);
+       clk_disable(dws->clock_spim);
+       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);
+       iounmap(dws->regs);
+}
+
+
+#ifdef CONFIG_PM
+
+static int rk2818_spim_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+    struct spi_master  *master = platform_get_drvdata(pdev);
+    struct rk2818_spi  *dws = spi_master_get_devdata(master);
+    clk_disable(dws->clock_spim);
+    return 0;
+}
+
+static int rk2818_spim_resume(struct platform_device *pdev)
+{
+       struct rk2818_spi *dws = platform_get_drvdata(pdev);
+       int ret;
+       
+       clk_enable(dws->clock_spim);    
+       spi_hw_init(dws);
+       ret = start_queue(dws);
+       if (ret)
+               dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
+       return ret;
+}
+
+#else
+#define        rk2818_spim_suspend     NULL
+#define        rk2818_spim_resume      NULL
+#endif
+
+static struct platform_driver rk2818_platform_spim_driver = {
+       .remove         = __exit_p(rk2818_spim_remove),
+       .driver         = {
+               .name   = "rk2818_spim",
+               .owner  = THIS_MODULE,
+       },
+       .suspend        = rk2818_spim_suspend,
+       .resume         = rk2818_spim_resume,
+};
+
+static int __init rk2818_spim_init(void)
+{
+       int ret;
+       ret = platform_driver_probe(&rk2818_platform_spim_driver, rk2818_spim_probe);   
+       return ret;
+}
+
+static void __exit rk2818_spim_exit(void)
+{
+       platform_driver_unregister(&rk2818_platform_spim_driver);
+}
+
+module_init(rk2818_spim_init);
+module_exit(rk2818_spim_exit);
+
+MODULE_AUTHOR("lhh lhh@rock-chips.com");
+MODULE_DESCRIPTION("Rockchip RK2818 spim port driver");
+MODULE_LICENSE("GPL");;
diff --git a/drivers/spi/rk2818_spim.h b/drivers/spi/rk2818_spim.h
new file mode 100644 (file)
index 0000000..1a91ef4
--- /dev/null
@@ -0,0 +1,223 @@
+/* drivers/spi/rk2818_spim.h
+ *
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+#ifndef __DRIVERS_SPIM_RK2818_HEADER_H
+#define __DRIVERS_SPIM_RK2818_HEADER_H
+#include <linux/io.h>
+
+/* 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_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)
+
+/* TX RX interrupt level threshhold, max can be 256 */
+#define SPI_INT_THRESHOLD              32
+
+#define SPIM_CTRLR0                            0x0000
+#define SPIM_CTRLR1                            0x0004
+#define SPIM_SPIENR                            0x0008
+#define SPIM_MWCR                              0x000c
+#define SPIM_SER                               0x0010
+#define SPIM_BAUDR                             0x0014
+#define SPIM_TXFTLR                            0x0018
+#define SPIM_RXFTLR                            0x001c
+#define SPIM_TXFLR                             0x0020
+#define SPIM_RXFLR                             0x0024
+#define SPIM_SR                                        0x0028
+#define SPIM_IMR                               0x002c
+#define SPIM_ISR                               0x0030
+#define SPIM_RISR                              0x0034
+#define SPIM_TXOICR                            0x0038
+#define SPIM_RXOICR                            0x003c
+#define SPIM_RXUICR                            0x0040
+#define SPIM_MSTICR                            0x0044
+#define SPIM_ICR                               0x0048
+#define SPIM_DMACR                             0x004c
+#define SPIM_DMATDLR                   0x0050
+#define SPIM_DMARDLR                   0x0054
+#define SPIM_IDR0                              0x0058
+#define SPIM_COMP_VERSION              0x005c
+#define SPIM_DR0                               0x0060
+
+enum rk2818_ssi_type {
+       SSI_MOTO_SPI = 0,
+       SSI_TI_SSP,
+       SSI_NS_MICROWIRE,
+};
+
+struct rk2818_spi {
+       struct spi_master       *master;
+       struct spi_device       *cur_dev;
+       enum rk2818_ssi_type    type;
+
+       void __iomem            *regs;
+       unsigned long           paddr;
+       u32                     iolen;
+       int                     irq;
+       u32                     fifo_len;       /* depth of the FIFO buffer */
+       struct clk      *clock_spim;    /* clk apb */
+       struct platform_device  *pdev;
+       
+       /* 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;
+       int                     (*write)(struct rk2818_spi *dws);
+       int                     (*read)(struct rk2818_spi *dws);
+       irqreturn_t             (*transfer_handler)(struct rk2818_spi *dws);
+       void                    (*cs_control)(u32 command);
+
+       /* Dma info */
+       int                     dma_inited;
+       struct dma_chan         *txchan;
+       struct dma_chan         *rxchan;
+       int                     txdma_done;
+       int                     rxdma_done;
+       u64                     tx_param;
+       u64                     rx_param;
+       struct device           *dma_dev;
+       dma_addr_t              dma_addr;
+
+       /* Bus interface info */
+       void                    *priv;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
+};
+
+#define rk2818_readl(dw, off) \
+       __raw_readl(dw->regs + off)
+#define rk2818_writel(dw,off,val) \
+       __raw_writel(val, dw->regs + off)
+#define rk2818_readw(dw, off) \
+       __raw_readw(dw->regs + off)
+#define rk2818_writew(dw,off,val) \
+       __raw_writel(val, dw->regs + off)
+
+static inline void spi_enable_chip(struct rk2818_spi *dws, int enable)
+{
+       rk2818_writel(dws, SPIM_SPIENR, (enable ? 1 : 0));
+}
+
+static inline void spi_set_clk(struct rk2818_spi *dws, u16 div)
+{
+       rk2818_writel(dws, SPIM_BAUDR, div);
+}
+
+static inline void spi_chip_sel(struct rk2818_spi *dws, u16 cs)
+{
+       if (cs > 1)
+               return;
+
+       if (dws->cs_control){
+               if(cs == 1)
+                       dws->cs_control(2);     
+               else
+                       dws->cs_control(1);
+       }
+       rk2818_writel(dws, SPIM_SER, 1 << cs);
+}
+
+/* Disable IRQ bits */
+static inline void spi_mask_intr(struct rk2818_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = rk2818_readl(dws, SPIM_IMR) & ~mask;
+       rk2818_writel(dws, SPIM_IMR, new_mask);
+}
+
+/* Enable IRQ bits */
+static inline void spi_umask_intr(struct rk2818_spi *dws, u32 mask)
+{
+       u32 new_mask;
+
+       new_mask = rk2818_readl(dws, SPIM_IMR) | mask;
+       rk2818_writel(dws, SPIM_IMR, new_mask);
+}
+
+/*
+ * Each SPI slave device to work with rk2818_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 rk2818_spi_chip {
+       u8 poll_mode;   /* 0 for contoller polling mode */
+       u8 type;        /* SPI/SSP/Micrwire */
+       u8 enable_dma;
+       void (*cs_control)(u32 command);
+};
+
+#endif /* __DRIVERS_SPIM_RK2818_HEADER_H */