NFC: trf7970a: Add pm_runtime support
authorMark A. Greer <mgreer@animalcreek.com>
Tue, 25 Mar 2014 15:54:38 +0000 (08:54 -0700)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 21 Apr 2014 22:37:28 +0000 (00:37 +0200)
Add pm_runtime support by moving the code that enables the trf7970a to
the pm_runtime hook routines. The pm_runtime 'autosuspend' feature is
used so that the device isn't disabled until at least 30 seconds have
passed since trf7970a_switch_rf_off() was last called.

The result is that when trf7970a_switch_rf_on() is called, the device
will be enabled and initialized (if it isn't already).  When
trf7970a_switch_rf_off() is called, it will turn off the RF immediately
but leave the device enabled for at least 30 seconds.
If 30 seconds have passed and the pm_runtime facility decides to suspend
the driver, the device will be disabled then.

Signed-off-by: Mark A. Greer <mgreer@animalcreek.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/nfc/trf7970a.c

index 0a46348f9af67f945f527e97009bc78974ffc663..26e744934068d9d502e22b760bdc31a3e470747a 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/device.h>
 #include <linux/netdevice.h>
 #include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
 #include <linux/nfc.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
                (NFC_PROTO_MIFARE_MASK | NFC_PROTO_ISO14443_MASK |      \
                 NFC_PROTO_ISO15693_MASK)
 
+#define TRF7970A_AUTOSUSPEND_DELAY             30000 /* 30 seconds */
+
 /* TX data must be prefixed with a FIFO reset cmd, a cmd that depends
  * on what the current framing is, the address of the TX length byte 1
  * register (0x1d), and the 2 byte length of the data to be transmitted.
@@ -330,7 +333,6 @@ struct trf7970a {
        struct regulator                *regulator;
        struct nfc_digital_dev          *ddev;
        u32                             quirks;
-       bool                            powering_up;
        bool                            aborting;
        struct sk_buff                  *tx_skb;
        struct sk_buff                  *rx_skb;
@@ -795,47 +797,22 @@ static void trf7970a_switch_rf_off(struct trf7970a *trf)
 
        trf7970a_write(trf, TRF7970A_CHIP_STATUS_CTRL, trf->chip_status_ctrl);
 
-       gpio_set_value(trf->en_gpio, 0);
-       gpio_set_value(trf->en2_gpio, 0);
-
        trf->aborting = false;
        trf->state = TRF7970A_ST_OFF;
+
+       pm_runtime_mark_last_busy(trf->dev);
+       pm_runtime_put_autosuspend(trf->dev);
 }
 
 static int trf7970a_switch_rf_on(struct trf7970a *trf)
 {
-       unsigned long delay;
-       int ret;
-
        dev_dbg(trf->dev, "Switching rf on\n");
 
-       if (trf->powering_up)
-               usleep_range(5000, 6000);
+       pm_runtime_get_sync(trf->dev);
 
-       gpio_set_value(trf->en2_gpio, 1);
-       usleep_range(1000, 2000);
-       gpio_set_value(trf->en_gpio, 1);
-
-       /* The delay between enabling the trf7970a and issuing the first
-        * command is significantly longer the very first time after powering
-        * up.  Make sure the longer delay is only done the first time.
-        */
-       if (trf->powering_up) {
-               delay = 20000;
-               trf->powering_up = false;
-       } else {
-               delay = 5000;
-       }
-
-       usleep_range(delay, delay + 1000);
-
-       ret = trf7970a_init(trf);
-       if (ret)
-               trf7970a_switch_rf_off(trf);
-       else
-               trf->state = TRF7970A_ST_IDLE;
+       trf->state = TRF7970A_ST_IDLE;
 
-       return ret;
+       return 0;
 }
 
 static int trf7970a_switch_rf(struct nfc_digital_dev *ddev, bool on)
@@ -1331,8 +1308,6 @@ static int trf7970a_probe(struct spi_device *spi)
        if (uvolts > 4000000)
                trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3;
 
-       trf->powering_up = true;
-
        trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops,
                        TRF7970A_SUPPORTED_PROTOCOLS,
                        NFC_DIGITAL_DRV_CAPS_IN_CRC, TRF7970A_TX_SKB_HEADROOM,
@@ -1347,6 +1322,10 @@ static int trf7970a_probe(struct spi_device *spi)
        nfc_digital_set_drvdata(trf->ddev, trf);
        spi_set_drvdata(spi, trf);
 
+       pm_runtime_set_autosuspend_delay(trf->dev, TRF7970A_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(trf->dev);
+       pm_runtime_enable(trf->dev);
+
        ret = nfc_digital_register_device(trf->ddev);
        if (ret) {
                dev_err(trf->dev, "Can't register NFC digital device: %d\n",
@@ -1357,6 +1336,7 @@ static int trf7970a_probe(struct spi_device *spi)
        return 0;
 
 err_free_ddev:
+       pm_runtime_disable(trf->dev);
        nfc_digital_free_device(trf->ddev);
 err_disable_regulator:
        regulator_disable(trf->regulator);
@@ -1371,15 +1351,16 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_lock(&trf->lock);
 
-       trf7970a_switch_rf_off(trf);
-       trf7970a_init(trf);
-
        switch (trf->state) {
        case TRF7970A_ST_WAIT_FOR_TX_FIFO:
        case TRF7970A_ST_WAIT_FOR_RX_DATA:
        case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
        case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
                trf7970a_send_err_upstream(trf, -ECANCELED);
+               /* FALLTHROUGH */
+       case TRF7970A_ST_IDLE:
+       case TRF7970A_ST_IDLE_RX_BLOCKED:
+               pm_runtime_put_sync(trf->dev);
                break;
        default:
                break;
@@ -1387,6 +1368,8 @@ static int trf7970a_remove(struct spi_device *spi)
 
        mutex_unlock(&trf->lock);
 
+       pm_runtime_disable(trf->dev);
+
        nfc_digital_unregister_device(trf->ddev);
        nfc_digital_free_device(trf->ddev);
 
@@ -1397,6 +1380,70 @@ static int trf7970a_remove(struct spi_device *spi)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int trf7970a_pm_runtime_suspend(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime suspend\n");
+
+       if (trf->state != TRF7970A_ST_OFF) {
+               dev_dbg(dev, "Can't suspend - not in OFF state (%d)\n",
+                               trf->state);
+               return -EBUSY;
+       }
+
+       gpio_set_value(trf->en_gpio, 0);
+       gpio_set_value(trf->en2_gpio, 0);
+
+       ret = regulator_disable(trf->regulator);
+       if (ret)
+               dev_err(dev, "%s - Can't disable VIN: %d\n", __func__, ret);
+
+       return ret;
+}
+
+static int trf7970a_pm_runtime_resume(struct device *dev)
+{
+       struct spi_device *spi = container_of(dev, struct spi_device, dev);
+       struct trf7970a *trf = spi_get_drvdata(spi);
+       int ret;
+
+       dev_dbg(dev, "Runtime resume\n");
+
+       ret = regulator_enable(trf->regulator);
+       if (ret) {
+               dev_err(dev, "%s - Can't enable VIN: %d\n", __func__, ret);
+               return ret;
+       }
+
+       usleep_range(5000, 6000);
+
+       gpio_set_value(trf->en2_gpio, 1);
+       usleep_range(1000, 2000);
+       gpio_set_value(trf->en_gpio, 1);
+
+       usleep_range(20000, 21000);
+
+       ret = trf7970a_init(trf);
+       if (ret) {
+               dev_err(dev, "%s - Can't initialize: %d\n", __func__, ret);
+               return ret;
+       }
+
+       pm_runtime_mark_last_busy(dev);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops trf7970a_pm_ops = {
+       SET_RUNTIME_PM_OPS(trf7970a_pm_runtime_suspend,
+                       trf7970a_pm_runtime_resume, NULL)
+};
+
 static const struct spi_device_id trf7970a_id_table[] = {
        { "trf7970a", TRF7970A_QUIRK_IRQ_STATUS_READ_ERRATA },
        { }
@@ -1410,6 +1457,7 @@ static struct spi_driver trf7970a_spi_driver = {
        .driver         = {
                .name   = "trf7970a",
                .owner  = THIS_MODULE,
+               .pm     = &trf7970a_pm_ops,
        },
 };