From e6403b7c756f9d694332870ba0c2be7a4aa0b8c0 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 25 Mar 2014 08:54:38 -0700 Subject: NFC: trf7970a: Add pm_runtime support 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 Signed-off-by: Samuel Ortiz --- drivers/nfc/trf7970a.c | 122 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 0a46348f9af..26e74493406 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,8 @@ (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, }, }; -- cgit v1.2.3-18-g5258