>From 76b99206d208614ef5a7202aee6ee7725d961226 Mon Sep 17 00:00:00 2001
From: Russ Gorby <[email protected]>
Date: Fri, 30 Jul 2010 10:48:26 -0700
Subject: [PATCH 1/1] PM support for SSP-SPI master controller driver v.01


Signed-off-by: Russ Gorby <[email protected]>

This is v.01 of the pm-supp patch for SSP-SPI
This patch enables some of the power management support like D3 suspend/resume
        and the PM runtime as well
This is a delta patch based on Alan's 2.6-mid-ref repo
---
 drivers/spi/pw_spi3.c |  341 ++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 282 insertions(+), 59 deletions(-)

diff --git a/drivers/spi/pw_spi3.c b/drivers/spi/pw_spi3.c
index fbb20a8..fa1ac8d 100644
--- a/drivers/spi/pw_spi3.c
+++ b/drivers/spi/pw_spi3.c
@@ -29,6 +29,7 @@
 #include <linux/intel_mid_dma.h>
 #include <linux/interrupt.h>
 #include <linux/spi/spi.h>
+#include <linux/pm_runtime.h>
 #include <linux/spi/pw_spi3.h>

 #define DRIVER_NAME            "pw_spi3"
@@ -92,6 +93,7 @@ struct callback_param {
        void *drv_data;
        int *donep;
 };
+
 struct driver_data {
        /* Driver model hookup */
        struct pci_dev *pdev;
@@ -110,12 +112,9 @@ struct driver_data {
        u32 clear_sr;
        u32 mask_sr;

-       struct tasklet_struct poll_transfer;
-
-       int busy;
-       int run;

        /* Current message transfer state info */
+       struct tasklet_struct poll_transfer;
        struct spi_message *cur_msg;
        size_t len;
        void *tx;
@@ -132,8 +131,17 @@ struct driver_data {
        int (*read)(struct driver_data *drv_data);
        irqreturn_t (*transfer_handler)(struct driver_data *drv_data);
        void (*cs_control)(u32 command);
+
+       /* controller state */
        int dma_inited;

+       /* pwrstate mgmt */
+       atomic_t active;
+       int pwrstate;
+#define PWRSTATE_ON            1
+#define PWRSTATE_IDLE          2
+#define PWRSTATE_OFF           3
+
        /* used by DMA code */
        struct pci_dev *dmac1;
        struct intel_mid_dma_slave    dmas_tx;
@@ -162,22 +170,47 @@ struct chip_data {
        int (*read)(struct driver_data *drv_data);
 };

+static struct pm_qos_request_list *pw_spi_pm_qos_req;
+
+static inline void active_inc(struct driver_data *drv_data)
+{
+       int i;
+       i = atomic_add_return(1, &drv_data->active);
+       pm_runtime_get(&drv_data->pdev->dev);
+}
+
+static inline void active_dec(struct driver_data *drv_data)
+{
+       int i;
+       i = atomic_sub_return(1, &drv_data->active);
+       pm_runtime_put(&drv_data->pdev->dev);
+}
+
+static inline int have_fifo_data(struct driver_data *drv_data, u32 *sssrp)
+{
+       u32 sssr;
+       void *reg = drv_data->ioaddr;
+       sssr = ioread32(reg + SSSR);
+
+       if (sssrp)
+               *sssrp = sssr;
+       return ((sssr & SSSR_TFL) || !(sssr & SSSR_TNF)) ||
+               ((sssr & SSSR_RFL) != SSSR_RFL || (sssr & SSSR_RNE));
+}
+
 static void flush(struct driver_data *drv_data)
 {
        void *reg = drv_data->ioaddr;
        u32 sssr;

        /* If the transmit fifo is not empty, reset the interface. */
-       sssr = ioread32(reg + SSSR);
-       if ((sssr & SSSR_TFL) || (sssr & SSSR_TNF) == 0) {
+       if (have_fifo_data(drv_data, &sssr)) {
+               dev_warn(&drv_data->pdev->dev,
+                        "ERROR: flush: fifos not empty! sssr:%x", sssr);
                iowrite32(ioread32(reg + SSCR0) & ~SSCR0_SSE, reg + SSCR0);
                return;
        }

-       /* FIXME?: Timeout reset */
-       while (ioread32(reg + SSSR) & SSSR_RNE)
-               ioread32(reg + SSDR);
-
        iowrite32(SSSR_ROR, reg + SSSR);
        iowrite32(SSSR_TUR, reg + SSSR);
 }
@@ -323,8 +356,6 @@ static void giveback(struct driver_data *drv_data)
                msg->complete(msg->context);
 }

-static void int_transfer_complete(struct driver_data *drv_data);
-
 static bool chan_filter(struct dma_chan *chan, void *param)
 {
        struct driver_data *drv_data = (struct driver_data *)param;
@@ -339,7 +370,7 @@ static bool chan_filter(struct dma_chan *chan, void *param)
        return ret;
 }

-static void pw_spi_dma_done(void *arg)
+static void dma_transfer_complete(void *arg)
 {
        struct callback_param *param = arg;
        struct driver_data *drv_data;
@@ -355,7 +386,6 @@ static void pw_spi_dma_done(void *arg)
        if (!drv_data->txdma_done || !drv_data->rxdma_done)
                return;

-
        /* Clear Status Register */
        iowrite32(drv_data->clear_sr, reg + SSSR);

@@ -376,6 +406,7 @@ static void pw_spi_dma_done(void *arg)

        drv_data->cur_msg->status = 0;
        giveback(drv_data);
+       active_dec(drv_data);
 }

 static void pw_spi_dma_init(struct driver_data *drv_data)
@@ -431,7 +462,6 @@ static void pw_spi_dma_init(struct driver_data *drv_data)
                goto free_rxchan;
        drv_data->txchan->private = txs;

-       /* set the dma done bit to 1 */
        drv_data->dma_inited = 1;
        drv_data->txdma_done = 1;
        drv_data->rxdma_done = 1;
@@ -464,7 +494,6 @@ static void dma_transfer(struct driver_data *drv_data)
        struct dma_chan *txchan, *rxchan;
        enum dma_ctrl_flags flag;

-
        /* get Data Read/Write address */
        ssdr_addr = (dma_addr_t)(drv_data->paddr + 0x10);

@@ -480,11 +509,6 @@ static void dma_transfer(struct driver_data *drv_data)

        flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;

-       /*
-        * RRG
-        * (re)set rxchan->private->src_with based on drv_data->nbytes
-        * (re)set txchan->private->dst_with based on drv_data->nbytes
-        */
        if (drv_data->rx_dma) {
                rxdesc = rxchan->device->device_prep_dma_memcpy
                        (rxchan,                /* DMA Channel */
@@ -493,7 +517,7 @@ static void dma_transfer(struct driver_data *drv_data)
                         drv_data->len,         /* Data Length */
                         flag);                 /* Flag */

-               rxdesc->callback = pw_spi_dma_done;
+               rxdesc->callback = dma_transfer_complete;
                rxdesc->callback_param = &drv_data->rx_param;
        }

@@ -506,15 +530,15 @@ static void dma_transfer(struct driver_data *drv_data)
                         drv_data->len,         /* Data Length */
                         flag);                 /* Flag */

-               txdesc->callback = pw_spi_dma_done;
+               txdesc->callback = dma_transfer_complete;
                txdesc->callback_param = &drv_data->tx_param;
        }

        if (rxdesc)
                rxdesc->tx_submit(rxdesc);
-
        if (txdesc)
                txdesc->tx_submit(txdesc);
+
 }


@@ -543,8 +567,7 @@ static int map_dma_buffers(struct driver_data *drv_data,

 static void set_dma_width(struct spi_device *spi, int bits)
 {
-       struct driver_data *drv_data = \
-               spi_master_get_devdata(spi->master);
+       struct driver_data *drv_data = spi_master_get_devdata(spi->master);
        struct intel_mid_dma_slave *rxs, *txs;
        rxs = &drv_data->dmas_rx;
        txs = &drv_data->dmas_tx;
@@ -602,6 +625,7 @@ static void int_transfer_complete(struct driver_data 
*drv_data)

        drv_data->cur_msg->status = 0;
        giveback(drv_data);
+       active_dec(drv_data);
 }

 static void transfer_complete(struct driver_data *drv_data)
@@ -612,6 +636,7 @@ static void transfer_complete(struct driver_data *drv_data)

        drv_data->cur_msg->status = 0;
        giveback(drv_data);
+       active_dec(drv_data);
 }

 static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
@@ -713,8 +738,7 @@ static unsigned int ssp_get_clk_div(int speed)

 static int transfer(struct spi_device *spi, struct spi_message *msg)
 {
-       struct driver_data *drv_data = \
-       spi_master_get_devdata(spi->master);
+       struct driver_data *drv_data = spi_master_get_devdata(spi->master);
        struct chip_data *chip = NULL;
        struct spi_transfer *transfer = NULL;
        void *reg = drv_data->ioaddr;
@@ -724,6 +748,11 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        u32 cr0;
        u32 cr1;

+       if (drv_data->pwrstate != PWRSTATE_ON) {
+               dev_dbg(&drv_data->pdev->dev, "transfer: busy, pwrstate:%d",
+                       drv_data->pwrstate);
+               return -EBUSY;
+       }
        msg->actual_length = 0;
        msg->status = -EINPROGRESS;
        drv_data->cur_msg = msg;
@@ -731,12 +760,8 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)

        /* We handle only one transfer message since the protocol module has to
           control the out of band signaling. */
-       transfer = list_entry(msg->transfers.next,
-       struct spi_transfer, transfer_list);
-
-       chip = spi_get_ctldata(msg->spi);
-
-       drv_data->busy = 1;
+       transfer = list_entry(msg->transfers.next, struct spi_transfer,
+                             transfer_list);

        /* Check transfer length */
        if (transfer->len > 8192) {
@@ -748,6 +773,8 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        }

        /* Setup the transfer state based on the type of transfer */
+       active_inc(drv_data);
+       chip = spi_get_ctldata(msg->spi);
        flush(drv_data);
        drv_data->n_bytes = chip->n_bytes;
        drv_data->tx = (void *)transfer->tx_buf;
@@ -821,14 +848,14 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
                cr1 |= drv_data->int_cr1;

        dev_dbg(&drv_data->pdev->dev,
-               "%s  drv_data:%p  len:%d  n_bytes:%d  cr0:%x  cr1:%x",
+               "%s drv_data:%p len:%d n_bytes:%d cr0:%x cr1:%x",
                (drv_data->dma_mapped ? "DMA io:" :
                 (chip->poll_mode ? "Poll io:" : "Intr io:")),
                drv_data, drv_data->len, drv_data->n_bytes, cr0, cr1);

        /* see if we need to reload the config registers */
        if ((ioread32(reg + SSCR0) != cr0)
-               || (ioread32(reg + SSCR0) & SSCR1_CHANGE_MASK) !=
+               || (ioread32(reg + SSCR1) & SSCR1_CHANGE_MASK) !=
                        (cr1 & SSCR1_CHANGE_MASK)) {

                /* stop the SSP, and update the other bits */
@@ -864,7 +891,13 @@ static int setup(struct spi_device *spi)
        uint tx_thres = TX_THRESH_DFLT;
        uint rx_thres = RX_THRESH_DFLT;
        u32 clk_div;
+       struct driver_data *drv_data = spi_master_get_devdata(spi->master);

+       if (drv_data->pwrstate != PWRSTATE_ON) {
+               dev_dbg(&drv_data->pdev->dev, "setup: busy, pwrstate:%d",
+                       drv_data->pwrstate);
+               return -EBUSY;
+       }

        if (!spi->bits_per_word)
                spi->bits_per_word = 8;
@@ -956,18 +989,17 @@ static void cleanup(struct spi_device *spi)
        spi_set_ctldata(spi, NULL);
 }

-static int pw_spi_probe(struct pci_dev *pdev,
-                         const struct pci_device_id *ent)
+static int pw_spi_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        struct device *dev = &pdev->dev;
        struct spi_master *master;
-       struct driver_data *drv_data = 0;
+       struct driver_data *drv_data;
        int status = 0;
        int pci_bar = 0;
        void __iomem *syscfg_ioaddr;
        unsigned long syscfg;

-       dev_info(&pdev->dev, "found PCI SSP controller(ID: %04x:%04x)",
+       dev_info(dev, "found PCI SSP controller(ID: %04x:%04x)",
                 pdev->vendor, pdev->device);

        status = pci_enable_device(pdev);
@@ -978,15 +1010,16 @@ static int pw_spi_probe(struct pci_dev *pdev,
        master = spi_alloc_master(dev, sizeof(struct driver_data));

        if (!master) {
-               dev_err(&pdev->dev, "cannot alloc spi_master");
+               dev_err(dev, "cannot alloc spi_master");
                status = -ENOMEM;
                goto err_free_0;
        }

        drv_data = spi_master_get_devdata(master);
+       atomic_set(&drv_data->active, 0);
        drv_data->master = master;
-
        drv_data->pdev = pdev;
+       drv_data->pwrstate = PWRSTATE_ON;

        master->mode_bits = SPI_CPOL | SPI_CPHA;
        master->bus_num = 3;
@@ -999,7 +1032,7 @@ static int pw_spi_probe(struct pci_dev *pdev,
        drv_data->paddr = pci_resource_start(pdev, pci_bar);
        drv_data->iolen = pci_resource_len(pdev, pci_bar);

-       status = pci_request_region(pdev, pci_bar, dev_name(&pdev->dev));
+       status = pci_request_region(pdev, pci_bar, dev_name(dev));
        if (status)
                goto err_free_1;

@@ -1008,9 +1041,9 @@ static int pw_spi_probe(struct pci_dev *pdev,
                status = -ENOMEM;
                goto err_free_2;
        }
-       dev_dbg(&pdev->dev, "paddr = : %08lx", drv_data->paddr);
-       dev_dbg(&pdev->dev, "ioaddr = : %p", drv_data->ioaddr);
-       dev_dbg(&pdev->dev, "attaching to IRQ: %04x", pdev->irq);
+       dev_dbg(dev, "paddr = : %08lx", drv_data->paddr);
+       dev_dbg(dev, "ioaddr = : %p", drv_data->ioaddr);
+       dev_dbg(dev, "attaching to IRQ: %04x", pdev->irq);

        /* Attach to IRQ */
        drv_data->irq = pdev->irq;
@@ -1018,7 +1051,7 @@ static int pw_spi_probe(struct pci_dev *pdev,
        status = request_irq(drv_data->irq, ssp_int, IRQF_SHARED,
                "pw_spi3", drv_data);
        if (status < 0) {
-               dev_err(&pdev->dev, "can not get IRQ %d", drv_data->irq);
+               dev_err(dev, "can not get IRQ %d", drv_data->irq);
                goto err_free_3;
        }

@@ -1041,7 +1074,7 @@ static int pw_spi_probe(struct pci_dev *pdev,
                     poll_transfer, (unsigned long)drv_data);

        /* Load default SSP configuration */
-       dev_info(&pdev->dev, "setup default SSP configuration");
+       dev_info(dev, "setup default SSP configuration");
        iowrite32(0, drv_data->ioaddr + SSCR0);
        iowrite32(SSCR1_RxTresh(RX_THRESH_DFLT) |
                    SSCR1_TxTresh(TX_THRESH_DFLT),
@@ -1051,18 +1084,18 @@ static int pw_spi_probe(struct pci_dev *pdev,
        iowrite32(PNWL_SSPSP, drv_data->ioaddr + SSPSP);

        /* Register with the SPI framework */
-       dev_info(&pdev->dev, "register with SPI framework");
-
+       dev_info(dev, "register with SPI framework");
        status = spi_register_master(master);
-
        if (status != 0) {
-               dev_err(&pdev->dev, "problem registering driver");
+               dev_err(dev, "problem registering driver");
                goto err_free_4;
        }

-       drv_data->dma_inited = 0;
-       pw_spi_dma_init(drv_data);
+       /* 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);

        return status;
@@ -1110,22 +1143,143 @@ static void __devexit pw_spi_remove(struct pci_dev 
*pdev)

 #ifdef CONFIG_PM

+/*
+ * for now IDLE and OFF states are treated the same
+ */
+static int _pm_suspend(struct pci_dev *pdev, int to)
+{
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
+       void *reg = drv_data->ioaddr;
+       int from = drv_data->pwrstate;
+       u32 sssr;
+
+
+       if (to != PWRSTATE_IDLE && to != PWRSTATE_OFF) {
+               dev_err(&pdev->dev, "ERROR: suspend: invalid dst pwrstate %x",
+                       to);
+               return -EINVAL;
+       }
+
+       switch (from) {
+       case PWRSTATE_ON:
+               /* to != PWRSTATE_ON: disables transfer() */
+               drv_data->pwrstate = to;
+               if (atomic_read(&drv_data->active) != 0) {
+                       drv_data->pwrstate = from;
+                       return -EBUSY;
+               }
+               dev_dbg(&pdev->dev, "suspend: turn off SSP");
+               tasklet_disable(&drv_data->poll_transfer);
+               if (have_fifo_data(drv_data, &sssr)) {
+                       dev_err(&pdev->dev,
+                               "ERROR: suspend: i/o present! sssr:%x", sssr);
+               }
+               iowrite32(0, reg + SSCR0);
+               dev_dbg(&pdev->dev, "suspend: cr0:%x cr1:%x sssr:%x",
+                       ioread32(reg + SSCR0), ioread32(reg + SSCR1),
+                       ioread32(reg + SSSR));
+               break;
+       case PWRSTATE_IDLE:
+       case PWRSTATE_OFF:
+               /* to != PWRSTATE_ON: disables transfer() */
+               drv_data->pwrstate = to;
+               break;
+       default:
+               dev_err(&pdev->dev, "ERROR: suspend: invalid src pwrstate %x",
+                       from);
+               return -EINVAL;
+       }
+
+       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");

-       tasklet_disable(&drv_data->poll_transfer);
+       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
+ */
+static void _pw_resume(struct pci_dev *pdev, int to)
+{
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
+       void *reg = drv_data->ioaddr;
+       int from = drv_data->pwrstate;
+
+       if (to != PWRSTATE_IDLE && to != PWRSTATE_ON) {
+               dev_err(&pdev->dev, "ERROR: resume: invalid dst pwrstate %x",
+                       to);
+               return;
+       }
+       switch (from) {
+       case PWRSTATE_IDLE:
+       case PWRSTATE_OFF:
+               if (to == PWRSTATE_ON) {
+                       dev_dbg(&pdev->dev, "resume: turn on SSP");
+
+                       /*
+                        * we don't bother reconfiguring the registers
+                        * on resume - that will get done when transfer()
+                        * is called
+                        */
+                       tasklet_enable(&drv_data->poll_transfer);
+                       dev_dbg(&pdev->dev, "resume: cr0:%x cr1:%x sssr:%x",
+                               ioread32(reg + SSCR0), ioread32(reg + SSCR1),
+                               ioread32(reg + SSSR));
+
+                       /* to == PWRSTATE_ON: enables transfer() */
+                       drv_data->pwrstate = PWRSTATE_ON;
+               }
+               break;
+       case PWRSTATE_ON:
+               break;
+       default:
+               dev_err(&pdev->dev, "ERROR: resume: invalid src pwrstate %x",
+                       from);
+               return;
+       }
+}
+
 static int pw_spi_resume(struct pci_dev *pdev)
 {
+       int retval;
        struct driver_data *drv_data = pci_get_drvdata(pdev);
+
        dev_dbg(&pdev->dev, "resume");

-       tasklet_enable(&drv_data->poll_transfer);
+       if (drv_data->pwrstate != PWRSTATE_OFF)
+               dev_warn(&pdev->dev, "resume: !off, pwrstate:%d",
+                        drv_data->pwrstate);
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "resume: back from sleep failed");
+               return retval;
+       }
+       retval = pci_back_from_sleep(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "resume: back from sleep failed");
+               return retval;
+       }
+       _pw_resume(pdev, PWRSTATE_ON);

        return 0;
 }
@@ -1135,12 +1289,77 @@ static int pw_spi_resume(struct pci_dev *pdev)
 #endif /* CONFIG_PM */


+static int pw_spi_pm_runtime_resume(struct device *dev)
+{
+       int retval;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
+
+       dev_dbg(dev, "pm runtime resume");
+
+       if (drv_data->pwrstate != PWRSTATE_IDLE)
+               dev_warn(&pdev->dev, "rt suspend: !idle, pwrstate:%d",
+                        drv_data->pwrstate);
+       retval = pci_back_from_sleep(pdev);
+       if (retval)
+               dev_err(&pdev->dev, "rt resume: back from sleep failed");
+       _pw_resume(pdev, PWRSTATE_ON);
+
+       return 0;
+}
+
+static int pw_spi_pm_runtime_suspend(struct device *dev)
+{
+       int retval;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
+
+       dev_dbg(dev, "pm runtime suspend");
+
+       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");
+
+       return 0;
+}
+
+/* check conditions and queue runtime suspend if idle */
+static int pw_spi_pm_runtime_idle(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 idle");
+
+       if (atomic_read(&drv_data->active) == 0) {
+               dev_dbg(dev, "rt idle: queue suspend request");
+               pm_runtime_suspend(dev);
+       }
+
+       return 0;
+}
+
+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 = {
        { PCI_VDEVICE(INTEL, 0x0816) },
        { }
 };

-static struct pci_driver pnwl_spi3_driver = {
+static struct pci_driver pnwl_spi_driver = {
+       .driver = {
+               .pm = &pw_spi_pm,
+       },
        .name =         DRIVER_NAME,
        .id_table =     pci_ids,
        .probe =        pw_spi_probe,
@@ -1151,12 +1370,16 @@ static struct pci_driver pnwl_spi3_driver = {

 static int __init pw_spi_init(void)
 {
-       return pci_register_driver(&pnwl_spi3_driver);
+       pw_spi_pm_qos_req =
+               pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY,
+                                  PM_QOS_DEFAULT_VALUE);
+       return pci_register_driver(&pnwl_spi_driver);
 }
 late_initcall(pw_spi_init);

 static void __exit pw_spi_exit(void)
 {
-       pci_unregister_driver(&pnwl_spi3_driver);
+       pci_unregister_driver(&pnwl_spi_driver);
+       pm_qos_remove_request(pw_spi_pm_qos_req);
 }
 module_exit(pw_spi_exit);
--
1.6.0.6

Attachment: 0001-PM-support-for-SSP-SPI-master-controller-driver.patch
Description: 0001-PM-support-for-SSP-SPI-master-controller-driver.patch

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

Reply via email to