spi: add spi transfer mode, full duplex and half duplex

This commit is contained in:
lyx 2010-09-23 23:36:32 -07:00
parent d2de78074e
commit 8dac1ea6cf
3 changed files with 73 additions and 407 deletions

View File

@ -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
};

View File

@ -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->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 @@ static int rk2818_pump_transfers(struct rk2818_spi *dws)
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);
if (chip_info && (chip_info->transfer_mode == RK2818_SPI_FULL_DUPLEX)) {
rk2818_pump_messages(dws,1);
//printk("+++++++++++++full transfer++++++++++++++\n");
}
else {
rk2818_pump_messages(dws,0);
//printk("+++++++++++++half transfer++++++++++++++\n");
}
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 */
@ -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

View File

@ -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;