Commit 5daa3ba0 authored by Stephen Street's avatar Stephen Street Committed by Linus Torvalds

[PATCH] pxa2xx-spi update

Fix some outstanding issues with the pxa2xx_spi driver when running on a
PXA270:

- Wrong timeout calculation in the setup function due to different
  peripheral clock rates in the PXAxxx family.

- Bad handling of SSSR_TFS interrupts in interrupt_transfer function.

- Added locking to interface between the pump_messages workqueue and the
  pump_transfers tasklet.

Much thanks to Juergen Beisert for the extensive testing on the PXA270.
Signed-off-by: default avatarStephen Street <stephen@streetfiresound.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7fba5340
...@@ -363,25 +363,30 @@ static void unmap_dma_buffers(struct driver_data *drv_data) ...@@ -363,25 +363,30 @@ static void unmap_dma_buffers(struct driver_data *drv_data)
} }
/* caller already set message->status; dma and pio irqs are blocked */ /* caller already set message->status; dma and pio irqs are blocked */
static void giveback(struct spi_message *message, struct driver_data *drv_data) static void giveback(struct driver_data *drv_data)
{ {
struct spi_transfer* last_transfer; 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(message->transfers.prev, last_transfer = list_entry(msg->transfers.prev,
struct spi_transfer, struct spi_transfer,
transfer_list); transfer_list);
if (!last_transfer->cs_change) if (!last_transfer->cs_change)
drv_data->cs_control(PXA2XX_CS_DEASSERT); drv_data->cs_control(PXA2XX_CS_DEASSERT);
message->state = NULL; msg->state = NULL;
if (message->complete) if (msg->complete)
message->complete(message->context); msg->complete(msg->context);
drv_data->cur_msg = NULL;
drv_data->cur_transfer = NULL;
drv_data->cur_chip = NULL;
queue_work(drv_data->workqueue, &drv_data->pump_messages);
} }
static int wait_ssp_rx_stall(void *ioaddr) static int wait_ssp_rx_stall(void *ioaddr)
...@@ -415,10 +420,11 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs) ...@@ -415,10 +420,11 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs)
if (irq_status & DCSR_BUSERR) { if (irq_status & DCSR_BUSERR) {
/* Disable interrupts, clear status and reset DMA */ /* Disable interrupts, clear status and reset DMA */
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP) if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg); write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg); write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
...@@ -454,8 +460,8 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs) ...@@ -454,8 +460,8 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs)
"dma_handler: ssp rx stall failed\n"); "dma_handler: ssp rx stall failed\n");
/* Clear and disable interrupts on SSP and DMA channels*/ /* Clear and disable interrupts on SSP and DMA channels*/
write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
write_SSSR(drv_data->clear_sr, reg);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
if (wait_dma_channel_stop(drv_data->rx_channel) == 0) if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
...@@ -497,10 +503,11 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) ...@@ -497,10 +503,11 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
irq_status = read_SSSR(reg) & drv_data->mask_sr; irq_status = read_SSSR(reg) & drv_data->mask_sr;
if (irq_status & SSSR_ROR) { if (irq_status & SSSR_ROR) {
/* Clear and disable interrupts on SSP and DMA channels*/ /* Clear and disable interrupts on SSP and DMA channels*/
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP) if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg); write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg); write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
unmap_dma_buffers(drv_data); unmap_dma_buffers(drv_data);
...@@ -526,10 +533,10 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) ...@@ -526,10 +533,10 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
/* Clear and disable interrupts on SSP and DMA channels*/ /* Clear and disable interrupts on SSP and DMA channels*/
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP) if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg); write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg); write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
...@@ -572,26 +579,30 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data) ...@@ -572,26 +579,30 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
static irqreturn_t interrupt_transfer(struct driver_data *drv_data) static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
{ {
u32 irq_status;
struct spi_message *msg = drv_data->cur_msg; struct spi_message *msg = drv_data->cur_msg;
void *reg = drv_data->ioaddr; void *reg = drv_data->ioaddr;
irqreturn_t handled = IRQ_NONE;
unsigned long limit = loops_per_jiffy << 1; unsigned long limit = loops_per_jiffy << 1;
u32 irq_status;
u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ?
drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;
while ((irq_status = (read_SSSR(reg) & drv_data->mask_sr))) { while ((irq_status = read_SSSR(reg) & irq_mask)) {
if (irq_status & SSSR_ROR) { if (irq_status & SSSR_ROR) {
/* Clear and disable interrupts */ /* Clear and disable interrupts */
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP) if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg); write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg); write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (flush(drv_data) == 0) if (flush(drv_data) == 0)
dev_err(&drv_data->pdev->dev, dev_err(&drv_data->pdev->dev,
"interrupt_transfer: flush fail\n"); "interrupt_transfer: flush fail\n");
/* Stop the SSP */
dev_warn(&drv_data->pdev->dev, dev_warn(&drv_data->pdev->dev,
"interrupt_transfer: fifo overun\n"); "interrupt_transfer: fifo overun\n");
...@@ -613,6 +624,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) ...@@ -613,6 +624,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
if (drv_data->tx == drv_data->tx_end) { if (drv_data->tx == drv_data->tx_end) {
/* Disable tx interrupt */ /* Disable tx interrupt */
write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg); write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
irq_mask = drv_data->mask_sr & ~SSSR_TFS;
/* PXA25x_SSP has no timeout, read trailing bytes */ /* PXA25x_SSP has no timeout, read trailing bytes */
if (drv_data->ssp_type == PXA25x_SSP) { if (drv_data->ssp_type == PXA25x_SSP) {
...@@ -630,10 +642,10 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) ...@@ -630,10 +642,10 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
|| (drv_data->rx == drv_data->rx_end)) { || (drv_data->rx == drv_data->rx_end)) {
/* Clear timeout */ /* Clear timeout */
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP) if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg); write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg); write_SSSR(drv_data->clear_sr, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
/* Update total byte transfered */ /* Update total byte transfered */
msg->actual_length += drv_data->len; msg->actual_length += drv_data->len;
...@@ -648,24 +660,29 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) ...@@ -648,24 +660,29 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
/* Schedule transfer tasklet */ /* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers); tasklet_schedule(&drv_data->pump_transfers);
return IRQ_HANDLED;
} }
/* We did something */
handled = IRQ_HANDLED;
} }
return handled; /* We did something */
return IRQ_HANDLED;
} }
static irqreturn_t ssp_int(int irq, void *dev_id, struct pt_regs *regs) static irqreturn_t ssp_int(int irq, void *dev_id, struct pt_regs *regs)
{ {
struct driver_data *drv_data = (struct driver_data *)dev_id; struct driver_data *drv_data = (struct driver_data *)dev_id;
void *reg = drv_data->ioaddr;
if (!drv_data->cur_msg) { if (!drv_data->cur_msg) {
write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
if (drv_data->ssp_type != PXA25x_SSP)
write_SSTO(0, reg);
write_SSSR(drv_data->clear_sr, reg);
dev_err(&drv_data->pdev->dev, "bad message state " dev_err(&drv_data->pdev->dev, "bad message state "
"in interrupt handler\n"); "in interrupt handler");
/* Never fail */ /* Never fail */
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -694,14 +711,14 @@ static void pump_transfers(unsigned long data) ...@@ -694,14 +711,14 @@ static void pump_transfers(unsigned long data)
/* Handle for abort */ /* Handle for abort */
if (message->state == ERROR_STATE) { if (message->state == ERROR_STATE) {
message->status = -EIO; message->status = -EIO;
giveback(message, drv_data); giveback(drv_data);
return; return;
} }
/* Handle end of message */ /* Handle end of message */
if (message->state == DONE_STATE) { if (message->state == DONE_STATE) {
message->status = 0; message->status = 0;
giveback(message, drv_data); giveback(drv_data);
return; return;
} }
...@@ -718,7 +735,7 @@ static void pump_transfers(unsigned long data) ...@@ -718,7 +735,7 @@ static void pump_transfers(unsigned long data)
if (flush(drv_data) == 0) { if (flush(drv_data) == 0) {
dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n"); dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
message->status = -EIO; message->status = -EIO;
giveback(message, drv_data); giveback(drv_data);
return; return;
} }
drv_data->n_bytes = chip->n_bytes; drv_data->n_bytes = chip->n_bytes;
...@@ -782,7 +799,7 @@ static void pump_transfers(unsigned long data) ...@@ -782,7 +799,7 @@ static void pump_transfers(unsigned long data)
cr0 = clk_div cr0 = clk_div
| SSCR0_Motorola | SSCR0_Motorola
| SSCR0_DataSize(bits & 0x0f) | SSCR0_DataSize(bits > 16 ? bits - 16 : bits)
| SSCR0_SSE | SSCR0_SSE
| (bits > 16 ? SSCR0_EDSS : 0); | (bits > 16 ? SSCR0_EDSS : 0);
...@@ -890,8 +907,6 @@ static void pump_messages(void *data) ...@@ -890,8 +907,6 @@ static void pump_messages(void *data)
drv_data->cur_msg = list_entry(drv_data->queue.next, drv_data->cur_msg = list_entry(drv_data->queue.next,
struct spi_message, queue); struct spi_message, queue);
list_del_init(&drv_data->cur_msg->queue); list_del_init(&drv_data->cur_msg->queue);
drv_data->busy = 1;
spin_unlock_irqrestore(&drv_data->lock, flags);
/* Initial message state*/ /* Initial message state*/
drv_data->cur_msg->state = START_STATE; drv_data->cur_msg->state = START_STATE;
...@@ -905,6 +920,9 @@ static void pump_messages(void *data) ...@@ -905,6 +920,9 @@ static void pump_messages(void *data)
/* Mark as busy and launch transfers */ /* Mark as busy and launch transfers */
tasklet_schedule(&drv_data->pump_transfers); tasklet_schedule(&drv_data->pump_transfers);
drv_data->busy = 1;
spin_unlock_irqrestore(&drv_data->lock, flags);
} }
static int transfer(struct spi_device *spi, struct spi_message *msg) static int transfer(struct spi_device *spi, struct spi_message *msg)
...@@ -958,7 +976,7 @@ static int setup(struct spi_device *spi) ...@@ -958,7 +976,7 @@ static int setup(struct spi_device *spi)
chip->cs_control = null_cs_control; chip->cs_control = null_cs_control;
chip->enable_dma = 0; chip->enable_dma = 0;
chip->timeout = 5; chip->timeout = SSP_TIMEOUT(1000);
chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1); chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1);
chip->dma_burst_size = drv_data->master_info->enable_dma ? chip->dma_burst_size = drv_data->master_info->enable_dma ?
DCMD_BURST8 : 0; DCMD_BURST8 : 0;
...@@ -971,7 +989,7 @@ static int setup(struct spi_device *spi) ...@@ -971,7 +989,7 @@ static int setup(struct spi_device *spi)
if (chip_info->cs_control) if (chip_info->cs_control)
chip->cs_control = chip_info->cs_control; chip->cs_control = chip_info->cs_control;
chip->timeout = (chip_info->timeout_microsecs * 10000) / 2712; chip->timeout = SSP_TIMEOUT(chip_info->timeout_microsecs);
chip->threshold = SSCR1_RxTresh(chip_info->rx_threshold) chip->threshold = SSCR1_RxTresh(chip_info->rx_threshold)
| SSCR1_TxTresh(chip_info->tx_threshold); | SSCR1_TxTresh(chip_info->tx_threshold);
...@@ -1013,7 +1031,8 @@ static int setup(struct spi_device *spi) ...@@ -1013,7 +1031,8 @@ static int setup(struct spi_device *spi)
chip->cr0 = clk_div chip->cr0 = clk_div
| SSCR0_Motorola | SSCR0_Motorola
| SSCR0_DataSize(spi->bits_per_word & 0x0f) | SSCR0_DataSize(spi->bits_per_word > 16 ?
spi->bits_per_word - 16 : spi->bits_per_word)
| SSCR0_SSE | SSCR0_SSE
| (spi->bits_per_word > 16 ? SSCR0_EDSS : 0); | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0);
chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) << 4) chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) << 4)
...@@ -1196,7 +1215,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1196,7 +1215,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
goto out_error_master_alloc; goto out_error_master_alloc;
} }
drv_data->ioaddr = (void *)io_p2v(memory_resource->start); drv_data->ioaddr = (void *)io_p2v((unsigned long)(memory_resource->start));
drv_data->ssdr_physical = memory_resource->start + 0x00000010; drv_data->ssdr_physical = memory_resource->start + 0x00000010;
if (platform_info->ssp_type == PXA25x_SSP) { if (platform_info->ssp_type == PXA25x_SSP) {
drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE;
...@@ -1218,7 +1237,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -1218,7 +1237,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
goto out_error_master_alloc; goto out_error_master_alloc;
} }
status = request_irq(irq, ssp_int, SA_INTERRUPT, dev->bus_id, drv_data); status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data);
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "can not get IRQ\n"); dev_err(&pdev->dev, "can not get IRQ\n");
goto out_error_master_alloc; goto out_error_master_alloc;
......
...@@ -27,13 +27,16 @@ ...@@ -27,13 +27,16 @@
#define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/2/(x+1))<<8)&0x0000ff00) #define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/2/(x+1))<<8)&0x0000ff00)
#define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) #define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00)
#define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) #define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00)
#define SSP_TIMEOUT_SCALE (2712)
#elif defined(CONFIG_PXA27x) #elif defined(CONFIG_PXA27x)
#define CLOCK_SPEED_HZ 13000000 #define CLOCK_SPEED_HZ 13000000
#define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) #define SSP1_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00)
#define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) #define SSP2_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00)
#define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00) #define SSP3_SerClkDiv(x) (((CLOCK_SPEED_HZ/(x+1))<<8)&0x000fff00)
#define SSP_TIMEOUT_SCALE (769)
#endif #endif
#define SSP_TIMEOUT(x) ((x*10000)/SSP_TIMEOUT_SCALE)
#define SSP1_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(1))))) #define SSP1_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(1)))))
#define SSP2_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(2))))) #define SSP2_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(2)))))
#define SSP3_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(3))))) #define SSP3_VIRT ((void *)(io_p2v(__PREG(SSCR0_P(3)))))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment