#include <asm/dma.h>
#include <asm/portmux.h>
#include <asm/bfin5xx_spi.h>
+#include <asm/cacheflush.h>
#define DRV_NAME "bfin-spi"
#define DRV_AUTHOR "Bryan Wu, Luke Yang"
if ((sclk % (2 * speed_hz)) > 0)
spi_baud++;
+ if (spi_baud < MIN_SPI_BAUD_VAL)
+ spi_baud = MIN_SPI_BAUD_VAL;
+
return spi_baud;
}
udelay(chip->cs_chg_udelay);
}
-#define MAX_SPI_SSEL 7
-
/* stop controller and re-config current chip*/
static void restore_state(struct driver_data *drv_data)
{
struct driver_data *drv_data = dev_id;
struct chip_data *chip = drv_data->cur_chip;
struct spi_message *msg = drv_data->cur_msg;
+ unsigned long timeout;
+ unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
+ u16 spistat = read_STAT(drv_data);
+
+ dev_dbg(&drv_data->pdev->dev,
+ "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
+ dmastat, spistat);
- dev_dbg(&drv_data->pdev->dev, "in dma_irq_handler\n");
clear_dma_irqstat(drv_data->dma_channel);
/* Wait for DMA to complete */
cpu_relax();
}
+ dev_dbg(&drv_data->pdev->dev,
+ "in dma_irq_handler dmastat:0x%x spistat:0x%x\n",
+ dmastat, read_STAT(drv_data));
+
+ timeout = jiffies + HZ;
while (!(read_STAT(drv_data) & SPIF))
- cpu_relax();
+ if (!time_before(jiffies, timeout)) {
+ dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF");
+ break;
+ } else
+ cpu_relax();
- msg->actual_length += drv_data->len_in_bytes;
+ if ((dmastat & DMA_ERR) && (spistat & RBSY)) {
+ msg->state = ERROR_STATE;
+ dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow\n");
+ } else {
+ msg->actual_length += drv_data->len_in_bytes;
- if (drv_data->cs_change)
- cs_deactive(drv_data, chip);
+ if (drv_data->cs_change)
+ cs_deactive(drv_data, chip);
- /* Move to next transfer */
- msg->state = next_transfer(drv_data);
+ /* Move to next transfer */
+ msg->state = next_transfer(drv_data);
+ }
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
/* Handle for abort */
if (message->state == ERROR_STATE) {
+ dev_dbg(&drv_data->pdev->dev, "transfer: we've hit an error\n");
message->status = -EIO;
giveback(drv_data);
return;
/* Handle end of message */
if (message->state == DONE_STATE) {
+ dev_dbg(&drv_data->pdev->dev, "transfer: all done!\n");
message->status = 0;
giveback(drv_data);
return;
/* Delay if requested at end of transfer */
if (message->state == RUNNING_STATE) {
+ dev_dbg(&drv_data->pdev->dev, "transfer: still running ...\n");
previous = list_entry(transfer->transfer_list.prev,
struct spi_transfer, transfer_list);
if (previous->delay_usecs)
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
+ * Try to map dma buffer and do a dma transfer. If successful use,
+ * different way to r/w according to the enable_dma settings and if
+ * we are not doing a full duplex transfer (since the hardware does
+ * not support full duplex DMA transfers).
*/
if (!full_duplex && drv_data->cur_chip->enable_dma
&& drv_data->len > 6) {
+ unsigned long dma_start_addr, flags;
+
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");
+ set_dma_x_count(drv_data->dma_channel, drv_data->len);
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;
}
enable_dma(drv_data->dma_channel);
/* start SPI transfer */
- write_CTRL(drv_data,
- (cr | CFG_SPI_DMAWRITE | BIT_CTL_ENABLE));
+ write_CTRL(drv_data, cr | BIT_CTL_TIMOD_DMA_TX);
/* just return here, there can only be one transfer
* in this mode
}
/* In dma mode, rx or tx must be NULL in one transfer */
+ dma_config = (RESTART | dma_width | DI_EN);
if (drv_data->rx != NULL) {
/* set transfer mode, and enable SPI */
- dev_dbg(&drv_data->pdev->dev, "doing DMA in.\n");
+ dev_dbg(&drv_data->pdev->dev, "doing DMA in to %p (size %zx)\n",
+ drv_data->rx, drv_data->len_in_bytes);
+
+ /* invalidate caches, if needed */
+ if (bfin_addr_dcachable((unsigned long) drv_data->rx))
+ invalidate_dcache_range((unsigned long) drv_data->rx,
+ (unsigned long) (drv_data->rx +
+ drv_data->len_in_bytes));
/* 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));
+ dma_config |= WNR;
+ dma_start_addr = (unsigned long)drv_data->rx;
+ cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
} 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);
+ /* flush caches, if needed */
+ if (bfin_addr_dcachable((unsigned long) drv_data->tx))
+ flush_dcache_range((unsigned long) drv_data->tx,
+ (unsigned long) (drv_data->tx +
+ drv_data->len_in_bytes));
+
+ dma_start_addr = (unsigned long)drv_data->tx;
+ cr |= BIT_CTL_TIMOD_DMA_TX;
+
+ } else
+ BUG();
+
+ /* oh man, here there be monsters ... and i dont mean the
+ * fluffy cute ones from pixar, i mean the kind that'll eat
+ * your data, kick your dog, and love it all. do *not* try
+ * and change these lines unless you (1) heavily test DMA
+ * with SPI flashes on a loaded system (e.g. ping floods),
+ * (2) know just how broken the DMA engine interaction with
+ * the SPI peripheral is, and (3) have someone else to blame
+ * when you screw it all up anyways.
+ */
+ set_dma_start_addr(drv_data->dma_channel, dma_start_addr);
+ set_dma_config(drv_data->dma_channel, dma_config);
+ local_irq_save(flags);
+ enable_dma(drv_data->dma_channel);
+ write_CTRL(drv_data, cr);
+ dma_enable_irq(drv_data->dma_channel);
+ local_irq_restore(flags);
- /* 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");
message->state = ERROR_STATE;
} else {
/* Update total byte transfered */
- message->actual_length += drv_data->len;
+ message->actual_length += drv_data->len_in_bytes;
/* Move to next transfer of this msg */
message->state = next_transfer(drv_data);
#define MAX_SPI_SSEL 7
-static u16 ssel[3][MAX_SPI_SSEL] = {
+static u16 ssel[][MAX_SPI_SSEL] = {
{P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
P_SPI0_SSEL4, P_SPI0_SSEL5,
P_SPI0_SSEL6, P_SPI0_SSEL7},
*/
if (chip->enable_dma && !drv_data->dma_requested) {
/* register dma irq handler */
- if (request_dma(drv_data->dma_channel, "BF53x_SPI_DMA") < 0) {
+ if (request_dma(drv_data->dma_channel, "BFIN_SPI_DMA") < 0) {
dev_dbg(&spi->dev,
"Unable to request BlackFin SPI DMA channel\n");
return -ENODEV;
}
if (set_dma_callback(drv_data->dma_channel,
- (void *)dma_irq_handler, drv_data) < 0) {
+ dma_irq_handler, drv_data) < 0) {
dev_dbg(&spi->dev, "Unable to set dma callback\n");
return -EPERM;
}
/* init messages workqueue */
INIT_WORK(&drv_data->pump_messages, pump_messages);
- drv_data->workqueue =
- create_singlethread_workqueue(drv_data->master->dev.parent->bus_id);
+ drv_data->workqueue = create_singlethread_workqueue(
+ dev_name(drv_data->master->dev.parent));
if (drv_data->workqueue == NULL)
return -EBUSY;