From 9153810215633ab30615800dfaead5ad504252f4 Mon Sep 17 00:00:00 2001 From: lyx Date: Thu, 9 Sep 2010 03:23:23 -0700 Subject: [PATCH] spi: improve spi driver --- drivers/spi/rk2818_spim.c | 1159 +++++++++++++++++++++++++++++-------- drivers/spi/rk2818_spim.h | 4 +- include/linux/spi/spi.h | 1 + 3 files changed, 908 insertions(+), 256 deletions(-) diff --git a/drivers/spi/rk2818_spim.c b/drivers/spi/rk2818_spim.c index 8b03031a78fc..3edc86a9ded7 100755 --- a/drivers/spi/rk2818_spim.c +++ b/drivers/spi/rk2818_spim.c @@ -32,6 +32,22 @@ #include #endif +/*Ô­ÓеÄspiÇý¶¯Ð§ÂʱȽϵͣ¬ +ÎÞ·¨Âú×ã´óÊý¾ÝÁ¿µÄ´«Ê䣻 +ÏÂÃæÌá¸ßÁ½ÖÖÓÅ»¯ºóµÄspiÇý¶¯£¬ +QUICK_INT_TRANSFERʹÓÃÖжϣ¬QUICK_TRANSFERÖ±½Ó´¦Àí£¬ +Á½ÖÖ·½Ê½×î¶àÖ»ÄÜ´ò¿ªÒ»ÖÖ*/ + +#define QUICK_TRANSFER +//#define QUICK_INT_TRANSFER + +#if 0 +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + #define START_STATE ((void *)0) #define RUNNING_STATE ((void *)1) #define DONE_STATE ((void *)2) @@ -44,12 +60,6 @@ #define MRST_SPI_ASSERT 1 ///CS0 #define MRST_SPI_ASSERT1 2 ///CS1 -#if 0 -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - /* Slave spi_dev related */ struct chip_data { u16 cr0; @@ -69,7 +79,7 @@ struct chip_data { u32 speed_hz; /* baud rate */ int (*write)(struct rk2818_spi *dws); int (*read)(struct rk2818_spi *dws); - void (*cs_control)(struct rk2818_spi *dws, u32 cs); + void (*cs_control)(struct rk2818_spi *dws, u32 cs, u8 flag); }; #ifdef CONFIG_DEBUG_FS @@ -182,6 +192,18 @@ static void wait_till_not_busy(struct rk2818_spi *dws) "DW SPI: Status keeps busy for 1000us after a read/write!\n"); } +static void wait_till_tf_empty(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_TF_EMPT) + 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) @@ -190,20 +212,18 @@ static void flush(struct rk2818_spi *dws) wait_till_not_busy(dws); } -static void spi_cs_control(struct rk2818_spi *dws, u32 cs) +static void spi_cs_control(struct rk2818_spi *dws, u32 cs, u8 flag) { #ifdef CONFIG_MACH_RK2818INFO_IT50 return; #else struct rk2818_spi_platform_data *pdata = dws->master->dev.platform_data; struct spi_cs_gpio *cs_gpios = pdata->chipselect_gpios; - int i; - - if (cs == 0) - for (i=0; inum_chipselect; i++) - gpio_direction_output(cs_gpios[i].cs_gpio, GPIO_HIGH); + + if (flag == 0) + gpio_direction_output(cs_gpios[cs].cs_gpio, GPIO_HIGH); else - gpio_direction_output(cs_gpios[cs-1].cs_gpio, GPIO_LOW); + gpio_direction_output(cs_gpios[cs].cs_gpio, GPIO_LOW); #endif } @@ -217,7 +237,7 @@ static int null_writer(struct rk2818_spi *dws) rk2818_writew(dws, SPIM_DR0, 0); dws->tx += n_bytes; - wait_till_not_busy(dws); + //wait_till_not_busy(dws); return 1; } @@ -241,7 +261,7 @@ static int u8_writer(struct rk2818_spi *dws) rk2818_writew(dws, SPIM_DR0, *(u8 *)(dws->tx)); ++dws->tx; - wait_till_not_busy(dws); + //wait_till_not_busy(dws); return 1; } @@ -266,7 +286,7 @@ static int u16_writer(struct rk2818_spi *dws) rk2818_writew(dws, SPIM_DR0, *(u16 *)(dws->tx)); dws->tx += 2; - wait_till_not_busy(dws); + //wait_till_not_busy(dws); return 1; } @@ -327,24 +347,23 @@ static void giveback(struct rk2818_spi *dws) struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); - //spin_lock_irqsave(&dws->lock, flags); + + 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); - dws->busy = 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(dws,MRST_SPI_DEASSERT); + dws->cs_control(dws,msg->spi->chip_select,MRST_SPI_DEASSERT); msg->state = NULL; if (msg->complete) @@ -362,12 +381,8 @@ static void int_error_stop(struct rk2818_spi *dws, const char *msg) tasklet_schedule(&dws->pump_transfers); } -static int pump_transfers(unsigned long data); - static void transfer_complete(struct rk2818_spi *dws) { - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); - /* Update total byte transfered return count actual bytes read */ dws->cur_msg->actual_length += dws->len; @@ -379,8 +394,7 @@ static void transfer_complete(struct rk2818_spi *dws) dws->cur_msg->status = 0; giveback(dws); } else - //tasklet_schedule(&dws->pump_transfers); - pump_transfers((unsigned long)dws); + tasklet_schedule(&dws->pump_transfers); } static irqreturn_t interrupt_transfer(struct rk2818_spi *dws) @@ -388,8 +402,7 @@ static irqreturn_t interrupt_transfer(struct rk2818_spi *dws) u16 irq_status, irq_mask = 0x3f; u32 int_level = dws->fifo_len / 2; u32 left; - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); - + irq_status = rk2818_readw(dws, SPIM_ISR) & irq_mask; /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { @@ -400,22 +413,24 @@ static irqreturn_t interrupt_transfer(struct rk2818_spi *dws) return IRQ_HANDLED; } - spi_mask_intr(dws, 0xff); + 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; - //DBG("+++++++++++++++left = %d++++++++++++++++++\n", left); - - while (left--) + while (left--) { dws->write(dws); + wait_till_not_busy(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, 0xff); + spi_umask_intr(dws, SPI_INT_TXEI); else transfer_complete(dws); + } return IRQ_HANDLED; } @@ -425,7 +440,7 @@ 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, 0xff); + spi_mask_intr(dws, SPI_INT_TXEI); /* Never fail */ return IRQ_HANDLED; } @@ -434,59 +449,13 @@ static irqreturn_t rk2818_spi_irq(int irq, void *dev_id) } /* Must be called inside pump_transfers() */ -static int poll_transfer(struct rk2818_spi *dws) +static void poll_transfer(struct rk2818_spi *dws) { -#if 0 - int level = dws->fifo_len; - int left = (dws->tx_end - dws->tx)/dws->n_bytes; - int length = left > level ? level : left; - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); - - while (left>0) { - left -= length; - while (length--) - dws->write(dws); - while(!(rk2818_readw(dws, SPIM_SR)& SR_TF_EMPT)); - cpu_relax(); - //while(rk2818_readw(dws, SPIM_SR) & SR_BUSY); - //cpu_relax(); + while (dws->write(dws)) { + wait_till_not_busy(dws); dws->read(dws); - - length = left > level ? level : left; } - transfer_complete(dws); -#else - if ((dws->read(dws))) { - goto comple; - } - - while (dws->txtx_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; - giveback(dws); - return 0; - } - else { - return -1; - } - -#endif } static void dma_transfer(struct rk2818_spi *dws, struct spi_transfer *xfer) //int cs_change) @@ -500,13 +469,13 @@ static void spi_chip_sel(struct rk2818_spi *dws, u16 cs) return; if (dws->cs_control){ - dws->cs_control(dws, cs+1); + dws->cs_control(dws, cs, 1); } //rk2818_writel(dws, SPIM_SER, 1 << cs); rk2818_writel(dws, SPIM_SER, 1 << 0); } -static int pump_transfers(unsigned long data) +static void pump_transfers(unsigned long data) { struct rk2818_spi *dws = (struct rk2818_spi *)data; struct spi_message *message = NULL; @@ -521,15 +490,12 @@ static int pump_transfers(unsigned long data) u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; - - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); /* 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) { @@ -648,7 +614,6 @@ static int pump_transfers(unsigned long data) * Interrupt mode * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ - chip->poll_mode = 1; if (!dws->dma_mapped && !chip->poll_mode) { int templen = dws->len / dws->n_bytes; txint_level = dws->fifo_len / 2; @@ -673,8 +638,8 @@ static int pump_transfers(unsigned long data) 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, 0xff); + if (imask) + spi_umask_intr(dws, imask); if (txint_level) rk2818_writew(dws, SPIM_TXFTLR, txint_level); @@ -686,15 +651,14 @@ static int pump_transfers(unsigned long data) if (dws->dma_mapped) dma_transfer(dws, transfer); ///cs_change); - if (chip->poll_mode) { - return poll_transfer(dws); - } - - return 0; + if (chip->poll_mode) + poll_transfer(dws); + + return; early_exit: giveback(dws); - return 0; + return; } static void pump_messages(struct work_struct *work) @@ -702,19 +666,18 @@ static void pump_messages(struct work_struct *work) struct rk2818_spi *dws = container_of(work, struct rk2818_spi, pump_messages); unsigned long flags; - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); /* Lock queue and check for queue work */ - //spin_lock_irqsave(&dws->lock, flags); + spin_lock_irqsave(&dws->lock, flags); if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) { dws->busy = 0; - //spin_unlock_irqrestore(&dws->lock, flags); + 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); + spin_unlock_irqrestore(&dws->lock, flags); return; } @@ -731,18 +694,10 @@ static void pump_messages(struct work_struct *work) dws->prev_chip = NULL; //ÿ¸öpump messageʱǿÖƸüÐÂcs dxj /* Mark as busy and launch transfers */ - #if 0 tasklet_schedule(&dws->pump_transfers); dws->busy = 1; - //spin_unlock_irqrestore(&dws->lock, flags); - - #else - dws->busy = 1; - //spin_unlock_irqrestore(&dws->lock, flags); - - while (pump_transfers((unsigned long)dws)) ; - #endif + spin_unlock_irqrestore(&dws->lock, flags); } /* spi_device use this to queue in their spi_msg */ @@ -750,7 +705,7 @@ 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; - DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); + spin_lock_irqsave(&dws->lock, flags); if (dws->run == QUEUE_STOPPED) { @@ -766,9 +721,6 @@ static int rk2818_spi_transfer(struct spi_device *spi, struct spi_message *msg) if (dws->run == QUEUE_RUNNING && !dws->busy) { - //DBG("+++++++++++++++add message++++++++++++++++++\n"); - - #if 0 if (dws->cur_transfer || dws->cur_msg) queue_work(dws->workqueue, &dws->pump_messages); @@ -778,169 +730,855 @@ static int rk2818_spi_transfer(struct spi_device *spi, struct spi_message *msg) pump_messages(&dws->pump_messages); return 0; } - #else - //spin_unlock_irqrestore(&dws->lock, flags); - //mutex_lock(&dws->mutex_lock); - pump_messages(&dws->pump_messages); - //mutex_unlock(&dws->mutex_lock); - //return 0; - #endif } spin_unlock_irqrestore(&dws->lock, flags); - DBG("+++++++++++++++quit %s++++++++++++++++++\n", __func__); + return 0; } -/* This may be called twice for each spi dev */ -static int rk2818_spi_setup(struct spi_device *spi) +static void do_read(struct rk2818_spi *dws) { - 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 = spi_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; + int count = 0; - if (!spi->max_speed_hz) { - dev_err(&spi->dev, "No max speed HZ parameter\n"); - return -EINVAL; + 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) { + if (dws->read(dws)) + break; + if (count ++ == 0x10) { + printk("+++++++++++spi receive data time out+++++++++++++\n"); + break; + } + } - 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) +static void do_write(struct rk2818_spi *dws) { - struct chip_data *chip = spi_get_ctldata(spi); - kfree(chip); + while (dws->txtx_end) { + dws->write(dws); + } } -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; -} +#if defined(QUICK_TRANSFER) -static int start_queue(struct rk2818_spi *dws) +/* Caller already set message->status; dma and pio irqs are blocked */ +static void msg_giveback(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; + struct spi_transfer *last_transfer; + struct spi_message *msg; + DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); + msg = dws->cur_msg; dws->cur_msg = NULL; dws->cur_transfer = NULL; + dws->prev_chip = dws->cur_chip; 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; + dws->dma_mapped = 0; + dws->busy = 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); - } + last_transfer = list_entry(msg->transfers.prev, + struct spi_transfer, + transfer_list); - if (!list_empty(&dws->queue) || dws->busy) - status = -EBUSY; - spin_unlock_irqrestore(&dws->lock, flags); + if (!last_transfer->cs_change) + dws->cs_control(dws,msg->spi->chip_select,MRST_SPI_DEASSERT); - return status; + msg->state = NULL; + if (msg->complete) + msg->complete(msg->context); } -static int destroy_queue(struct rk2818_spi *dws) +/* Must be called inside pump_transfers() */ +static int do_transfer(struct rk2818_spi *dws) { - int status; + if (dws->rx) { + if (dws->tx) { + do_write(dws); + } + do_read(dws); + } + else { + do_write(dws); + } - status = stop_queue(dws); + wait_till_tf_empty(dws); + wait_till_not_busy(dws); + + 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; + } + +} + +static int rk2818_pump_transfers(struct rk2818_spi *dws) +{ + 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 cs_change = 0; + u16 clk_div = 0; + u32 speed = 0; + u32 cr0 = 0; + + DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); + + /* 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; + 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; + } + 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); + } + + /* + * 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) { + 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_enable_chip(dws, 1); + if (cs_change) + dws->prev_chip = chip; + } + + return do_transfer(dws); + +early_exit: + + msg_giveback(dws); + + return 0; +} + +static void rk2818_pump_messages(struct work_struct *work) +{ + 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) { + dws->busy = 0; + return; + } + + /* Make sure we are not already running a message */ + if (dws->cur_msg) { + 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); + dws->prev_chip = NULL; //ÿ¸öpump messageʱǿÖƸüÐÂcs dxj + + /* Mark as busy and launch transfers */ + dws->busy = 1; + + while (rk2818_pump_transfers(dws)) ; +} + +/* spi_device use this to queue in their spi_msg */ +static int rk2818_spi_quick_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct rk2818_spi *dws = spi_master_get_devdata(spi->master); + unsigned long flags; + + DBG("+++++++++++++++enter %s++++++++++++++++++\n", __func__); + + 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) { + 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); + } +} + +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; + } + 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 */ +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 = spi_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); @@ -1014,7 +1652,7 @@ static int __init rk2818_spim_probe(struct platform_device *pdev) struct rk2818_spi *dws; struct spi_master *master; int irq; - int ret; + int ret; struct rk2818_spi_platform_data *pdata = pdev->dev.platform_data; if (pdata && pdata->io_init) { @@ -1054,8 +1692,13 @@ 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; @@ -1066,7 +1709,14 @@ static int __init rk2818_spim_probe(struct platform_device *pdev) master->dev.platform_data = pdata; master->cleanup = rk2818_spi_cleanup; 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 + dws->pdev = pdev; /* Basic HW init */ spi_hw_init(dws); @@ -1201,3 +1851,4 @@ 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 index 66b86ffb64c8..38bf6da57dfb 100755 --- a/drivers/spi/rk2818_spim.h +++ b/drivers/spi/rk2818_spim.h @@ -136,7 +136,7 @@ struct rk2818_spi { int (*write)(struct rk2818_spi *dws); int (*read)(struct rk2818_spi *dws); irqreturn_t (*transfer_handler)(struct rk2818_spi *dws); - void (*cs_control)(struct rk2818_spi *dws, u32 cs); + void (*cs_control)(struct rk2818_spi *dws, u32 cs, u8 flag); /* Dma info */ int dma_inited; @@ -206,7 +206,7 @@ struct rk2818_spi_chip { u8 poll_mode; /* 0 for contoller polling mode */ u8 type; /* SPI/SSP/Micrwire */ u8 enable_dma; - void (*cs_control)(struct rk2818_spi *dws, u32 cs); + void (*cs_control)(struct rk2818_spi *dws, u32 cs, u8 flag); }; #endif /* __DRIVERS_SPIM_RK2818_HEADER_H */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 97b60b37f445..662bc778bcfb 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -430,6 +430,7 @@ struct spi_transfer { u8 bits_per_word; u16 delay_usecs; u32 speed_hz; + void *state; struct list_head transfer_list; }; -- 2.34.1