Skip to content

Commit

Permalink
iio: humidity: hdc3020: add power management
Browse files Browse the repository at this point in the history
The HDC3020 sensor carries out periodic measurements during normal
operation, but as long as the power supply is enabled, it will carry on
in low-power modes. In order to avoid that and reduce power consumption,
the device can be switched to Trigger-on Demand mode, and if possible,
turn off its regulator.

According to the datasheet, the maximum "Power Up Ready" is 5 ms.

Add resume/suspend pm operations to manage measurement mode and
regulator state.

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Link: https://lore.kernel.org/r/20240303-hdc3020-pm-v3-1-48bc02b5241b@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
  • Loading branch information
Javier Carrasco authored and Jonathan Cameron committed Mar 25, 2024
1 parent d1efcf8 commit f764c29
Showing 1 changed file with 76 additions and 19 deletions.
95 changes: 76 additions & 19 deletions drivers/iio/humidity/hdc3020.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <linux/interrupt.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>
Expand Down Expand Up @@ -68,6 +70,7 @@

struct hdc3020_data {
struct i2c_client *client;
struct regulator *vdd_supply;
/*
* Ensure that the sensor configuration (currently only heater is
* supported) will not be changed during the process of reading
Expand Down Expand Up @@ -551,9 +554,45 @@ 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);

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->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)
Expand All @@ -569,6 +608,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);
Expand All @@ -580,6 +621,20 @@ 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");

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,
Expand All @@ -588,32 +643,33 @@ 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");

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" },
Expand All @@ -633,6 +689,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,
Expand Down

0 comments on commit f764c29

Please sign in to comment.