spi_bfin5xx.c
来自「linux 内核源代码」· C语言 代码 · 共 1,489 行 · 第 1/3 页
C
1,489 行
write_TDBR(drv_data, 0xFFFF); cs_active(drv_data, chip); dummy_read(drv_data); while (drv_data->rx < drv_data->rx_end - 2) { cs_deactive(drv_data, chip); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); cs_active(drv_data, chip); *(u16 *) (drv_data->rx) = read_RDBR(drv_data); drv_data->rx += 2; } cs_deactive(drv_data, chip); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); *(u16 *) (drv_data->rx) = read_SHAW(drv_data); drv_data->rx += 2;}static void u16_duplex(struct driver_data *drv_data){ /* poll for SPI completion before start */ while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); /* in duplex mode, clk is triggered by writing of TDBR */ while (drv_data->tx < drv_data->tx_end) { write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); *(u16 *) (drv_data->rx) = read_RDBR(drv_data); drv_data->rx += 2; drv_data->tx += 2; }}static void u16_cs_chg_duplex(struct driver_data *drv_data){ struct chip_data *chip = drv_data->cur_chip; /* poll for SPI completion before start */ while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); while (drv_data->tx < drv_data->tx_end) { cs_active(drv_data, chip); write_TDBR(drv_data, (*(u16 *) (drv_data->tx))); while (read_STAT(drv_data) & BIT_STAT_TXS) cpu_relax(); while (!(read_STAT(drv_data) & BIT_STAT_RXS)) cpu_relax(); *(u16 *) (drv_data->rx) = read_RDBR(drv_data); cs_deactive(drv_data, chip); drv_data->rx += 2; drv_data->tx += 2; }}/* test if ther is more transfer to be done */static void *next_transfer(struct driver_data *drv_data){ struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; /* Move to next transfer */ if (trans->transfer_list.next != &msg->transfers) { drv_data->cur_transfer = list_entry(trans->transfer_list.next, struct spi_transfer, transfer_list); return RUNNING_STATE; } else return DONE_STATE;}/* * caller already set message->status; * dma and pio irqs are blocked give finished message back */static void giveback(struct driver_data *drv_data){ struct chip_data *chip = drv_data->cur_chip; struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; spin_lock_irqsave(&drv_data->lock, flags); msg = drv_data->cur_msg; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; queue_work(drv_data->workqueue, &drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); last_transfer = list_entry(msg->transfers.prev, struct spi_transfer, transfer_list); msg->state = NULL; /* disable chip select signal. And not stop spi in autobuffer mode */ if (drv_data->tx_dma != 0xFFFF) { cs_deactive(drv_data, chip); bfin_spi_disable(drv_data); } if (!drv_data->cs_change) cs_deactive(drv_data, chip); if (msg->complete) msg->complete(msg->context);}static irqreturn_t dma_irq_handler(int irq, void *dev_id){ struct driver_data *drv_data = (struct driver_data *)dev_id; struct chip_data *chip = drv_data->cur_chip; struct spi_message *msg = drv_data->cur_msg; dev_dbg(&drv_data->pdev->dev, "in dma_irq_handler\n"); clear_dma_irqstat(drv_data->dma_channel); /* Wait for DMA to complete */ while (get_dma_curr_irqstat(drv_data->dma_channel) & DMA_RUN) cpu_relax(); /* * wait for the last transaction shifted out. HRM states: * at this point there may still be data in the SPI DMA FIFO waiting * to be transmitted ... software needs to poll TXS in the SPI_STAT * register until it goes low for 2 successive reads */ if (drv_data->tx != NULL) { while ((read_STAT(drv_data) & TXS) || (read_STAT(drv_data) & TXS)) cpu_relax(); } while (!(read_STAT(drv_data) & SPIF)) cpu_relax(); msg->actual_length += drv_data->len_in_bytes; if (drv_data->cs_change) cs_deactive(drv_data, chip); /* Move to next transfer */ msg->state = next_transfer(drv_data); /* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); /* free the irq handler before next transfer */ dev_dbg(&drv_data->pdev->dev, "disable dma channel irq%d\n", drv_data->dma_channel); dma_disable_irq(drv_data->dma_channel); return IRQ_HANDLED;}static void pump_transfers(unsigned long data){ struct driver_data *drv_data = (struct driver_data *)data; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; u8 width; u16 cr, dma_width, dma_config; u32 tranf_success = 1; /* Get current state information */ message = drv_data->cur_msg; transfer = drv_data->cur_transfer; chip = drv_data->cur_chip; /* * if msg is error or done, report it back using complete() callback */ /* Handle for abort */ if (message->state == ERROR_STATE) { message->status = -EIO; giveback(drv_data); return; } /* Handle end of message */ if (message->state == DONE_STATE) { message->status = 0; giveback(drv_data); return; } /* 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); } /* Setup the transfer state based on the type of transfer */ if (flush(drv_data) == 0) { dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); message->status = -EIO; giveback(drv_data); return; } if (transfer->tx_buf != NULL) { drv_data->tx = (void *)transfer->tx_buf; drv_data->tx_end = drv_data->tx + transfer->len; dev_dbg(&drv_data->pdev->dev, "tx_buf is %p, tx_end is %p\n", transfer->tx_buf, drv_data->tx_end); } else { drv_data->tx = NULL; } if (transfer->rx_buf != NULL) { drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; dev_dbg(&drv_data->pdev->dev, "rx_buf is %p, rx_end is %p\n", transfer->rx_buf, drv_data->rx_end); } else { drv_data->rx = NULL; } drv_data->rx_dma = transfer->rx_dma; drv_data->tx_dma = transfer->tx_dma; drv_data->len_in_bytes = transfer->len; drv_data->cs_change = transfer->cs_change; /* Bits per word setup */ switch (transfer->bits_per_word) { case 8: drv_data->n_bytes = 1; width = CFG_SPI_WORDSIZE8; drv_data->read = chip->cs_change_per_word ? u8_cs_chg_reader : u8_reader; drv_data->write = chip->cs_change_per_word ? u8_cs_chg_writer : u8_writer; drv_data->duplex = chip->cs_change_per_word ? u8_cs_chg_duplex : u8_duplex; break; case 16: drv_data->n_bytes = 2; width = CFG_SPI_WORDSIZE16; drv_data->read = chip->cs_change_per_word ? u16_cs_chg_reader : u16_reader; drv_data->write = chip->cs_change_per_word ? u16_cs_chg_writer : u16_writer; drv_data->duplex = chip->cs_change_per_word ? u16_cs_chg_duplex : u16_duplex; break; default: /* No change, the same as default setting */ drv_data->n_bytes = chip->n_bytes; width = chip->width; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; drv_data->duplex = chip->duplex ? chip->duplex : null_writer; break; } cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); cr |= (width << 8); write_CTRL(drv_data, cr); if (width == CFG_SPI_WORDSIZE16) { drv_data->len = (transfer->len) >> 1; } else { drv_data->len = transfer->len; } dev_dbg(&drv_data->pdev->dev, "transfer: ", "drv_data->write is %p, chip->write is %p, null_wr is %p\n", drv_data->write, chip->write, null_writer); /* speed and width has been set on per message */ message->state = RUNNING_STATE; dma_config = 0; /* Speed setup (surely valid because already checked) */ if (transfer->speed_hz) write_BAUD(drv_data, hz_to_spi_baud(transfer->speed_hz)); else write_BAUD(drv_data, chip->baud); write_STAT(drv_data, BIT_STAT_CLR); cr = (read_CTRL(drv_data) & (~BIT_CTL_TIMOD)); cs_active(drv_data, chip); dev_dbg(&drv_data->pdev->dev, "now pumping a transfer: width is %d, len is %d\n", width, transfer->len); /* * Try to map dma buffer and do a dma transfer if * successful use different way to r/w according to * drv_data->cur_chip->enable_dma */ if (drv_data->cur_chip->enable_dma && drv_data->len > 6) { disable_dma(drv_data->dma_channel); clear_dma_irqstat(drv_data->dma_channel); bfin_spi_disable(drv_data); /* config dma channel */ dev_dbg(&drv_data->pdev->dev, "doing dma transfer\n"); if (width == CFG_SPI_WORDSIZE16) { set_dma_x_count(drv_data->dma_channel, drv_data->len); set_dma_x_modify(drv_data->dma_channel, 2); dma_width = WDSIZE_16; } else { set_dma_x_count(drv_data->dma_channel, drv_data->len); set_dma_x_modify(drv_data->dma_channel, 1); dma_width = WDSIZE_8; } /* poll for SPI completion before start */ while (!(read_STAT(drv_data) & BIT_STAT_SPIF)) cpu_relax(); /* dirty hack for autobuffer DMA mode */ if (drv_data->tx_dma == 0xFFFF) { dev_dbg(&drv_data->pdev->dev, "doing autobuffer DMA out.\n"); /* no irq in autobuffer mode */ dma_config = (DMAFLOW_AUTO | RESTART | dma_width | DI_EN); set_dma_config(drv_data->dma_channel, dma_config); set_dma_start_addr(drv_data->dma_channel, (unsigned long)drv_data->tx); enable_dma(drv_data->dma_channel); /* start SPI transfer */ write_CTRL(drv_data, (cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE)); /* just return here, there can only be one transfer * in this mode */ message->status = 0; giveback(drv_data); return; } /* In dma mode, rx or tx must be NULL in one transfer */ if (drv_data->rx != NULL) { /* set transfer mode, and enable SPI */ dev_dbg(&drv_data->pdev->dev, "doing DMA in.\n"); /* clear tx reg soformer data is not shifted out */ write_TDBR(drv_data, 0xFFFF); set_dma_x_count(drv_data->dma_channel, drv_data->len); /* start dma */ dma_enable_irq(drv_data->dma_channel); dma_config = (WNR | RESTART | dma_width | DI_EN); set_dma_config(drv_data->dma_channel, dma_config); set_dma_start_addr(drv_data->dma_channel, (unsigned long)drv_data->rx); enable_dma(drv_data->dma_channel); /* start SPI transfer */ write_CTRL(drv_data, (cr | CFG_SPI_DMAREAD | BIT_CTL_ENABLE)); } else if (drv_data->tx != NULL) { dev_dbg(&drv_data->pdev->dev, "doing DMA out.\n"); /* start dma */ dma_enable_irq(drv_data->dma_channel); dma_config = (RESTART | dma_width | DI_EN); set_dma_config(drv_data->dma_channel, dma_config); set_dma_start_addr(drv_data->dma_channel, (unsigned long)drv_data->tx); enable_dma(drv_data->dma_channel); /* start SPI transfer */ write_CTRL(drv_data, (cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE)); } } else { /* IO mode write then read */ dev_dbg(&drv_data->pdev->dev, "doing IO transfer\n"); if (drv_data->tx != NULL && drv_data->rx != NULL) { /* full duplex mode */ BUG_ON((drv_data->tx_end - drv_data->tx) != (drv_data->rx_end - drv_data->rx)); dev_dbg(&drv_data->pdev->dev, "IO duplex: cr is 0x%x\n", cr); /* set SPI transfer mode */ write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); drv_data->duplex(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; } else if (drv_data->tx != NULL) { /* write only half duplex */ dev_dbg(&drv_data->pdev->dev, "IO write: cr is 0x%x\n", cr); /* set SPI transfer mode */ write_CTRL(drv_data, (cr | CFG_SPI_WRITE)); drv_data->write(drv_data); if (drv_data->tx != drv_data->tx_end) tranf_success = 0; } else if (drv_data->rx != NULL) { /* read only half duplex */ dev_dbg(&drv_data->pdev->dev, "IO read: cr is 0x%x\n", cr); /* set SPI transfer mode */ write_CTRL(drv_data, (cr | CFG_SPI_READ)); drv_data->read(drv_data); if (drv_data->rx != drv_data->rx_end) tranf_success = 0; } if (!tranf_success) { dev_dbg(&drv_data->pdev->dev, "IO write error!\n"); message->state = ERROR_STATE; } else { /* Update total byte transfered */ message->actual_length += drv_data->len; /* Move to next transfer of this msg */ message->state = next_transfer(drv_data); } /* Schedule next transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); }}/* pop a msg from queue and kick off real transfer */static void pump_messages(struct work_struct *work){ struct driver_data *drv_data; unsigned long flags; drv_data = container_of(work, struct driver_data, pump_messages); /* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) { /* pumper kicked off but no work to do */ drv_data->busy = 0; spin_unlock_irqrestore(&drv_data->lock, flags); return; } /* Make sure we are not already running a message */ if (drv_data->cur_msg) { spin_unlock_irqrestore(&drv_data->lock, flags); return; } /* Extract head of queue */ drv_data->cur_msg = list_entry(drv_data->queue.next, struct spi_message, queue); /* Setup the SSP using the per chip configuration */ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); if (restore_state(drv_data)) { spin_unlock_irqrestore(&drv_data->lock, flags); return; }; list_del_init(&drv_data->cur_msg->queue); /* Initial message state */ drv_data->cur_msg->state = START_STATE; drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, struct spi_transfer, transfer_list); dev_dbg(&drv_data->pdev->dev, "got a message to pump, " "state is set to: baud %d, flag 0x%x, ctl 0x%x\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?