Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 94103
b: refs/heads/master
c: 5d9f3f6
h: refs/heads/master
i:
  94101: 6b9cc87
  94099: 8706632
  94095: 49b0b7f
v: v3
  • Loading branch information
Andrea Paterniani authored and Linus Torvalds committed Apr 28, 2008
1 parent 517609d commit 026541a
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 110 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 06719814780da741e7acf587367a86c3965c03a2
refs/heads/master: 5d9f3f6b7c4c9fe1706006f24f964e7c0fa49fb7
223 changes: 114 additions & 109 deletions trunk/drivers/spi/spi_imx.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,19 +270,26 @@ struct chip_data {

static void pump_messages(struct work_struct *work);

static int flush(struct driver_data *drv_data)
static void flush(struct driver_data *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
void __iomem *regs = drv_data->regs;
volatile u32 d;
u32 control;

dev_dbg(&drv_data->pdev->dev, "flush\n");

/* Wait for end of transaction */
do {
while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR)
d = readl(regs + SPI_RXDATA);
} while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--);
control = readl(regs + SPI_CONTROL);
} while (control & SPI_CONTROL_XCH);

/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);

return limit;
/* Disable SPI to flush FIFOs */
writel(control & ~SPI_CONTROL_SPIEN, regs + SPI_CONTROL);
writel(control, regs + SPI_CONTROL);
}

static void restore_state(struct driver_data *drv_data)
Expand Down Expand Up @@ -570,6 +577,7 @@ static void giveback(struct spi_message *message, struct driver_data *drv_data)
writel(0, regs + SPI_INT_STATUS);
writel(0, regs + SPI_DMA);

/* Unconditioned deselct */
drv_data->cs_control(SPI_CS_DEASSERT);

message->state = NULL;
Expand All @@ -592,13 +600,10 @@ static void dma_err_handler(int channel, void *data, int errcode)
/* Disable both rx and tx dma channels */
imx_dma_disable(drv_data->rx_channel);
imx_dma_disable(drv_data->tx_channel);

if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_err_handler - flush failed\n");

unmap_dma_buffers(drv_data);

flush(drv_data);

msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
}
Expand All @@ -612,28 +617,26 @@ static void dma_tx_handler(int channel, void *data)
imx_dma_disable(channel);

/* Now waits for TX FIFO empty */
writel(readl(drv_data->regs + SPI_INT_STATUS) | SPI_INTEN_TE,
drv_data->regs + SPI_INT_STATUS);
writel(SPI_INTEN_TE, drv_data->regs + SPI_INT_STATUS);
}

static irqreturn_t dma_transfer(struct driver_data *drv_data)
{
u32 status;
struct spi_message *msg = drv_data->cur_msg;
void __iomem *regs = drv_data->regs;
unsigned long limit;

status = readl(regs + SPI_INT_STATUS);

if ((status & SPI_INTEN_RO) && (status & SPI_STATUS_RO)) {
if ((status & (SPI_INTEN_RO | SPI_STATUS_RO))
== (SPI_INTEN_RO | SPI_STATUS_RO)) {
writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);

imx_dma_disable(drv_data->tx_channel);
imx_dma_disable(drv_data->rx_channel);
unmap_dma_buffers(drv_data);

if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - flush failed\n");
flush(drv_data);

dev_warn(&drv_data->pdev->dev,
"dma_transfer - fifo overun\n");
Expand All @@ -649,20 +652,17 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)

if (drv_data->rx) {
/* Wait end of transfer before read trailing data */
limit = loops_per_jiffy << 1;
while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) &&
limit--);

if (limit == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - end of tx failed\n");
else
dev_dbg(&drv_data->pdev->dev,
"dma_transfer - end of tx\n");
while (readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH)
cpu_relax();

imx_dma_disable(drv_data->rx_channel);
unmap_dma_buffers(drv_data);

/* Release chip select if requested, transfer delays are
handled in pump_transfers() */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);

/* Calculate number of trailing data and read them */
dev_dbg(&drv_data->pdev->dev,
"dma_transfer - test = 0x%08X\n",
Expand All @@ -676,19 +676,12 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
/* Write only transfer */
unmap_dma_buffers(drv_data);

if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"dma_transfer - flush failed\n");
flush(drv_data);
}

/* End of transfer, update total byte transfered */
msg->actual_length += drv_data->len;

/* Release chip select if requested, transfer delays are
handled in pump_transfers() */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);

/* Move to next transfer */
msg->state = next_transfer(drv_data);

Expand All @@ -711,44 +704,43 @@ static irqreturn_t interrupt_wronly_transfer(struct driver_data *drv_data)

status = readl(regs + SPI_INT_STATUS);

while (status & SPI_STATUS_TH) {
if (status & SPI_INTEN_TE) {
/* TXFIFO Empty Interrupt on the last transfered word */
writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
dev_dbg(&drv_data->pdev->dev,
"interrupt_wronly_transfer - status = 0x%08X\n", status);
"interrupt_wronly_transfer - end of tx\n");

/* Pump data */
if (write(drv_data)) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
regs + SPI_INT_STATUS);
flush(drv_data);

dev_dbg(&drv_data->pdev->dev,
"interrupt_wronly_transfer - end of tx\n");
/* Update total byte transfered */
msg->actual_length += drv_data->len;

if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"interrupt_wronly_transfer - "
"flush failed\n");
/* Move to next transfer */
msg->state = next_transfer(drv_data);

/* End of transfer, update total byte transfered */
msg->actual_length += drv_data->len;
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);

/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);
return IRQ_HANDLED;
} else {
while (status & SPI_STATUS_TH) {
dev_dbg(&drv_data->pdev->dev,
"interrupt_wronly_transfer - status = 0x%08X\n",
status);

/* Move to next transfer */
msg->state = next_transfer(drv_data);
/* Pump data */
if (write(drv_data)) {
/* End of TXFIFO writes,
now wait until TXFIFO is empty */
writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
return IRQ_HANDLED;
}

/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
status = readl(regs + SPI_INT_STATUS);

return IRQ_HANDLED;
/* We did something */
handled = IRQ_HANDLED;
}

status = readl(regs + SPI_INT_STATUS);

/* We did something */
handled = IRQ_HANDLED;
}

return handled;
Expand All @@ -758,45 +750,31 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->cur_msg;
void __iomem *regs = drv_data->regs;
u32 status;
u32 status, control;
irqreturn_t handled = IRQ_NONE;
unsigned long limit;

status = readl(regs + SPI_INT_STATUS);

while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
if (status & SPI_INTEN_TE) {
/* TXFIFO Empty Interrupt on the last transfered word */
writel(status & ~SPI_INTEN, regs + SPI_INT_STATUS);
dev_dbg(&drv_data->pdev->dev,
"interrupt_transfer - status = 0x%08X\n", status);

if (status & SPI_STATUS_RO) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
regs + SPI_INT_STATUS);

dev_warn(&drv_data->pdev->dev,
"interrupt_transfer - fifo overun\n"
" data not yet written = %d\n"
" data not yet read = %d\n",
data_to_write(drv_data),
data_to_read(drv_data));

if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev,
"interrupt_transfer - flush failed\n");

msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
"interrupt_transfer - end of tx\n");

return IRQ_HANDLED;
}

/* Pump data */
read(drv_data);
if (write(drv_data)) {
writel(readl(regs + SPI_INT_STATUS) & ~SPI_INTEN,
regs + SPI_INT_STATUS);
if (msg->state == ERROR_STATE) {
/* RXFIFO overrun was detected and message aborted */
flush(drv_data);
} else {
/* Wait for end of transaction */
do {
control = readl(regs + SPI_CONTROL);
} while (control & SPI_CONTROL_XCH);

dev_dbg(&drv_data->pdev->dev,
"interrupt_transfer - end of tx\n");
/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);

/* Read trailing bytes */
limit = loops_per_jiffy << 1;
Expand All @@ -810,27 +788,54 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
dev_dbg(&drv_data->pdev->dev,
"interrupt_transfer - end of rx\n");

/* End of transfer, update total byte transfered */
/* Update total byte transfered */
msg->actual_length += drv_data->len;

/* Release chip select if requested, transfer delays are
handled in pump_transfers */
if (drv_data->cs_change)
drv_data->cs_control(SPI_CS_DEASSERT);

/* Move to next transfer */
msg->state = next_transfer(drv_data);
}

/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);

return IRQ_HANDLED;
}
return IRQ_HANDLED;
} else {
while (status & (SPI_STATUS_TH | SPI_STATUS_RO)) {
dev_dbg(&drv_data->pdev->dev,
"interrupt_transfer - status = 0x%08X\n",
status);

if (status & SPI_STATUS_RO) {
/* RXFIFO overrun, abort message end wait
until TXFIFO is empty */
writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);

dev_warn(&drv_data->pdev->dev,
"interrupt_transfer - fifo overun\n"
" data not yet written = %d\n"
" data not yet read = %d\n",
data_to_write(drv_data),
data_to_read(drv_data));

msg->state = ERROR_STATE;

return IRQ_HANDLED;
}

status = readl(regs + SPI_INT_STATUS);
/* Pump data */
read(drv_data);
if (write(drv_data)) {
/* End of TXFIFO writes,
now wait until TXFIFO is empty */
writel(SPI_INTEN_TE, regs + SPI_INT_STATUS);
return IRQ_HANDLED;
}

/* We did something */
handled = IRQ_HANDLED;
status = readl(regs + SPI_INT_STATUS);

/* We did something */
handled = IRQ_HANDLED;
}
}

return handled;
Expand Down

0 comments on commit 026541a

Please sign in to comment.