From 8dac1ea6cffbe33f3145c355de1f96ea3b8784f0 Mon Sep 17 00:00:00 2001 From: lyx Date: Thu, 23 Sep 2010 23:36:32 -0700 Subject: [PATCH] spi: add spi transfer mode, full duplex and half duplex --- arch/arm/mach-rk2818/board-raho.c | 5 + drivers/spi/rk2818_spim.c | 468 ++++-------------------------- drivers/spi/rk2818_spim.h | 5 + 3 files changed, 72 insertions(+), 406 deletions(-) diff --git a/arch/arm/mach-rk2818/board-raho.c b/arch/arm/mach-rk2818/board-raho.c index 7920817f9ed1..b68ed5cdddbf 100755 --- a/arch/arm/mach-rk2818/board-raho.c +++ b/arch/arm/mach-rk2818/board-raho.c @@ -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 }; diff --git a/drivers/spi/rk2818_spim.c b/drivers/spi/rk2818_spim.c index 8c47834b1784..d5f408efa714 100755 --- a/drivers/spi/rk2818_spim.c +++ b/drivers/spi/rk2818_spim.c @@ -34,12 +34,11 @@ /*Ô­ÓеÄ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->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; + 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 diff --git a/drivers/spi/rk2818_spim.h b/drivers/spi/rk2818_spim.h index 38bf6da57dfb..0912245eb9fb 100755 --- a/drivers/spi/rk2818_spim.h +++ b/drivers/spi/rk2818_spim.h @@ -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; -- 2.34.1