spi: add spi transfer mode, full duplex and half duplex
authorlyx <lyx@rock-chips.com>
Fri, 24 Sep 2010 06:36:32 +0000 (23:36 -0700)
committerlyx <lyx@rock-chips.com>
Fri, 24 Sep 2010 06:37:07 +0000 (23:37 -0700)
arch/arm/mach-rk2818/board-raho.c
drivers/spi/rk2818_spim.c
drivers/spi/rk2818_spim.h

index 7920817f9ed1a6abc1b35e8d516ea097ff7987c2..b68ed5cdddbfa98b796873bb932d976e11020bb6 100755 (executable)
@@ -1367,6 +1367,10 @@ static struct xpt2046_platform_data xpt2046_info = {
 };
 #endif
 
+static struct rk2818_spi_chip cmb_spi_chip = {
+       .transfer_mode = RK2818_SPI_FULL_DUPLEX,
+};
+
 static struct spi_board_info board_spi_devices[] = {
 #if defined(CONFIG_SPI_FPGA)
        {       /* fpga ice65l08xx */
@@ -1405,6 +1409,7 @@ static struct spi_board_info board_spi_devices[] = {
                .max_speed_hz   = 12*1000*1000,         
                .bus_num        = 0,
                .irq            = RK2818_PIN_PA6,
+               .controller_data = &cmb_spi_chip,
        },
 #endif
 }; 
index 8c47834b1784b952c6e14d6f60756f36d30061d0..d5f408efa714d868708cbe6d5d1c1621e2f05569 100755 (executable)
 
 /*Ô­ÓеÄspiÇý¶¯Ð§ÂʱȽϵͣ¬
 ÎÞ·¨Âú×ã´óÊý¾ÝÁ¿µÄ´«Ê䣻
-ÏÂÃæÌá¸ßÁ½ÖÖÓÅ»¯ºóµÄspiÇý¶¯£¬
-QUICK_INT_TRANSFERʹÓÃÖжϣ¬QUICK_TRANSFERÖ±½Ó´¦Àí£¬
-Á½ÖÖ·½Ê½×î¶àÖ»ÄÜ´ò¿ªÒ»ÖÖ*/
+QUICK_TRANSFERÓÃÓÚ¿ìËÙ´«Ê䣬ͬʱ¿ÉÖ¸¶¨°ëË«¹¤»òȫ˫¹¤£¬
+ĬÈÏʹÓðëË«¹¤
+*/
 
 #define QUICK_TRANSFER         
-//#define QUICK_INT_TRANSFER
 
 #if 0
 #define DBG(x...)   printk(x)
@@ -236,8 +235,8 @@ static int null_writer(struct rk2818_spi *dws)
                return 0;
        rk2818_writew(dws, SPIM_DR0, 0);
        dws->tx += n_bytes;
-
        //wait_till_not_busy(dws);
+
        return 1;
 }
 
@@ -260,8 +259,8 @@ static int u8_writer(struct rk2818_spi *dws)
                return 0;
        rk2818_writew(dws, SPIM_DR0, *(u8 *)(dws->tx));
        ++dws->tx;
-
        //wait_till_not_busy(dws);
+
        return 1;
 }
 
@@ -285,8 +284,8 @@ static int u16_writer(struct rk2818_spi *dws)
 
        rk2818_writew(dws, SPIM_DR0, *(u16 *)(dws->tx));
        dws->tx += 2;
-
        //wait_till_not_busy(dws);
+
        return 1;
 }
 
@@ -737,6 +736,7 @@ static int rk2818_spi_transfer(struct spi_device *spi, struct spi_message *msg)
        return 0;
 }
 
+#if defined(QUICK_TRANSFER)
 static void do_read(struct rk2818_spi *dws)
 {
        int count = 0;
@@ -744,11 +744,11 @@ static void do_read(struct rk2818_spi *dws)
        spi_enable_chip(dws, 0);
        rk2818_writew(dws, SPIM_CTRLR1, dws->rx_end-dws->rx-1);
        spi_enable_chip(dws, 1);                
-       rk2818_writew(dws, SPIM_DR0, 0);
        while (1) {
+               rk2818_writew(dws, SPIM_DR0, 0);
                if (dws->read(dws))
                        break;
-               if (count ++ == 0x10) {
+               if (count ++ == 0x20) {
                        printk("+++++++++++spi receive data time out+++++++++++++\n");
                        break;
                }
@@ -763,9 +763,6 @@ static void do_write(struct rk2818_spi *dws)
        }
 }
 
-
-#if defined(QUICK_TRANSFER)
-
 /* Caller already set message->status; dma and pio irqs are blocked */
 static void msg_giveback(struct rk2818_spi *dws)
 {
@@ -793,7 +790,42 @@ static void msg_giveback(struct rk2818_spi *dws)
 }
 
 /* Must be called inside pump_transfers() */
-static int do_transfer(struct rk2818_spi *dws)
+static int do_full_transfer(struct rk2818_spi *dws)
+{
+       if ((dws->read(dws))) {
+               goto comple;
+       }
+       
+       while (dws->tx<dws->tx_end){
+               dws->write(dws);                
+               dws->read(dws);
+       }
+       
+       if (dws->rx < dws->rx_end) {
+               dws->read(dws);
+       }
+
+comple:
+       
+       dws->cur_msg->actual_length += dws->len;
+       
+       /* Move to next transfer */
+       dws->cur_msg->state = next_transfer(dws);
+                                       
+       if (dws->cur_msg->state == DONE_STATE) {
+               dws->cur_msg->status = 0;
+               msg_giveback(dws);
+               return 0;
+       }
+       else {
+               return -1;
+       }
+       
+}
+
+
+/* Must be called inside pump_transfers() */
+static int do_half_transfer(struct rk2818_spi *dws)
 {
        if (dws->rx) {
                if (dws->tx) {
@@ -822,10 +854,10 @@ static int do_transfer(struct rk2818_spi *dws)
        else {
                return -1;
        }
-       
 }
 
-static int rk2818_pump_transfers(struct rk2818_spi *dws)
+
+static int rk2818_pump_transfers(struct rk2818_spi *dws, int mode)
 {
        struct spi_message *message = NULL;
        struct spi_transfer *transfer = NULL;
@@ -972,20 +1004,21 @@ static int rk2818_pump_transfers(struct rk2818_spi *dws)
         *      2. clk_div is changed
         *      3. control value changes
         */
-       if (rk2818_readw(dws, SPIM_CTRLR0) != cr0 || cs_change || clk_div) {
                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);
+               spi_chip_sel(dws, spi->chip_select);            
+               rk2818_writew(dws, SPIM_CTRLR1, 0);//add by lyx
                spi_enable_chip(dws, 1);
                if (cs_change)
                        dws->prev_chip = chip;
-       }
 
-       return do_transfer(dws);
-       
+       if (mode)
+               return do_full_transfer(dws);
+       else
+               return do_half_transfer(dws);
 early_exit:
        
        msg_giveback(dws);
@@ -993,10 +1026,8 @@ early_exit:
        return 0;
 }
 
-static void rk2818_pump_messages(struct work_struct *work)
+static void rk2818_pump_messages(struct rk2818_spi *dws, int mode)
 {
-       struct rk2818_spi *dws =
-               container_of(work, struct rk2818_spi, pump_messages);
        DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__);
 
        if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
@@ -1024,7 +1055,7 @@ static void rk2818_pump_messages(struct work_struct *work)
        /* Mark as busy and launch transfers */
        dws->busy = 1;
 
-       while (rk2818_pump_transfers(dws)) ;
+       while (rk2818_pump_transfers(dws, mode)) ;
 }
 
 /* spi_device use this to queue in their spi_msg */
@@ -1032,6 +1063,7 @@ static int rk2818_spi_quick_transfer(struct spi_device *spi, struct spi_message
 {
        struct rk2818_spi *dws = spi_master_get_devdata(spi->master);
        unsigned long flags;
+       struct rk2818_spi_chip *chip_info = spi->controller_data;
        
        DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__);
        
@@ -1048,387 +1080,18 @@ static int rk2818_spi_quick_transfer(struct spi_device *spi, struct spi_message
 
        list_add_tail(&msg->queue, &dws->queue);
 
-       if (dws->run == QUEUE_RUNNING && !dws->busy) {
-               rk2818_pump_messages(&dws->pump_messages);
-       }
-
-       spin_unlock_irqrestore(&dws->lock, flags);
-       return 0;
-}
-
-#elif defined(QUICK_INT_TRANSFER)
-
-/* Caller already set message->status; dma and pio irqs are blocked */
-static void msg_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;
-       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,msg->spi->chip_select,MRST_SPI_DEASSERT);
-
-       msg->state = NULL;
-       if (msg->complete)
-               msg->complete(msg->context);
-}
-
-static void *get_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);
-               dws->cur_transfer->state = START_STATE;
-               return RUNNING_STATE;
-       } else
-               return DONE_STATE;
-}
-
-static void msg_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 = get_next_transfer(dws);
-
-       /* Handle end of message */
-       if (dws->cur_msg->state == DONE_STATE) {
-               dws->cur_msg->status = 0;
-               msg_giveback(dws);
+       if (chip_info && (chip_info->transfer_mode == RK2818_SPI_FULL_DUPLEX)) {
+               rk2818_pump_messages(dws,1);
+               //printk("+++++++++++++full transfer++++++++++++++\n");
        }
-}
-
-static void spi_set_transfers(struct rk2818_spi *dws)
-{
-       struct spi_message *message = NULL;
-       struct spi_transfer *transfer = 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; 
-
-       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; 
-       if (dws->rx && dws->tx) {
-               int temp_len = transfer->len;
-               int len;
-               unsigned char *tx_buf;
-               for (len=0; *tx_buf++ != 0; len++);
-               dws->tx_end = dws->tx + len;
-               dws->rx_end = dws->rx + temp_len - len;
+       else {          
+               rk2818_pump_messages(dws,0);
+               //printk("+++++++++++++half transfer++++++++++++++\n");
        }
-       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);
-       }
-       
-       /*
-        * 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);
-       }
-
-       //set state
-       dws->cur_msg->state = RUNNING_STATE;
-       dws->cur_transfer->state = RUNNING_STATE;
-
-       dws->busy = 1;
-
-       /*
-        * Interrupt mode
-        * we only need set the TXEI IRQ, as TX/RX always happen syncronizely
-        */
-       txint_level = dws->fifo_len / 2;
-       imask |= SPI_INT_TXEI;
-       
-       /*
-        * 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) {           
-               DBG("[function: %s] [line: %d] [reprogram registers]\n", __func__, __LINE__);
-
-               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, SPI_INT_TXEI | SPI_INT_RXFI);
-               if (txint_level) {
-                       rk2818_writew(dws, SPIM_TXFTLR, txint_level);                   
-                       rk2818_writew(dws, SPIM_RXFTLR, txint_level);
-               }
-               if (cs_change)
-                       dws->prev_chip = chip;
-                               
-               spi_enable_chip(dws, 1);
-               
-       }
-       
-
-       return;
-
-early_exit:
-       giveback(dws);
-       dws->busy = 0;
-
-       return;
-}
-
-static void transfer_delay(struct rk2818_spi *dws)
-{
-       /* Delay if requested at end of transfer*/
-       if (dws->cur_msg->state == RUNNING_STATE) {
-               if (dws->cur_transfer->state == RUNNING_STATE) {
-                       DBG("[function: %s] [line: %d] [msg is under transfer]\n", __func__, __LINE__);
-                       return ;
-               }
-               else {                                  
-                       /* Delay if requested at end of transfer*/
-                       struct spi_transfer *previous = list_entry(dws->cur_transfer->transfer_list.prev,
-                                               struct spi_transfer,
-                                               transfer_list);
-                       if (previous->delay_usecs)
-                               udelay(previous->delay_usecs);
-               }
-       }
-
-}
-
-static void rk2818_pump_transfers(struct rk2818_spi *dws)
-{
-       unsigned long flags;
-
-       DBG("enter [function: %s] [line: %d]\n", __func__, __LINE__);
-
-       /* Lock queue and check for queue work */
-       spin_lock_irqsave(&dws->lock, flags);
-       
-       if (!(dws->cur_msg)) {          
-               //check for queue work
-               if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {                     
-                       DBG("[function: %s] [line: %d] [queue is empty]\n", __func__, __LINE__);
-                       dws->busy = 0;
-                       spin_unlock_irqrestore(&dws->lock, flags);
-                       return;
-               }
-               DBG("[function: %s] [line: %d]\n", __func__, __LINE__);
-               /* 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_transfer->state = START_STATE;
-               dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
-               dws->prev_chip = NULL; //ÿ¸öpump messageÊ±Ç¿ÖÆ¸üÐÂcs dxj
-       }
-       
-       spin_unlock_irqrestore(&dws->lock, flags);
-       
-       //init transfer 
-       transfer_delay(dws);
-       return spi_set_transfers(dws);
-}
-
-static irqreturn_t rk2818_spi_irq_handle(int irq, void *dev_id)
-{
-       struct rk2818_spi *dws = dev_id;
-       u16 irq_status, irq_mask = 0x3f;
-       
-       DBG("enter [function: %s] [line: %d]\n", __func__, __LINE__);
-       
-       if (!dws->cur_msg) {
-               spi_mask_intr(dws, SPI_INT_TXEI);               
-               rk2818_pump_transfers(dws);
-               return IRQ_HANDLED;
-       }
-       
-       irq_status = rk2818_readw(dws, SPIM_ISR) & irq_mask;
-       /* Error handling */
-       if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {                
-               DBG("[function: %s] [line: %d] [Error handling]\n", __func__, __LINE__);
-               rk2818_readw(dws, SPIM_TXOICR);
-               rk2818_readw(dws, SPIM_RXOICR);
-               rk2818_readw(dws, SPIM_RXUICR);
-               int_error_stop(dws, "interrupt_transfer: fifo overrun");
-               rk2818_pump_transfers(dws);
-               return IRQ_HANDLED;
-       }
-       
-       spi_mask_intr(dws, SPI_INT_TXEI | SPI_INT_RXFI);
-
-       if (dws->rx) {
-               if (dws->tx) {
-                       do_write(dws);
-               }
-               do_read(dws);
-       }
-       else {
-               do_write(dws);
-       }
-       
-       wait_till_tf_empty(dws);
-       wait_till_not_busy(dws);
-
-       msg_transfer_complete(dws);
-       rk2818_pump_transfers(dws);
-
-       return IRQ_HANDLED;
-
-}
-
-static int rk2818_spi_quick_int_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);
-
-       DBG("[function: %s] [line: %d] [post msg]\n", __func__, __LINE__);
 
        spin_unlock_irqrestore(&dws->lock, flags);
-
-       if (dws->run == QUEUE_RUNNING && !dws->busy) {          
-               dws->busy = 1;
-               rk2818_pump_transfers(dws);
-       }
-
-       
        return 0;
 }
-
 #endif
 
 /* This may be called twice for each spi dev */
@@ -1693,13 +1356,8 @@ static int __init rk2818_spim_probe(struct platform_device *pdev)
        dws->prev_chip = NULL;
        dws->dma_inited = 1;  ///0;     
        ///dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
-       #ifndef QUICK_INT_TRANSFER
        ret = request_irq(dws->irq, rk2818_spi_irq, 0,
                        "rk2818_spim", dws);
-       #else
-       ret = request_irq(dws->irq, rk2818_spi_irq_handle, 0,
-                       "rk2818_spim", dws);
-       #endif
        if (ret < 0) {
                dev_err(&master->dev, "can not get IRQ\n");
                goto err_free_master;
@@ -1712,8 +1370,6 @@ static int __init rk2818_spim_probe(struct platform_device *pdev)
        master->setup = rk2818_spi_setup;
        #if defined(QUICK_TRANSFER)
        master->transfer = rk2818_spi_quick_transfer;
-       #elif defined(QUICK_INT_TRANSFER)
-       master->transfer = rk2818_spi_quick_int_transfer;
        #else
        master->transfer = rk2818_spi_transfer;
        #endif
index 38bf6da57dfbb143d52f6c289775b74249710d5f..0912245eb9fb149d54573f6903866de4b78bc240 100755 (executable)
@@ -196,6 +196,10 @@ static inline void spi_umask_intr(struct rk2818_spi *dws, u32 mask)
        rk2818_writel(dws, SPIM_IMR, new_mask);
 }
 
+//spi transfer mode                   add by lyx
+#define RK2818_SPI_HALF_DUPLEX 0
+#define RK2818_SPI_FULL_DUPLEX 1
+
 /*
  * Each SPI slave device to work with rk2818_api controller should
  * has such a structure claiming its working mode (PIO/DMA etc),
@@ -203,6 +207,7 @@ static inline void spi_umask_intr(struct rk2818_spi *dws, u32 mask)
  * struct spi_device
  */
 struct rk2818_spi_chip {
+       u8 transfer_mode;/*full or half duplex*/
        u8 poll_mode;   /* 0 for contoller polling mode */
        u8 type;        /* SPI/SSP/Micrwire */
        u8 enable_dma;