If the controller driver receives I/O from SPI core while in runtime-PM suspend it must autonomously resume but if it is called from a non-sleepable context it may return EBUSY until fully resumed. Testing has shown the trasklet is rescheduled a large number of times waiting the resume processing to complete.
We've added a retry delay timer (1ms) in case spi_async returns EBUSY. Signed-off-by: Russ Gorby <[email protected]> --- drivers/serial/ifx6x60.c | 51 +++++++++++++++++++++++++++++++++++++++------ drivers/serial/ifx6x60.h | 3 ++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c index 79b108b..bb85be2 100644 --- a/drivers/serial/ifx6x60.c +++ b/drivers/serial/ifx6x60.c @@ -936,6 +936,19 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_serial *ifx_ser, } /** + * ifx_spi_retry_timer_fn - Timer function for ifx_spi_complete() + * @data: Device pointer passed by ifx_spi_complete(). + * + * The delay timer has expired so reschedule the tasklet + */ +static void ifx_spi_retry_timer_fn(unsigned long data) +{ + struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)data; + + tasklet_schedule(&ifx_dev->io_work_tasklet); +} + +/** * ifx_spi_complete - SPI transfer completed * @ctx: our SPI device * @@ -1011,6 +1024,10 @@ complete_exit: } clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); + clear_bit(IFX_SPI_STATE_IO_RETRY, &ifx_dev->flags); + WARN_ON(timer_pending(&ifx_dev->spi_retry_timer)); + del_timer(&ifx_dev->spi_retry_timer); + queue_length = (port_data == NULL) ? 0 : kfifo_len(&port_data->tx_fifo); srdy = gpio_get_value(ifx_dev->gpio.srdy); @@ -1074,6 +1091,21 @@ complete_exit: } /** + * ifx_spi_retry - start or extend the I/O retry delay timer + */ +static void +ifx_spi_retry(struct ifx_spi_device *ifx_dev) +{ + unsigned long expires; + + if (!test_and_set_bit(IFX_SPI_STATE_IO_RETRY, &ifx_dev->flags)) + setup_timer(&ifx_dev->spi_retry_timer, ifx_spi_retry_timer_fn, + (unsigned long)ifx_dev); + expires = jiffies + IFX_RETRY_TIMEOUT; + mod_timer(&ifx_dev->spi_retry_timer, expires); +} + +/** * ifx_spio_io - I/O tasklet * @data: our SPI device * @@ -1145,13 +1177,17 @@ static void ifx_spi_io(unsigned long data) if (retval) { if (retval != -EBUSY) ifx_spi_complete((void *)ifx_dev); - else { - dev_dbg(&ifx_dev->spi_dev->dev, - "spi_async failed (%d)", retval); - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, - &ifx_dev->flags); - tasklet_schedule(&ifx_dev->io_work_tasklet); - } + else + ifx_spi_retry(ifx_dev); + return; + } + } else if (test_bit(IFX_SPI_STATE_IO_RETRY, &ifx_dev->flags)) { + retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg); + if (retval) { + if (retval != -EBUSY) + ifx_spi_complete((void *)ifx_dev); + else + ifx_spi_retry(ifx_dev); return; } } else { @@ -1460,6 +1496,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) spin_lock_init(&ifx_dev->power_lock); ifx_dev->power_status = 0; init_timer(&ifx_dev->spi_timer); + init_timer(&ifx_dev->spi_retry_timer); ifx_dev->spi_timer.function = ifx_spi_timeout; ifx_dev->spi_timer.data = (unsigned long)ifx_dev; diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h index c4d11fe..da1efc6 100644 --- a/drivers/serial/ifx6x60.h +++ b/drivers/serial/ifx6x60.h @@ -38,12 +38,14 @@ #define IFX_SPI_FIFO_SIZE 4096 #define IFX_SPI_HEADER_OVERHEAD 4 +#define IFX_RETRY_TIMEOUT msecs_to_jiffies(1) #define IFX_RESET_TIMEOUT msecs_to_jiffies(50) /* device state bit offsets */ enum ifx_spi_state { IFX_SPI_STATE_PRESENT, IFX_SPI_STATE_IO_IN_PROGRESS, + IFX_SPI_STATE_IO_RETRY, IFX_SPI_STATE_IO_READY, IFX_SPI_STATE_TIMER_PENDING, }; @@ -103,6 +105,7 @@ struct ifx_spi_device { unsigned char spi_slave_cts; struct timer_list spi_timer; + struct timer_list spi_retry_timer; struct spi_message spi_msg; struct spi_transfer spi_xfer; -- 1.6.0.6 _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
