diff options
Diffstat (limited to 'drivers/iio/humidity/hdc3020.c')
-rw-r--r-- | drivers/iio/humidity/hdc3020.c | 111 |
1 files changed, 92 insertions, 19 deletions
diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 1e702fcc48..a82dcc3da4 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -15,12 +15,15 @@ #include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/math64.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> #include <linux/units.h> #include <asm/unaligned.h> @@ -71,6 +74,8 @@ struct hdc3020_data { struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator *vdd_supply; /* * Ensure that the sensor configuration (currently only heater is * supported) will not be changed during the process of reading @@ -724,9 +729,53 @@ static const struct iio_info hdc3020_info = { .write_event_value = hdc3020_write_thresh, }; -static void hdc3020_stop(void *data) +static int hdc3020_power_off(struct hdc3020_data *data) { - hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO); + hdc3020_exec_cmd(data, HDC3020_EXIT_AUTO); + + if (data->reset_gpio) + gpiod_set_value_cansleep(data->reset_gpio, 1); + + return regulator_disable(data->vdd_supply); +} + +static int hdc3020_power_on(struct hdc3020_data *data) +{ + int ret; + + ret = regulator_enable(data->vdd_supply); + if (ret) + return ret; + + fsleep(5000); + + if (data->reset_gpio) { + gpiod_set_value_cansleep(data->reset_gpio, 0); + fsleep(3000); + } + + if (data->client->irq) { + /* + * The alert output is activated by default upon power up, + * hardware reset, and soft reset. Clear the status register. + */ + ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); + if (ret) { + hdc3020_power_off(data); + return ret; + } + } + + ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); + if (ret) + hdc3020_power_off(data); + + return ret; +} + +static void hdc3020_exit(void *data) +{ + hdc3020_power_off(data); } static int hdc3020_probe(struct i2c_client *client) @@ -742,6 +791,8 @@ static int hdc3020_probe(struct i2c_client *client) if (!indio_dev) return -ENOMEM; + dev_set_drvdata(&client->dev, indio_dev); + data = iio_priv(indio_dev); data->client = client; mutex_init(&data->lock); @@ -753,6 +804,26 @@ static int hdc3020_probe(struct i2c_client *client) indio_dev->info = &hdc3020_info; indio_dev->channels = hdc3020_channels; indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels); + + data->vdd_supply = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply), + "Unable to get VDD regulator\n"); + + data->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio), + "Cannot get reset GPIO\n"); + + ret = hdc3020_power_on(data); + if (ret) + return dev_err_probe(&client->dev, ret, "Power on failed\n"); + + ret = devm_add_action_or_reset(&data->client->dev, hdc3020_exit, data); + if (ret) + return ret; + if (client->irq) { ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, hdc3020_interrupt_handler, @@ -761,25 +832,8 @@ static int hdc3020_probe(struct i2c_client *client) if (ret) return dev_err_probe(&client->dev, ret, "Failed to request IRQ\n"); - - /* - * The alert output is activated by default upon power up, - * hardware reset, and soft reset. Clear the status register. - */ - ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); - if (ret) - return ret; } - ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); - if (ret) - return dev_err_probe(&client->dev, ret, - "Unable to set up measurement\n"); - - ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data); - if (ret) - return ret; - ret = devm_iio_device_register(&data->client->dev, indio_dev); if (ret) return dev_err_probe(&client->dev, ret, "Failed to add device"); @@ -787,6 +841,24 @@ static int hdc3020_probe(struct i2c_client *client) return 0; } +static int hdc3020_suspend(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct hdc3020_data *data = iio_priv(iio_dev); + + return hdc3020_power_off(data); +} + +static int hdc3020_resume(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct hdc3020_data *data = iio_priv(iio_dev); + + return hdc3020_power_on(data); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume); + static const struct i2c_device_id hdc3020_id[] = { { "hdc3020" }, { "hdc3021" }, @@ -806,6 +878,7 @@ MODULE_DEVICE_TABLE(of, hdc3020_dt_ids); static struct i2c_driver hdc3020_driver = { .driver = { .name = "hdc3020", + .pm = pm_sleep_ptr(&hdc3020_pm_ops), .of_match_table = hdc3020_dt_ids, }, .probe = hdc3020_probe, |