xref: /openbmc/linux/drivers/iio/accel/kxsd9.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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 				  &regval);
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