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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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, ®val);
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