1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
281ca486fSJonathan Cameron /*
381ca486fSJonathan Cameron * kxsd9.c simple support for the Kionix KXSD9 3D
481ca486fSJonathan Cameron * accelerometer.
581ca486fSJonathan Cameron *
681ca486fSJonathan Cameron * Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.org>
781ca486fSJonathan Cameron *
881ca486fSJonathan Cameron * The i2c interface is very similar, so shouldn't be a problem once
981ca486fSJonathan Cameron * I have a suitable wire made up.
1081ca486fSJonathan Cameron *
1181ca486fSJonathan Cameron * TODO: Support the motion detector
1281ca486fSJonathan Cameron */
1381ca486fSJonathan Cameron
1481ca486fSJonathan Cameron #include <linux/device.h>
1581ca486fSJonathan Cameron #include <linux/kernel.h>
1681ca486fSJonathan Cameron #include <linux/sysfs.h>
1781ca486fSJonathan Cameron #include <linux/slab.h>
1881ca486fSJonathan Cameron #include <linux/module.h>
190d1fb2d5SLinus Walleij #include <linux/regmap.h>
2011adc2b2SLinus Walleij #include <linux/bitops.h>
212bb4a02aSLinus Walleij #include <linux/delay.h>
222bb4a02aSLinus Walleij #include <linux/regulator/consumer.h>
239a9a369dSLinus Walleij #include <linux/pm_runtime.h>
2481ca486fSJonathan Cameron #include <linux/iio/iio.h>
2581ca486fSJonathan Cameron #include <linux/iio/sysfs.h>
260427a106SLinus Walleij #include <linux/iio/buffer.h>
270427a106SLinus Walleij #include <linux/iio/triggered_buffer.h>
280427a106SLinus Walleij #include <linux/iio/trigger_consumer.h>
2981ca486fSJonathan Cameron
30bf96f6e8SLinus Walleij #include "kxsd9.h"
31bf96f6e8SLinus Walleij
3281ca486fSJonathan Cameron #define KXSD9_REG_X 0x00
3381ca486fSJonathan Cameron #define KXSD9_REG_Y 0x02
3481ca486fSJonathan Cameron #define KXSD9_REG_Z 0x04
3581ca486fSJonathan Cameron #define KXSD9_REG_AUX 0x06
3681ca486fSJonathan Cameron #define KXSD9_REG_RESET 0x0a
3781ca486fSJonathan Cameron #define KXSD9_REG_CTRL_C 0x0c
3881ca486fSJonathan Cameron
3911adc2b2SLinus Walleij #define KXSD9_CTRL_C_FS_MASK 0x03
4011adc2b2SLinus Walleij #define KXSD9_CTRL_C_FS_8G 0x00
4111adc2b2SLinus Walleij #define KXSD9_CTRL_C_FS_6G 0x01
4211adc2b2SLinus Walleij #define KXSD9_CTRL_C_FS_4G 0x02
4311adc2b2SLinus Walleij #define KXSD9_CTRL_C_FS_2G 0x03
4411adc2b2SLinus Walleij #define KXSD9_CTRL_C_MOT_LAT BIT(3)
4511adc2b2SLinus Walleij #define KXSD9_CTRL_C_MOT_LEV BIT(4)
4611adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_MASK 0xe0
4711adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_NONE 0x00
4811adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_2000HZC BIT(5)
4911adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_2000HZB BIT(6)
5011adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_2000HZA (BIT(5)|BIT(6))
5111adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_1000HZ BIT(7)
5211adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_500HZ (BIT(7)|BIT(5))
5311adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_100HZ (BIT(7)|BIT(6))
5411adc2b2SLinus Walleij #define KXSD9_CTRL_C_LP_50HZ (BIT(7)|BIT(6)|BIT(5))
5581ca486fSJonathan Cameron
5681ca486fSJonathan Cameron #define KXSD9_REG_CTRL_B 0x0d
5711adc2b2SLinus Walleij
5811adc2b2SLinus Walleij #define KXSD9_CTRL_B_CLK_HLD BIT(7)
5911adc2b2SLinus Walleij #define KXSD9_CTRL_B_ENABLE BIT(6)
6011adc2b2SLinus Walleij #define KXSD9_CTRL_B_ST BIT(5) /* Self-test */
6111adc2b2SLinus Walleij
6281ca486fSJonathan Cameron #define KXSD9_REG_CTRL_A 0x0e
6381ca486fSJonathan Cameron
649f907972SLinus Walleij /**
659f907972SLinus Walleij * struct kxsd9_state - device related storage
660427a106SLinus Walleij * @dev: pointer to the parent device
67dc6ac050SLinus Walleij * @map: regmap to the device
6812884004SLinus Walleij * @orientation: mounting matrix, flipped axis etc
692bb4a02aSLinus Walleij * @regs: regulators for this device, VDD and IOVDD
709a9a369dSLinus Walleij * @scale: the current scaling setting
71dc6ac050SLinus Walleij */
729f907972SLinus Walleij struct kxsd9_state {
730427a106SLinus Walleij struct device *dev;
740d1fb2d5SLinus Walleij struct regmap *map;
7512884004SLinus Walleij struct iio_mount_matrix orientation;
762bb4a02aSLinus Walleij struct regulator_bulk_data regs[2];
779a9a369dSLinus Walleij u8 scale;
789f907972SLinus Walleij };
799f907972SLinus Walleij
8081ca486fSJonathan Cameron #define KXSD9_SCALE_2G "0.011978"
8181ca486fSJonathan Cameron #define KXSD9_SCALE_4G "0.023927"
8281ca486fSJonathan Cameron #define KXSD9_SCALE_6G "0.035934"
8381ca486fSJonathan Cameron #define KXSD9_SCALE_8G "0.047853"
8481ca486fSJonathan Cameron
8581ca486fSJonathan Cameron /* reverse order */
8681ca486fSJonathan Cameron static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
8781ca486fSJonathan Cameron
8884e2f6f9SLinus Walleij #define KXSD9_ZERO_G_OFFSET -2048
8984e2f6f9SLinus Walleij
902bb4a02aSLinus Walleij /*
912bb4a02aSLinus Walleij * Regulator names
922bb4a02aSLinus Walleij */
932bb4a02aSLinus Walleij static const char kxsd9_reg_vdd[] = "vdd";
942bb4a02aSLinus Walleij static const char kxsd9_reg_iovdd[] = "iovdd";
952bb4a02aSLinus Walleij
kxsd9_write_scale(struct iio_dev * indio_dev,int micro)9681ca486fSJonathan Cameron static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
9781ca486fSJonathan Cameron {
9881ca486fSJonathan Cameron int ret, i;
9981ca486fSJonathan Cameron struct kxsd9_state *st = iio_priv(indio_dev);
10081ca486fSJonathan Cameron bool foundit = false;
10181ca486fSJonathan Cameron
10281ca486fSJonathan Cameron for (i = 0; i < 4; i++)
10381ca486fSJonathan Cameron if (micro == kxsd9_micro_scales[i]) {
10481ca486fSJonathan Cameron foundit = true;
10581ca486fSJonathan Cameron break;
10681ca486fSJonathan Cameron }
10781ca486fSJonathan Cameron if (!foundit)
10881ca486fSJonathan Cameron return -EINVAL;
10981ca486fSJonathan Cameron
11011adc2b2SLinus Walleij ret = regmap_update_bits(st->map,
1110d1fb2d5SLinus Walleij KXSD9_REG_CTRL_C,
11211adc2b2SLinus Walleij KXSD9_CTRL_C_FS_MASK,
11311adc2b2SLinus Walleij i);
114bf96f6e8SLinus Walleij if (ret < 0)
11581ca486fSJonathan Cameron goto error_ret;
1169a9a369dSLinus Walleij
1179a9a369dSLinus Walleij /* Cached scale when the sensor is powered down */
1189a9a369dSLinus Walleij st->scale = i;
1199a9a369dSLinus Walleij
12081ca486fSJonathan Cameron error_ret:
12181ca486fSJonathan Cameron return ret;
12281ca486fSJonathan Cameron }
12381ca486fSJonathan Cameron
12481ca486fSJonathan Cameron static IIO_CONST_ATTR(accel_scale_available,
12581ca486fSJonathan Cameron KXSD9_SCALE_2G " "
12681ca486fSJonathan Cameron KXSD9_SCALE_4G " "
12781ca486fSJonathan Cameron KXSD9_SCALE_6G " "
12881ca486fSJonathan Cameron KXSD9_SCALE_8G);
12981ca486fSJonathan Cameron
13081ca486fSJonathan Cameron static struct attribute *kxsd9_attributes[] = {
13181ca486fSJonathan Cameron &iio_const_attr_accel_scale_available.dev_attr.attr,
13281ca486fSJonathan Cameron NULL,
13381ca486fSJonathan Cameron };
13481ca486fSJonathan Cameron
kxsd9_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)13581ca486fSJonathan Cameron static int kxsd9_write_raw(struct iio_dev *indio_dev,
13681ca486fSJonathan Cameron struct iio_chan_spec const *chan,
13781ca486fSJonathan Cameron int val,
13881ca486fSJonathan Cameron int val2,
13981ca486fSJonathan Cameron long mask)
14081ca486fSJonathan Cameron {
14181ca486fSJonathan Cameron int ret = -EINVAL;
1429a9a369dSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
1439a9a369dSLinus Walleij
1449a9a369dSLinus Walleij pm_runtime_get_sync(st->dev);
14581ca486fSJonathan Cameron
14681ca486fSJonathan Cameron if (mask == IIO_CHAN_INFO_SCALE) {
14781ca486fSJonathan Cameron /* Check no integer component */
14881ca486fSJonathan Cameron if (val)
14981ca486fSJonathan Cameron return -EINVAL;
15081ca486fSJonathan Cameron ret = kxsd9_write_scale(indio_dev, val2);
15181ca486fSJonathan Cameron }
15281ca486fSJonathan Cameron
1539a9a369dSLinus Walleij pm_runtime_mark_last_busy(st->dev);
1549a9a369dSLinus Walleij pm_runtime_put_autosuspend(st->dev);
1559a9a369dSLinus Walleij
15681ca486fSJonathan Cameron return ret;
15781ca486fSJonathan Cameron }
15881ca486fSJonathan Cameron
kxsd9_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)15981ca486fSJonathan Cameron static int kxsd9_read_raw(struct iio_dev *indio_dev,
16081ca486fSJonathan Cameron struct iio_chan_spec const *chan,
16181ca486fSJonathan Cameron int *val, int *val2, long mask)
16281ca486fSJonathan Cameron {
16381ca486fSJonathan Cameron int ret = -EINVAL;
16481ca486fSJonathan Cameron struct kxsd9_state *st = iio_priv(indio_dev);
1650d1fb2d5SLinus Walleij unsigned int regval;
16684e2f6f9SLinus Walleij __be16 raw_val;
16784e2f6f9SLinus Walleij u16 nval;
16881ca486fSJonathan Cameron
1699a9a369dSLinus Walleij pm_runtime_get_sync(st->dev);
1709a9a369dSLinus Walleij
17181ca486fSJonathan Cameron switch (mask) {
17281ca486fSJonathan Cameron case IIO_CHAN_INFO_RAW:
17384e2f6f9SLinus Walleij ret = regmap_bulk_read(st->map, chan->address, &raw_val,
17484e2f6f9SLinus Walleij sizeof(raw_val));
17584e2f6f9SLinus Walleij if (ret)
17681ca486fSJonathan Cameron goto error_ret;
17784e2f6f9SLinus Walleij nval = be16_to_cpu(raw_val);
17884e2f6f9SLinus Walleij /* Only 12 bits are valid */
17984e2f6f9SLinus Walleij nval >>= 4;
18084e2f6f9SLinus Walleij *val = nval;
18184e2f6f9SLinus Walleij ret = IIO_VAL_INT;
18284e2f6f9SLinus Walleij break;
18384e2f6f9SLinus Walleij case IIO_CHAN_INFO_OFFSET:
18484e2f6f9SLinus Walleij /* This has a bias of -2048 */
18584e2f6f9SLinus Walleij *val = KXSD9_ZERO_G_OFFSET;
1867ac61a06SLinus Walleij ret = IIO_VAL_INT;
18781ca486fSJonathan Cameron break;
18881ca486fSJonathan Cameron case IIO_CHAN_INFO_SCALE:
1890d1fb2d5SLinus Walleij ret = regmap_read(st->map,
1900d1fb2d5SLinus Walleij KXSD9_REG_CTRL_C,
1910d1fb2d5SLinus Walleij ®val);
192bf96f6e8SLinus Walleij if (ret < 0)
19381ca486fSJonathan Cameron goto error_ret;
194307fe9ddSLinus Walleij *val = 0;
19511adc2b2SLinus Walleij *val2 = kxsd9_micro_scales[regval & KXSD9_CTRL_C_FS_MASK];
19681ca486fSJonathan Cameron ret = IIO_VAL_INT_PLUS_MICRO;
19781ca486fSJonathan Cameron break;
19881ca486fSJonathan Cameron }
19981ca486fSJonathan Cameron
20081ca486fSJonathan Cameron error_ret:
2019a9a369dSLinus Walleij pm_runtime_mark_last_busy(st->dev);
2029a9a369dSLinus Walleij pm_runtime_put_autosuspend(st->dev);
2039a9a369dSLinus Walleij
20481ca486fSJonathan Cameron return ret;
20581ca486fSJonathan Cameron };
2060427a106SLinus Walleij
kxsd9_trigger_handler(int irq,void * p)2070427a106SLinus Walleij static irqreturn_t kxsd9_trigger_handler(int irq, void *p)
2080427a106SLinus Walleij {
2090427a106SLinus Walleij const struct iio_poll_func *pf = p;
2100427a106SLinus Walleij struct iio_dev *indio_dev = pf->indio_dev;
2110427a106SLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
21295ad6757SJonathan Cameron /*
21395ad6757SJonathan Cameron * Ensure correct positioning and alignment of timestamp.
21495ad6757SJonathan Cameron * No need to zero initialize as all elements written.
21595ad6757SJonathan Cameron */
21695ad6757SJonathan Cameron struct {
21795ad6757SJonathan Cameron __be16 chan[4];
21895ad6757SJonathan Cameron s64 ts __aligned(8);
21995ad6757SJonathan Cameron } hw_values;
2200427a106SLinus Walleij int ret;
2210427a106SLinus Walleij
2220427a106SLinus Walleij ret = regmap_bulk_read(st->map,
2230427a106SLinus Walleij KXSD9_REG_X,
22495ad6757SJonathan Cameron hw_values.chan,
22595ad6757SJonathan Cameron sizeof(hw_values.chan));
2260427a106SLinus Walleij if (ret) {
22745febe0dSLars-Peter Clausen dev_err(st->dev, "error reading data: %d\n", ret);
22845febe0dSLars-Peter Clausen goto out;
2290427a106SLinus Walleij }
2300427a106SLinus Walleij
2310427a106SLinus Walleij iio_push_to_buffers_with_timestamp(indio_dev,
23295ad6757SJonathan Cameron &hw_values,
2330427a106SLinus Walleij iio_get_time_ns(indio_dev));
23445febe0dSLars-Peter Clausen out:
2350427a106SLinus Walleij iio_trigger_notify_done(indio_dev->trig);
2360427a106SLinus Walleij
2370427a106SLinus Walleij return IRQ_HANDLED;
2380427a106SLinus Walleij }
2390427a106SLinus Walleij
kxsd9_buffer_preenable(struct iio_dev * indio_dev)2409a9a369dSLinus Walleij static int kxsd9_buffer_preenable(struct iio_dev *indio_dev)
2419a9a369dSLinus Walleij {
2429a9a369dSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
2439a9a369dSLinus Walleij
2449a9a369dSLinus Walleij pm_runtime_get_sync(st->dev);
2459a9a369dSLinus Walleij
2469a9a369dSLinus Walleij return 0;
2479a9a369dSLinus Walleij }
2489a9a369dSLinus Walleij
kxsd9_buffer_postdisable(struct iio_dev * indio_dev)2499a9a369dSLinus Walleij static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev)
2509a9a369dSLinus Walleij {
2519a9a369dSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
2529a9a369dSLinus Walleij
2539a9a369dSLinus Walleij pm_runtime_mark_last_busy(st->dev);
2549a9a369dSLinus Walleij pm_runtime_put_autosuspend(st->dev);
2559a9a369dSLinus Walleij
2569a9a369dSLinus Walleij return 0;
2579a9a369dSLinus Walleij }
2589a9a369dSLinus Walleij
2599a9a369dSLinus Walleij static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = {
2609a9a369dSLinus Walleij .preenable = kxsd9_buffer_preenable,
2619a9a369dSLinus Walleij .postdisable = kxsd9_buffer_postdisable,
2629a9a369dSLinus Walleij };
2639a9a369dSLinus Walleij
26412884004SLinus Walleij static const struct iio_mount_matrix *
kxsd9_get_mount_matrix(const struct iio_dev * indio_dev,const struct iio_chan_spec * chan)26512884004SLinus Walleij kxsd9_get_mount_matrix(const struct iio_dev *indio_dev,
26612884004SLinus Walleij const struct iio_chan_spec *chan)
26712884004SLinus Walleij {
26812884004SLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
26912884004SLinus Walleij
27012884004SLinus Walleij return &st->orientation;
27112884004SLinus Walleij }
27212884004SLinus Walleij
27312884004SLinus Walleij static const struct iio_chan_spec_ext_info kxsd9_ext_info[] = {
27412884004SLinus Walleij IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxsd9_get_mount_matrix),
27512884004SLinus Walleij { },
27612884004SLinus Walleij };
27712884004SLinus Walleij
2780427a106SLinus Walleij #define KXSD9_ACCEL_CHAN(axis, index) \
27981ca486fSJonathan Cameron { \
28081ca486fSJonathan Cameron .type = IIO_ACCEL, \
28181ca486fSJonathan Cameron .modified = 1, \
28281ca486fSJonathan Cameron .channel2 = IIO_MOD_##axis, \
2832f6bb534SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
28484e2f6f9SLinus Walleij .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
28584e2f6f9SLinus Walleij BIT(IIO_CHAN_INFO_OFFSET), \
28612884004SLinus Walleij .ext_info = kxsd9_ext_info, \
28781ca486fSJonathan Cameron .address = KXSD9_REG_##axis, \
2880427a106SLinus Walleij .scan_index = index, \
2890427a106SLinus Walleij .scan_type = { \
2900427a106SLinus Walleij .sign = 'u', \
2910427a106SLinus Walleij .realbits = 12, \
2920427a106SLinus Walleij .storagebits = 16, \
2930427a106SLinus Walleij .shift = 4, \
2940427a106SLinus Walleij .endianness = IIO_BE, \
2950427a106SLinus Walleij }, \
29681ca486fSJonathan Cameron }
29781ca486fSJonathan Cameron
29881ca486fSJonathan Cameron static const struct iio_chan_spec kxsd9_channels[] = {
2990427a106SLinus Walleij KXSD9_ACCEL_CHAN(X, 0),
3000427a106SLinus Walleij KXSD9_ACCEL_CHAN(Y, 1),
3010427a106SLinus Walleij KXSD9_ACCEL_CHAN(Z, 2),
30281ca486fSJonathan Cameron {
30381ca486fSJonathan Cameron .type = IIO_VOLTAGE,
3042f6bb534SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
30581ca486fSJonathan Cameron .indexed = 1,
30681ca486fSJonathan Cameron .address = KXSD9_REG_AUX,
3070427a106SLinus Walleij .scan_index = 3,
3080427a106SLinus Walleij .scan_type = {
3090427a106SLinus Walleij .sign = 'u',
3100427a106SLinus Walleij .realbits = 12,
3110427a106SLinus Walleij .storagebits = 16,
3120427a106SLinus Walleij .shift = 4,
3130427a106SLinus Walleij .endianness = IIO_BE,
3140427a106SLinus Walleij },
3150427a106SLinus Walleij },
3160427a106SLinus Walleij IIO_CHAN_SOFT_TIMESTAMP(4),
31781ca486fSJonathan Cameron };
31881ca486fSJonathan Cameron
31981ca486fSJonathan Cameron static const struct attribute_group kxsd9_attribute_group = {
32081ca486fSJonathan Cameron .attrs = kxsd9_attributes,
32181ca486fSJonathan Cameron };
32281ca486fSJonathan Cameron
kxsd9_power_up(struct kxsd9_state * st)32381ca486fSJonathan Cameron static int kxsd9_power_up(struct kxsd9_state *st)
32481ca486fSJonathan Cameron {
32581ca486fSJonathan Cameron int ret;
32681ca486fSJonathan Cameron
3272bb4a02aSLinus Walleij /* Enable the regulators */
3282bb4a02aSLinus Walleij ret = regulator_bulk_enable(ARRAY_SIZE(st->regs), st->regs);
3292bb4a02aSLinus Walleij if (ret) {
3302bb4a02aSLinus Walleij dev_err(st->dev, "Cannot enable regulators\n");
3312bb4a02aSLinus Walleij return ret;
3322bb4a02aSLinus Walleij }
3332bb4a02aSLinus Walleij
3342bb4a02aSLinus Walleij /* Power up */
3352bb4a02aSLinus Walleij ret = regmap_write(st->map,
3362bb4a02aSLinus Walleij KXSD9_REG_CTRL_B,
3372bb4a02aSLinus Walleij KXSD9_CTRL_B_ENABLE);
33881ca486fSJonathan Cameron if (ret)
33981ca486fSJonathan Cameron return ret;
3402bb4a02aSLinus Walleij
3412bb4a02aSLinus Walleij /*
3422bb4a02aSLinus Walleij * Set 1000Hz LPF, 2g fullscale, motion wakeup threshold 1g,
3432bb4a02aSLinus Walleij * latched wakeup
3442bb4a02aSLinus Walleij */
3452bb4a02aSLinus Walleij ret = regmap_write(st->map,
3462bb4a02aSLinus Walleij KXSD9_REG_CTRL_C,
3472bb4a02aSLinus Walleij KXSD9_CTRL_C_LP_1000HZ |
3482bb4a02aSLinus Walleij KXSD9_CTRL_C_MOT_LEV |
3492bb4a02aSLinus Walleij KXSD9_CTRL_C_MOT_LAT |
3509a9a369dSLinus Walleij st->scale);
3512bb4a02aSLinus Walleij if (ret)
3522bb4a02aSLinus Walleij return ret;
3532bb4a02aSLinus Walleij
3542bb4a02aSLinus Walleij /*
3552bb4a02aSLinus Walleij * Power-up time depends on the LPF setting, but typ 15.9 ms, let's
3562bb4a02aSLinus Walleij * set 20 ms to allow for some slack.
3572bb4a02aSLinus Walleij */
3582bb4a02aSLinus Walleij msleep(20);
3592bb4a02aSLinus Walleij
3602bb4a02aSLinus Walleij return 0;
36181ca486fSJonathan Cameron };
36281ca486fSJonathan Cameron
kxsd9_power_down(struct kxsd9_state * st)3632bb4a02aSLinus Walleij static int kxsd9_power_down(struct kxsd9_state *st)
3642bb4a02aSLinus Walleij {
3652bb4a02aSLinus Walleij int ret;
3662bb4a02aSLinus Walleij
3672bb4a02aSLinus Walleij /*
3682bb4a02aSLinus Walleij * Set into low power mode - since there may be more users of the
3692bb4a02aSLinus Walleij * regulators this is the first step of the power saving: it will
3702bb4a02aSLinus Walleij * make sure we conserve power even if there are others users on the
3712bb4a02aSLinus Walleij * regulators.
3722bb4a02aSLinus Walleij */
3732bb4a02aSLinus Walleij ret = regmap_update_bits(st->map,
3742bb4a02aSLinus Walleij KXSD9_REG_CTRL_B,
3752bb4a02aSLinus Walleij KXSD9_CTRL_B_ENABLE,
3762bb4a02aSLinus Walleij 0);
3772bb4a02aSLinus Walleij if (ret)
3782bb4a02aSLinus Walleij return ret;
3792bb4a02aSLinus Walleij
3802bb4a02aSLinus Walleij /* Disable the regulators */
3812bb4a02aSLinus Walleij ret = regulator_bulk_disable(ARRAY_SIZE(st->regs), st->regs);
3822bb4a02aSLinus Walleij if (ret) {
3832bb4a02aSLinus Walleij dev_err(st->dev, "Cannot disable regulators\n");
3842bb4a02aSLinus Walleij return ret;
3852bb4a02aSLinus Walleij }
3862bb4a02aSLinus Walleij
3872bb4a02aSLinus Walleij return 0;
3882bb4a02aSLinus Walleij }
3892bb4a02aSLinus Walleij
39081ca486fSJonathan Cameron static const struct iio_info kxsd9_info = {
39181ca486fSJonathan Cameron .read_raw = &kxsd9_read_raw,
39281ca486fSJonathan Cameron .write_raw = &kxsd9_write_raw,
39381ca486fSJonathan Cameron .attrs = &kxsd9_attribute_group,
39481ca486fSJonathan Cameron };
39581ca486fSJonathan Cameron
3960427a106SLinus Walleij /* Four channels apart from timestamp, scan mask = 0x0f */
3970427a106SLinus Walleij static const unsigned long kxsd9_scan_masks[] = { 0xf, 0 };
3980427a106SLinus Walleij
kxsd9_common_probe(struct device * dev,struct regmap * map,const char * name)39979383aaeSLinus Walleij int kxsd9_common_probe(struct device *dev,
4000d1fb2d5SLinus Walleij struct regmap *map,
401154021a3SLinus Walleij const char *name)
40281ca486fSJonathan Cameron {
40381ca486fSJonathan Cameron struct iio_dev *indio_dev;
40481ca486fSJonathan Cameron struct kxsd9_state *st;
4059f907972SLinus Walleij int ret;
40681ca486fSJonathan Cameron
40779383aaeSLinus Walleij indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
4084ee30933SSachin Kamat if (!indio_dev)
4094ee30933SSachin Kamat return -ENOMEM;
4104ee30933SSachin Kamat
41181ca486fSJonathan Cameron st = iio_priv(indio_dev);
41279383aaeSLinus Walleij st->dev = dev;
4130d1fb2d5SLinus Walleij st->map = map;
41481ca486fSJonathan Cameron
41581ca486fSJonathan Cameron indio_dev->channels = kxsd9_channels;
41681ca486fSJonathan Cameron indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
4179f907972SLinus Walleij indio_dev->name = name;
41881ca486fSJonathan Cameron indio_dev->info = &kxsd9_info;
41981ca486fSJonathan Cameron indio_dev->modes = INDIO_DIRECT_MODE;
4200427a106SLinus Walleij indio_dev->available_scan_masks = kxsd9_scan_masks;
42181ca486fSJonathan Cameron
42212884004SLinus Walleij /* Read the mounting matrix, if present */
423b892770aSAndy Shevchenko ret = iio_read_mount_matrix(dev, &st->orientation);
42412884004SLinus Walleij if (ret)
42512884004SLinus Walleij return ret;
42612884004SLinus Walleij
4272bb4a02aSLinus Walleij /* Fetch and turn on regulators */
4282bb4a02aSLinus Walleij st->regs[0].supply = kxsd9_reg_vdd;
4292bb4a02aSLinus Walleij st->regs[1].supply = kxsd9_reg_iovdd;
43079383aaeSLinus Walleij ret = devm_regulator_bulk_get(dev,
4312bb4a02aSLinus Walleij ARRAY_SIZE(st->regs),
4322bb4a02aSLinus Walleij st->regs);
4332bb4a02aSLinus Walleij if (ret) {
43479383aaeSLinus Walleij dev_err(dev, "Cannot get regulators\n");
4352bb4a02aSLinus Walleij return ret;
4362bb4a02aSLinus Walleij }
4379a9a369dSLinus Walleij /* Default scaling */
4389a9a369dSLinus Walleij st->scale = KXSD9_CTRL_C_FS_2G;
4392bb4a02aSLinus Walleij
44081ca486fSJonathan Cameron kxsd9_power_up(st);
44181ca486fSJonathan Cameron
4420427a106SLinus Walleij ret = iio_triggered_buffer_setup(indio_dev,
4430427a106SLinus Walleij iio_pollfunc_store_time,
4440427a106SLinus Walleij kxsd9_trigger_handler,
4459a9a369dSLinus Walleij &kxsd9_buffer_setup_ops);
4460427a106SLinus Walleij if (ret) {
44779383aaeSLinus Walleij dev_err(dev, "triggered buffer setup failed\n");
4482bb4a02aSLinus Walleij goto err_power_down;
4490427a106SLinus Walleij }
4500427a106SLinus Walleij
4519f907972SLinus Walleij ret = iio_device_register(indio_dev);
4529f907972SLinus Walleij if (ret)
4530427a106SLinus Walleij goto err_cleanup_buffer;
4549f907972SLinus Walleij
45579383aaeSLinus Walleij dev_set_drvdata(dev, indio_dev);
456154021a3SLinus Walleij
4579a9a369dSLinus Walleij /* Enable runtime PM */
4589a9a369dSLinus Walleij pm_runtime_get_noresume(dev);
4599a9a369dSLinus Walleij pm_runtime_set_active(dev);
4609a9a369dSLinus Walleij pm_runtime_enable(dev);
4619a9a369dSLinus Walleij /*
4629a9a369dSLinus Walleij * Set autosuspend to two orders of magnitude larger than the
4639a9a369dSLinus Walleij * start-up time. 20ms start-up time means 2000ms autosuspend,
4649a9a369dSLinus Walleij * i.e. 2 seconds.
4659a9a369dSLinus Walleij */
4669a9a369dSLinus Walleij pm_runtime_set_autosuspend_delay(dev, 2000);
4679a9a369dSLinus Walleij pm_runtime_use_autosuspend(dev);
4689a9a369dSLinus Walleij pm_runtime_put(dev);
4699a9a369dSLinus Walleij
470154021a3SLinus Walleij return 0;
4710427a106SLinus Walleij
4720427a106SLinus Walleij err_cleanup_buffer:
4730427a106SLinus Walleij iio_triggered_buffer_cleanup(indio_dev);
4742bb4a02aSLinus Walleij err_power_down:
4752bb4a02aSLinus Walleij kxsd9_power_down(st);
4760427a106SLinus Walleij
4770427a106SLinus Walleij return ret;
478154021a3SLinus Walleij }
479c24ef124SJonathan Cameron EXPORT_SYMBOL_NS(kxsd9_common_probe, IIO_KXSD9);
480154021a3SLinus Walleij
kxsd9_common_remove(struct device * dev)481df2171c6SUwe Kleine-König void kxsd9_common_remove(struct device *dev)
482154021a3SLinus Walleij {
48379383aaeSLinus Walleij struct iio_dev *indio_dev = dev_get_drvdata(dev);
4842bb4a02aSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
485154021a3SLinus Walleij
4860427a106SLinus Walleij iio_triggered_buffer_cleanup(indio_dev);
487154021a3SLinus Walleij iio_device_unregister(indio_dev);
4889a9a369dSLinus Walleij pm_runtime_get_sync(dev);
4899a9a369dSLinus Walleij pm_runtime_put_noidle(dev);
4909a9a369dSLinus Walleij pm_runtime_disable(dev);
4912bb4a02aSLinus Walleij kxsd9_power_down(st);
49281ca486fSJonathan Cameron }
493c24ef124SJonathan Cameron EXPORT_SYMBOL_NS(kxsd9_common_remove, IIO_KXSD9);
49481ca486fSJonathan Cameron
kxsd9_runtime_suspend(struct device * dev)4959a9a369dSLinus Walleij static int kxsd9_runtime_suspend(struct device *dev)
4969a9a369dSLinus Walleij {
4979a9a369dSLinus Walleij struct iio_dev *indio_dev = dev_get_drvdata(dev);
4989a9a369dSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
4999a9a369dSLinus Walleij
5009a9a369dSLinus Walleij return kxsd9_power_down(st);
5019a9a369dSLinus Walleij }
5029a9a369dSLinus Walleij
kxsd9_runtime_resume(struct device * dev)5039a9a369dSLinus Walleij static int kxsd9_runtime_resume(struct device *dev)
5049a9a369dSLinus Walleij {
5059a9a369dSLinus Walleij struct iio_dev *indio_dev = dev_get_drvdata(dev);
5069a9a369dSLinus Walleij struct kxsd9_state *st = iio_priv(indio_dev);
5079a9a369dSLinus Walleij
5089a9a369dSLinus Walleij return kxsd9_power_up(st);
5099a9a369dSLinus Walleij }
5109a9a369dSLinus Walleij
511*cb490b10SJonathan Cameron EXPORT_NS_RUNTIME_DEV_PM_OPS(kxsd9_dev_pm_ops, kxsd9_runtime_suspend,
512*cb490b10SJonathan Cameron kxsd9_runtime_resume, NULL, IIO_KXSD9);
5139a9a369dSLinus Walleij
51481ca486fSJonathan Cameron MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
515bf96f6e8SLinus Walleij MODULE_DESCRIPTION("Kionix KXSD9 driver");
51681ca486fSJonathan Cameron MODULE_LICENSE("GPL v2");
517