xref: /openbmc/linux/drivers/iio/proximity/sx9310.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
172ad02b1SDaniel Campello // SPDX-License-Identifier: GPL-2.0
272ad02b1SDaniel Campello /*
372ad02b1SDaniel Campello  * Copyright 2018 Google LLC.
472ad02b1SDaniel Campello  *
572ad02b1SDaniel Campello  * Driver for Semtech's SX9310/SX9311 capacitive proximity/button solution.
672ad02b1SDaniel Campello  * Based on SX9500 driver and Semtech driver using the input framework
772ad02b1SDaniel Campello  * <https://my.syncplicity.com/share/teouwsim8niiaud/
872ad02b1SDaniel Campello  *          linux-driver-SX9310_NoSmartHSensing>.
9124cbc33SDaniel Campello  * Reworked in April 2019 by Evan Green <evgreen@chromium.org>
10124cbc33SDaniel Campello  * and in January 2020 by Daniel Campello <campello@chromium.org>.
1172ad02b1SDaniel Campello  */
1272ad02b1SDaniel Campello 
13d9f753f3SDaniel Campello #include <linux/bitfield.h>
1472ad02b1SDaniel Campello #include <linux/delay.h>
1572ad02b1SDaniel Campello #include <linux/i2c.h>
16caa8ce7fSGwendal Grignou #include <linux/interrupt.h>
1772ad02b1SDaniel Campello #include <linux/kernel.h>
18227c83faSStephen Boyd #include <linux/log2.h>
19ef5bdbabSDaniel Campello #include <linux/mod_devicetable.h>
2072ad02b1SDaniel Campello #include <linux/module.h>
2172ad02b1SDaniel Campello #include <linux/pm.h>
227a3605beSGwendal Grignou #include <linux/property.h>
2372ad02b1SDaniel Campello #include <linux/regmap.h>
2472ad02b1SDaniel Campello #include <linux/iio/iio.h>
25caa8ce7fSGwendal Grignou 
26caa8ce7fSGwendal Grignou #include "sx_common.h"
2772ad02b1SDaniel Campello 
2872ad02b1SDaniel Campello /* Register definitions. */
29caa8ce7fSGwendal Grignou #define SX9310_REG_IRQ_SRC				SX_COMMON_REG_IRQ_SRC
3072ad02b1SDaniel Campello #define SX9310_REG_STAT0				0x01
3172ad02b1SDaniel Campello #define SX9310_REG_STAT1				0x02
32d9f753f3SDaniel Campello #define SX9310_REG_STAT1_COMPSTAT_MASK			GENMASK(3, 0)
3372ad02b1SDaniel Campello #define SX9310_REG_IRQ_MSK				0x03
3472ad02b1SDaniel Campello #define   SX9310_CONVDONE_IRQ				BIT(3)
3572ad02b1SDaniel Campello #define   SX9310_FAR_IRQ				BIT(5)
3672ad02b1SDaniel Campello #define   SX9310_CLOSE_IRQ				BIT(6)
3772ad02b1SDaniel Campello #define SX9310_REG_IRQ_FUNC				0x04
3872ad02b1SDaniel Campello 
3972ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL0				0x10
40d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL0_SENSOREN_MASK		GENMASK(3, 0)
41d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK		GENMASK(7, 4)
42d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL0_SCANPERIOD_15MS		0x01
4372ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL1				0x11
4472ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL2				0x12
455b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_COMBMODE_MASK		GENMASK(7, 6)
465b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_COMBMODE_CS0_CS1_CS2_CS3 (0x03 << 6)
47d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL2_COMBMODE_CS1_CS2	(0x02 << 6)
485b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_COMBMODE_CS0_CS1	(0x01 << 6)
495b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_COMBMODE_CS3		(0x00 << 6)
505b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_SHIELDEN_MASK		GENMASK(3, 2)
51d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL2_SHIELDEN_DYNAMIC	(0x01 << 2)
525b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL2_SHIELDEN_GROUND		(0x02 << 2)
5372ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL3				0x13
54227c83faSStephen Boyd #define   SX9310_REG_PROX_CTRL3_GAIN0_MASK		GENMASK(3, 2)
55d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL3_GAIN0_X8		(0x03 << 2)
56227c83faSStephen Boyd #define   SX9310_REG_PROX_CTRL3_GAIN12_MASK		GENMASK(1, 0)
5772ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL3_GAIN12_X4		0x02
5872ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL4				0x14
595b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_MASK		GENMASK(2, 0)
6072ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL4_RESOLUTION_FINEST	0x07
615b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_VERY_FINE	0x06
625b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_FINE		0x05
635b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_MEDIUM	0x04
645b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_MEDIUM_COARSE 0x03
655b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_COARSE	0x02
665b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_VERY_COARSE	0x01
675b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL4_RESOLUTION_COARSEST	0x00
6872ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL5				0x15
69d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL5_RANGE_SMALL		(0x03 << 6)
705b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL5_STARTUPSENS_MASK	GENMASK(3, 2)
71d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL5_STARTUPSENS_CS1		(0x01 << 2)
725b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL5_RAWFILT_MASK		GENMASK(1, 0)
735b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL5_RAWFILT_SHIFT		0
7472ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL5_RAWFILT_1P25		0x02
7572ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL6				0x16
76d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL6_AVGTHRESH_DEFAULT	0x20
7772ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL7				0x17
78d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL7_AVGNEGFILT_2		(0x01 << 3)
795b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL7_AVGPOSFILT_MASK		GENMASK(2, 0)
805b19ca2cSStephen Boyd #define   SX9310_REG_PROX_CTRL7_AVGPOSFILT_SHIFT	0
8172ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL7_AVGPOSFILT_512		0x05
8272ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL8				0x18
83ad2b473eSStephen Boyd #define   SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK		GENMASK(7, 3)
8472ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL9				0x19
85d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL8_9_PTHRESH_28		(0x08 << 3)
86d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL8_9_PTHRESH_96		(0x11 << 3)
8772ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL8_9_BODYTHRESH_900	0x03
8872ad02b1SDaniel Campello #define   SX9310_REG_PROX_CTRL8_9_BODYTHRESH_1500	0x05
8972ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL10				0x1a
9008f0411cSStephen Boyd #define   SX9310_REG_PROX_CTRL10_HYST_MASK		GENMASK(5, 4)
91d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL10_HYST_6PCT		(0x01 << 4)
921b687201SStephen Boyd #define   SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK	GENMASK(3, 2)
931b687201SStephen Boyd #define   SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK	GENMASK(1, 0)
94d9f753f3SDaniel Campello #define   SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_2		0x01
9572ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL11				0x1b
9672ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL12				0x1c
9772ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL13				0x1d
9872ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL14				0x1e
9972ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL15				0x1f
10072ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL16				0x20
10172ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL17				0x21
10272ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL18				0x22
10372ad02b1SDaniel Campello #define SX9310_REG_PROX_CTRL19				0x23
10472ad02b1SDaniel Campello #define SX9310_REG_SAR_CTRL0				0x2a
105d9f753f3SDaniel Campello #define   SX9310_REG_SAR_CTRL0_SARDEB_4_SAMPLES		(0x02 << 5)
106d9f753f3SDaniel Campello #define   SX9310_REG_SAR_CTRL0_SARHYST_8		(0x02 << 3)
10772ad02b1SDaniel Campello #define SX9310_REG_SAR_CTRL1				0x2b
10872ad02b1SDaniel Campello /* Each increment of the slope register is 0.0078125. */
10972ad02b1SDaniel Campello #define   SX9310_REG_SAR_CTRL1_SLOPE(_hnslope)		(_hnslope / 78125)
11072ad02b1SDaniel Campello #define SX9310_REG_SAR_CTRL2				0x2c
11172ad02b1SDaniel Campello #define   SX9310_REG_SAR_CTRL2_SAROFFSET_DEFAULT	0x3c
11272ad02b1SDaniel Campello 
11372ad02b1SDaniel Campello #define SX9310_REG_SENSOR_SEL				0x30
11472ad02b1SDaniel Campello #define SX9310_REG_USE_MSB				0x31
11572ad02b1SDaniel Campello #define SX9310_REG_USE_LSB				0x32
11672ad02b1SDaniel Campello #define SX9310_REG_AVG_MSB				0x33
11772ad02b1SDaniel Campello #define SX9310_REG_AVG_LSB				0x34
11872ad02b1SDaniel Campello #define SX9310_REG_DIFF_MSB				0x35
11972ad02b1SDaniel Campello #define SX9310_REG_DIFF_LSB				0x36
12072ad02b1SDaniel Campello #define SX9310_REG_OFFSET_MSB				0x37
12172ad02b1SDaniel Campello #define SX9310_REG_OFFSET_LSB				0x38
12272ad02b1SDaniel Campello #define SX9310_REG_SAR_MSB				0x39
12372ad02b1SDaniel Campello #define SX9310_REG_SAR_LSB				0x3a
124d9f753f3SDaniel Campello #define SX9310_REG_I2C_ADDR				0x40
12572ad02b1SDaniel Campello #define SX9310_REG_PAUSE				0x41
12672ad02b1SDaniel Campello #define SX9310_REG_WHOAMI				0x42
12772ad02b1SDaniel Campello #define   SX9310_WHOAMI_VALUE				0x01
12872ad02b1SDaniel Campello #define   SX9311_WHOAMI_VALUE				0x02
12972ad02b1SDaniel Campello #define SX9310_REG_RESET				0x7f
13072ad02b1SDaniel Campello 
13172ad02b1SDaniel Campello 
13272ad02b1SDaniel Campello /* 4 hardware channels, as defined in STAT0: COMB, CS2, CS1 and CS0. */
13372ad02b1SDaniel Campello #define SX9310_NUM_CHANNELS				4
134caa8ce7fSGwendal Grignou static_assert(SX9310_NUM_CHANNELS <= SX_COMMON_MAX_NUM_CHANNELS);
13572ad02b1SDaniel Campello 
13672ad02b1SDaniel Campello #define SX9310_NAMED_CHANNEL(idx, name)				 \
13772ad02b1SDaniel Campello {								 \
13872ad02b1SDaniel Campello 	.type = IIO_PROXIMITY,					 \
139227c83faSStephen Boyd 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		 \
140227c83faSStephen Boyd 			      BIT(IIO_CHAN_INFO_HARDWAREGAIN),   \
14172ad02b1SDaniel Campello 	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
142227c83faSStephen Boyd 	.info_mask_separate_available =				 \
143227c83faSStephen Boyd 		BIT(IIO_CHAN_INFO_HARDWAREGAIN),		 \
144fc5d805eSGwendal Grignou 	.info_mask_shared_by_all_available =			 \
145fc5d805eSGwendal Grignou 		BIT(IIO_CHAN_INFO_SAMP_FREQ),			 \
14672ad02b1SDaniel Campello 	.indexed = 1,						 \
14772ad02b1SDaniel Campello 	.channel = idx,						 \
14872ad02b1SDaniel Campello 	.extend_name = name,					 \
14972ad02b1SDaniel Campello 	.address = SX9310_REG_DIFF_MSB,				 \
150caa8ce7fSGwendal Grignou 	.event_spec = sx_common_events,				 \
151caa8ce7fSGwendal Grignou 	.num_event_specs = ARRAY_SIZE(sx_common_events),	 \
15272ad02b1SDaniel Campello 	.scan_index = idx,					 \
15372ad02b1SDaniel Campello 	.scan_type = {						 \
15472ad02b1SDaniel Campello 		.sign = 's',					 \
15572ad02b1SDaniel Campello 		.realbits = 12,					 \
15672ad02b1SDaniel Campello 		.storagebits = 16,				 \
15772ad02b1SDaniel Campello 		.endianness = IIO_BE,				 \
15872ad02b1SDaniel Campello 	},							 \
15972ad02b1SDaniel Campello }
16072ad02b1SDaniel Campello #define SX9310_CHANNEL(idx) SX9310_NAMED_CHANNEL(idx, NULL)
16172ad02b1SDaniel Campello 
16272ad02b1SDaniel Campello static const struct iio_chan_spec sx9310_channels[] = {
16372ad02b1SDaniel Campello 	SX9310_CHANNEL(0),			/* CS0 */
16472ad02b1SDaniel Campello 	SX9310_CHANNEL(1),			/* CS1 */
16572ad02b1SDaniel Campello 	SX9310_CHANNEL(2),			/* CS2 */
16672ad02b1SDaniel Campello 	SX9310_NAMED_CHANNEL(3, "comb"),	/* COMB */
16772ad02b1SDaniel Campello 
16872ad02b1SDaniel Campello 	IIO_CHAN_SOFT_TIMESTAMP(4),
16972ad02b1SDaniel Campello };
17072ad02b1SDaniel Campello 
17172ad02b1SDaniel Campello /*
17272ad02b1SDaniel Campello  * Each entry contains the integer part (val) and the fractional part, in micro
17372ad02b1SDaniel Campello  * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO.
17472ad02b1SDaniel Campello  */
17572ad02b1SDaniel Campello static const struct {
17672ad02b1SDaniel Campello 	int val;
17772ad02b1SDaniel Campello 	int val2;
17872ad02b1SDaniel Campello } sx9310_samp_freq_table[] = {
17972ad02b1SDaniel Campello 	{ 500, 0 }, /* 0000: Min (no idle time) */
18072ad02b1SDaniel Campello 	{ 66, 666666 }, /* 0001: 15 ms */
18172ad02b1SDaniel Campello 	{ 33, 333333 }, /* 0010: 30 ms (Typ.) */
18272ad02b1SDaniel Campello 	{ 22, 222222 }, /* 0011: 45 ms */
18372ad02b1SDaniel Campello 	{ 16, 666666 }, /* 0100: 60 ms */
18472ad02b1SDaniel Campello 	{ 11, 111111 }, /* 0101: 90 ms */
18572ad02b1SDaniel Campello 	{ 8, 333333 }, /* 0110: 120 ms */
18672ad02b1SDaniel Campello 	{ 5, 0 }, /* 0111: 200 ms */
18772ad02b1SDaniel Campello 	{ 2, 500000 }, /* 1000: 400 ms */
18872ad02b1SDaniel Campello 	{ 1, 666666 }, /* 1001: 600 ms */
18972ad02b1SDaniel Campello 	{ 1, 250000 }, /* 1010: 800 ms */
19072ad02b1SDaniel Campello 	{ 1, 0 }, /* 1011: 1 s */
19172ad02b1SDaniel Campello 	{ 0, 500000 }, /* 1100: 2 s */
19272ad02b1SDaniel Campello 	{ 0, 333333 }, /* 1101: 3 s */
19372ad02b1SDaniel Campello 	{ 0, 250000 }, /* 1110: 4 s */
19472ad02b1SDaniel Campello 	{ 0, 200000 }, /* 1111: 5 s */
19572ad02b1SDaniel Campello };
19672ad02b1SDaniel Campello static const unsigned int sx9310_scan_period_table[] = {
19772ad02b1SDaniel Campello 	2,   15,  30,  45,   60,   90,	 120,  200,
19872ad02b1SDaniel Campello 	400, 600, 800, 1000, 2000, 3000, 4000, 5000,
19972ad02b1SDaniel Campello };
20072ad02b1SDaniel Campello 
20172ad02b1SDaniel Campello static const struct regmap_range sx9310_writable_reg_ranges[] = {
20272ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_IRQ_MSK, SX9310_REG_IRQ_FUNC),
20372ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL19),
20472ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_SAR_CTRL0, SX9310_REG_SAR_CTRL2),
20572ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_SENSOR_SEL, SX9310_REG_SENSOR_SEL),
20672ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_OFFSET_MSB, SX9310_REG_OFFSET_LSB),
20772ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_PAUSE, SX9310_REG_PAUSE),
20872ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_RESET, SX9310_REG_RESET),
20972ad02b1SDaniel Campello };
21072ad02b1SDaniel Campello 
21172ad02b1SDaniel Campello static const struct regmap_access_table sx9310_writeable_regs = {
21272ad02b1SDaniel Campello 	.yes_ranges = sx9310_writable_reg_ranges,
21372ad02b1SDaniel Campello 	.n_yes_ranges = ARRAY_SIZE(sx9310_writable_reg_ranges),
21472ad02b1SDaniel Campello };
21572ad02b1SDaniel Campello 
21672ad02b1SDaniel Campello static const struct regmap_range sx9310_readable_reg_ranges[] = {
21772ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_IRQ_SRC, SX9310_REG_IRQ_FUNC),
21872ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL19),
21972ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_SAR_CTRL0, SX9310_REG_SAR_CTRL2),
22072ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_SENSOR_SEL, SX9310_REG_SAR_LSB),
221d9f753f3SDaniel Campello 	regmap_reg_range(SX9310_REG_I2C_ADDR, SX9310_REG_WHOAMI),
22272ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_RESET, SX9310_REG_RESET),
22372ad02b1SDaniel Campello };
22472ad02b1SDaniel Campello 
22572ad02b1SDaniel Campello static const struct regmap_access_table sx9310_readable_regs = {
22672ad02b1SDaniel Campello 	.yes_ranges = sx9310_readable_reg_ranges,
22772ad02b1SDaniel Campello 	.n_yes_ranges = ARRAY_SIZE(sx9310_readable_reg_ranges),
22872ad02b1SDaniel Campello };
22972ad02b1SDaniel Campello 
23072ad02b1SDaniel Campello static const struct regmap_range sx9310_volatile_reg_ranges[] = {
23172ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_IRQ_SRC, SX9310_REG_STAT1),
23272ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_USE_MSB, SX9310_REG_DIFF_LSB),
23372ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_SAR_MSB, SX9310_REG_SAR_LSB),
23472ad02b1SDaniel Campello 	regmap_reg_range(SX9310_REG_RESET, SX9310_REG_RESET),
23572ad02b1SDaniel Campello };
23672ad02b1SDaniel Campello 
23772ad02b1SDaniel Campello static const struct regmap_access_table sx9310_volatile_regs = {
23872ad02b1SDaniel Campello 	.yes_ranges = sx9310_volatile_reg_ranges,
23972ad02b1SDaniel Campello 	.n_yes_ranges = ARRAY_SIZE(sx9310_volatile_reg_ranges),
24072ad02b1SDaniel Campello };
24172ad02b1SDaniel Campello 
24272ad02b1SDaniel Campello static const struct regmap_config sx9310_regmap_config = {
24372ad02b1SDaniel Campello 	.reg_bits = 8,
24472ad02b1SDaniel Campello 	.val_bits = 8,
24572ad02b1SDaniel Campello 
24672ad02b1SDaniel Campello 	.max_register = SX9310_REG_RESET,
24772ad02b1SDaniel Campello 	.cache_type = REGCACHE_RBTREE,
24872ad02b1SDaniel Campello 
24972ad02b1SDaniel Campello 	.wr_table = &sx9310_writeable_regs,
25072ad02b1SDaniel Campello 	.rd_table = &sx9310_readable_regs,
25172ad02b1SDaniel Campello 	.volatile_table = &sx9310_volatile_regs,
25272ad02b1SDaniel Campello };
25372ad02b1SDaniel Campello 
sx9310_read_prox_data(struct sx_common_data * data,const struct iio_chan_spec * chan,__be16 * val)254caa8ce7fSGwendal Grignou static int sx9310_read_prox_data(struct sx_common_data *data,
25572ad02b1SDaniel Campello 				 const struct iio_chan_spec *chan, __be16 *val)
25672ad02b1SDaniel Campello {
25772ad02b1SDaniel Campello 	int ret;
25872ad02b1SDaniel Campello 
25972ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_SENSOR_SEL, chan->channel);
260a917af2aSDaniel Campello 	if (ret)
26172ad02b1SDaniel Campello 		return ret;
26272ad02b1SDaniel Campello 
26301b9cb0dSDaniel Campello 	return regmap_bulk_read(data->regmap, chan->address, val, sizeof(*val));
26472ad02b1SDaniel Campello }
26572ad02b1SDaniel Campello 
26672ad02b1SDaniel Campello /*
26772ad02b1SDaniel Campello  * If we have no interrupt support, we have to wait for a scan period
26872ad02b1SDaniel Campello  * after enabling a channel to get a result.
26972ad02b1SDaniel Campello  */
sx9310_wait_for_sample(struct sx_common_data * data)270caa8ce7fSGwendal Grignou static int sx9310_wait_for_sample(struct sx_common_data *data)
27172ad02b1SDaniel Campello {
27272ad02b1SDaniel Campello 	int ret;
27372ad02b1SDaniel Campello 	unsigned int val;
27472ad02b1SDaniel Campello 
27572ad02b1SDaniel Campello 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, &val);
276a917af2aSDaniel Campello 	if (ret)
27772ad02b1SDaniel Campello 		return ret;
27872ad02b1SDaniel Campello 
279d9f753f3SDaniel Campello 	val = FIELD_GET(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, val);
28072ad02b1SDaniel Campello 
28172ad02b1SDaniel Campello 	msleep(sx9310_scan_period_table[val]);
28272ad02b1SDaniel Campello 
28372ad02b1SDaniel Campello 	return 0;
28472ad02b1SDaniel Campello }
28572ad02b1SDaniel Campello 
sx9310_read_gain(struct sx_common_data * data,const struct iio_chan_spec * chan,int * val)286caa8ce7fSGwendal Grignou static int sx9310_read_gain(struct sx_common_data *data,
287227c83faSStephen Boyd 			    const struct iio_chan_spec *chan, int *val)
288227c83faSStephen Boyd {
289227c83faSStephen Boyd 	unsigned int regval, gain;
290227c83faSStephen Boyd 	int ret;
291227c83faSStephen Boyd 
292227c83faSStephen Boyd 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL3, &regval);
293227c83faSStephen Boyd 	if (ret)
294227c83faSStephen Boyd 		return ret;
295227c83faSStephen Boyd 
296227c83faSStephen Boyd 	switch (chan->channel) {
297227c83faSStephen Boyd 	case 0:
298227c83faSStephen Boyd 	case 3:
299227c83faSStephen Boyd 		gain = FIELD_GET(SX9310_REG_PROX_CTRL3_GAIN0_MASK, regval);
300227c83faSStephen Boyd 		break;
301227c83faSStephen Boyd 	case 1:
302227c83faSStephen Boyd 	case 2:
303227c83faSStephen Boyd 		gain = FIELD_GET(SX9310_REG_PROX_CTRL3_GAIN12_MASK, regval);
304227c83faSStephen Boyd 		break;
305227c83faSStephen Boyd 	default:
306227c83faSStephen Boyd 		return -EINVAL;
307227c83faSStephen Boyd 	}
308227c83faSStephen Boyd 
309227c83faSStephen Boyd 	*val = 1 << gain;
310227c83faSStephen Boyd 
311227c83faSStephen Boyd 	return IIO_VAL_INT;
312227c83faSStephen Boyd }
313227c83faSStephen Boyd 
sx9310_read_samp_freq(struct sx_common_data * data,int * val,int * val2)314caa8ce7fSGwendal Grignou static int sx9310_read_samp_freq(struct sx_common_data *data, int *val, int *val2)
31572ad02b1SDaniel Campello {
31672ad02b1SDaniel Campello 	unsigned int regval;
317de479073SDaniel Campello 	int ret;
31872ad02b1SDaniel Campello 
319de479073SDaniel Campello 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, &regval);
320a917af2aSDaniel Campello 	if (ret)
32172ad02b1SDaniel Campello 		return ret;
32272ad02b1SDaniel Campello 
323d9f753f3SDaniel Campello 	regval = FIELD_GET(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, regval);
32472ad02b1SDaniel Campello 	*val = sx9310_samp_freq_table[regval].val;
32572ad02b1SDaniel Campello 	*val2 = sx9310_samp_freq_table[regval].val2;
32672ad02b1SDaniel Campello 
32772ad02b1SDaniel Campello 	return IIO_VAL_INT_PLUS_MICRO;
32872ad02b1SDaniel Campello }
32972ad02b1SDaniel Campello 
sx9310_read_raw(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,int * val,int * val2,long mask)33072ad02b1SDaniel Campello static int sx9310_read_raw(struct iio_dev *indio_dev,
33172ad02b1SDaniel Campello 			   const struct iio_chan_spec *chan, int *val,
33272ad02b1SDaniel Campello 			   int *val2, long mask)
33372ad02b1SDaniel Campello {
334caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
33572ad02b1SDaniel Campello 	int ret;
33672ad02b1SDaniel Campello 
33772ad02b1SDaniel Campello 	if (chan->type != IIO_PROXIMITY)
33872ad02b1SDaniel Campello 		return -EINVAL;
33972ad02b1SDaniel Campello 
34072ad02b1SDaniel Campello 	switch (mask) {
34172ad02b1SDaniel Campello 	case IIO_CHAN_INFO_RAW:
34272ad02b1SDaniel Campello 		ret = iio_device_claim_direct_mode(indio_dev);
34372ad02b1SDaniel Campello 		if (ret)
34472ad02b1SDaniel Campello 			return ret;
34572ad02b1SDaniel Campello 
346caa8ce7fSGwendal Grignou 		ret = sx_common_read_proximity(data, chan, val);
34772ad02b1SDaniel Campello 		iio_device_release_direct_mode(indio_dev);
34872ad02b1SDaniel Campello 		return ret;
349227c83faSStephen Boyd 	case IIO_CHAN_INFO_HARDWAREGAIN:
350227c83faSStephen Boyd 		ret = iio_device_claim_direct_mode(indio_dev);
351227c83faSStephen Boyd 		if (ret)
352227c83faSStephen Boyd 			return ret;
353227c83faSStephen Boyd 
354227c83faSStephen Boyd 		ret = sx9310_read_gain(data, chan, val);
355227c83faSStephen Boyd 		iio_device_release_direct_mode(indio_dev);
356227c83faSStephen Boyd 		return ret;
35772ad02b1SDaniel Campello 	case IIO_CHAN_INFO_SAMP_FREQ:
35872ad02b1SDaniel Campello 		return sx9310_read_samp_freq(data, val, val2);
35972ad02b1SDaniel Campello 	default:
36072ad02b1SDaniel Campello 		return -EINVAL;
36172ad02b1SDaniel Campello 	}
36272ad02b1SDaniel Campello }
36372ad02b1SDaniel Campello 
364227c83faSStephen Boyd static const int sx9310_gain_vals[] = { 1, 2, 4, 8 };
365227c83faSStephen Boyd 
sx9310_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)366227c83faSStephen Boyd static int sx9310_read_avail(struct iio_dev *indio_dev,
367227c83faSStephen Boyd 			     struct iio_chan_spec const *chan,
368227c83faSStephen Boyd 			     const int **vals, int *type, int *length,
369227c83faSStephen Boyd 			     long mask)
370227c83faSStephen Boyd {
371227c83faSStephen Boyd 	if (chan->type != IIO_PROXIMITY)
372227c83faSStephen Boyd 		return -EINVAL;
373227c83faSStephen Boyd 
374227c83faSStephen Boyd 	switch (mask) {
375227c83faSStephen Boyd 	case IIO_CHAN_INFO_HARDWAREGAIN:
376227c83faSStephen Boyd 		*type = IIO_VAL_INT;
377227c83faSStephen Boyd 		*length = ARRAY_SIZE(sx9310_gain_vals);
378227c83faSStephen Boyd 		*vals = sx9310_gain_vals;
379227c83faSStephen Boyd 		return IIO_AVAIL_LIST;
380fc5d805eSGwendal Grignou 	case IIO_CHAN_INFO_SAMP_FREQ:
381fc5d805eSGwendal Grignou 		*type = IIO_VAL_INT_PLUS_MICRO;
382fc5d805eSGwendal Grignou 		*length = ARRAY_SIZE(sx9310_samp_freq_table) * 2;
383fc5d805eSGwendal Grignou 		*vals = (int *)sx9310_samp_freq_table;
384fc5d805eSGwendal Grignou 		return IIO_AVAIL_LIST;
385caa8ce7fSGwendal Grignou 	default:
386227c83faSStephen Boyd 		return -EINVAL;
387227c83faSStephen Boyd 	}
388caa8ce7fSGwendal Grignou }
389227c83faSStephen Boyd 
390ad2b473eSStephen Boyd static const unsigned int sx9310_pthresh_codes[] = {
391ad2b473eSStephen Boyd 	2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80, 88, 96, 112,
392ad2b473eSStephen Boyd 	128, 144, 160, 192, 224, 256, 320, 384, 512, 640, 768, 1024, 1536
393ad2b473eSStephen Boyd };
394ad2b473eSStephen Boyd 
sx9310_get_thresh_reg(unsigned int channel)395ad2b473eSStephen Boyd static int sx9310_get_thresh_reg(unsigned int channel)
396ad2b473eSStephen Boyd {
397ad2b473eSStephen Boyd 	switch (channel) {
398ad2b473eSStephen Boyd 	case 0:
399ad2b473eSStephen Boyd 	case 3:
400ad2b473eSStephen Boyd 		return SX9310_REG_PROX_CTRL8;
401ad2b473eSStephen Boyd 	case 1:
402ad2b473eSStephen Boyd 	case 2:
403ad2b473eSStephen Boyd 		return SX9310_REG_PROX_CTRL9;
404caa8ce7fSGwendal Grignou 	default:
405ad2b473eSStephen Boyd 		return -EINVAL;
406ad2b473eSStephen Boyd 	}
407caa8ce7fSGwendal Grignou }
408ad2b473eSStephen Boyd 
sx9310_read_thresh(struct sx_common_data * data,const struct iio_chan_spec * chan,int * val)409caa8ce7fSGwendal Grignou static int sx9310_read_thresh(struct sx_common_data *data,
410ad2b473eSStephen Boyd 			      const struct iio_chan_spec *chan, int *val)
411ad2b473eSStephen Boyd {
412ad2b473eSStephen Boyd 	unsigned int reg;
413ad2b473eSStephen Boyd 	unsigned int regval;
414ad2b473eSStephen Boyd 	int ret;
415ad2b473eSStephen Boyd 
416ad2b473eSStephen Boyd 	reg = ret = sx9310_get_thresh_reg(chan->channel);
417ad2b473eSStephen Boyd 	if (ret < 0)
418ad2b473eSStephen Boyd 		return ret;
419ad2b473eSStephen Boyd 
420ad2b473eSStephen Boyd 	ret = regmap_read(data->regmap, reg, &regval);
421ad2b473eSStephen Boyd 	if (ret)
422ad2b473eSStephen Boyd 		return ret;
423ad2b473eSStephen Boyd 
424ad2b473eSStephen Boyd 	regval = FIELD_GET(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
425a06b63a1SDan Carpenter 	if (regval >= ARRAY_SIZE(sx9310_pthresh_codes))
426ad2b473eSStephen Boyd 		return -EINVAL;
427ad2b473eSStephen Boyd 
428ad2b473eSStephen Boyd 	*val = sx9310_pthresh_codes[regval];
429ad2b473eSStephen Boyd 	return IIO_VAL_INT;
430ad2b473eSStephen Boyd }
431ad2b473eSStephen Boyd 
sx9310_read_hysteresis(struct sx_common_data * data,const struct iio_chan_spec * chan,int * val)432caa8ce7fSGwendal Grignou static int sx9310_read_hysteresis(struct sx_common_data *data,
43308f0411cSStephen Boyd 				  const struct iio_chan_spec *chan, int *val)
43408f0411cSStephen Boyd {
43508f0411cSStephen Boyd 	unsigned int regval, pthresh;
43608f0411cSStephen Boyd 	int ret;
43708f0411cSStephen Boyd 
43808f0411cSStephen Boyd 	ret = sx9310_read_thresh(data, chan, &pthresh);
43908f0411cSStephen Boyd 	if (ret < 0)
44008f0411cSStephen Boyd 		return ret;
44108f0411cSStephen Boyd 
44208f0411cSStephen Boyd 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL10, &regval);
44308f0411cSStephen Boyd 	if (ret)
44408f0411cSStephen Boyd 		return ret;
44508f0411cSStephen Boyd 
44608f0411cSStephen Boyd 	regval = FIELD_GET(SX9310_REG_PROX_CTRL10_HYST_MASK, regval);
44708f0411cSStephen Boyd 	if (!regval)
44808f0411cSStephen Boyd 		regval = 5;
44908f0411cSStephen Boyd 
45008f0411cSStephen Boyd 	/* regval is at most 5 */
45108f0411cSStephen Boyd 	*val = pthresh >> (5 - regval);
45208f0411cSStephen Boyd 
45308f0411cSStephen Boyd 	return IIO_VAL_INT;
45408f0411cSStephen Boyd }
45508f0411cSStephen Boyd 
sx9310_read_far_debounce(struct sx_common_data * data,int * val)456caa8ce7fSGwendal Grignou static int sx9310_read_far_debounce(struct sx_common_data *data, int *val)
4571b687201SStephen Boyd {
4581b687201SStephen Boyd 	unsigned int regval;
4591b687201SStephen Boyd 	int ret;
4601b687201SStephen Boyd 
4611b687201SStephen Boyd 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL10, &regval);
4621b687201SStephen Boyd 	if (ret)
4631b687201SStephen Boyd 		return ret;
4641b687201SStephen Boyd 
4651b687201SStephen Boyd 	regval = FIELD_GET(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, regval);
4661b687201SStephen Boyd 	if (regval)
4671b687201SStephen Boyd 		*val = 1 << regval;
4681b687201SStephen Boyd 	else
4691b687201SStephen Boyd 		*val = 0;
4701b687201SStephen Boyd 
4711b687201SStephen Boyd 	return IIO_VAL_INT;
4721b687201SStephen Boyd }
4731b687201SStephen Boyd 
sx9310_read_close_debounce(struct sx_common_data * data,int * val)474caa8ce7fSGwendal Grignou static int sx9310_read_close_debounce(struct sx_common_data *data, int *val)
4751b687201SStephen Boyd {
4761b687201SStephen Boyd 	unsigned int regval;
4771b687201SStephen Boyd 	int ret;
4781b687201SStephen Boyd 
4791b687201SStephen Boyd 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL10, &regval);
4801b687201SStephen Boyd 	if (ret)
4811b687201SStephen Boyd 		return ret;
4821b687201SStephen Boyd 
4831b687201SStephen Boyd 	regval = FIELD_GET(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, regval);
4841b687201SStephen Boyd 	if (regval)
4851b687201SStephen Boyd 		*val = 1 << regval;
4861b687201SStephen Boyd 	else
4871b687201SStephen Boyd 		*val = 0;
4881b687201SStephen Boyd 
4891b687201SStephen Boyd 	return IIO_VAL_INT;
4901b687201SStephen Boyd }
4911b687201SStephen Boyd 
sx9310_read_event_val(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int * val,int * val2)492ad2b473eSStephen Boyd static int sx9310_read_event_val(struct iio_dev *indio_dev,
493ad2b473eSStephen Boyd 				 const struct iio_chan_spec *chan,
494ad2b473eSStephen Boyd 				 enum iio_event_type type,
495ad2b473eSStephen Boyd 				 enum iio_event_direction dir,
496ad2b473eSStephen Boyd 				 enum iio_event_info info, int *val, int *val2)
497ad2b473eSStephen Boyd {
498caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
499ad2b473eSStephen Boyd 
500ad2b473eSStephen Boyd 	if (chan->type != IIO_PROXIMITY)
501ad2b473eSStephen Boyd 		return -EINVAL;
502ad2b473eSStephen Boyd 
503ad2b473eSStephen Boyd 	switch (info) {
504ad2b473eSStephen Boyd 	case IIO_EV_INFO_VALUE:
505ad2b473eSStephen Boyd 		return sx9310_read_thresh(data, chan, val);
5061b687201SStephen Boyd 	case IIO_EV_INFO_PERIOD:
5071b687201SStephen Boyd 		switch (dir) {
5081b687201SStephen Boyd 		case IIO_EV_DIR_RISING:
5091b687201SStephen Boyd 			return sx9310_read_far_debounce(data, val);
5101b687201SStephen Boyd 		case IIO_EV_DIR_FALLING:
5111b687201SStephen Boyd 			return sx9310_read_close_debounce(data, val);
5121b687201SStephen Boyd 		default:
5131b687201SStephen Boyd 			return -EINVAL;
5141b687201SStephen Boyd 		}
51508f0411cSStephen Boyd 	case IIO_EV_INFO_HYSTERESIS:
51608f0411cSStephen Boyd 		return sx9310_read_hysteresis(data, chan, val);
517ad2b473eSStephen Boyd 	default:
518ad2b473eSStephen Boyd 		return -EINVAL;
519ad2b473eSStephen Boyd 	}
520ad2b473eSStephen Boyd }
521ad2b473eSStephen Boyd 
sx9310_write_thresh(struct sx_common_data * data,const struct iio_chan_spec * chan,int val)522caa8ce7fSGwendal Grignou static int sx9310_write_thresh(struct sx_common_data *data,
523ad2b473eSStephen Boyd 			       const struct iio_chan_spec *chan, int val)
524ad2b473eSStephen Boyd {
525ad2b473eSStephen Boyd 	unsigned int reg;
526ad2b473eSStephen Boyd 	unsigned int regval;
527ad2b473eSStephen Boyd 	int ret, i;
528ad2b473eSStephen Boyd 
529ad2b473eSStephen Boyd 	reg = ret = sx9310_get_thresh_reg(chan->channel);
530ad2b473eSStephen Boyd 	if (ret < 0)
531ad2b473eSStephen Boyd 		return ret;
532ad2b473eSStephen Boyd 
533ad2b473eSStephen Boyd 	for (i = 0; i < ARRAY_SIZE(sx9310_pthresh_codes); i++) {
534ad2b473eSStephen Boyd 		if (sx9310_pthresh_codes[i] == val) {
535ad2b473eSStephen Boyd 			regval = i;
536ad2b473eSStephen Boyd 			break;
537ad2b473eSStephen Boyd 		}
538ad2b473eSStephen Boyd 	}
539ad2b473eSStephen Boyd 
540ad2b473eSStephen Boyd 	if (i == ARRAY_SIZE(sx9310_pthresh_codes))
541ad2b473eSStephen Boyd 		return -EINVAL;
542ad2b473eSStephen Boyd 
543ad2b473eSStephen Boyd 	regval = FIELD_PREP(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
544ad2b473eSStephen Boyd 	mutex_lock(&data->mutex);
545ad2b473eSStephen Boyd 	ret = regmap_update_bits(data->regmap, reg,
546ad2b473eSStephen Boyd 				 SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
547ad2b473eSStephen Boyd 	mutex_unlock(&data->mutex);
548ad2b473eSStephen Boyd 
549ad2b473eSStephen Boyd 	return ret;
550ad2b473eSStephen Boyd }
551ad2b473eSStephen Boyd 
sx9310_write_hysteresis(struct sx_common_data * data,const struct iio_chan_spec * chan,int _val)552caa8ce7fSGwendal Grignou static int sx9310_write_hysteresis(struct sx_common_data *data,
55308f0411cSStephen Boyd 				   const struct iio_chan_spec *chan, int _val)
55408f0411cSStephen Boyd {
55508f0411cSStephen Boyd 	unsigned int hyst, val = _val;
55608f0411cSStephen Boyd 	int ret, pthresh;
55708f0411cSStephen Boyd 
55808f0411cSStephen Boyd 	ret = sx9310_read_thresh(data, chan, &pthresh);
55908f0411cSStephen Boyd 	if (ret < 0)
56008f0411cSStephen Boyd 		return ret;
56108f0411cSStephen Boyd 
56208f0411cSStephen Boyd 	if (val == 0)
56308f0411cSStephen Boyd 		hyst = 0;
56408f0411cSStephen Boyd 	else if (val == pthresh >> 2)
56508f0411cSStephen Boyd 		hyst = 3;
56608f0411cSStephen Boyd 	else if (val == pthresh >> 3)
56708f0411cSStephen Boyd 		hyst = 2;
56808f0411cSStephen Boyd 	else if (val == pthresh >> 4)
56908f0411cSStephen Boyd 		hyst = 1;
57008f0411cSStephen Boyd 	else
57108f0411cSStephen Boyd 		return -EINVAL;
57208f0411cSStephen Boyd 
57308f0411cSStephen Boyd 	hyst = FIELD_PREP(SX9310_REG_PROX_CTRL10_HYST_MASK, hyst);
57408f0411cSStephen Boyd 	mutex_lock(&data->mutex);
57508f0411cSStephen Boyd 	ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
57608f0411cSStephen Boyd 				 SX9310_REG_PROX_CTRL10_HYST_MASK, hyst);
57708f0411cSStephen Boyd 	mutex_unlock(&data->mutex);
57808f0411cSStephen Boyd 
57908f0411cSStephen Boyd 	return ret;
58008f0411cSStephen Boyd }
581ad2b473eSStephen Boyd 
sx9310_write_far_debounce(struct sx_common_data * data,int val)582caa8ce7fSGwendal Grignou static int sx9310_write_far_debounce(struct sx_common_data *data, int val)
5831b687201SStephen Boyd {
5841b687201SStephen Boyd 	int ret;
5851b687201SStephen Boyd 	unsigned int regval;
5861b687201SStephen Boyd 
587fc948409SGwendal Grignou 	if (val > 0)
5881b687201SStephen Boyd 		val = ilog2(val);
589fc948409SGwendal Grignou 	if (!FIELD_FIT(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val))
590fc948409SGwendal Grignou 		return -EINVAL;
591fc948409SGwendal Grignou 
5921b687201SStephen Boyd 	regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val);
5931b687201SStephen Boyd 
5941b687201SStephen Boyd 	mutex_lock(&data->mutex);
5951b687201SStephen Boyd 	ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
5961b687201SStephen Boyd 				 SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK,
5971b687201SStephen Boyd 				 regval);
5981b687201SStephen Boyd 	mutex_unlock(&data->mutex);
5991b687201SStephen Boyd 
6001b687201SStephen Boyd 	return ret;
6011b687201SStephen Boyd }
6021b687201SStephen Boyd 
sx9310_write_close_debounce(struct sx_common_data * data,int val)603caa8ce7fSGwendal Grignou static int sx9310_write_close_debounce(struct sx_common_data *data, int val)
6041b687201SStephen Boyd {
6051b687201SStephen Boyd 	int ret;
6061b687201SStephen Boyd 	unsigned int regval;
6071b687201SStephen Boyd 
608fc948409SGwendal Grignou 	if (val > 0)
6091b687201SStephen Boyd 		val = ilog2(val);
610fc948409SGwendal Grignou 	if (!FIELD_FIT(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val))
611fc948409SGwendal Grignou 		return -EINVAL;
612fc948409SGwendal Grignou 
6131b687201SStephen Boyd 	regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val);
6141b687201SStephen Boyd 
6151b687201SStephen Boyd 	mutex_lock(&data->mutex);
6161b687201SStephen Boyd 	ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
6171b687201SStephen Boyd 				 SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK,
6181b687201SStephen Boyd 				 regval);
6191b687201SStephen Boyd 	mutex_unlock(&data->mutex);
6201b687201SStephen Boyd 
6211b687201SStephen Boyd 	return ret;
6221b687201SStephen Boyd }
6231b687201SStephen Boyd 
sx9310_write_event_val(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,enum iio_event_info info,int val,int val2)624ad2b473eSStephen Boyd static int sx9310_write_event_val(struct iio_dev *indio_dev,
625ad2b473eSStephen Boyd 				  const struct iio_chan_spec *chan,
626ad2b473eSStephen Boyd 				  enum iio_event_type type,
627ad2b473eSStephen Boyd 				  enum iio_event_direction dir,
628ad2b473eSStephen Boyd 				  enum iio_event_info info, int val, int val2)
629ad2b473eSStephen Boyd {
630caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
631ad2b473eSStephen Boyd 
632ad2b473eSStephen Boyd 	if (chan->type != IIO_PROXIMITY)
633ad2b473eSStephen Boyd 		return -EINVAL;
634ad2b473eSStephen Boyd 
635ad2b473eSStephen Boyd 	switch (info) {
636ad2b473eSStephen Boyd 	case IIO_EV_INFO_VALUE:
637ad2b473eSStephen Boyd 		return sx9310_write_thresh(data, chan, val);
6381b687201SStephen Boyd 	case IIO_EV_INFO_PERIOD:
6391b687201SStephen Boyd 		switch (dir) {
6401b687201SStephen Boyd 		case IIO_EV_DIR_RISING:
6411b687201SStephen Boyd 			return sx9310_write_far_debounce(data, val);
6421b687201SStephen Boyd 		case IIO_EV_DIR_FALLING:
6431b687201SStephen Boyd 			return sx9310_write_close_debounce(data, val);
6441b687201SStephen Boyd 		default:
6451b687201SStephen Boyd 			return -EINVAL;
6461b687201SStephen Boyd 		}
64708f0411cSStephen Boyd 	case IIO_EV_INFO_HYSTERESIS:
64808f0411cSStephen Boyd 		return sx9310_write_hysteresis(data, chan, val);
649ad2b473eSStephen Boyd 	default:
650ad2b473eSStephen Boyd 		return -EINVAL;
651ad2b473eSStephen Boyd 	}
652ad2b473eSStephen Boyd }
653ad2b473eSStephen Boyd 
sx9310_set_samp_freq(struct sx_common_data * data,int val,int val2)654caa8ce7fSGwendal Grignou static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2)
65572ad02b1SDaniel Campello {
65672ad02b1SDaniel Campello 	int i, ret;
65772ad02b1SDaniel Campello 
65872ad02b1SDaniel Campello 	for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++)
65972ad02b1SDaniel Campello 		if (val == sx9310_samp_freq_table[i].val &&
66072ad02b1SDaniel Campello 		    val2 == sx9310_samp_freq_table[i].val2)
66172ad02b1SDaniel Campello 			break;
66272ad02b1SDaniel Campello 
66372ad02b1SDaniel Campello 	if (i == ARRAY_SIZE(sx9310_samp_freq_table))
66472ad02b1SDaniel Campello 		return -EINVAL;
66572ad02b1SDaniel Campello 
66672ad02b1SDaniel Campello 	mutex_lock(&data->mutex);
66772ad02b1SDaniel Campello 
668d9f753f3SDaniel Campello 	ret = regmap_update_bits(
669d9f753f3SDaniel Campello 		data->regmap, SX9310_REG_PROX_CTRL0,
670d9f753f3SDaniel Campello 		SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK,
671d9f753f3SDaniel Campello 		FIELD_PREP(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, i));
67272ad02b1SDaniel Campello 
67372ad02b1SDaniel Campello 	mutex_unlock(&data->mutex);
67472ad02b1SDaniel Campello 
67572ad02b1SDaniel Campello 	return ret;
67672ad02b1SDaniel Campello }
67772ad02b1SDaniel Campello 
sx9310_write_gain(struct sx_common_data * data,const struct iio_chan_spec * chan,int val)678caa8ce7fSGwendal Grignou static int sx9310_write_gain(struct sx_common_data *data,
679227c83faSStephen Boyd 			     const struct iio_chan_spec *chan, int val)
680227c83faSStephen Boyd {
681227c83faSStephen Boyd 	unsigned int gain, mask;
682227c83faSStephen Boyd 	int ret;
683227c83faSStephen Boyd 
684227c83faSStephen Boyd 	gain = ilog2(val);
685227c83faSStephen Boyd 
686227c83faSStephen Boyd 	switch (chan->channel) {
687227c83faSStephen Boyd 	case 0:
688227c83faSStephen Boyd 	case 3:
689227c83faSStephen Boyd 		mask = SX9310_REG_PROX_CTRL3_GAIN0_MASK;
690227c83faSStephen Boyd 		gain = FIELD_PREP(SX9310_REG_PROX_CTRL3_GAIN0_MASK, gain);
691227c83faSStephen Boyd 		break;
692227c83faSStephen Boyd 	case 1:
693227c83faSStephen Boyd 	case 2:
694227c83faSStephen Boyd 		mask = SX9310_REG_PROX_CTRL3_GAIN12_MASK;
695227c83faSStephen Boyd 		gain = FIELD_PREP(SX9310_REG_PROX_CTRL3_GAIN12_MASK, gain);
696227c83faSStephen Boyd 		break;
697227c83faSStephen Boyd 	default:
698227c83faSStephen Boyd 		return -EINVAL;
699227c83faSStephen Boyd 	}
700227c83faSStephen Boyd 
701227c83faSStephen Boyd 	mutex_lock(&data->mutex);
702227c83faSStephen Boyd 	ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask,
703227c83faSStephen Boyd 				 gain);
704227c83faSStephen Boyd 	mutex_unlock(&data->mutex);
705227c83faSStephen Boyd 
706227c83faSStephen Boyd 	return ret;
707227c83faSStephen Boyd }
708227c83faSStephen Boyd 
sx9310_write_raw(struct iio_dev * indio_dev,const struct iio_chan_spec * chan,int val,int val2,long mask)70972ad02b1SDaniel Campello static int sx9310_write_raw(struct iio_dev *indio_dev,
71072ad02b1SDaniel Campello 			    const struct iio_chan_spec *chan, int val, int val2,
71172ad02b1SDaniel Campello 			    long mask)
71272ad02b1SDaniel Campello {
713caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
71472ad02b1SDaniel Campello 
71572ad02b1SDaniel Campello 	if (chan->type != IIO_PROXIMITY)
71672ad02b1SDaniel Campello 		return -EINVAL;
71772ad02b1SDaniel Campello 
718227c83faSStephen Boyd 	switch (mask) {
719227c83faSStephen Boyd 	case IIO_CHAN_INFO_SAMP_FREQ:
72072ad02b1SDaniel Campello 		return sx9310_set_samp_freq(data, val, val2);
721227c83faSStephen Boyd 	case IIO_CHAN_INFO_HARDWAREGAIN:
722227c83faSStephen Boyd 		return sx9310_write_gain(data, chan, val);
723caa8ce7fSGwendal Grignou 	default:
724227c83faSStephen Boyd 		return -EINVAL;
72572ad02b1SDaniel Campello 	}
72672ad02b1SDaniel Campello }
72772ad02b1SDaniel Campello 
728caa8ce7fSGwendal Grignou static const struct sx_common_reg_default sx9310_default_regs[] = {
729d9f753f3SDaniel Campello 	{ SX9310_REG_IRQ_MSK, 0x00 },
730d9f753f3SDaniel Campello 	{ SX9310_REG_IRQ_FUNC, 0x00 },
73172ad02b1SDaniel Campello 	/*
73272ad02b1SDaniel Campello 	 * The lower 4 bits should not be set as it enable sensors measurements.
73372ad02b1SDaniel Campello 	 * Turning the detection on before the configuration values are set to
73472ad02b1SDaniel Campello 	 * good values can cause the device to return erroneous readings.
73572ad02b1SDaniel Campello 	 */
736d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL0_SCANPERIOD_15MS },
737d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL1, 0x00 },
738d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL2, SX9310_REG_PROX_CTRL2_COMBMODE_CS1_CS2 |
739d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL2_SHIELDEN_DYNAMIC },
740d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL3, SX9310_REG_PROX_CTRL3_GAIN0_X8 |
741d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL3_GAIN12_X4 },
742d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL4, SX9310_REG_PROX_CTRL4_RESOLUTION_FINEST },
743d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL5, SX9310_REG_PROX_CTRL5_RANGE_SMALL |
74472ad02b1SDaniel Campello 				 SX9310_REG_PROX_CTRL5_STARTUPSENS_CS1 |
745d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL5_RAWFILT_1P25 },
746d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL6, SX9310_REG_PROX_CTRL6_AVGTHRESH_DEFAULT },
747d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL7, SX9310_REG_PROX_CTRL7_AVGNEGFILT_2 |
748d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL7_AVGPOSFILT_512 },
749d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL8, SX9310_REG_PROX_CTRL8_9_PTHRESH_96 |
750d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL8_9_BODYTHRESH_1500 },
751d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL9, SX9310_REG_PROX_CTRL8_9_PTHRESH_28 |
752d9f753f3SDaniel Campello 				 SX9310_REG_PROX_CTRL8_9_BODYTHRESH_900 },
753d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL10, SX9310_REG_PROX_CTRL10_HYST_6PCT |
754d9f753f3SDaniel Campello 				  SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_2 },
755d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL11, 0x00 },
756d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL12, 0x00 },
757d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL13, 0x00 },
758d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL14, 0x00 },
759d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL15, 0x00 },
760d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL16, 0x00 },
761d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL17, 0x00 },
762d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL18, 0x00 },
763d9f753f3SDaniel Campello 	{ SX9310_REG_PROX_CTRL19, 0x00 },
764d9f753f3SDaniel Campello 	{ SX9310_REG_SAR_CTRL0, SX9310_REG_SAR_CTRL0_SARDEB_4_SAMPLES |
765d9f753f3SDaniel Campello 				SX9310_REG_SAR_CTRL0_SARHYST_8 },
766d9f753f3SDaniel Campello 	{ SX9310_REG_SAR_CTRL1, SX9310_REG_SAR_CTRL1_SLOPE(10781250) },
767d9f753f3SDaniel Campello 	{ SX9310_REG_SAR_CTRL2, SX9310_REG_SAR_CTRL2_SAROFFSET_DEFAULT },
76872ad02b1SDaniel Campello };
76972ad02b1SDaniel Campello 
77072ad02b1SDaniel Campello /* Activate all channels and perform an initial compensation. */
sx9310_init_compensation(struct iio_dev * indio_dev)77172ad02b1SDaniel Campello static int sx9310_init_compensation(struct iio_dev *indio_dev)
77272ad02b1SDaniel Campello {
773caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
774dc46198fSDaniel Campello 	int ret;
77572ad02b1SDaniel Campello 	unsigned int val;
77672ad02b1SDaniel Campello 	unsigned int ctrl0;
77772ad02b1SDaniel Campello 
77872ad02b1SDaniel Campello 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, &ctrl0);
779a917af2aSDaniel Campello 	if (ret)
78072ad02b1SDaniel Campello 		return ret;
78172ad02b1SDaniel Campello 
78272ad02b1SDaniel Campello 	/* run the compensation phase on all channels */
78372ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0,
784d9f753f3SDaniel Campello 			   ctrl0 | SX9310_REG_PROX_CTRL0_SENSOREN_MASK);
785a917af2aSDaniel Campello 	if (ret)
78672ad02b1SDaniel Campello 		return ret;
78772ad02b1SDaniel Campello 
788dc46198fSDaniel Campello 	ret = regmap_read_poll_timeout(data->regmap, SX9310_REG_STAT1, val,
789dc46198fSDaniel Campello 				       !(val & SX9310_REG_STAT1_COMPSTAT_MASK),
790dc46198fSDaniel Campello 				       20000, 2000000);
791caa8ce7fSGwendal Grignou 	if (ret)
792dc46198fSDaniel Campello 		return ret;
79372ad02b1SDaniel Campello 
79472ad02b1SDaniel Campello 	regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0);
79572ad02b1SDaniel Campello 	return ret;
79672ad02b1SDaniel Campello }
79772ad02b1SDaniel Campello 
798caa8ce7fSGwendal Grignou static const struct sx_common_reg_default *
sx9310_get_default_reg(struct device * dev,int idx,struct sx_common_reg_default * reg_def)7997a3605beSGwendal Grignou sx9310_get_default_reg(struct device *dev, int idx,
800caa8ce7fSGwendal Grignou 		       struct sx_common_reg_default *reg_def)
8015b19ca2cSStephen Boyd {
8026f0078aeSGwendal Grignou 	u32 combined[SX9310_NUM_CHANNELS];
8035b19ca2cSStephen Boyd 	u32 start = 0, raw = 0, pos = 0;
8046f0078aeSGwendal Grignou 	unsigned long comb_mask = 0;
8056f0078aeSGwendal Grignou 	int ret, i, count;
8066f0078aeSGwendal Grignou 	const char *res;
8075b19ca2cSStephen Boyd 
8086f0078aeSGwendal Grignou 	memcpy(reg_def, &sx9310_default_regs[idx], sizeof(*reg_def));
8095b19ca2cSStephen Boyd 	switch (reg_def->reg) {
8105b19ca2cSStephen Boyd 	case SX9310_REG_PROX_CTRL2:
8117a3605beSGwendal Grignou 		if (device_property_read_bool(dev, "semtech,cs0-ground")) {
8125b19ca2cSStephen Boyd 			reg_def->def &= ~SX9310_REG_PROX_CTRL2_SHIELDEN_MASK;
8135b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL2_SHIELDEN_GROUND;
8145b19ca2cSStephen Boyd 		}
8155b19ca2cSStephen Boyd 
8167a3605beSGwendal Grignou 		count = device_property_count_u32(dev, "semtech,combined-sensors");
8177a3605beSGwendal Grignou 		if (count < 0 || count > ARRAY_SIZE(combined))
8187a3605beSGwendal Grignou 			break;
8197a3605beSGwendal Grignou 		ret = device_property_read_u32_array(dev, "semtech,combined-sensors",
8206f0078aeSGwendal Grignou 				combined, count);
8216f0078aeSGwendal Grignou 		if (ret)
8226f0078aeSGwendal Grignou 			break;
8237a3605beSGwendal Grignou 
8247a3605beSGwendal Grignou 		for (i = 0; i < count; i++)
8255b19ca2cSStephen Boyd 			comb_mask |= BIT(combined[i]);
8265b19ca2cSStephen Boyd 
8276f0078aeSGwendal Grignou 		reg_def->def &= ~SX9310_REG_PROX_CTRL2_COMBMODE_MASK;
8285b19ca2cSStephen Boyd 		if (comb_mask == (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
8295b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL2_COMBMODE_CS0_CS1_CS2_CS3;
8305b19ca2cSStephen Boyd 		else if (comb_mask == (BIT(1) | BIT(2)))
8315b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL2_COMBMODE_CS1_CS2;
8325b19ca2cSStephen Boyd 		else if (comb_mask == (BIT(0) | BIT(1)))
8335b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL2_COMBMODE_CS0_CS1;
8345b19ca2cSStephen Boyd 		else if (comb_mask == BIT(3))
8355b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL2_COMBMODE_CS3;
8365b19ca2cSStephen Boyd 
8375b19ca2cSStephen Boyd 		break;
8385b19ca2cSStephen Boyd 	case SX9310_REG_PROX_CTRL4:
8397a3605beSGwendal Grignou 		ret = device_property_read_string(dev, "semtech,resolution", &res);
8405b19ca2cSStephen Boyd 		if (ret)
8415b19ca2cSStephen Boyd 			break;
8425b19ca2cSStephen Boyd 
8435b19ca2cSStephen Boyd 		reg_def->def &= ~SX9310_REG_PROX_CTRL4_RESOLUTION_MASK;
8445b19ca2cSStephen Boyd 		if (!strcmp(res, "coarsest"))
8455b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_COARSEST;
8465b19ca2cSStephen Boyd 		else if (!strcmp(res, "very-coarse"))
8475b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_VERY_COARSE;
8485b19ca2cSStephen Boyd 		else if (!strcmp(res, "coarse"))
8495b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_COARSE;
8505b19ca2cSStephen Boyd 		else if (!strcmp(res, "medium-coarse"))
8515b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_MEDIUM_COARSE;
8525b19ca2cSStephen Boyd 		else if (!strcmp(res, "medium"))
8535b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_MEDIUM;
8545b19ca2cSStephen Boyd 		else if (!strcmp(res, "fine"))
8555b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_FINE;
8565b19ca2cSStephen Boyd 		else if (!strcmp(res, "very-fine"))
8575b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_VERY_FINE;
8585b19ca2cSStephen Boyd 		else if (!strcmp(res, "finest"))
8595b19ca2cSStephen Boyd 			reg_def->def |= SX9310_REG_PROX_CTRL4_RESOLUTION_FINEST;
8605b19ca2cSStephen Boyd 
8615b19ca2cSStephen Boyd 		break;
8625b19ca2cSStephen Boyd 	case SX9310_REG_PROX_CTRL5:
8637a3605beSGwendal Grignou 		ret = device_property_read_u32(dev, "semtech,startup-sensor", &start);
8645b19ca2cSStephen Boyd 		if (ret) {
8655b19ca2cSStephen Boyd 			start = FIELD_GET(SX9310_REG_PROX_CTRL5_STARTUPSENS_MASK,
8665b19ca2cSStephen Boyd 					  reg_def->def);
8675b19ca2cSStephen Boyd 		}
8685b19ca2cSStephen Boyd 
8695b19ca2cSStephen Boyd 		reg_def->def &= ~SX9310_REG_PROX_CTRL5_STARTUPSENS_MASK;
8705b19ca2cSStephen Boyd 		reg_def->def |= FIELD_PREP(SX9310_REG_PROX_CTRL5_STARTUPSENS_MASK,
8715b19ca2cSStephen Boyd 					   start);
8725b19ca2cSStephen Boyd 
8737a3605beSGwendal Grignou 		ret = device_property_read_u32(dev, "semtech,proxraw-strength", &raw);
8745b19ca2cSStephen Boyd 		if (ret) {
8755b19ca2cSStephen Boyd 			raw = FIELD_GET(SX9310_REG_PROX_CTRL5_RAWFILT_MASK,
8765b19ca2cSStephen Boyd 					reg_def->def);
8775b19ca2cSStephen Boyd 		} else {
8785b19ca2cSStephen Boyd 			raw = ilog2(raw);
8795b19ca2cSStephen Boyd 		}
8805b19ca2cSStephen Boyd 
8815b19ca2cSStephen Boyd 		reg_def->def &= ~SX9310_REG_PROX_CTRL5_RAWFILT_MASK;
8825b19ca2cSStephen Boyd 		reg_def->def |= FIELD_PREP(SX9310_REG_PROX_CTRL5_RAWFILT_MASK,
8835b19ca2cSStephen Boyd 					   raw);
8845b19ca2cSStephen Boyd 		break;
8855b19ca2cSStephen Boyd 	case SX9310_REG_PROX_CTRL7:
8867a3605beSGwendal Grignou 		ret = device_property_read_u32(dev, "semtech,avg-pos-strength", &pos);
8875b19ca2cSStephen Boyd 		if (ret)
8885b19ca2cSStephen Boyd 			break;
8895b19ca2cSStephen Boyd 
890b8653affSStephen Boyd 		/* Powers of 2, except for a gap between 16 and 64 */
891b8653affSStephen Boyd 		pos = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3);
8925b19ca2cSStephen Boyd 		reg_def->def &= ~SX9310_REG_PROX_CTRL7_AVGPOSFILT_MASK;
8935b19ca2cSStephen Boyd 		reg_def->def |= FIELD_PREP(SX9310_REG_PROX_CTRL7_AVGPOSFILT_MASK,
8945b19ca2cSStephen Boyd 					   pos);
8955b19ca2cSStephen Boyd 		break;
8965b19ca2cSStephen Boyd 	}
8975b19ca2cSStephen Boyd 
8985b19ca2cSStephen Boyd 	return reg_def;
8995b19ca2cSStephen Boyd }
9005b19ca2cSStephen Boyd 
sx9310_check_whoami(struct device * dev,struct iio_dev * indio_dev)901caa8ce7fSGwendal Grignou static int sx9310_check_whoami(struct device *dev,
902caa8ce7fSGwendal Grignou 			       struct iio_dev *indio_dev)
90372ad02b1SDaniel Campello {
904caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(indio_dev);
9059b2cac94SDaniel Campello 	unsigned int long ddata;
906caa8ce7fSGwendal Grignou 	unsigned int whoami;
907caa8ce7fSGwendal Grignou 	int ret;
908caa8ce7fSGwendal Grignou 
909caa8ce7fSGwendal Grignou 	ret = regmap_read(data->regmap, SX9310_REG_WHOAMI, &whoami);
910caa8ce7fSGwendal Grignou 	if (ret)
911caa8ce7fSGwendal Grignou 		return ret;
91272ad02b1SDaniel Campello 
9139b2cac94SDaniel Campello 	ddata = (uintptr_t)device_get_match_data(dev);
914caa8ce7fSGwendal Grignou 	if (ddata != whoami)
915caa8ce7fSGwendal Grignou 		return -EINVAL;
91672ad02b1SDaniel Campello 
91772ad02b1SDaniel Campello 	switch (whoami) {
91872ad02b1SDaniel Campello 	case SX9310_WHOAMI_VALUE:
91972ad02b1SDaniel Campello 		indio_dev->name = "sx9310";
92072ad02b1SDaniel Campello 		break;
92172ad02b1SDaniel Campello 	case SX9311_WHOAMI_VALUE:
92272ad02b1SDaniel Campello 		indio_dev->name = "sx9311";
92372ad02b1SDaniel Campello 		break;
92472ad02b1SDaniel Campello 	default:
92572ad02b1SDaniel Campello 		return -ENODEV;
92672ad02b1SDaniel Campello 	}
92772ad02b1SDaniel Campello 
92872ad02b1SDaniel Campello 	return 0;
92972ad02b1SDaniel Campello }
93072ad02b1SDaniel Campello 
931caa8ce7fSGwendal Grignou static const struct sx_common_chip_info sx9310_chip_info = {
932caa8ce7fSGwendal Grignou 	.reg_stat = SX9310_REG_STAT0,
933caa8ce7fSGwendal Grignou 	.reg_irq_msk = SX9310_REG_IRQ_MSK,
934caa8ce7fSGwendal Grignou 	.reg_enable_chan = SX9310_REG_PROX_CTRL0,
935caa8ce7fSGwendal Grignou 	.reg_reset = SX9310_REG_RESET,
936f86ff748SStephen Boyd 
937caa8ce7fSGwendal Grignou 	.mask_enable_chan = SX9310_REG_STAT1_COMPSTAT_MASK,
938caa8ce7fSGwendal Grignou 	.irq_msk_offset = 3,
939caa8ce7fSGwendal Grignou 	.num_channels = SX9310_NUM_CHANNELS,
940caa8ce7fSGwendal Grignou 	.num_default_regs = ARRAY_SIZE(sx9310_default_regs),
941caa8ce7fSGwendal Grignou 
942caa8ce7fSGwendal Grignou 	.ops = {
943caa8ce7fSGwendal Grignou 		.read_prox_data = sx9310_read_prox_data,
944caa8ce7fSGwendal Grignou 		.check_whoami = sx9310_check_whoami,
945caa8ce7fSGwendal Grignou 		.init_compensation = sx9310_init_compensation,
946caa8ce7fSGwendal Grignou 		.wait_for_sample = sx9310_wait_for_sample,
947caa8ce7fSGwendal Grignou 		.get_default_reg = sx9310_get_default_reg,
948caa8ce7fSGwendal Grignou 	},
949caa8ce7fSGwendal Grignou 
950caa8ce7fSGwendal Grignou 	.iio_channels = sx9310_channels,
951caa8ce7fSGwendal Grignou 	.num_iio_channels = ARRAY_SIZE(sx9310_channels),
952caa8ce7fSGwendal Grignou 	.iio_info =  {
953caa8ce7fSGwendal Grignou 		.read_raw = sx9310_read_raw,
954caa8ce7fSGwendal Grignou 		.read_avail = sx9310_read_avail,
955caa8ce7fSGwendal Grignou 		.read_event_value = sx9310_read_event_val,
956caa8ce7fSGwendal Grignou 		.write_event_value = sx9310_write_event_val,
957caa8ce7fSGwendal Grignou 		.write_raw = sx9310_write_raw,
958caa8ce7fSGwendal Grignou 		.read_event_config = sx_common_read_event_config,
959caa8ce7fSGwendal Grignou 		.write_event_config = sx_common_write_event_config,
960caa8ce7fSGwendal Grignou 	},
961caa8ce7fSGwendal Grignou };
962f86ff748SStephen Boyd 
sx9310_probe(struct i2c_client * client)9639b2cac94SDaniel Campello static int sx9310_probe(struct i2c_client *client)
96472ad02b1SDaniel Campello {
965caa8ce7fSGwendal Grignou 	return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config);
96672ad02b1SDaniel Campello }
96772ad02b1SDaniel Campello 
sx9310_suspend(struct device * dev)96893176aceSJonathan Cameron static int sx9310_suspend(struct device *dev)
96972ad02b1SDaniel Campello {
970caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
97172ad02b1SDaniel Campello 	u8 ctrl0;
97272ad02b1SDaniel Campello 	int ret;
97372ad02b1SDaniel Campello 
97472ad02b1SDaniel Campello 	disable_irq_nosync(data->client->irq);
97572ad02b1SDaniel Campello 
97672ad02b1SDaniel Campello 	mutex_lock(&data->mutex);
97772ad02b1SDaniel Campello 	ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0,
978caa8ce7fSGwendal Grignou 			  &data->suspend_ctrl);
97972ad02b1SDaniel Campello 	if (ret)
98072ad02b1SDaniel Campello 		goto out;
98172ad02b1SDaniel Campello 
982caa8ce7fSGwendal Grignou 	ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK;
98372ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0);
98472ad02b1SDaniel Campello 	if (ret)
98572ad02b1SDaniel Campello 		goto out;
98672ad02b1SDaniel Campello 
98772ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 0);
98872ad02b1SDaniel Campello 
98972ad02b1SDaniel Campello out:
99072ad02b1SDaniel Campello 	mutex_unlock(&data->mutex);
99172ad02b1SDaniel Campello 	return ret;
99272ad02b1SDaniel Campello }
99372ad02b1SDaniel Campello 
sx9310_resume(struct device * dev)99493176aceSJonathan Cameron static int sx9310_resume(struct device *dev)
99572ad02b1SDaniel Campello {
996caa8ce7fSGwendal Grignou 	struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
99772ad02b1SDaniel Campello 	int ret;
99872ad02b1SDaniel Campello 
99972ad02b1SDaniel Campello 	mutex_lock(&data->mutex);
100072ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1);
100172ad02b1SDaniel Campello 	if (ret)
100272ad02b1SDaniel Campello 		goto out;
100372ad02b1SDaniel Campello 
100472ad02b1SDaniel Campello 	ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0,
1005caa8ce7fSGwendal Grignou 			   data->suspend_ctrl);
100672ad02b1SDaniel Campello 
100772ad02b1SDaniel Campello out:
100872ad02b1SDaniel Campello 	mutex_unlock(&data->mutex);
1009364e853cSDaniel Campello 	if (ret)
1010364e853cSDaniel Campello 		return ret;
101172ad02b1SDaniel Campello 
101272ad02b1SDaniel Campello 	enable_irq(data->client->irq);
1013364e853cSDaniel Campello 	return 0;
101472ad02b1SDaniel Campello }
101572ad02b1SDaniel Campello 
101693176aceSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume);
101772ad02b1SDaniel Campello 
101872ad02b1SDaniel Campello static const struct acpi_device_id sx9310_acpi_match[] = {
101972ad02b1SDaniel Campello 	{ "STH9310", SX9310_WHOAMI_VALUE },
102072ad02b1SDaniel Campello 	{ "STH9311", SX9311_WHOAMI_VALUE },
1021de479073SDaniel Campello 	{}
102272ad02b1SDaniel Campello };
102372ad02b1SDaniel Campello MODULE_DEVICE_TABLE(acpi, sx9310_acpi_match);
102472ad02b1SDaniel Campello 
102572ad02b1SDaniel Campello static const struct of_device_id sx9310_of_match[] = {
10269b2cac94SDaniel Campello 	{ .compatible = "semtech,sx9310", (void *)SX9310_WHOAMI_VALUE },
10279b2cac94SDaniel Campello 	{ .compatible = "semtech,sx9311", (void *)SX9311_WHOAMI_VALUE },
1028de479073SDaniel Campello 	{}
102972ad02b1SDaniel Campello };
103072ad02b1SDaniel Campello MODULE_DEVICE_TABLE(of, sx9310_of_match);
103172ad02b1SDaniel Campello 
103272ad02b1SDaniel Campello static const struct i2c_device_id sx9310_id[] = {
103372ad02b1SDaniel Campello 	{ "sx9310", SX9310_WHOAMI_VALUE },
103472ad02b1SDaniel Campello 	{ "sx9311", SX9311_WHOAMI_VALUE },
1035de479073SDaniel Campello 	{}
103672ad02b1SDaniel Campello };
103772ad02b1SDaniel Campello MODULE_DEVICE_TABLE(i2c, sx9310_id);
103872ad02b1SDaniel Campello 
103972ad02b1SDaniel Campello static struct i2c_driver sx9310_driver = {
104072ad02b1SDaniel Campello 	.driver = {
104172ad02b1SDaniel Campello 		.name	= "sx9310",
1042ef5bdbabSDaniel Campello 		.acpi_match_table = sx9310_acpi_match,
1043ef5bdbabSDaniel Campello 		.of_match_table = sx9310_of_match,
104493176aceSJonathan Cameron 		.pm = pm_sleep_ptr(&sx9310_pm_ops),
104552f5b683SDouglas Anderson 
104652f5b683SDouglas Anderson 		/*
104752f5b683SDouglas Anderson 		 * Lots of i2c transfers in probe + over 200 ms waiting in
104852f5b683SDouglas Anderson 		 * sx9310_init_compensation() mean a slow probe; prefer async
104952f5b683SDouglas Anderson 		 * so we don't delay boot if we're builtin to the kernel.
105052f5b683SDouglas Anderson 		 */
105152f5b683SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
105272ad02b1SDaniel Campello 	},
1053*7cf15f42SUwe Kleine-König 	.probe		= sx9310_probe,
105472ad02b1SDaniel Campello 	.id_table	= sx9310_id,
105572ad02b1SDaniel Campello };
105672ad02b1SDaniel Campello module_i2c_driver(sx9310_driver);
105772ad02b1SDaniel Campello 
105872ad02b1SDaniel Campello MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
105972ad02b1SDaniel Campello MODULE_AUTHOR("Daniel Campello <campello@chromium.org>");
106072ad02b1SDaniel Campello MODULE_DESCRIPTION("Driver for Semtech SX9310/SX9311 proximity sensor");
106172ad02b1SDaniel Campello MODULE_LICENSE("GPL v2");
1062caa8ce7fSGwendal Grignou MODULE_IMPORT_NS(SEMTECH_PROX);
1063