1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2021 Google LLC. |
4 | * |
5 | * Driver for Semtech's SX9360 capacitive proximity/button solution. |
6 | * Based on SX9360 driver and copy of datasheet at: |
7 | * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf |
8 | */ |
9 | |
10 | #include <linux/acpi.h> |
11 | #include <linux/bits.h> |
12 | #include <linux/bitfield.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/log2.h> |
18 | #include <linux/mod_devicetable.h> |
19 | #include <linux/module.h> |
20 | #include <linux/pm.h> |
21 | #include <linux/property.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | #include <linux/iio/iio.h> |
25 | |
26 | #include "sx_common.h" |
27 | |
28 | /* Nominal Oscillator Frequency. */ |
29 | #define SX9360_FOSC_MHZ 4 |
30 | #define SX9360_FOSC_HZ (SX9360_FOSC_MHZ * 1000000) |
31 | |
32 | /* Register definitions. */ |
33 | #define SX9360_REG_IRQ_SRC SX_COMMON_REG_IRQ_SRC |
34 | #define SX9360_REG_STAT 0x01 |
35 | #define SX9360_REG_STAT_COMPSTAT_MASK GENMASK(2, 1) |
36 | #define SX9360_REG_IRQ_MSK 0x02 |
37 | #define SX9360_CONVDONE_IRQ BIT(0) |
38 | #define SX9360_FAR_IRQ BIT(2) |
39 | #define SX9360_CLOSE_IRQ BIT(3) |
40 | #define SX9360_REG_IRQ_CFG 0x03 |
41 | |
42 | #define SX9360_REG_GNRL_CTRL0 0x10 |
43 | #define SX9360_REG_GNRL_CTRL0_PHEN_MASK GENMASK(1, 0) |
44 | #define SX9360_REG_GNRL_CTRL1 0x11 |
45 | #define SX9360_REG_GNRL_CTRL1_SCANPERIOD_MASK GENMASK(2, 0) |
46 | #define SX9360_REG_GNRL_CTRL2 0x12 |
47 | #define SX9360_REG_GNRL_CTRL2_PERIOD_102MS 0x32 |
48 | #define SX9360_REG_GNRL_REG_2_PERIOD_MS(_r) \ |
49 | (((_r) * 8192) / (SX9360_FOSC_HZ / 1000)) |
50 | #define SX9360_REG_GNRL_FREQ_2_REG(_f) (((_f) * 8192) / SX9360_FOSC_HZ) |
51 | #define SX9360_REG_GNRL_REG_2_FREQ(_r) (SX9360_FOSC_HZ / ((_r) * 8192)) |
52 | |
53 | #define SX9360_REG_AFE_CTRL1 0x21 |
54 | #define SX9360_REG_AFE_CTRL1_RESFILTIN_MASK GENMASK(3, 0) |
55 | #define SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS 0 |
56 | #define SX9360_REG_AFE_PARAM0_PHR 0x22 |
57 | #define SX9360_REG_AFE_PARAM1_PHR 0x23 |
58 | #define SX9360_REG_AFE_PARAM0_PHM 0x24 |
59 | #define SX9360_REG_AFE_PARAM0_RSVD 0x08 |
60 | #define SX9360_REG_AFE_PARAM0_RESOLUTION_MASK GENMASK(2, 0) |
61 | #define SX9360_REG_AFE_PARAM0_RESOLUTION_128 0x02 |
62 | #define SX9360_REG_AFE_PARAM1_PHM 0x25 |
63 | #define SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF 0x40 |
64 | #define SX9360_REG_AFE_PARAM1_FREQ_83_33HZ 0x06 |
65 | |
66 | #define SX9360_REG_PROX_CTRL0_PHR 0x40 |
67 | #define SX9360_REG_PROX_CTRL0_PHM 0x41 |
68 | #define SX9360_REG_PROX_CTRL0_GAIN_MASK GENMASK(5, 3) |
69 | #define SX9360_REG_PROX_CTRL0_GAIN_1 0x80 |
70 | #define SX9360_REG_PROX_CTRL0_RAWFILT_MASK GENMASK(2, 0) |
71 | #define SX9360_REG_PROX_CTRL0_RAWFILT_1P50 0x01 |
72 | #define SX9360_REG_PROX_CTRL1 0x42 |
73 | #define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_MASK GENMASK(5, 3) |
74 | #define SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K 0x20 |
75 | #define SX9360_REG_PROX_CTRL2 0x43 |
76 | #define SX9360_REG_PROX_CTRL2_AVGDEB_MASK GENMASK(7, 6) |
77 | #define SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES 0x40 |
78 | #define SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K 0x20 |
79 | #define SX9360_REG_PROX_CTRL3 0x44 |
80 | #define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_MASK GENMASK(5, 3) |
81 | #define SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 0x08 |
82 | #define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK GENMASK(2, 0) |
83 | #define SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256 0x04 |
84 | #define SX9360_REG_PROX_CTRL4 0x45 |
85 | #define SX9360_REG_PROX_CTRL4_HYST_MASK GENMASK(5, 4) |
86 | #define SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK GENMASK(3, 2) |
87 | #define SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK GENMASK(1, 0) |
88 | #define SX9360_REG_PROX_CTRL5 0x46 |
89 | #define SX9360_REG_PROX_CTRL5_PROXTHRESH_32 0x08 |
90 | |
91 | #define SX9360_REG_REF_CORR0 0x60 |
92 | #define SX9360_REG_REF_CORR1 0x61 |
93 | |
94 | #define SX9360_REG_USEFUL_PHR_MSB 0x90 |
95 | #define SX9360_REG_USEFUL_PHR_LSB 0x91 |
96 | |
97 | #define SX9360_REG_OFFSET_PMR_MSB 0x92 |
98 | #define SX9360_REG_OFFSET_PMR_LSB 0x93 |
99 | |
100 | #define SX9360_REG_USEFUL_PHM_MSB 0x94 |
101 | #define SX9360_REG_USEFUL_PHM_LSB 0x95 |
102 | |
103 | #define SX9360_REG_AVG_PHM_MSB 0x96 |
104 | #define SX9360_REG_AVG_PHM_LSB 0x97 |
105 | |
106 | #define SX9360_REG_DIFF_PHM_MSB 0x98 |
107 | #define SX9360_REG_DIFF_PHM_LSB 0x99 |
108 | |
109 | #define SX9360_REG_OFFSET_PHM_MSB 0x9a |
110 | #define SX9360_REG_OFFSET_PHM_LSB 0x9b |
111 | |
112 | #define SX9360_REG_USE_FILTER_MSB 0x9a |
113 | #define SX9360_REG_USE_FILTER_LSB 0x9b |
114 | |
115 | #define SX9360_REG_RESET 0xcf |
116 | /* Write this to REG_RESET to do a soft reset. */ |
117 | #define SX9360_SOFT_RESET 0xde |
118 | |
119 | #define SX9360_REG_WHOAMI 0xfa |
120 | #define SX9360_WHOAMI_VALUE 0x60 |
121 | |
122 | #define SX9360_REG_REVISION 0xfe |
123 | |
124 | /* 2 channels, Phase Reference and Measurement. */ |
125 | #define SX9360_NUM_CHANNELS 2 |
126 | |
127 | static const struct iio_chan_spec sx9360_channels[] = { |
128 | { |
129 | .type = IIO_PROXIMITY, |
130 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
131 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
132 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), |
133 | .info_mask_separate_available = |
134 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
135 | .info_mask_shared_by_all_available = |
136 | BIT(IIO_CHAN_INFO_SAMP_FREQ), |
137 | .indexed = 1, |
138 | .address = SX9360_REG_USEFUL_PHR_MSB, |
139 | .channel = 0, |
140 | .scan_index = 0, |
141 | .scan_type = { |
142 | .sign = 's', |
143 | .realbits = 12, |
144 | .storagebits = 16, |
145 | .endianness = IIO_BE, |
146 | }, |
147 | }, |
148 | { |
149 | .type = IIO_PROXIMITY, |
150 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
151 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
152 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), |
153 | .info_mask_separate_available = |
154 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
155 | .info_mask_shared_by_all_available = |
156 | BIT(IIO_CHAN_INFO_SAMP_FREQ), |
157 | .indexed = 1, |
158 | .address = SX9360_REG_USEFUL_PHM_MSB, |
159 | .event_spec = sx_common_events, |
160 | .num_event_specs = ARRAY_SIZE(sx_common_events), |
161 | .channel = 1, |
162 | .scan_index = 1, |
163 | .scan_type = { |
164 | .sign = 's', |
165 | .realbits = 12, |
166 | .storagebits = 16, |
167 | .endianness = IIO_BE, |
168 | }, |
169 | }, |
170 | IIO_CHAN_SOFT_TIMESTAMP(2), |
171 | }; |
172 | |
173 | /* |
174 | * Each entry contains the integer part (val) and the fractional part, in micro |
175 | * seconds. It conforms to the IIO output IIO_VAL_INT_PLUS_MICRO. |
176 | * |
177 | * The frequency control register holds the period, with a ~2ms increment. |
178 | * Therefore the smallest frequency is 4MHz / (2047 * 8192), |
179 | * The fastest is 4MHz / 8192. |
180 | * The interval is not linear, but given there is 2047 possible value, |
181 | * Returns the fake increment of (Max-Min)/2047 |
182 | */ |
183 | static const struct { |
184 | int val; |
185 | int val2; |
186 | } sx9360_samp_freq_interval[] = { |
187 | { 0, 281250 }, /* 4MHz / (8192 * 2047) */ |
188 | { 0, 281250 }, |
189 | { 448, 281250 }, /* 4MHz / 8192 */ |
190 | }; |
191 | |
192 | static const struct regmap_range sx9360_writable_reg_ranges[] = { |
193 | /* |
194 | * To set COMPSTAT for compensation, even if datasheet says register is |
195 | * RO. |
196 | */ |
197 | regmap_reg_range(SX9360_REG_STAT, SX9360_REG_IRQ_CFG), |
198 | regmap_reg_range(SX9360_REG_GNRL_CTRL0, SX9360_REG_GNRL_CTRL2), |
199 | regmap_reg_range(SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_PARAM1_PHM), |
200 | regmap_reg_range(SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL5), |
201 | regmap_reg_range(SX9360_REG_REF_CORR0, SX9360_REG_REF_CORR1), |
202 | regmap_reg_range(SX9360_REG_OFFSET_PMR_MSB, SX9360_REG_OFFSET_PMR_LSB), |
203 | regmap_reg_range(SX9360_REG_RESET, SX9360_REG_RESET), |
204 | }; |
205 | |
206 | static const struct regmap_access_table sx9360_writeable_regs = { |
207 | .yes_ranges = sx9360_writable_reg_ranges, |
208 | .n_yes_ranges = ARRAY_SIZE(sx9360_writable_reg_ranges), |
209 | }; |
210 | |
211 | /* |
212 | * All allocated registers are readable, so we just list unallocated |
213 | * ones. |
214 | */ |
215 | static const struct regmap_range sx9360_non_readable_reg_ranges[] = { |
216 | regmap_reg_range(SX9360_REG_IRQ_CFG + 1, SX9360_REG_GNRL_CTRL0 - 1), |
217 | regmap_reg_range(SX9360_REG_GNRL_CTRL2 + 1, SX9360_REG_AFE_CTRL1 - 1), |
218 | regmap_reg_range(SX9360_REG_AFE_PARAM1_PHM + 1, |
219 | SX9360_REG_PROX_CTRL0_PHR - 1), |
220 | regmap_reg_range(SX9360_REG_PROX_CTRL5 + 1, SX9360_REG_REF_CORR0 - 1), |
221 | regmap_reg_range(SX9360_REG_REF_CORR1 + 1, |
222 | SX9360_REG_USEFUL_PHR_MSB - 1), |
223 | regmap_reg_range(SX9360_REG_USE_FILTER_LSB + 1, SX9360_REG_RESET - 1), |
224 | regmap_reg_range(SX9360_REG_RESET + 1, SX9360_REG_WHOAMI - 1), |
225 | regmap_reg_range(SX9360_REG_WHOAMI + 1, SX9360_REG_REVISION - 1), |
226 | }; |
227 | |
228 | static const struct regmap_access_table sx9360_readable_regs = { |
229 | .no_ranges = sx9360_non_readable_reg_ranges, |
230 | .n_no_ranges = ARRAY_SIZE(sx9360_non_readable_reg_ranges), |
231 | }; |
232 | |
233 | static const struct regmap_range sx9360_volatile_reg_ranges[] = { |
234 | regmap_reg_range(SX9360_REG_IRQ_SRC, SX9360_REG_STAT), |
235 | regmap_reg_range(SX9360_REG_USEFUL_PHR_MSB, SX9360_REG_USE_FILTER_LSB), |
236 | regmap_reg_range(SX9360_REG_WHOAMI, SX9360_REG_WHOAMI), |
237 | regmap_reg_range(SX9360_REG_REVISION, SX9360_REG_REVISION), |
238 | }; |
239 | |
240 | static const struct regmap_access_table sx9360_volatile_regs = { |
241 | .yes_ranges = sx9360_volatile_reg_ranges, |
242 | .n_yes_ranges = ARRAY_SIZE(sx9360_volatile_reg_ranges), |
243 | }; |
244 | |
245 | static const struct regmap_config sx9360_regmap_config = { |
246 | .reg_bits = 8, |
247 | .val_bits = 8, |
248 | |
249 | .max_register = SX9360_REG_REVISION, |
250 | .cache_type = REGCACHE_RBTREE, |
251 | |
252 | .wr_table = &sx9360_writeable_regs, |
253 | .rd_table = &sx9360_readable_regs, |
254 | .volatile_table = &sx9360_volatile_regs, |
255 | }; |
256 | |
257 | static int sx9360_read_prox_data(struct sx_common_data *data, |
258 | const struct iio_chan_spec *chan, |
259 | __be16 *val) |
260 | { |
261 | return regmap_bulk_read(map: data->regmap, reg: chan->address, val, val_count: sizeof(*val)); |
262 | } |
263 | |
264 | /* |
265 | * If we have no interrupt support, we have to wait for a scan period |
266 | * after enabling a channel to get a result. |
267 | */ |
268 | static int sx9360_wait_for_sample(struct sx_common_data *data) |
269 | { |
270 | int ret; |
271 | __be16 buf; |
272 | |
273 | ret = regmap_bulk_read(map: data->regmap, SX9360_REG_GNRL_CTRL1, |
274 | val: &buf, val_count: sizeof(buf)); |
275 | if (ret < 0) |
276 | return ret; |
277 | msleep(SX9360_REG_GNRL_REG_2_PERIOD_MS(be16_to_cpu(buf))); |
278 | |
279 | return 0; |
280 | } |
281 | |
282 | static int sx9360_read_gain(struct sx_common_data *data, |
283 | const struct iio_chan_spec *chan, int *val) |
284 | { |
285 | unsigned int reg, regval; |
286 | int ret; |
287 | |
288 | reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; |
289 | ret = regmap_read(map: data->regmap, reg, val: ®val); |
290 | if (ret) |
291 | return ret; |
292 | |
293 | *val = 1 << FIELD_GET(SX9360_REG_PROX_CTRL0_GAIN_MASK, regval); |
294 | |
295 | return IIO_VAL_INT; |
296 | } |
297 | |
298 | static int sx9360_read_samp_freq(struct sx_common_data *data, |
299 | int *val, int *val2) |
300 | { |
301 | int ret, divisor; |
302 | __be16 buf; |
303 | |
304 | ret = regmap_bulk_read(map: data->regmap, SX9360_REG_GNRL_CTRL1, |
305 | val: &buf, val_count: sizeof(buf)); |
306 | if (ret < 0) |
307 | return ret; |
308 | divisor = be16_to_cpu(buf); |
309 | if (divisor == 0) { |
310 | *val = 0; |
311 | return IIO_VAL_INT; |
312 | } |
313 | |
314 | *val = SX9360_FOSC_HZ; |
315 | *val2 = divisor * 8192; |
316 | |
317 | return IIO_VAL_FRACTIONAL; |
318 | } |
319 | |
320 | static int sx9360_read_raw(struct iio_dev *indio_dev, |
321 | const struct iio_chan_spec *chan, |
322 | int *val, int *val2, long mask) |
323 | { |
324 | struct sx_common_data *data = iio_priv(indio_dev); |
325 | |
326 | switch (mask) { |
327 | case IIO_CHAN_INFO_RAW: |
328 | iio_device_claim_direct_scoped(return -EBUSY, indio_dev) |
329 | return sx_common_read_proximity(data, chan, val); |
330 | unreachable(); |
331 | case IIO_CHAN_INFO_HARDWAREGAIN: |
332 | iio_device_claim_direct_scoped(return -EBUSY, indio_dev) |
333 | return sx9360_read_gain(data, chan, val); |
334 | unreachable(); |
335 | case IIO_CHAN_INFO_SAMP_FREQ: |
336 | return sx9360_read_samp_freq(data, val, val2); |
337 | default: |
338 | return -EINVAL; |
339 | } |
340 | } |
341 | |
342 | static const char *sx9360_channel_labels[SX9360_NUM_CHANNELS] = { |
343 | "reference" , "main" , |
344 | }; |
345 | |
346 | static int sx9360_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, |
347 | char *label) |
348 | { |
349 | return sysfs_emit(buf: label, fmt: "%s\n" , sx9360_channel_labels[chan->channel]); |
350 | } |
351 | |
352 | static const int sx9360_gain_vals[] = { 1, 2, 4, 8 }; |
353 | |
354 | static int sx9360_read_avail(struct iio_dev *indio_dev, |
355 | struct iio_chan_spec const *chan, |
356 | const int **vals, int *type, int *length, |
357 | long mask) |
358 | { |
359 | if (chan->type != IIO_PROXIMITY) |
360 | return -EINVAL; |
361 | |
362 | switch (mask) { |
363 | case IIO_CHAN_INFO_HARDWAREGAIN: |
364 | *type = IIO_VAL_INT; |
365 | *length = ARRAY_SIZE(sx9360_gain_vals); |
366 | *vals = sx9360_gain_vals; |
367 | return IIO_AVAIL_LIST; |
368 | case IIO_CHAN_INFO_SAMP_FREQ: |
369 | *type = IIO_VAL_INT_PLUS_MICRO; |
370 | *length = ARRAY_SIZE(sx9360_samp_freq_interval) * 2; |
371 | *vals = (int *)sx9360_samp_freq_interval; |
372 | return IIO_AVAIL_RANGE; |
373 | default: |
374 | return -EINVAL; |
375 | } |
376 | } |
377 | |
378 | static int sx9360_set_samp_freq(struct sx_common_data *data, |
379 | int val, int val2) |
380 | { |
381 | int reg; |
382 | __be16 buf; |
383 | |
384 | reg = val * 8192 / SX9360_FOSC_HZ + val2 * 8192 / (SX9360_FOSC_MHZ); |
385 | buf = cpu_to_be16(reg); |
386 | guard(mutex)(T: &data->mutex); |
387 | |
388 | return regmap_bulk_write(map: data->regmap, SX9360_REG_GNRL_CTRL1, val: &buf, |
389 | val_count: sizeof(buf)); |
390 | } |
391 | |
392 | static int sx9360_read_thresh(struct sx_common_data *data, int *val) |
393 | { |
394 | unsigned int regval; |
395 | int ret; |
396 | |
397 | ret = regmap_read(map: data->regmap, SX9360_REG_PROX_CTRL5, val: ®val); |
398 | if (ret) |
399 | return ret; |
400 | |
401 | if (regval <= 1) |
402 | *val = regval; |
403 | else |
404 | *val = (regval * regval) / 2; |
405 | |
406 | return IIO_VAL_INT; |
407 | } |
408 | |
409 | static int sx9360_read_hysteresis(struct sx_common_data *data, int *val) |
410 | { |
411 | unsigned int regval, pthresh; |
412 | int ret; |
413 | |
414 | ret = sx9360_read_thresh(data, val: &pthresh); |
415 | if (ret < 0) |
416 | return ret; |
417 | |
418 | ret = regmap_read(map: data->regmap, SX9360_REG_PROX_CTRL4, val: ®val); |
419 | if (ret) |
420 | return ret; |
421 | |
422 | regval = FIELD_GET(SX9360_REG_PROX_CTRL4_HYST_MASK, regval); |
423 | if (!regval) |
424 | *val = 0; |
425 | else |
426 | *val = pthresh >> (5 - regval); |
427 | |
428 | return IIO_VAL_INT; |
429 | } |
430 | |
431 | static int sx9360_read_far_debounce(struct sx_common_data *data, int *val) |
432 | { |
433 | unsigned int regval; |
434 | int ret; |
435 | |
436 | ret = regmap_read(map: data->regmap, SX9360_REG_PROX_CTRL4, val: ®val); |
437 | if (ret) |
438 | return ret; |
439 | |
440 | regval = FIELD_GET(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, regval); |
441 | if (regval) |
442 | *val = 1 << regval; |
443 | else |
444 | *val = 0; |
445 | |
446 | return IIO_VAL_INT; |
447 | } |
448 | |
449 | static int sx9360_read_close_debounce(struct sx_common_data *data, int *val) |
450 | { |
451 | unsigned int regval; |
452 | int ret; |
453 | |
454 | ret = regmap_read(map: data->regmap, SX9360_REG_PROX_CTRL4, val: ®val); |
455 | if (ret) |
456 | return ret; |
457 | |
458 | regval = FIELD_GET(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, regval); |
459 | if (regval) |
460 | *val = 1 << regval; |
461 | else |
462 | *val = 0; |
463 | |
464 | return IIO_VAL_INT; |
465 | } |
466 | |
467 | static int sx9360_read_event_val(struct iio_dev *indio_dev, |
468 | const struct iio_chan_spec *chan, |
469 | enum iio_event_type type, |
470 | enum iio_event_direction dir, |
471 | enum iio_event_info info, int *val, int *val2) |
472 | { |
473 | struct sx_common_data *data = iio_priv(indio_dev); |
474 | |
475 | if (chan->type != IIO_PROXIMITY) |
476 | return -EINVAL; |
477 | |
478 | switch (info) { |
479 | case IIO_EV_INFO_VALUE: |
480 | return sx9360_read_thresh(data, val); |
481 | case IIO_EV_INFO_PERIOD: |
482 | switch (dir) { |
483 | case IIO_EV_DIR_RISING: |
484 | return sx9360_read_far_debounce(data, val); |
485 | case IIO_EV_DIR_FALLING: |
486 | return sx9360_read_close_debounce(data, val); |
487 | default: |
488 | return -EINVAL; |
489 | } |
490 | case IIO_EV_INFO_HYSTERESIS: |
491 | return sx9360_read_hysteresis(data, val); |
492 | default: |
493 | return -EINVAL; |
494 | } |
495 | } |
496 | |
497 | static int sx9360_write_thresh(struct sx_common_data *data, int _val) |
498 | { |
499 | unsigned int val = _val; |
500 | |
501 | if (val >= 1) |
502 | val = int_sqrt(2 * val); |
503 | |
504 | if (val > 0xff) |
505 | return -EINVAL; |
506 | |
507 | guard(mutex)(T: &data->mutex); |
508 | return regmap_write(map: data->regmap, SX9360_REG_PROX_CTRL5, val); |
509 | } |
510 | |
511 | static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) |
512 | { |
513 | unsigned int hyst, val = _val; |
514 | int ret, pthresh; |
515 | |
516 | ret = sx9360_read_thresh(data, val: &pthresh); |
517 | if (ret < 0) |
518 | return ret; |
519 | |
520 | if (val == 0) |
521 | hyst = 0; |
522 | else if (val >= pthresh >> 2) |
523 | hyst = 3; |
524 | else if (val >= pthresh >> 3) |
525 | hyst = 2; |
526 | else if (val >= pthresh >> 4) |
527 | hyst = 1; |
528 | else |
529 | return -EINVAL; |
530 | |
531 | hyst = FIELD_PREP(SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); |
532 | guard(mutex)(T: &data->mutex); |
533 | return regmap_update_bits(map: data->regmap, SX9360_REG_PROX_CTRL4, |
534 | SX9360_REG_PROX_CTRL4_HYST_MASK, val: hyst); |
535 | } |
536 | |
537 | static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) |
538 | { |
539 | unsigned int regval, val = _val; |
540 | |
541 | if (val > 0) |
542 | val = ilog2(val); |
543 | if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val)) |
544 | return -EINVAL; |
545 | |
546 | regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val); |
547 | |
548 | guard(mutex)(T: &data->mutex); |
549 | return regmap_update_bits(map: data->regmap, SX9360_REG_PROX_CTRL4, |
550 | SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, |
551 | val: regval); |
552 | } |
553 | |
554 | static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) |
555 | { |
556 | unsigned int regval, val = _val; |
557 | |
558 | if (val > 0) |
559 | val = ilog2(val); |
560 | if (!FIELD_FIT(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val)) |
561 | return -EINVAL; |
562 | |
563 | regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val); |
564 | |
565 | guard(mutex)(T: &data->mutex); |
566 | return regmap_update_bits(map: data->regmap, SX9360_REG_PROX_CTRL4, |
567 | SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, |
568 | val: regval); |
569 | } |
570 | |
571 | static int sx9360_write_event_val(struct iio_dev *indio_dev, |
572 | const struct iio_chan_spec *chan, |
573 | enum iio_event_type type, |
574 | enum iio_event_direction dir, |
575 | enum iio_event_info info, int val, int val2) |
576 | { |
577 | struct sx_common_data *data = iio_priv(indio_dev); |
578 | |
579 | if (chan->type != IIO_PROXIMITY) |
580 | return -EINVAL; |
581 | |
582 | switch (info) { |
583 | case IIO_EV_INFO_VALUE: |
584 | return sx9360_write_thresh(data, val: val); |
585 | case IIO_EV_INFO_PERIOD: |
586 | switch (dir) { |
587 | case IIO_EV_DIR_RISING: |
588 | return sx9360_write_far_debounce(data, val: val); |
589 | case IIO_EV_DIR_FALLING: |
590 | return sx9360_write_close_debounce(data, val: val); |
591 | default: |
592 | return -EINVAL; |
593 | } |
594 | case IIO_EV_INFO_HYSTERESIS: |
595 | return sx9360_write_hysteresis(data, val: val); |
596 | default: |
597 | return -EINVAL; |
598 | } |
599 | } |
600 | |
601 | static int sx9360_write_gain(struct sx_common_data *data, |
602 | const struct iio_chan_spec *chan, int val) |
603 | { |
604 | unsigned int gain, reg; |
605 | |
606 | gain = ilog2(val); |
607 | reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; |
608 | gain = FIELD_PREP(SX9360_REG_PROX_CTRL0_GAIN_MASK, gain); |
609 | |
610 | guard(mutex)(T: &data->mutex); |
611 | return regmap_update_bits(map: data->regmap, reg, |
612 | SX9360_REG_PROX_CTRL0_GAIN_MASK, |
613 | val: gain); |
614 | } |
615 | |
616 | static int sx9360_write_raw(struct iio_dev *indio_dev, |
617 | const struct iio_chan_spec *chan, int val, int val2, |
618 | long mask) |
619 | { |
620 | struct sx_common_data *data = iio_priv(indio_dev); |
621 | |
622 | switch (mask) { |
623 | case IIO_CHAN_INFO_SAMP_FREQ: |
624 | return sx9360_set_samp_freq(data, val, val2); |
625 | case IIO_CHAN_INFO_HARDWAREGAIN: |
626 | return sx9360_write_gain(data, chan, val); |
627 | default: |
628 | return -EINVAL; |
629 | } |
630 | } |
631 | |
632 | static const struct sx_common_reg_default sx9360_default_regs[] = { |
633 | { SX9360_REG_IRQ_MSK, 0x00 }, |
634 | { SX9360_REG_IRQ_CFG, 0x00, "irq_cfg" }, |
635 | /* |
636 | * The lower 2 bits should not be set as it enable sensors measurements. |
637 | * Turning the detection on before the configuration values are set to |
638 | * good values can cause the device to return erroneous readings. |
639 | */ |
640 | { SX9360_REG_GNRL_CTRL0, 0x00, "gnrl_ctrl0" }, |
641 | { SX9360_REG_GNRL_CTRL1, 0x00, "gnrl_ctrl1" }, |
642 | { SX9360_REG_GNRL_CTRL2, SX9360_REG_GNRL_CTRL2_PERIOD_102MS, "gnrl_ctrl2" }, |
643 | |
644 | { SX9360_REG_AFE_CTRL1, SX9360_REG_AFE_CTRL1_RESFILTIN_0OHMS, "afe_ctrl0" }, |
645 | { SX9360_REG_AFE_PARAM0_PHR, SX9360_REG_AFE_PARAM0_RSVD | |
646 | SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phr" }, |
647 | { SX9360_REG_AFE_PARAM1_PHR, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | |
648 | SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phr" }, |
649 | { SX9360_REG_AFE_PARAM0_PHM, SX9360_REG_AFE_PARAM0_RSVD | |
650 | SX9360_REG_AFE_PARAM0_RESOLUTION_128, "afe_param0_phm" }, |
651 | { SX9360_REG_AFE_PARAM1_PHM, SX9360_REG_AFE_PARAM1_AGAIN_PHM_6PF | |
652 | SX9360_REG_AFE_PARAM1_FREQ_83_33HZ, "afe_param1_phm" }, |
653 | |
654 | { SX9360_REG_PROX_CTRL0_PHR, SX9360_REG_PROX_CTRL0_GAIN_1 | |
655 | SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phr" }, |
656 | { SX9360_REG_PROX_CTRL0_PHM, SX9360_REG_PROX_CTRL0_GAIN_1 | |
657 | SX9360_REG_PROX_CTRL0_RAWFILT_1P50, "prox_ctrl0_phm" }, |
658 | { SX9360_REG_PROX_CTRL1, SX9360_REG_PROX_CTRL1_AVGNEG_THRESH_16K, "prox_ctrl1" }, |
659 | { SX9360_REG_PROX_CTRL2, SX9360_REG_PROX_CTRL2_AVGDEB_2SAMPLES | |
660 | SX9360_REG_PROX_CTRL2_AVGPOS_THRESH_16K, "prox_ctrl2" }, |
661 | { SX9360_REG_PROX_CTRL3, SX9360_REG_PROX_CTRL3_AVGNEG_FILT_2 | |
662 | SX9360_REG_PROX_CTRL3_AVGPOS_FILT_256, "prox_ctrl3" }, |
663 | { SX9360_REG_PROX_CTRL4, 0x00, "prox_ctrl4" }, |
664 | { SX9360_REG_PROX_CTRL5, SX9360_REG_PROX_CTRL5_PROXTHRESH_32, "prox_ctrl5" }, |
665 | }; |
666 | |
667 | /* Activate all channels and perform an initial compensation. */ |
668 | static int sx9360_init_compensation(struct iio_dev *indio_dev) |
669 | { |
670 | struct sx_common_data *data = iio_priv(indio_dev); |
671 | unsigned int val; |
672 | int ret; |
673 | |
674 | /* run the compensation phase on all channels */ |
675 | ret = regmap_update_bits(map: data->regmap, SX9360_REG_STAT, |
676 | SX9360_REG_STAT_COMPSTAT_MASK, |
677 | SX9360_REG_STAT_COMPSTAT_MASK); |
678 | if (ret) |
679 | return ret; |
680 | |
681 | return regmap_read_poll_timeout(data->regmap, SX9360_REG_STAT, val, |
682 | !(val & SX9360_REG_STAT_COMPSTAT_MASK), |
683 | 20000, 2000000); |
684 | } |
685 | |
686 | static const struct sx_common_reg_default * |
687 | sx9360_get_default_reg(struct device *dev, int idx, |
688 | struct sx_common_reg_default *reg_def) |
689 | { |
690 | u32 raw = 0, pos = 0; |
691 | int ret; |
692 | |
693 | memcpy(reg_def, &sx9360_default_regs[idx], sizeof(*reg_def)); |
694 | switch (reg_def->reg) { |
695 | case SX9360_REG_AFE_CTRL1: |
696 | ret = device_property_read_u32(dev, |
697 | propname: "semtech,input-precharge-resistor-ohms" , |
698 | val: &raw); |
699 | if (ret) |
700 | break; |
701 | |
702 | reg_def->def &= ~SX9360_REG_AFE_CTRL1_RESFILTIN_MASK; |
703 | reg_def->def |= FIELD_PREP(SX9360_REG_AFE_CTRL1_RESFILTIN_MASK, |
704 | raw / 2000); |
705 | break; |
706 | case SX9360_REG_AFE_PARAM0_PHR: |
707 | case SX9360_REG_AFE_PARAM0_PHM: |
708 | ret = device_property_read_u32(dev, propname: "semtech,resolution" , val: &raw); |
709 | if (ret) |
710 | break; |
711 | |
712 | raw = ilog2(raw) - 3; |
713 | |
714 | reg_def->def &= ~SX9360_REG_AFE_PARAM0_RESOLUTION_MASK; |
715 | reg_def->def |= FIELD_PREP(SX9360_REG_AFE_PARAM0_RESOLUTION_MASK, raw); |
716 | break; |
717 | case SX9360_REG_PROX_CTRL0_PHR: |
718 | case SX9360_REG_PROX_CTRL0_PHM: |
719 | ret = device_property_read_u32(dev, propname: "semtech,proxraw-strength" , val: &raw); |
720 | if (ret) |
721 | break; |
722 | |
723 | reg_def->def &= ~SX9360_REG_PROX_CTRL0_RAWFILT_MASK; |
724 | reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL0_RAWFILT_MASK, raw); |
725 | break; |
726 | case SX9360_REG_PROX_CTRL3: |
727 | ret = device_property_read_u32(dev, propname: "semtech,avg-pos-strength" , |
728 | val: &pos); |
729 | if (ret) |
730 | break; |
731 | |
732 | /* Powers of 2, except for a gap between 16 and 64 */ |
733 | raw = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3); |
734 | reg_def->def &= ~SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK; |
735 | reg_def->def |= FIELD_PREP(SX9360_REG_PROX_CTRL3_AVGPOS_FILT_MASK, raw); |
736 | break; |
737 | } |
738 | |
739 | return reg_def; |
740 | } |
741 | |
742 | static int sx9360_check_whoami(struct device *dev, struct iio_dev *indio_dev) |
743 | { |
744 | /* |
745 | * Only one sensor for this driver. Assuming the device tree |
746 | * is correct, just set the sensor name. |
747 | */ |
748 | indio_dev->name = "sx9360" ; |
749 | return 0; |
750 | } |
751 | |
752 | static const struct sx_common_chip_info sx9360_chip_info = { |
753 | .reg_stat = SX9360_REG_STAT, |
754 | .reg_irq_msk = SX9360_REG_IRQ_MSK, |
755 | .reg_enable_chan = SX9360_REG_GNRL_CTRL0, |
756 | .reg_reset = SX9360_REG_RESET, |
757 | |
758 | .mask_enable_chan = SX9360_REG_GNRL_CTRL0_PHEN_MASK, |
759 | .stat_offset = 2, |
760 | .num_channels = SX9360_NUM_CHANNELS, |
761 | .num_default_regs = ARRAY_SIZE(sx9360_default_regs), |
762 | |
763 | .ops = { |
764 | .read_prox_data = sx9360_read_prox_data, |
765 | .check_whoami = sx9360_check_whoami, |
766 | .init_compensation = sx9360_init_compensation, |
767 | .wait_for_sample = sx9360_wait_for_sample, |
768 | .get_default_reg = sx9360_get_default_reg, |
769 | }, |
770 | |
771 | .iio_channels = sx9360_channels, |
772 | .num_iio_channels = ARRAY_SIZE(sx9360_channels), |
773 | .iio_info = { |
774 | .read_raw = sx9360_read_raw, |
775 | .read_avail = sx9360_read_avail, |
776 | .read_label = sx9360_read_label, |
777 | .read_event_value = sx9360_read_event_val, |
778 | .write_event_value = sx9360_write_event_val, |
779 | .write_raw = sx9360_write_raw, |
780 | .read_event_config = sx_common_read_event_config, |
781 | .write_event_config = sx_common_write_event_config, |
782 | }, |
783 | }; |
784 | |
785 | static int sx9360_probe(struct i2c_client *client) |
786 | { |
787 | return sx_common_probe(client, chip_info: &sx9360_chip_info, regmap_config: &sx9360_regmap_config); |
788 | } |
789 | |
790 | static int sx9360_suspend(struct device *dev) |
791 | { |
792 | struct sx_common_data *data = iio_priv(indio_dev: dev_get_drvdata(dev)); |
793 | unsigned int regval; |
794 | int ret; |
795 | |
796 | disable_irq_nosync(irq: data->client->irq); |
797 | |
798 | guard(mutex)(T: &data->mutex); |
799 | ret = regmap_read(map: data->regmap, SX9360_REG_GNRL_CTRL0, val: ®val); |
800 | if (ret < 0) |
801 | return ret; |
802 | |
803 | data->suspend_ctrl = |
804 | FIELD_GET(SX9360_REG_GNRL_CTRL0_PHEN_MASK, regval); |
805 | |
806 | |
807 | /* Disable all phases, send the device to sleep. */ |
808 | return regmap_write(map: data->regmap, SX9360_REG_GNRL_CTRL0, val: 0); |
809 | } |
810 | |
811 | static int sx9360_resume(struct device *dev) |
812 | { |
813 | struct sx_common_data *data = iio_priv(indio_dev: dev_get_drvdata(dev)); |
814 | |
815 | scoped_guard(mutex, &data->mutex) { |
816 | int ret = regmap_update_bits(map: data->regmap, |
817 | SX9360_REG_GNRL_CTRL0, |
818 | SX9360_REG_GNRL_CTRL0_PHEN_MASK, |
819 | val: data->suspend_ctrl); |
820 | if (ret) |
821 | return ret; |
822 | } |
823 | enable_irq(irq: data->client->irq); |
824 | return 0; |
825 | } |
826 | |
827 | static DEFINE_SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume); |
828 | |
829 | static const struct acpi_device_id sx9360_acpi_match[] = { |
830 | { "STH9360" , SX9360_WHOAMI_VALUE }, |
831 | { "SAMM0208" , SX9360_WHOAMI_VALUE }, |
832 | { } |
833 | }; |
834 | MODULE_DEVICE_TABLE(acpi, sx9360_acpi_match); |
835 | |
836 | static const struct of_device_id sx9360_of_match[] = { |
837 | { .compatible = "semtech,sx9360" , (void *)SX9360_WHOAMI_VALUE }, |
838 | { } |
839 | }; |
840 | MODULE_DEVICE_TABLE(of, sx9360_of_match); |
841 | |
842 | static const struct i2c_device_id sx9360_id[] = { |
843 | {"sx9360" , SX9360_WHOAMI_VALUE }, |
844 | { } |
845 | }; |
846 | MODULE_DEVICE_TABLE(i2c, sx9360_id); |
847 | |
848 | static struct i2c_driver sx9360_driver = { |
849 | .driver = { |
850 | .name = "sx9360" , |
851 | .acpi_match_table = sx9360_acpi_match, |
852 | .of_match_table = sx9360_of_match, |
853 | .pm = pm_sleep_ptr(&sx9360_pm_ops), |
854 | |
855 | /* |
856 | * Lots of i2c transfers in probe + over 200 ms waiting in |
857 | * sx9360_init_compensation() mean a slow probe; prefer async |
858 | * so we don't delay boot if we're builtin to the kernel. |
859 | */ |
860 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
861 | }, |
862 | .probe = sx9360_probe, |
863 | .id_table = sx9360_id, |
864 | }; |
865 | module_i2c_driver(sx9360_driver); |
866 | |
867 | MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>" ); |
868 | MODULE_DESCRIPTION("Driver for Semtech SX9360 proximity sensor" ); |
869 | MODULE_LICENSE("GPL v2" ); |
870 | MODULE_IMPORT_NS(SEMTECH_PROX); |
871 | |