Signed-off-by: Russ Gorby <[email protected]>
---
 drivers/serial/ifx6x60.c |  183 ++++++++++++++++++----------------------------
 drivers/serial/ifx6x60.h |    4 +
 drivers/spi/pw_spi3.c    |  130 +++++++++++++++++++-------------
 3 files changed, 152 insertions(+), 165 deletions(-)

diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c
index d68fafb..d7bf507 100644
--- a/drivers/serial/ifx6x60.c
+++ b/drivers/serial/ifx6x60.c
@@ -134,6 +134,7 @@ static void
 ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val)
 {
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&ifx_dev->power_lock, flags);
 
@@ -142,8 +143,9 @@ ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, 
unsigned char val)
         * tell power system
         */
        if (!ifx_dev->power_status) {
-               dev_dbg(&ifx_dev->spi_dev->dev, "pm_runtime_get called");
-               pm_runtime_get(&ifx_dev->spi_dev->dev);
+               ret = pm_runtime_get(ifx_dev->spi_dev->dev.parent);
+               if (ret < 0)
+                       pr_warn("%s: rt_get failed: %d", DRVNAME, ret);
        }
        ifx_dev->power_status |= val;
 
@@ -161,15 +163,16 @@ static void
 ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
 {
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&ifx_dev->power_lock, flags);
 
        if (ifx_dev->power_status) {
                ifx_dev->power_status &= ~val;
                if (!ifx_dev->power_status) {
-                       dev_dbg(&ifx_dev->spi_dev->dev,
-                               "pm_runtime_put called");
-                       pm_runtime_put(&ifx_dev->spi_dev->dev);
+                       ret = pm_runtime_put(ifx_dev->spi_dev->dev.parent);
+                       if (ret < 0)
+                               pr_warn("%s: rt_put failed: %d", DRVNAME, ret);
                }
        }
 
@@ -273,6 +276,7 @@ static void ifx_spi_timeout(unsigned long arg)
        }
 
        dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***");
+       ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING);
        ifx_spi_ttyhangup(ifx_dev);
        mrdy_set_low(ifx_dev);
        clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
@@ -623,6 +627,14 @@ static int ifx_spi_write(struct tty_struct *tty, const 
unsigned char *buf,
        int srdy_value;
 
        dev_dbg(&ifx_dev->spi_dev->dev, "%s called", __func__);
+       if (test_bit(IFX_SPI_STATE_SUSPEND,  &ifx_dev->flags)) {
+               /*
+                * FIXME:
+                * some modems have been observed to die (requiring reset)
+                * if MRDY/SRDY handshake occurs w/o clocking data
+                */
+               return -EIO;
+       }
 
        tx_count = kfifo_in_locked(&port_data->tx_fifo, tmp_buf, count,
                                   &port_data->fifo_lock);
@@ -955,6 +967,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
  *
@@ -1030,6 +1055,9 @@ complete_exit:
        }
 
        clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags));
+       clear_bit(IFX_SPI_STATE_IO_RETRY, &(ifx_dev->flags));
+       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);
@@ -1092,6 +1120,18 @@ complete_exit:
        }
 }
 
+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
@@ -1162,15 +1202,28 @@ static void ifx_spi_io(unsigned long data)
 
                retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg);
                if (retval) {
-                       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);
+                       if (retval != -EBUSY)
+                               ifx_spi_complete((void *)ifx_dev);
+                       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 {
-               /* print error if called in progress */
+               /*
+                * ifx_spi_complete processing can extend beyond
+                * the point where the modem drops SRDY and so we can
+                * get a second SRDY interrupt for new I/O before
+                * completion processing is finished
+                */
                dev_dbg(&ifx_dev->spi_dev->dev,
                        "spi_io called while SPI in progress");
                ifx_dev->write_pending = 1;
@@ -1470,6 +1523,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;
 
@@ -1618,9 +1672,6 @@ static int ifx_spi_spi_probe(struct spi_device *spi)
                goto error_ret2;
        }
 
-       /* set pm runtime power state and register with power system */
-       pm_runtime_set_active(&spi->dev);
-       pm_runtime_enable(&spi->dev);
 
        /* handle case that modem is already signaling SRDY */
        /* no outgoing tty open at this point, this just satisfies the
@@ -1690,7 +1741,10 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi)
  */
 static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg)
 {
+       struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+
        dev_dbg(&spi->dev, "%s called", __func__);
+       set_bit(IFX_SPI_STATE_SUSPEND,  &ifx_dev->flags);
        return 0;
 }
 
@@ -1703,107 +1757,13 @@ static int ifx_spi_spi_suspend(struct spi_device *spi, 
pm_message_t msg)
  */
 static int ifx_spi_spi_resume(struct spi_device *spi)
 {
-       dev_dbg(&spi->dev, "%s called", __func__);
-       return 0;
-}
-
-/**
- *     ifx_spi_pm_suspend      -       suspend modem on system suspend
- *     @dev: device being suspended
- *
- *     Suspend the modem. No action needed on Intel MID platforms, may
- *     need extending for other systems.
- */
-static int ifx_spi_pm_suspend(struct device *dev)
-{
-       int retval = 0;
-       struct spi_device *spi = to_spi_device(dev);
-
-       dev_dbg(&spi->dev, "pm suspend");
-
-       return retval;
-}
-
-/**
- *     ifx_spi_pm_resume       -       resume modem on system resume
- *     @dev: device being suspended
- *
- *     Allow the modem to resume. No action needed.
- *
- *     FIXME: do we need to reset anything here ?
- */
-static int ifx_spi_pm_resume(struct device *dev)
-{
-       int retval = 0;
-       struct spi_device *spi = to_spi_device(dev);
-
-       dev_dbg(&spi->dev, "pm resume");
-
-       return retval;
-}
-
-/**
- *     ifx_spi_pm_runtime_resume       -       suspend modem
- *     @dev: device being suspended
- *
- *     Allow the modem to resume. No action needed.
- */
-static int ifx_spi_pm_runtime_resume(struct device *dev)
-{
-       int retval = 0;
-       struct spi_device *spi = to_spi_device(dev);
-
-       dev_dbg(&spi->dev, "pm runtime resume");
-
-       return retval;
-}
-
-/**
- *     ifx_spi_pm_runtime_suspend      -       suspend modem
- *     @dev: device being suspended
- *
- *     Allow the modem to suspend and thus suspend to continue up the
- *     device tree.
- */
-static int ifx_spi_pm_runtime_suspend(struct device *dev)
-{
-       int retval = 0;
-       struct spi_device *spi = to_spi_device(dev);
-
-       dev_dbg(&spi->dev, "pm runtime_suspend");
-
-       return retval;
-}
-
-/**
- *     ifx_spi_pm_runtime_idle         -       check if modem idle
- *     @dev: our device
- *
- *     Check conditions and queue runtime suspend if idle.
- *     FIXME: What locking/races are there on the power_status variable ?
- */
-static int ifx_spi_pm_runtime_idle(struct device *dev)
-{
-       int retval = 0;
-       struct spi_device *spi = to_spi_device(dev);
        struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
 
-       dev_dbg(&spi->dev, "pm runtime_idle");
-
-       if (!ifx_dev->power_status)
-               pm_runtime_suspend(dev);
-
-       return retval;
+       dev_dbg(&spi->dev, "%s called", __func__);
+       clear_bit(IFX_SPI_STATE_SUSPEND,  &ifx_dev->flags);
+       return 0;
 }
 
-static const struct dev_pm_ops ifx_spi_pm = {
-       .resume = ifx_spi_pm_resume,
-       .suspend = ifx_spi_pm_suspend,
-       .runtime_resume = ifx_spi_pm_runtime_resume,
-       .runtime_suspend = ifx_spi_pm_runtime_suspend,
-       .runtime_idle = ifx_spi_pm_runtime_idle
-};
-
 static const struct spi_device_id ifx_spi_device_ids[] = {
        { MODEMNAME_6160, IFX_6160 },
        { MODEMNAME_6260, IFX_6260 },
@@ -1816,7 +1776,6 @@ static const struct spi_driver ifx_spi_driver = {
        .driver = {
                .name = DRVNAME,
                .bus = &spi_bus_type,
-               .pm = &ifx_spi_pm,
                .owner = THIS_MODULE},
        .id_table = ifx_spi_device_ids,
        .probe = ifx_spi_spi_probe,
diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h
index 458e385..6a1e88f 100644
--- a/drivers/serial/ifx6x60.h
+++ b/drivers/serial/ifx6x60.h
@@ -38,14 +38,17 @@
 #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,
+       IFX_SPI_STATE_SUSPEND,
 };
 
 /* flow control bit offsets */
@@ -104,6 +107,7 @@ struct ifx_spi_device {
        int ifx_transfer_size;
 
        struct timer_list spi_timer;
+       struct timer_list spi_retry_timer;
 
        struct spi_message spi_msg;
        struct spi_transfer spi_xfer;
diff --git a/drivers/spi/pw_spi3.c b/drivers/spi/pw_spi3.c
index 61f4d27..06a2d69 100644
--- a/drivers/spi/pw_spi3.c
+++ b/drivers/spi/pw_spi3.c
@@ -107,6 +107,16 @@ struct callback_param {
        int *donep;
 };
 
+enum dd_pwrstate {
+       PWRSTATE_ON = 1,
+       PWRSTATE_IDLE,
+       PWRSTATE_OFF,
+};
+
+enum dd_pwrflags {
+       PWRFLAG_RTRESUMING,
+};
+
 struct driver_data {
        /* Driver model hookup */
        struct pci_dev *pdev;
@@ -149,10 +159,8 @@ struct driver_data {
        int dma_inited;
 
        /* pwrstate mgmt */
-       int pwrstate;
-#define PWRSTATE_ON            1
-#define PWRSTATE_IDLE          2
-#define PWRSTATE_OFF           3
+       int pwrstate;           /* enum dd_pwrstate */
+       unsigned long pwrflags; /* enum dd_pwrflags */
 
        /* used by DMA code */
        struct pci_dev *dmac1;
@@ -752,15 +760,19 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        u32 cr0;
        u32 cr1;
 
-       if (drv_data->pwrstate != PWRSTATE_ON) {
+       if (drv_data->pwrstate == PWRSTATE_OFF) {
                dev_dbg(&drv_data->pdev->dev, "transfer: busy, pwrstate:%d",
                        drv_data->pwrstate);
+               return -EIO;
+       }
+       if (drv_data->pwrstate == PWRSTATE_IDLE) {
+               if (!test_and_set_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+                       pm_runtime_get(&drv_data->pdev->dev);
                return -EBUSY;
        }
        msg->actual_length = 0;
        msg->status = -EINPROGRESS;
        drv_data->cur_msg = msg;
-       /* Initial message state*/
 
        /* We handle only one transfer message since the protocol module has to
           control the out of band signaling. */
@@ -777,7 +789,8 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        }
 
        /* Setup the transfer state based on the type of transfer */
-       pm_runtime_get(&drv_data->pdev->dev);
+       if (!test_and_clear_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+               pm_runtime_get(&drv_data->pdev->dev);
        chip = spi_get_ctldata(msg->spi);
        flush(drv_data);
        drv_data->n_bytes = chip->n_bytes;
@@ -897,10 +910,10 @@ static int setup(struct spi_device *spi)
        u32 clk_div;
        struct driver_data *drv_data = spi_master_get_devdata(spi->master);
 
-       if (drv_data->pwrstate != PWRSTATE_ON) {
+       if (drv_data->pwrstate == PWRSTATE_OFF) {
                dev_dbg(&drv_data->pdev->dev, "setup: busy, pwrstate:%d",
                        drv_data->pwrstate);
-               return -EBUSY;
+               return -EIO;
        }
 
        if (!spi->bits_per_word)
@@ -929,7 +942,6 @@ static int setup(struct spi_device *spi)
        chip_info = spi->controller_data;
 
        /* chip_info isn't always needed */
-
        if (chip_info) {
                if (chip_info->timeout)
                        chip->timeout = chip_info->timeout;
@@ -1114,13 +1126,16 @@ static int pw_spi_probe(struct pci_dev *pdev, const 
struct pci_device_id *ent)
                goto err_free_4;
        }
 
-       /* set pm runtime power state and register with power system */
-       pm_runtime_set_active(dev);
-       pm_runtime_enable(dev);
+
 
        pw_spi_dma_init(drv_data);
        pci_set_drvdata(pdev, drv_data);
 
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+       pm_runtime_allow(dev);
+
        return status;
 
 err_free_4:
@@ -1168,8 +1183,6 @@ static void __devexit pw_spi_remove(struct pci_dev *pdev)
        return;
 }
 
-#ifdef CONFIG_PM
-
 /*
  * for now IDLE and OFF states are treated the same
  */
@@ -1215,29 +1228,6 @@ static int _pm_suspend(struct pci_dev *pdev, int to)
        return 0;
 }
 
-static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-       int retval;
-       struct driver_data *drv_data = pci_get_drvdata(pdev);
-
-       dev_dbg(&pdev->dev, "suspend");
-
-       if (drv_data->pwrstate != PWRSTATE_ON)
-               dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
-                        drv_data->pwrstate);
-       retval = _pm_suspend(pdev, PWRSTATE_OFF);
-       if (retval)
-               return retval;
-       retval = pci_prepare_to_sleep(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "suspend: prepare to sleep failed");
-               return retval;
-       }
-       pci_disable_device(pdev);
-
-       return 0;
-}
-
 /*
  * for now IDLE and OFF states are treated the same
  */
@@ -1272,13 +1262,36 @@ static void _pm_resume(struct pci_dev *pdev)
        }
 }
 
-static int pw_spi_resume(struct pci_dev *pdev)
+
+#ifdef CONFIG_PM
+
+static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        int retval;
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(&pdev->dev, "resume");
+       dev_dbg(&pdev->dev, "%s called", __func__);
+       if (drv_data->pwrstate != PWRSTATE_ON)
+               dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
+                        drv_data->pwrstate);
+       retval = _pm_suspend(pdev, PWRSTATE_OFF);
+       if (retval)
+               return retval;
+       retval = pci_prepare_to_sleep(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "suspend: prepare to sleep failed");
+               return retval;
+       }
+       pci_disable_device(pdev);
+       return 0;
+}
+
+static int pw_spi_resume(struct pci_dev *pdev)
+{
+       int retval;
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
 
+       dev_dbg(&pdev->dev, "%s called", __func__);
        if (drv_data->pwrstate != PWRSTATE_OFF)
                dev_warn(&pdev->dev, "resume: !off, pwrstate:%d",
                         drv_data->pwrstate);
@@ -1293,9 +1306,9 @@ static int pw_spi_resume(struct pci_dev *pdev)
                return retval;
        }
        _pm_resume(pdev);
-
        return 0;
 }
+
 #else
 #define pw_spi_suspend NULL
 #define pw_spi_resume NULL
@@ -1308,17 +1321,20 @@ static int pw_spi_pm_runtime_resume(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(dev, "pm runtime resume");
-
+       dev_dbg(dev, "%s called", __func__);
+       if (drv_data->pwrstate == PWRSTATE_ON)
+               return 0;
        if (drv_data->pwrstate != PWRSTATE_IDLE)
-               dev_warn(&pdev->dev, "rt suspend: !idle, pwrstate:%d",
+               dev_warn(&pdev->dev, "rt resume: !idle, pwrstate:%d",
                         drv_data->pwrstate);
-       retval = pci_back_from_sleep(pdev);
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       retval = pci_enable_device(pdev);
        if (retval)
-               dev_err(&pdev->dev, "rt resume: back from sleep failed");
+               return retval;
        _pm_resume(pdev);
 
-       return 0;
+       return retval;
 }
 
 static int pw_spi_pm_runtime_suspend(struct device *dev)
@@ -1327,24 +1343,32 @@ static int pw_spi_pm_runtime_suspend(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(dev, "pm runtime suspend");
-
+       dev_dbg(dev, "%s called", __func__);
        if (drv_data->pwrstate != PWRSTATE_ON)
                dev_warn(&pdev->dev, "rt suspend: !on, pwrstate:%d",
                         drv_data->pwrstate);
        retval = _pm_suspend(pdev, PWRSTATE_IDLE);
        if (retval)
                return retval;
-       retval = pci_prepare_to_sleep(pdev);
-       if (retval)
-               dev_err(&pdev->dev, "rt suspend: prepare to sleep failed");
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, PCI_D3hot, 0);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
 
-       return 0;
+       return retval;
+}
+
+static int pw_spi_pm_runtime_idle(struct device *dev)
+{
+       dev_dbg(dev, "%s called", __func__);
+       pm_schedule_suspend(dev, 100);
+       return -EBUSY;
 }
 
 static const struct dev_pm_ops pw_spi_pm = {
        .runtime_resume = pw_spi_pm_runtime_resume,
        .runtime_suspend = pw_spi_pm_runtime_suspend,
+       .runtime_idle =  pw_spi_pm_runtime_idle,
 };
 
 static const struct pci_device_id pci_ids[] __devinitdata = {
-- 
1.6.0.6

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to