#include <linux/debugfs.h>
#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)
#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;
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
"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)
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; i<pdata->num_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
}
rk2818_writew(dws, SPIM_DR0, 0);
dws->tx += n_bytes;
- wait_till_not_busy(dws);
+ //wait_till_not_busy(dws);
return 1;
}
rk2818_writew(dws, SPIM_DR0, *(u8 *)(dws->tx));
++dws->tx;
- wait_till_not_busy(dws);
+ //wait_till_not_busy(dws);
return 1;
}
rk2818_writew(dws, SPIM_DR0, *(u16 *)(dws->tx));
dws->tx += 2;
- wait_till_not_busy(dws);
+ //wait_till_not_busy(dws);
return 1;
}
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)
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;
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)
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)) {
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;
}
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;
}
}
/* 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->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;
- giveback(dws);
- return 0;
- }
- else {
- return -1;
- }
-
-#endif
}
static void dma_transfer(struct rk2818_spi *dws, struct spi_transfer *xfer) //int cs_change)
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;
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) {
* 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;
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);
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)
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;
}
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 */
{
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) {
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);
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->tx<dws->tx_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);
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) {
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;
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);
MODULE_AUTHOR("lhh lhh@rock-chips.com");
MODULE_DESCRIPTION("Rockchip RK2818 spim port driver");
MODULE_LICENSE("GPL");;
+