diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/iio/accel/kxsd9.c | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 8c6a4559256e..dc53f70e616e 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -21,6 +21,8 @@ #include <linux/module.h> #include <linux/regmap.h> #include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> @@ -65,10 +67,12 @@ * struct kxsd9_state - device related storage * @dev: pointer to the parent device * @map: regmap to the device + * @regs: regulators for this device, VDD and IOVDD */ struct kxsd9_state { struct device *dev; struct regmap *map; + struct regulator_bulk_data regs[2]; }; #define KXSD9_SCALE_2G "0.011978" @@ -81,6 +85,12 @@ static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 }; #define KXSD9_ZERO_G_OFFSET -2048 +/* + * Regulator names + */ +static const char kxsd9_reg_vdd[] = "vdd"; +static const char kxsd9_reg_iovdd[] = "iovdd"; + static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro) { int ret, i; @@ -252,12 +262,69 @@ static int kxsd9_power_up(struct kxsd9_state *st) { int ret; - ret = regmap_write(st->map, KXSD9_REG_CTRL_B, 0x40); + /* Enable the regulators */ + ret = regulator_bulk_enable(ARRAY_SIZE(st->regs), st->regs); + if (ret) { + dev_err(st->dev, "Cannot enable regulators\n"); + return ret; + } + + /* Power up */ + ret = regmap_write(st->map, + KXSD9_REG_CTRL_B, + KXSD9_CTRL_B_ENABLE); if (ret) return ret; - return regmap_write(st->map, KXSD9_REG_CTRL_C, 0x9b); + + /* + * Set 1000Hz LPF, 2g fullscale, motion wakeup threshold 1g, + * latched wakeup + */ + ret = regmap_write(st->map, + KXSD9_REG_CTRL_C, + KXSD9_CTRL_C_LP_1000HZ | + KXSD9_CTRL_C_MOT_LEV | + KXSD9_CTRL_C_MOT_LAT | + KXSD9_CTRL_C_FS_2G); + if (ret) + return ret; + + /* + * Power-up time depends on the LPF setting, but typ 15.9 ms, let's + * set 20 ms to allow for some slack. + */ + msleep(20); + + return 0; }; +static int kxsd9_power_down(struct kxsd9_state *st) +{ + int ret; + + /* + * Set into low power mode - since there may be more users of the + * regulators this is the first step of the power saving: it will + * make sure we conserve power even if there are others users on the + * regulators. + */ + ret = regmap_update_bits(st->map, + KXSD9_REG_CTRL_B, + KXSD9_CTRL_B_ENABLE, + 0); + if (ret) + return ret; + + /* Disable the regulators */ + ret = regulator_bulk_disable(ARRAY_SIZE(st->regs), st->regs); + if (ret) { + dev_err(st->dev, "Cannot disable regulators\n"); + return ret; + } + + return 0; +} + static const struct iio_info kxsd9_info = { .read_raw = &kxsd9_read_raw, .write_raw = &kxsd9_write_raw, @@ -292,6 +359,17 @@ int kxsd9_common_probe(struct device *parent, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = kxsd9_scan_masks; + /* Fetch and turn on regulators */ + st->regs[0].supply = kxsd9_reg_vdd; + st->regs[1].supply = kxsd9_reg_iovdd; + ret = devm_regulator_bulk_get(parent, + ARRAY_SIZE(st->regs), + st->regs); + if (ret) { + dev_err(parent, "Cannot get regulators\n"); + return ret; + } + kxsd9_power_up(st); ret = iio_triggered_buffer_setup(indio_dev, @@ -300,7 +378,7 @@ int kxsd9_common_probe(struct device *parent, NULL); if (ret) { dev_err(parent, "triggered buffer setup failed\n"); - return ret; + goto err_power_down; } ret = iio_device_register(indio_dev); @@ -313,6 +391,8 @@ int kxsd9_common_probe(struct device *parent, err_cleanup_buffer: iio_triggered_buffer_cleanup(indio_dev); +err_power_down: + kxsd9_power_down(st); return ret; } @@ -321,9 +401,11 @@ EXPORT_SYMBOL(kxsd9_common_probe); int kxsd9_common_remove(struct device *parent) { struct iio_dev *indio_dev = dev_get_drvdata(parent); + struct kxsd9_state *st = iio_priv(indio_dev); iio_triggered_buffer_cleanup(indio_dev); iio_device_unregister(indio_dev); + kxsd9_power_down(st); return 0; } |