diff options
Diffstat (limited to 'drivers/iio/proximity')
-rw-r--r-- | drivers/iio/proximity/Kconfig | 27 | ||||
-rw-r--r-- | drivers/iio/proximity/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/as3935.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/aw96103.c | 846 | ||||
-rw-r--r-- | drivers/iio/proximity/cros_ec_mkbp_proximity.c | 8 | ||||
-rw-r--r-- | drivers/iio/proximity/hx9023s.c | 1224 | ||||
-rw-r--r-- | drivers/iio/proximity/irsd200.c | 7 | ||||
-rw-r--r-- | drivers/iio/proximity/isl29501.c | 5 | ||||
-rw-r--r-- | drivers/iio/proximity/mb1232.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 8 | ||||
-rw-r--r-- | drivers/iio/proximity/rfd77402.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/srf04.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/srf08.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/sx9310.c | 116 | ||||
-rw-r--r-- | drivers/iio/proximity/sx9324.c | 205 | ||||
-rw-r--r-- | drivers/iio/proximity/sx9360.c | 123 | ||||
-rw-r--r-- | drivers/iio/proximity/sx9500.c | 25 | ||||
-rw-r--r-- | drivers/iio/proximity/sx_common.c | 48 | ||||
-rw-r--r-- | drivers/iio/proximity/sx_common.h | 8 | ||||
-rw-r--r-- | drivers/iio/proximity/vcnl3020.c | 2 | ||||
-rw-r--r-- | drivers/iio/proximity/vl53l0x-i2c.c | 176 |
21 files changed, 2476 insertions, 364 deletions
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 2ca3b0bc5eba..a562a78b7d0d 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -32,6 +32,20 @@ config CROS_EC_MKBP_PROXIMITY To compile this driver as a module, choose M here: the module will be called cros_ec_mkbp_proximity. +config HX9023S + tristate "TYHX HX9023S SAR sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for TYHX HX9023S capacitive SAR sensor. + This driver supports the TYHX HX9023S capacitive + SAR sensors. This sensors is used for proximity detection applications. + + To compile this driver as a module, choose M here: the + module will be called hx9023s. + config IRSD200 tristate "Murata IRS-D200 PIR sensor" select IIO_BUFFER @@ -72,6 +86,8 @@ config LIDAR_LITE_V2 config MB1232 tristate "MaxSonar I2CXL family ultrasonic sensors" depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say Y to build a driver for the ultrasonic sensors I2CXL of MaxBotix which have an i2c interface. It can be used to measure @@ -219,4 +235,15 @@ config VL53L0X_I2C To compile this driver as a module, choose M here: the module will be called vl53l0x-i2c. +config AW96103 + tristate "AW96103/AW96105 Awinic proximity sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for Awinic's AW96103/AW96105 capacitive + proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called aw96103. + endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index f36598380446..c5e76995764a 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -6,6 +6,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o +obj-$(CONFIG_HX9023S) += hx9023s.o obj-$(CONFIG_IRSD200) += irsd200.o obj-$(CONFIG_ISL29501) += isl29501.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o @@ -21,4 +22,5 @@ obj-$(CONFIG_SX_COMMON) += sx_common.o obj-$(CONFIG_SX9500) += sx9500.o obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o +obj-$(CONFIG_AW96103) += aw96103.o diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 96fa97451cbf..9d3caf2bef18 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -63,7 +63,7 @@ struct as3935_state { /* Ensure timestamp is naturally aligned */ struct { u8 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 buf[2] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c new file mode 100644 index 000000000000..3472a2c36e44 --- /dev/null +++ b/drivers/iio/proximity/aw96103.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AWINIC aw96103 proximity sensor driver + * + * Author: Wang Shuaijie <wangshuaijie@awinic.com> + * + * Copyright (c) 2024 awinic Technology CO., LTD + */ +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/unaligned.h> + +#define AW_DATA_PROCESS_FACTOR 1024 +#define AW96103_CHIP_ID 0xa961 +#define AW96103_BIN_VALID_DATA_OFFSET 64 +#define AW96103_BIN_DATA_LEN_OFFSET 16 +#define AW96103_BIN_DATA_REG_NUM_SIZE 4 +#define AW96103_BIN_CHIP_TYPE_SIZE 8 +#define AW96103_BIN_CHIP_TYPE_OFFSET 24 + +#define AW96103_REG_SCANCTRL0 0x0000 +#define AW96103_REG_STAT0 0x0090 +#define AW96103_REG_BLFILT_CH0 0x00A8 +#define AW96103_REG_BLRSTRNG_CH0 0x00B4 +#define AW96103_REG_DIFF_CH0 0x0240 +#define AW96103_REG_FWVER2 0x0410 +#define AW96103_REG_CMD 0xF008 +#define AW96103_REG_IRQSRC 0xF080 +#define AW96103_REG_IRQEN 0xF084 +#define AW96103_REG_RESET 0xFF0C +#define AW96103_REG_CHIPID 0xFF10 +#define AW96103_REG_EEDA0 0x0408 +#define AW96103_REG_EEDA1 0x040C +#define AW96103_REG_PROXCTRL_CH0 0x00B0 +#define AW96103_REG_PROXTH0_CH0 0x00B8 +#define AW96103_PROXTH_CH_STEP 0x3C +#define AW96103_THHYST_MASK GENMASK(13, 12) +#define AW96103_INDEB_MASK GENMASK(11, 10) +#define AW96103_OUTDEB_MASK GENMASK(9, 8) +#define AW96103_INITOVERIRQ_MASK BIT(0) +#define AW96103_BLFILT_CH_STEP 0x3C +#define AW96103_BLRSTRNG_MASK GENMASK(5, 0) +#define AW96103_CHIPID_MASK GENMASK(31, 16) +#define AW96103_BLERRTRIG_MASK BIT(25) +#define AW96103_CHAN_EN_MASK GENMASK(5, 0) +#define AW96103_REG_PROXCTRL_CH(x) \ + (AW96103_REG_PROXCTRL_CH0 + (x) * AW96103_PROXTH_CH_STEP) + +#define AW96103_REG_PROXTH0_CH(x) \ + (AW96103_REG_PROXTH0_CH0 + (x) * AW96103_PROXTH_CH_STEP) + +/** + * struct aw_bin - Store the data obtained from parsing the configuration file. + * @chip_type: Frame header information-chip type + * @valid_data_len: Length of valid data obtained after parsing + * @valid_data_addr: The offset address of the valid data obtained + * after parsing relative to info + * @len: The size of the bin file obtained from the firmware + * @data: Store the bin file obtained from the firmware + */ +struct aw_bin { + unsigned char chip_type[8]; + unsigned int valid_data_len; + unsigned int valid_data_addr; + unsigned int len; + unsigned char data[] __counted_by(len); +}; + +enum aw96103_sar_vers { + AW96103 = 2, + AW96103A = 6, + AW96103B = 0xa, +}; + +enum aw96103_operation_mode { + AW96103_ACTIVE_MODE = 1, + AW96103_SLEEP_MODE = 2, + AW96103_DEEPSLEEP_MODE = 3, + AW96103B_DEEPSLEEP_MODE = 4, +}; + +enum aw96103_sensor_type { + AW96103_VAL, + AW96105_VAL, +}; + +struct aw_channels_info { + bool used; + unsigned int old_irq_status; +}; + +struct aw_chip_info { + const char *name; + struct iio_chan_spec const *channels; + int num_channels; +}; + +struct aw96103 { + unsigned int hostirqen; + struct regmap *regmap; + struct device *dev; + /* + * There is one more logical channel than the actual channels, + * and the extra logical channel is used for temperature detection + * but not for status detection. The specific channel used for + * temperature detection is determined by the register configuration. + */ + struct aw_channels_info channels_arr[6]; + unsigned int max_channels; + unsigned int chan_en; +}; + +static const unsigned int aw96103_reg_default[] = { + 0x0000, 0x00003f3f, 0x0004, 0x00000064, 0x0008, 0x0017c11e, + 0x000c, 0x05000000, 0x0010, 0x00093ffd, 0x0014, 0x19240009, + 0x0018, 0xd81c0207, 0x001c, 0xff000000, 0x0020, 0x00241900, + 0x0024, 0x00093ff7, 0x0028, 0x58020009, 0x002c, 0xd81c0207, + 0x0030, 0xff000000, 0x0034, 0x00025800, 0x0038, 0x00093fdf, + 0x003c, 0x7d3b0009, 0x0040, 0xd81c0207, 0x0044, 0xff000000, + 0x0048, 0x003b7d00, 0x004c, 0x00093f7f, 0x0050, 0xe9310009, + 0x0054, 0xd81c0207, 0x0058, 0xff000000, 0x005c, 0x0031e900, + 0x0060, 0x00093dff, 0x0064, 0x1a0c0009, 0x0068, 0xd81c0207, + 0x006c, 0xff000000, 0x0070, 0x000c1a00, 0x0074, 0x80093fff, + 0x0078, 0x043d0009, 0x007c, 0xd81c0207, 0x0080, 0xff000000, + 0x0084, 0x003d0400, 0x00a0, 0xe6400000, 0x00a4, 0x00000000, + 0x00a8, 0x010408d2, 0x00ac, 0x00000000, 0x00b0, 0x00000000, + 0x00b8, 0x00005fff, 0x00bc, 0x00000000, 0x00c0, 0x00000000, + 0x00c4, 0x00000000, 0x00c8, 0x00000000, 0x00cc, 0x00000000, + 0x00d0, 0x00000000, 0x00d4, 0x00000000, 0x00d8, 0x00000000, + 0x00dc, 0xe6447800, 0x00e0, 0x78000000, 0x00e4, 0x010408d2, + 0x00e8, 0x00000000, 0x00ec, 0x00000000, 0x00f4, 0x00005fff, + 0x00f8, 0x00000000, 0x00fc, 0x00000000, 0x0100, 0x00000000, + 0x0104, 0x00000000, 0x0108, 0x00000000, 0x010c, 0x02000000, + 0x0110, 0x00000000, 0x0114, 0x00000000, 0x0118, 0xe6447800, + 0x011c, 0x78000000, 0x0120, 0x010408d2, 0x0124, 0x00000000, + 0x0128, 0x00000000, 0x0130, 0x00005fff, 0x0134, 0x00000000, + 0x0138, 0x00000000, 0x013c, 0x00000000, 0x0140, 0x00000000, + 0x0144, 0x00000000, 0x0148, 0x02000000, 0x014c, 0x00000000, + 0x0150, 0x00000000, 0x0154, 0xe6447800, 0x0158, 0x78000000, + 0x015c, 0x010408d2, 0x0160, 0x00000000, 0x0164, 0x00000000, + 0x016c, 0x00005fff, 0x0170, 0x00000000, 0x0174, 0x00000000, + 0x0178, 0x00000000, 0x017c, 0x00000000, 0x0180, 0x00000000, + 0x0184, 0x02000000, 0x0188, 0x00000000, 0x018c, 0x00000000, + 0x0190, 0xe6447800, 0x0194, 0x78000000, 0x0198, 0x010408d2, + 0x019c, 0x00000000, 0x01a0, 0x00000000, 0x01a8, 0x00005fff, + 0x01ac, 0x00000000, 0x01b0, 0x00000000, 0x01b4, 0x00000000, + 0x01b8, 0x00000000, 0x01bc, 0x00000000, 0x01c0, 0x02000000, + 0x01c4, 0x00000000, 0x01c8, 0x00000000, 0x01cc, 0xe6407800, + 0x01d0, 0x78000000, 0x01d4, 0x010408d2, 0x01d8, 0x00000000, + 0x01dc, 0x00000000, 0x01e4, 0x00005fff, 0x01e8, 0x00000000, + 0x01ec, 0x00000000, 0x01f0, 0x00000000, 0x01f4, 0x00000000, + 0x01f8, 0x00000000, 0x01fc, 0x02000000, 0x0200, 0x00000000, + 0x0204, 0x00000000, 0x0208, 0x00000008, 0x020c, 0x0000000d, + 0x41fc, 0x00000000, 0x4400, 0x00000000, 0x4410, 0x00000000, + 0x4420, 0x00000000, 0x4430, 0x00000000, 0x4440, 0x00000000, + 0x4450, 0x00000000, 0x4460, 0x00000000, 0x4470, 0x00000000, + 0xf080, 0x00003018, 0xf084, 0x00000fff, 0xf800, 0x00000000, + 0xf804, 0x00002e00, 0xf8d0, 0x00000001, 0xf8d4, 0x00000000, + 0xff00, 0x00000301, 0xff0c, 0x01000000, 0xffe0, 0x00000000, + 0xfff4, 0x00004011, 0x0090, 0x00000000, 0x0094, 0x00000000, + 0x0098, 0x00000000, 0x009c, 0x3f3f3f3f, +}; + +static const struct iio_event_spec aw_common_events[3] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_VALUE), + } +}; + +#define AW_IIO_CHANNEL(idx) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = aw_common_events, \ + .num_event_specs = ARRAY_SIZE(aw_common_events), \ +} \ + +static const struct iio_chan_spec aw96103_channels[] = { + AW_IIO_CHANNEL(0), + AW_IIO_CHANNEL(1), + AW_IIO_CHANNEL(2), + AW_IIO_CHANNEL(3), +}; + +static const struct iio_chan_spec aw96105_channels[] = { + AW_IIO_CHANNEL(0), + AW_IIO_CHANNEL(1), + AW_IIO_CHANNEL(2), + AW_IIO_CHANNEL(3), + AW_IIO_CHANNEL(4), + AW_IIO_CHANNEL(5), +}; + +static const struct aw_chip_info aw_chip_info_tbl[] = { + [AW96103_VAL] = { + .name = "aw96103_sensor", + .channels = aw96103_channels, + .num_channels = ARRAY_SIZE(aw96103_channels), + }, + [AW96105_VAL] = { + .name = "aw96105_sensor", + .channels = aw96105_channels, + .num_channels = ARRAY_SIZE(aw96105_channels), + }, +}; + +static void aw96103_parsing_bin_file(struct aw_bin *bin) +{ + bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET; + bin->valid_data_len = + *(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) - + AW96103_BIN_DATA_REG_NUM_SIZE; + memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET, + AW96103_BIN_CHIP_TYPE_SIZE); +} + +static const struct regmap_config aw96103_regmap_confg = { + .reg_bits = 16, + .val_bits = 32, +}; + +static int aw96103_get_diff_raw(struct aw96103 *aw96103, unsigned int chan, + int *buf) +{ + u32 data; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_DIFF_CH0 + chan * 4, &data); + if (ret) + return ret; + *buf = (int)(data / AW_DATA_PROCESS_FACTOR); + + return 0; +} + +static int aw96103_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = aw96103_get_diff_raw(aw96103, chan->channel, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int aw96103_read_thresh(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXTH0_CH(chan->channel), val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int aw96103_read_out_debounce(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, + int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_OUTDEB_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_read_in_debounce(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_INDEB_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_read_hysteresis(struct aw96103 *aw96103, + const struct iio_chan_spec *chan, int *val) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), ®_val); + if (ret) + return ret; + *val = FIELD_GET(AW96103_THHYST_MASK, reg_val); + + return IIO_VAL_INT; +} + +static int aw96103_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) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return aw96103_read_thresh(aw96103, chan, val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return aw96103_read_out_debounce(aw96103, chan, val); + case IIO_EV_DIR_FALLING: + return aw96103_read_in_debounce(aw96103, chan, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return aw96103_read_hysteresis(aw96103, chan, val); + default: + return -EINVAL; + } +} + +static int aw96103_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) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + return regmap_write(aw96103->regmap, + AW96103_REG_PROXTH0_CH(chan->channel), val); + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_OUTDEB_MASK, + FIELD_PREP(AW96103_OUTDEB_MASK, val)); + + case IIO_EV_DIR_FALLING: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_INDEB_MASK, + FIELD_PREP(AW96103_INDEB_MASK, val)); + default: + return -EINVAL; + } + case IIO_EV_INFO_HYSTERESIS: + return regmap_update_bits(aw96103->regmap, + AW96103_REG_PROXCTRL_CH(chan->channel), + AW96103_THHYST_MASK, + FIELD_PREP(AW96103_THHYST_MASK, val)); + default: + return -EINVAL; + } +} + +static int aw96103_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + return aw96103->channels_arr[chan->channel].used; +} + +static int aw96103_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + + aw96103->channels_arr[chan->channel].used = !!state; + + return regmap_update_bits(aw96103->regmap, AW96103_REG_SCANCTRL0, + BIT(chan->channel), + state ? BIT(chan->channel) : 0); +} + +static const struct iio_info iio_info = { + .read_raw = aw96103_read_raw, + .read_event_value = aw96103_read_event_val, + .write_event_value = aw96103_write_event_val, + .read_event_config = aw96103_read_event_config, + .write_event_config = aw96103_write_event_config, +}; + +static int aw96103_channel_scan_start(struct aw96103 *aw96103) +{ + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_CMD, + AW96103_ACTIVE_MODE); + if (ret) + return ret; + + return regmap_write(aw96103->regmap, AW96103_REG_IRQEN, + aw96103->hostirqen); +} + +static int aw96103_reg_version_comp(struct aw96103 *aw96103, + struct aw_bin *aw_bin) +{ + u32 blfilt1_data, fw_ver; + unsigned char i; + int ret; + + ret = regmap_read(aw96103->regmap, AW96103_REG_FWVER2, &fw_ver); + if (ret) + return ret; + /* + * If the chip version is AW96103A and the loaded register + * configuration file is for AW96103, special handling of the + * AW96103_REG_BLRSTRNG_CH0 register is required. + */ + if ((fw_ver != AW96103A) || (aw_bin->chip_type[7] != '\0')) + return 0; + + for (i = 0; i < aw96103->max_channels; i++) { + ret = regmap_read(aw96103->regmap, + AW96103_REG_BLFILT_CH0 + (AW96103_BLFILT_CH_STEP * i), + &blfilt1_data); + if (ret) + return ret; + if (FIELD_GET(AW96103_BLERRTRIG_MASK, blfilt1_data) != 1) + return 0; + + ret = regmap_update_bits(aw96103->regmap, + AW96103_REG_BLRSTRNG_CH0 + (AW96103_BLFILT_CH_STEP * i), + AW96103_BLRSTRNG_MASK, 1 << i); + if (ret) + return ret; + } + + return 0; +} + +static int aw96103_bin_valid_loaded(struct aw96103 *aw96103, + struct aw_bin *aw_bin_data_s) +{ + unsigned int start_addr = aw_bin_data_s->valid_data_addr; + u32 i, reg_data; + u16 reg_addr; + int ret; + + for (i = 0; i < aw_bin_data_s->valid_data_len; + i += 6, start_addr += 6) { + reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr); + reg_data = get_unaligned_le32(aw_bin_data_s->data + + start_addr + 2); + if ((reg_addr == AW96103_REG_EEDA0) || + (reg_addr == AW96103_REG_EEDA1)) + continue; + if (reg_addr == AW96103_REG_IRQEN) { + aw96103->hostirqen = reg_data; + continue; + } + if (reg_addr == AW96103_REG_SCANCTRL0) + aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK, + reg_data); + + ret = regmap_write(aw96103->regmap, reg_addr, reg_data); + if (ret < 0) + return ret; + } + + ret = aw96103_reg_version_comp(aw96103, aw_bin_data_s); + if (ret) + return ret; + + return aw96103_channel_scan_start(aw96103); +} + +static int aw96103_para_loaded(struct aw96103 *aw96103) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(aw96103_reg_default); i += 2) { + ret = regmap_write(aw96103->regmap, + (u16)aw96103_reg_default[i], + (u32)aw96103_reg_default[i + 1]); + if (ret) + return ret; + if (aw96103_reg_default[i] == AW96103_REG_IRQEN) + aw96103->hostirqen = aw96103_reg_default[i + 1]; + else if (aw96103_reg_default[i] == AW96103_REG_SCANCTRL0) + aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK, + aw96103_reg_default[i + 1]); + } + + return aw96103_channel_scan_start(aw96103); +} + +static int aw96103_cfg_all_loaded(const struct firmware *cont, + struct aw96103 *aw96103) +{ + if (!cont) + return -EINVAL; + + struct aw_bin *aw_bin __free(kfree) = + kzalloc(cont->size + sizeof(*aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->len = cont->size; + memcpy(aw_bin->data, cont->data, cont->size); + release_firmware(cont); + aw96103_parsing_bin_file(aw_bin); + + return aw96103_bin_valid_loaded(aw96103, aw_bin); +} + +static void aw96103_cfg_update(const struct firmware *fw, void *data) +{ + struct aw96103 *aw96103 = data; + int ret, i; + + if (!fw || !fw->data) { + dev_err(aw96103->dev, "No firmware.\n"); + return; + } + + ret = aw96103_cfg_all_loaded(fw, aw96103); + /* + * If loading the register configuration file fails, + * load the default register configuration in the driver to + * ensure the basic functionality of the device. + */ + if (ret) { + ret = aw96103_para_loaded(aw96103); + if (ret) { + dev_err(aw96103->dev, "load param error.\n"); + return; + } + } + + for (i = 0; i < aw96103->max_channels; i++) { + if ((aw96103->chan_en >> i) & 0x01) + aw96103->channels_arr[i].used = true; + else + aw96103->channels_arr[i].used = false; + } +} + +static int aw96103_sw_reset(struct aw96103 *aw96103) +{ + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_RESET, 0); + /* + * After reset, the initialization process starts to perform and + * it will last for a bout 20ms. + */ + msleep(20); + + return ret; +} + +enum aw96103_irq_trigger_position { + FAR = 0, + TRIGGER_TH0 = 0x01, + TRIGGER_TH1 = 0x03, + TRIGGER_TH2 = 0x07, + TRIGGER_TH3 = 0x0f, +}; + +static irqreturn_t aw96103_irq(int irq, void *data) +{ + unsigned int irq_status, curr_status_val, curr_status; + struct iio_dev *indio_dev = data; + struct aw96103 *aw96103 = iio_priv(indio_dev); + int ret, i; + + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status); + if (ret) + return IRQ_HANDLED; + + ret = regmap_read(aw96103->regmap, AW96103_REG_STAT0, &curr_status_val); + if (ret) + return IRQ_HANDLED; + + /* + * Iteratively analyze the interrupt status of different channels, + * with each channel having 4 interrupt states. + */ + for (i = 0; i < aw96103->max_channels; i++) { + if (!aw96103->channels_arr[i].used) + continue; + + curr_status = (((curr_status_val >> (24 + i)) & 0x1)) | + (((curr_status_val >> (16 + i)) & 0x1) << 1) | + (((curr_status_val >> (8 + i)) & 0x1) << 2) | + (((curr_status_val >> i) & 0x1) << 3); + if (aw96103->channels_arr[i].old_irq_status == curr_status) + continue; + + switch (curr_status) { + case FAR: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + break; + case TRIGGER_TH0: + case TRIGGER_TH1: + case TRIGGER_TH2: + case TRIGGER_TH3: + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + break; + default: + return IRQ_HANDLED; + } + aw96103->channels_arr[i].old_irq_status = curr_status; + } + + return IRQ_HANDLED; +} + +static int aw96103_interrupt_init(struct iio_dev *indio_dev, + struct i2c_client *i2c) +{ + struct aw96103 *aw96103 = iio_priv(indio_dev); + unsigned int irq_status; + int ret; + + ret = regmap_write(aw96103->regmap, AW96103_REG_IRQEN, 0); + if (ret) + return ret; + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status); + if (ret) + return ret; + ret = devm_request_threaded_irq(aw96103->dev, i2c->irq, NULL, + aw96103_irq, IRQF_ONESHOT, + "aw96103_irq", indio_dev); + if (ret) + return ret; + + return regmap_write(aw96103->regmap, AW96103_REG_IRQEN, + aw96103->hostirqen); +} + +static int aw96103_wait_chip_init(struct aw96103 *aw96103) +{ + unsigned int cnt = 20; + u32 reg_data; + int ret; + + while (cnt--) { + /* + * The device should generate an initialization completion + * interrupt within 20ms. + */ + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, + ®_data); + if (ret) + return ret; + + if (FIELD_GET(AW96103_INITOVERIRQ_MASK, reg_data)) + return 0; + fsleep(1000); + } + + return -ETIMEDOUT; +} + +static int aw96103_read_chipid(struct aw96103 *aw96103) +{ + unsigned char cnt = 0; + u32 reg_val = 0; + int ret; + + while (cnt < 3) { + /* + * This retry mechanism and the subsequent delay are just + * attempts to read the chip ID as much as possible, + * preventing occasional communication failures from causing + * the chip ID read to fail. + */ + ret = regmap_read(aw96103->regmap, AW96103_REG_CHIPID, + ®_val); + if (ret < 0) { + cnt++; + fsleep(2000); + continue; + } + break; + } + if (cnt == 3) + return -ETIMEDOUT; + + if (FIELD_GET(AW96103_CHIPID_MASK, reg_val) != AW96103_CHIP_ID) + dev_info(aw96103->dev, + "unexpected chipid, id=0x%08X\n", reg_val); + + return 0; +} + +static int aw96103_i2c_probe(struct i2c_client *i2c) +{ + const struct aw_chip_info *chip_info; + struct iio_dev *indio_dev; + struct aw96103 *aw96103; + int ret; + + indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*aw96103)); + if (!indio_dev) + return -ENOMEM; + + aw96103 = iio_priv(indio_dev); + aw96103->dev = &i2c->dev; + chip_info = i2c_get_match_data(i2c); + aw96103->max_channels = chip_info->num_channels; + + aw96103->regmap = devm_regmap_init_i2c(i2c, &aw96103_regmap_confg); + if (IS_ERR(aw96103->regmap)) + return PTR_ERR(aw96103->regmap); + + ret = devm_regulator_get_enable(aw96103->dev, "vcc"); + if (ret < 0) + return ret; + + ret = aw96103_read_chipid(aw96103); + if (ret) + return ret; + + ret = aw96103_sw_reset(aw96103); + if (ret) + return ret; + + ret = aw96103_wait_chip_init(aw96103); + if (ret) + return ret; + + ret = request_firmware_nowait(THIS_MODULE, true, "aw96103_0.bin", + aw96103->dev, GFP_KERNEL, aw96103, + aw96103_cfg_update); + if (ret) + return ret; + + ret = aw96103_interrupt_init(indio_dev, i2c); + if (ret) + return ret; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = chip_info->num_channels; + indio_dev->channels = chip_info->channels; + indio_dev->info = &iio_info; + indio_dev->name = chip_info->name; + + return devm_iio_device_register(aw96103->dev, indio_dev); +} + +static const struct of_device_id aw96103_dt_match[] = { + { + .compatible = "awinic,aw96103", + .data = &aw_chip_info_tbl[AW96103_VAL] + }, + { + .compatible = "awinic,aw96105", + .data = &aw_chip_info_tbl[AW96105_VAL] + }, + { } +}; +MODULE_DEVICE_TABLE(of, aw96103_dt_match); + +static const struct i2c_device_id aw96103_i2c_id[] = { + { "aw96103", (kernel_ulong_t)&aw_chip_info_tbl[AW96103_VAL] }, + { "aw96105", (kernel_ulong_t)&aw_chip_info_tbl[AW96105_VAL] }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw96103_i2c_id); + +static struct i2c_driver aw96103_i2c_driver = { + .driver = { + .name = "aw96103_sensor", + .of_match_table = aw96103_dt_match, + }, + .probe = aw96103_i2c_probe, + .id_table = aw96103_i2c_id, +}; +module_i2c_driver(aw96103_i2c_driver); + +MODULE_AUTHOR("Wang Shuaijie <wangshuaijie@awinic.com>"); +MODULE_DESCRIPTION("Driver for Awinic AW96103 proximity sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c index 4df506bb8b38..667369be0555 100644 --- a/drivers/iio/proximity/cros_ec_mkbp_proximity.c +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -6,10 +6,10 @@ */ #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/notifier.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> @@ -21,7 +21,7 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> struct cros_ec_mkbp_proximity_data { struct cros_ec_device *ec; @@ -167,7 +167,7 @@ static int cros_ec_mkbp_proximity_read_event_config(struct iio_dev *indio_dev, static int cros_ec_mkbp_proximity_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); @@ -261,7 +261,7 @@ static struct platform_driver cros_ec_mkbp_proximity_driver = { .pm = pm_sleep_ptr(&cros_ec_mkbp_proximity_pm_ops), }, .probe = cros_ec_mkbp_proximity_probe, - .remove_new = cros_ec_mkbp_proximity_remove, + .remove = cros_ec_mkbp_proximity_remove, }; module_platform_driver(cros_ec_mkbp_proximity_driver); diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c new file mode 100644 index 000000000000..5aa8e5a22f32 --- /dev/null +++ b/drivers/iio/proximity/hx9023s.c @@ -0,0 +1,1224 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 NanjingTianyihexin Electronics Ltd. + * http://www.tianyihexin.com + * + * Driver for NanjingTianyihexin HX9023S Cap Sensor. + * Datasheet available at: + * http://www.tianyihexin.com/ueditor/php/upload/file/20240614/1718336303992081.pdf + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <asm/byteorder.h> +#include <linux/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/types.h> + +#define HX9023S_CHIP_ID 0x1D +#define HX9023S_CH_NUM 5 +#define HX9023S_POS 0x03 +#define HX9023S_NEG 0x02 +#define HX9023S_NOT_CONNECTED 16 + +#define HX9023S_GLOBAL_CTRL0 0x00 +#define HX9023S_PRF_CFG 0x02 +#define HX9023S_CH0_CFG_7_0 0x03 +#define HX9023S_CH4_CFG_9_8 0x0C +#define HX9023S_RANGE_7_0 0x0D +#define HX9023S_RANGE_9_8 0x0E +#define HX9023S_RANGE_18_16 0x0F +#define HX9023S_AVG0_NOSR0_CFG 0x10 +#define HX9023S_NOSR12_CFG 0x11 +#define HX9023S_NOSR34_CFG 0x12 +#define HX9023S_AVG12_CFG 0x13 +#define HX9023S_AVG34_CFG 0x14 +#define HX9023S_OFFSET_DAC0_7_0 0x15 +#define HX9023S_OFFSET_DAC4_9_8 0x1E +#define HX9023S_SAMPLE_NUM_7_0 0x1F +#define HX9023S_INTEGRATION_NUM_7_0 0x21 +#define HX9023S_CH_NUM_CFG 0x24 +#define HX9023S_LP_ALP_4_CFG 0x29 +#define HX9023S_LP_ALP_1_0_CFG 0x2A +#define HX9023S_LP_ALP_3_2_CFG 0x2B +#define HX9023S_UP_ALP_1_0_CFG 0x2C +#define HX9023S_UP_ALP_3_2_CFG 0x2D +#define HX9023S_DN_UP_ALP_0_4_CFG 0x2E +#define HX9023S_DN_ALP_2_1_CFG 0x2F +#define HX9023S_DN_ALP_4_3_CFG 0x30 +#define HX9023S_RAW_BL_RD_CFG 0x38 +#define HX9023S_INTERRUPT_CFG 0x39 +#define HX9023S_INTERRUPT_CFG1 0x3A +#define HX9023S_CALI_DIFF_CFG 0x3B +#define HX9023S_DITHER_CFG 0x3C +#define HX9023S_DEVICE_ID 0x60 +#define HX9023S_PROX_STATUS 0x6B +#define HX9023S_PROX_INT_HIGH_CFG 0x6C +#define HX9023S_PROX_INT_LOW_CFG 0x6D +#define HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 0x80 +#define HX9023S_PROX_LOW_DIFF_CFG_CH0_0 0x88 +#define HX9023S_PROX_LOW_DIFF_CFG_CH3_1 0x8F +#define HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 0x9E +#define HX9023S_PROX_HIGH_DIFF_CFG_CH4_1 0x9F +#define HX9023S_PROX_LOW_DIFF_CFG_CH4_0 0xA2 +#define HX9023S_PROX_LOW_DIFF_CFG_CH4_1 0xA3 +#define HX9023S_CAP_INI_CH4_0 0xB3 +#define HX9023S_LP_DIFF_CH4_2 0xBA +#define HX9023S_RAW_BL_CH4_0 0xB5 +#define HX9023S_LP_DIFF_CH4_0 0xB8 +#define HX9023S_DSP_CONFIG_CTRL1 0xC8 +#define HX9023S_CAP_INI_CH0_0 0xE0 +#define HX9023S_RAW_BL_CH0_0 0xE8 +#define HX9023S_LP_DIFF_CH0_0 0xF4 +#define HX9023S_LP_DIFF_CH3_2 0xFF + +#define HX9023S_DATA_LOCK_MASK BIT(4) +#define HX9023S_INTERRUPT_MASK GENMASK(9, 0) +#define HX9023S_PROX_DEBOUNCE_MASK GENMASK(3, 0) + +#define FW_VER_OFFSET 2 +#define FW_REG_CNT_OFFSET 3 +#define FW_DATA_OFFSET 16 + +struct hx9023s_bin { + u16 reg_count; + u16 fw_size; + u8 fw_ver; + u8 data[] __counted_by(fw_size); +}; + +struct hx9023s_ch_data { + s16 raw; /* Raw Data*/ + s16 lp; /* Low Pass Filter Data*/ + s16 bl; /* Base Line Data */ + s16 diff; /* Difference of Low Pass Data and Base Line Data */ + + struct { + unsigned int near; + unsigned int far; + } thres; + + u16 dac; + u8 channel_positive; + u8 channel_negative; + bool sel_bl; + bool sel_raw; + bool sel_diff; + bool sel_lp; + bool enable; +}; + +struct hx9023s_data { + struct iio_trigger *trig; + struct regmap *regmap; + unsigned long chan_prox_stat; + unsigned long chan_read; + unsigned long chan_event; + unsigned long ch_en_stat; + unsigned long chan_in_use; + unsigned int prox_state_reg; + bool trigger_enabled; + + struct { + __le16 channels[HX9023S_CH_NUM]; + aligned_s64 ts; + } buffer; + + /* + * Serialize access to registers below: + * HX9023S_PROX_INT_LOW_CFG, + * HX9023S_PROX_INT_HIGH_CFG, + * HX9023S_INTERRUPT_CFG, + * HX9023S_CH_NUM_CFG + * Serialize access to channel configuration in + * hx9023s_push_events and hx9023s_trigger_handler. + */ + struct mutex mutex; + struct hx9023s_ch_data ch_data[HX9023S_CH_NUM]; +}; + +static const struct reg_sequence hx9023s_reg_init_list[] = { + /* scan period */ + REG_SEQ0(HX9023S_PRF_CFG, 0x17), + + /* full scale of conversion phase of each channel */ + REG_SEQ0(HX9023S_RANGE_7_0, 0x11), + REG_SEQ0(HX9023S_RANGE_9_8, 0x02), + REG_SEQ0(HX9023S_RANGE_18_16, 0x00), + + /* ADC average number and OSR number of each channel */ + REG_SEQ0(HX9023S_AVG0_NOSR0_CFG, 0x71), + REG_SEQ0(HX9023S_NOSR12_CFG, 0x44), + REG_SEQ0(HX9023S_NOSR34_CFG, 0x00), + REG_SEQ0(HX9023S_AVG12_CFG, 0x33), + REG_SEQ0(HX9023S_AVG34_CFG, 0x00), + + /* sample & integration frequency of the ADC */ + REG_SEQ0(HX9023S_SAMPLE_NUM_7_0, 0x65), + REG_SEQ0(HX9023S_INTEGRATION_NUM_7_0, 0x65), + + /* coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_LP_ALP_1_0_CFG, 0x22), + REG_SEQ0(HX9023S_LP_ALP_3_2_CFG, 0x22), + REG_SEQ0(HX9023S_LP_ALP_4_CFG, 0x02), + + /* up coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_UP_ALP_1_0_CFG, 0x88), + REG_SEQ0(HX9023S_UP_ALP_3_2_CFG, 0x88), + REG_SEQ0(HX9023S_DN_UP_ALP_0_4_CFG, 0x18), + + /* down coefficient of the first order low pass filter during each channel */ + REG_SEQ0(HX9023S_DN_ALP_2_1_CFG, 0x11), + REG_SEQ0(HX9023S_DN_ALP_4_3_CFG, 0x11), + + /* selection of data for the Data Mux Register to output data */ + REG_SEQ0(HX9023S_RAW_BL_RD_CFG, 0xF0), + + /* enable the interrupt function */ + REG_SEQ0(HX9023S_INTERRUPT_CFG, 0xFF), + REG_SEQ0(HX9023S_INTERRUPT_CFG1, 0x3B), + REG_SEQ0(HX9023S_DITHER_CFG, 0x21), + + /* threshold of the offset compensation */ + REG_SEQ0(HX9023S_CALI_DIFF_CFG, 0x07), + + /* proximity persistency number(near & far) */ + REG_SEQ0(HX9023S_PROX_INT_HIGH_CFG, 0x01), + REG_SEQ0(HX9023S_PROX_INT_LOW_CFG, 0x01), + + /* disable the data lock */ + REG_SEQ0(HX9023S_DSP_CONFIG_CTRL1, 0x00), +}; + +static const struct iio_event_spec hx9023s_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_VALUE), + + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define HX9023S_CHANNEL(idx) \ +{ \ + .type = IIO_PROXIMITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .indexed = 1, \ + .channel = idx, \ + .address = 0, \ + .event_spec = hx9023s_events, \ + .num_event_specs = ARRAY_SIZE(hx9023s_events), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec hx9023s_channels[] = { + HX9023S_CHANNEL(0), + HX9023S_CHANNEL(1), + HX9023S_CHANNEL(2), + HX9023S_CHANNEL(3), + HX9023S_CHANNEL(4), + IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const unsigned int hx9023s_samp_freq_table[] = { + 2, 2, 4, 6, 8, 10, 14, 18, 22, 26, + 30, 34, 38, 42, 46, 50, 56, 62, 68, 74, + 80, 90, 100, 200, 300, 400, 600, 800, 1000, 2000, + 3000, 4000, +}; + +static const struct regmap_range hx9023s_rd_reg_ranges[] = { + regmap_reg_range(HX9023S_GLOBAL_CTRL0, HX9023S_LP_DIFF_CH3_2), +}; + +static const struct regmap_range hx9023s_wr_reg_ranges[] = { + regmap_reg_range(HX9023S_GLOBAL_CTRL0, HX9023S_LP_DIFF_CH3_2), +}; + +static const struct regmap_range hx9023s_volatile_reg_ranges[] = { + regmap_reg_range(HX9023S_CAP_INI_CH4_0, HX9023S_LP_DIFF_CH4_2), + regmap_reg_range(HX9023S_CAP_INI_CH0_0, HX9023S_LP_DIFF_CH3_2), + regmap_reg_range(HX9023S_PROX_STATUS, HX9023S_PROX_STATUS), +}; + +static const struct regmap_access_table hx9023s_rd_regs = { + .yes_ranges = hx9023s_rd_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_rd_reg_ranges), +}; + +static const struct regmap_access_table hx9023s_wr_regs = { + .yes_ranges = hx9023s_wr_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_wr_reg_ranges), +}; + +static const struct regmap_access_table hx9023s_volatile_regs = { + .yes_ranges = hx9023s_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(hx9023s_volatile_reg_ranges), +}; + +static const struct regmap_config hx9023s_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .rd_table = &hx9023s_rd_regs, + .wr_table = &hx9023s_wr_regs, + .volatile_table = &hx9023s_volatile_regs, +}; + +static int hx9023s_interrupt_enable(struct hx9023s_data *data) +{ + return regmap_update_bits(data->regmap, HX9023S_INTERRUPT_CFG, + HX9023S_INTERRUPT_MASK, HX9023S_INTERRUPT_MASK); +} + +static int hx9023s_interrupt_disable(struct hx9023s_data *data) +{ + return regmap_update_bits(data->regmap, HX9023S_INTERRUPT_CFG, + HX9023S_INTERRUPT_MASK, 0x00); +} + +static int hx9023s_data_lock(struct hx9023s_data *data, bool locked) +{ + if (locked) + return regmap_update_bits(data->regmap, + HX9023S_DSP_CONFIG_CTRL1, + HX9023S_DATA_LOCK_MASK, + HX9023S_DATA_LOCK_MASK); + else + return regmap_update_bits(data->regmap, + HX9023S_DSP_CONFIG_CTRL1, + HX9023S_DATA_LOCK_MASK, 0); +} + +static int hx9023s_ch_cfg(struct hx9023s_data *data) +{ + __le16 reg_list[HX9023S_CH_NUM]; + u8 ch_pos[HX9023S_CH_NUM]; + u8 ch_neg[HX9023S_CH_NUM]; + /* Bit positions corresponding to input pin connections */ + u8 conn_cs[HX9023S_CH_NUM] = { 0, 2, 4, 6, 8 }; + unsigned int i; + u16 reg; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + ch_pos[i] = data->ch_data[i].channel_positive == HX9023S_NOT_CONNECTED ? + HX9023S_NOT_CONNECTED : conn_cs[data->ch_data[i].channel_positive]; + ch_neg[i] = data->ch_data[i].channel_negative == HX9023S_NOT_CONNECTED ? + HX9023S_NOT_CONNECTED : conn_cs[data->ch_data[i].channel_negative]; + + reg = (HX9023S_POS << ch_pos[i]) | (HX9023S_NEG << ch_neg[i]); + reg_list[i] = cpu_to_le16(reg); + } + + return regmap_bulk_write(data->regmap, HX9023S_CH0_CFG_7_0, reg_list, + sizeof(reg_list)); +} + +static int hx9023s_write_far_debounce(struct hx9023s_data *data, int val) +{ + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, HX9023S_PROX_INT_LOW_CFG, + HX9023S_PROX_DEBOUNCE_MASK, + FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, val)); +} + +static int hx9023s_write_near_debounce(struct hx9023s_data *data, int val) +{ + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, HX9023S_PROX_INT_HIGH_CFG, + HX9023S_PROX_DEBOUNCE_MASK, + FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, val)); +} + +static int hx9023s_read_far_debounce(struct hx9023s_data *data, int *val) +{ + int ret; + + ret = regmap_read(data->regmap, HX9023S_PROX_INT_LOW_CFG, val); + if (ret) + return ret; + + *val = FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, *val); + + return IIO_VAL_INT; +} + +static int hx9023s_read_near_debounce(struct hx9023s_data *data, int *val) +{ + int ret; + + ret = regmap_read(data->regmap, HX9023S_PROX_INT_HIGH_CFG, val); + if (ret) + return ret; + + *val = FIELD_GET(HX9023S_PROX_DEBOUNCE_MASK, *val); + + return IIO_VAL_INT; +} + +static int hx9023s_get_thres_near(struct hx9023s_data *data, u8 ch, int *val) +{ + int ret; + __le16 buf; + unsigned int reg, tmp; + + reg = (ch == 4) ? HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 : + HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 + (ch * 2); + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + tmp = (le16_to_cpu(buf) & GENMASK(9, 0)) * 32; + data->ch_data[ch].thres.near = tmp; + *val = tmp; + + return IIO_VAL_INT; +} + +static int hx9023s_get_thres_far(struct hx9023s_data *data, u8 ch, int *val) +{ + int ret; + __le16 buf; + unsigned int reg, tmp; + + reg = (ch == 4) ? HX9023S_PROX_LOW_DIFF_CFG_CH4_0 : + HX9023S_PROX_LOW_DIFF_CFG_CH0_0 + (ch * 2); + + ret = regmap_bulk_read(data->regmap, reg, &buf, sizeof(buf)); + if (ret) + return ret; + + tmp = (le16_to_cpu(buf) & GENMASK(9, 0)) * 32; + data->ch_data[ch].thres.far = tmp; + *val = tmp; + + return IIO_VAL_INT; +} + +static int hx9023s_set_thres_near(struct hx9023s_data *data, u8 ch, int val) +{ + __le16 val_le16 = cpu_to_le16((val / 32) & GENMASK(9, 0)); + unsigned int reg; + + data->ch_data[ch].thres.near = ((val / 32) & GENMASK(9, 0)) * 32; + reg = (ch == 4) ? HX9023S_PROX_HIGH_DIFF_CFG_CH4_0 : + HX9023S_PROX_HIGH_DIFF_CFG_CH0_0 + (ch * 2); + + return regmap_bulk_write(data->regmap, reg, &val_le16, sizeof(val_le16)); +} + +static int hx9023s_set_thres_far(struct hx9023s_data *data, u8 ch, int val) +{ + __le16 val_le16 = cpu_to_le16((val / 32) & GENMASK(9, 0)); + unsigned int reg; + + data->ch_data[ch].thres.far = ((val / 32) & GENMASK(9, 0)) * 32; + reg = (ch == 4) ? HX9023S_PROX_LOW_DIFF_CFG_CH4_0 : + HX9023S_PROX_LOW_DIFF_CFG_CH0_0 + (ch * 2); + + return regmap_bulk_write(data->regmap, reg, &val_le16, sizeof(val_le16)); +} + +static int hx9023s_get_prox_state(struct hx9023s_data *data) +{ + return regmap_read(data->regmap, HX9023S_PROX_STATUS, &data->prox_state_reg); +} + +static int hx9023s_data_select(struct hx9023s_data *data) +{ + int ret; + unsigned int i, buf; + unsigned long tmp; + + ret = regmap_read(data->regmap, HX9023S_RAW_BL_RD_CFG, &buf); + if (ret) + return ret; + + tmp = buf; + for (i = 0; i < 4; i++) { + data->ch_data[i].sel_diff = test_bit(i, &tmp); + data->ch_data[i].sel_lp = !data->ch_data[i].sel_diff; + data->ch_data[i].sel_bl = test_bit(i + 4, &tmp); + data->ch_data[i].sel_raw = !data->ch_data[i].sel_bl; + } + + ret = regmap_read(data->regmap, HX9023S_INTERRUPT_CFG1, &buf); + if (ret) + return ret; + + tmp = buf; + data->ch_data[4].sel_diff = test_bit(2, &tmp); + data->ch_data[4].sel_lp = !data->ch_data[4].sel_diff; + data->ch_data[4].sel_bl = test_bit(3, &tmp); + data->ch_data[4].sel_raw = !data->ch_data[4].sel_bl; + + return 0; +} + +static int hx9023s_sample(struct hx9023s_data *data) +{ + int ret; + unsigned int i; + u8 buf[HX9023S_CH_NUM * 3]; + u16 value; + + ret = hx9023s_data_lock(data, true); + if (ret) + return ret; + + ret = hx9023s_data_select(data); + if (ret) + goto err; + + /* 3 bytes for each of channels 0 to 3 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH0_0, buf, 12); + if (ret) + goto err; + + /* 3 bytes for channel 4 */ + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH4_0, buf + 12, 3); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 3 + 1]); + data->ch_data[i].raw = 0; + data->ch_data[i].bl = 0; + if (data->ch_data[i].sel_raw) + data->ch_data[i].raw = value; + if (data->ch_data[i].sel_bl) + data->ch_data[i].bl = value; + } + + /* 3 bytes for each of channels 0 to 3 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH0_0, buf, 12); + if (ret) + goto err; + + /* 3 bytes for channel 4 */ + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH4_0, buf + 12, 3); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 3 + 1]); + data->ch_data[i].lp = 0; + data->ch_data[i].diff = 0; + if (data->ch_data[i].sel_lp) + data->ch_data[i].lp = value; + if (data->ch_data[i].sel_diff) + data->ch_data[i].diff = value; + } + + for (i = 0; i < HX9023S_CH_NUM; i++) { + if (data->ch_data[i].sel_lp && data->ch_data[i].sel_bl) + data->ch_data[i].diff = data->ch_data[i].lp - data->ch_data[i].bl; + } + + /* 2 bytes for each of channels 0 to 4 which have contiguous registers */ + ret = regmap_bulk_read(data->regmap, HX9023S_OFFSET_DAC0_7_0, buf, 10); + if (ret) + goto err; + + for (i = 0; i < HX9023S_CH_NUM; i++) { + value = get_unaligned_le16(&buf[i * 2]); + value = FIELD_GET(GENMASK(11, 0), value); + data->ch_data[i].dac = value; + } + +err: + return hx9023s_data_lock(data, false); +} + +static int hx9023s_ch_en(struct hx9023s_data *data, u8 ch_id, bool en) +{ + int ret; + unsigned int buf; + + ret = regmap_read(data->regmap, HX9023S_CH_NUM_CFG, &buf); + if (ret) + return ret; + + data->ch_en_stat = buf; + if (en && data->ch_en_stat == 0) + data->prox_state_reg = 0; + + data->ch_data[ch_id].enable = en; + __assign_bit(ch_id, &data->ch_en_stat, en); + + return regmap_write(data->regmap, HX9023S_CH_NUM_CFG, data->ch_en_stat); +} + +static int hx9023s_property_get(struct hx9023s_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + u32 array[2]; + u32 i, reg, temp; + int ret; + + data->chan_in_use = 0; + for (i = 0; i < HX9023S_CH_NUM; i++) { + data->ch_data[i].channel_positive = HX9023S_NOT_CONNECTED; + data->ch_data[i].channel_negative = HX9023S_NOT_CONNECTED; + } + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg >= HX9023S_CH_NUM) + return dev_err_probe(dev, ret < 0 ? ret : -EINVAL, + "Failed to read reg\n"); + __set_bit(reg, &data->chan_in_use); + + ret = fwnode_property_read_u32(child, "single-channel", &temp); + if (ret == 0) { + data->ch_data[reg].channel_positive = temp; + data->ch_data[reg].channel_negative = HX9023S_NOT_CONNECTED; + } else { + ret = fwnode_property_read_u32_array(child, "diff-channels", + array, ARRAY_SIZE(array)); + if (ret == 0) { + data->ch_data[reg].channel_positive = array[0]; + data->ch_data[reg].channel_negative = array[1]; + } else { + return dev_err_probe(dev, ret, + "Property read failed: %d\n", + reg); + } + } + } + + return 0; +} + +static int hx9023s_update_chan_en(struct hx9023s_data *data, + unsigned long chan_read, + unsigned long chan_event) +{ + unsigned int i; + unsigned long channels = chan_read | chan_event; + + if ((data->chan_read | data->chan_event) != channels) { + for_each_set_bit(i, &channels, HX9023S_CH_NUM) + hx9023s_ch_en(data, i, test_bit(i, &data->chan_in_use)); + for_each_clear_bit(i, &channels, HX9023S_CH_NUM) + hx9023s_ch_en(data, i, false); + } + + data->chan_read = chan_read; + data->chan_event = chan_event; + + return 0; +} + +static int hx9023s_get_proximity(struct hx9023s_data *data, + const struct iio_chan_spec *chan, + int *val) +{ + int ret; + + ret = hx9023s_sample(data); + if (ret) + return ret; + + ret = hx9023s_get_prox_state(data); + if (ret) + return ret; + + *val = data->ch_data[chan->channel].diff; + return IIO_VAL_INT; +} + +static int hx9023s_get_samp_freq(struct hx9023s_data *data, int *val, int *val2) +{ + int ret; + unsigned int odr, index; + + ret = regmap_read(data->regmap, HX9023S_PRF_CFG, &index); + if (ret) + return ret; + + odr = hx9023s_samp_freq_table[index]; + *val = KILO / odr; + *val2 = div_u64((KILO % odr) * MICRO, odr); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int hx9023s_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = hx9023s_get_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return hx9023s_get_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int hx9023s_set_samp_freq(struct hx9023s_data *data, int val, int val2) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int i, period_ms; + + period_ms = div_u64(NANO, (val * MEGA + val2)); + + for (i = 0; i < ARRAY_SIZE(hx9023s_samp_freq_table); i++) { + if (period_ms == hx9023s_samp_freq_table[i]) + break; + } + if (i == ARRAY_SIZE(hx9023s_samp_freq_table)) { + dev_err(dev, "Period:%dms NOT found!\n", period_ms); + return -EINVAL; + } + + return regmap_write(data->regmap, HX9023S_PRF_CFG, i); +} + +static int hx9023s_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long mask) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + return hx9023s_set_samp_freq(data, val, val2); +} + +static irqreturn_t hx9023s_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct hx9023s_data *data = iio_priv(indio_dev); + + if (data->trigger_enabled) + iio_trigger_poll(data->trig); + + return IRQ_WAKE_THREAD; +} + +static void hx9023s_push_events(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long prox_changed; + unsigned int chan; + int ret; + + ret = hx9023s_sample(data); + if (ret) + return; + + ret = hx9023s_get_prox_state(data); + if (ret) + return; + + prox_changed = (data->chan_prox_stat ^ data->prox_state_reg) & data->chan_event; + for_each_set_bit(chan, &prox_changed, HX9023S_CH_NUM) { + unsigned int dir; + + dir = (data->prox_state_reg & BIT(chan)) ? + IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, + IIO_EV_TYPE_THRESH, dir), + timestamp); + } + data->chan_prox_stat = data->prox_state_reg; +} + +static irqreturn_t hx9023s_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + hx9023s_push_events(indio_dev); + + return IRQ_HANDLED; +} + +static int hx9023s_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) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_get_thres_far(data, chan->channel, val); + case IIO_EV_DIR_FALLING: + return hx9023s_get_thres_near(data, chan->channel, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_read_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return hx9023s_read_near_debounce(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int hx9023s_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) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (chan->type != IIO_PROXIMITY) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_set_thres_far(data, chan->channel, val); + case IIO_EV_DIR_FALLING: + return hx9023s_set_thres_near(data, chan->channel, val); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + switch (dir) { + case IIO_EV_DIR_RISING: + return hx9023s_write_far_debounce(data, val); + case IIO_EV_DIR_FALLING: + return hx9023s_write_near_debounce(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int hx9023s_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + return test_bit(chan->channel, &data->chan_event); +} + +static int hx9023s_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + if (test_bit(chan->channel, &data->chan_in_use)) { + hx9023s_ch_en(data, chan->channel, state); + __assign_bit(chan->channel, &data->chan_event, + data->ch_data[chan->channel].enable); + } + + return 0; +} + +static const struct iio_info hx9023s_info = { + .read_raw = hx9023s_read_raw, + .write_raw = hx9023s_write_raw, + .read_event_value = hx9023s_read_event_val, + .write_event_value = hx9023s_write_event_val, + .read_event_config = hx9023s_read_event_config, + .write_event_config = hx9023s_write_event_config, +}; + +static int hx9023s_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + if (state) + hx9023s_interrupt_enable(data); + else if (!data->chan_read) + hx9023s_interrupt_disable(data); + data->trigger_enabled = state; + + return 0; +} + +static const struct iio_trigger_ops hx9023s_trigger_ops = { + .set_trigger_state = hx9023s_set_trigger_state, +}; + +static irqreturn_t hx9023s_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct hx9023s_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int bit, index, i = 0; + int ret; + + guard(mutex)(&data->mutex); + ret = hx9023s_sample(data); + if (ret) { + dev_warn(dev, "sampling failed\n"); + goto out; + } + + ret = hx9023s_get_prox_state(data); + if (ret) { + dev_warn(dev, "get prox failed\n"); + goto out; + } + + iio_for_each_active_channel(indio_dev, bit) { + index = indio_dev->channels[bit].channel; + data->buffer.channels[i++] = cpu_to_le16(data->ch_data[index].diff); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int hx9023s_buffer_preenable(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + unsigned long channels = 0; + unsigned int bit; + + guard(mutex)(&data->mutex); + iio_for_each_active_channel(indio_dev, bit) + __set_bit(indio_dev->channels[bit].channel, &channels); + + hx9023s_update_chan_en(data, channels, data->chan_event); + + return 0; +} + +static int hx9023s_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + hx9023s_update_chan_en(data, 0, data->chan_event); + + return 0; +} + +static const struct iio_buffer_setup_ops hx9023s_buffer_setup_ops = { + .preenable = hx9023s_buffer_preenable, + .postdisable = hx9023s_buffer_postdisable, +}; + +static int hx9023s_id_check(struct iio_dev *indio_dev) +{ + struct hx9023s_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned int id; + int ret; + + ret = regmap_read(data->regmap, HX9023S_DEVICE_ID, &id); + if (ret) + return ret; + + if (id != HX9023S_CHIP_ID) + dev_warn(dev, "Unexpected chip ID, assuming compatible\n"); + + return 0; +} + +static int hx9023s_bin_load(struct hx9023s_data *data, struct hx9023s_bin *bin) +{ + u8 *cfg_start = bin->data + FW_DATA_OFFSET; + u8 addr, val; + u16 i; + int ret; + + for (i = 0; i < bin->reg_count; i++) { + addr = cfg_start[i * 2]; + val = cfg_start[i * 2 + 1]; + ret = regmap_write(data->regmap, addr, val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int hx9023s_send_cfg(const struct firmware *fw, struct hx9023s_data *data) +{ + struct hx9023s_bin *bin __free(kfree) = + kzalloc(fw->size + sizeof(*bin), GFP_KERNEL); + if (!bin) + return -ENOMEM; + + memcpy(bin->data, fw->data, fw->size); + + bin->fw_size = fw->size; + bin->fw_ver = bin->data[FW_VER_OFFSET]; + bin->reg_count = get_unaligned_le16(bin->data + FW_REG_CNT_OFFSET); + + release_firmware(fw); + + return hx9023s_bin_load(data, bin); +} + +static void hx9023s_cfg_update(const struct firmware *fw, void *context) +{ + struct hx9023s_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + int ret; + + if (!fw || !fw->data) { + dev_warn(dev, "No firmware\n"); + goto no_fw; + } + + ret = hx9023s_send_cfg(fw, data); + if (ret) { + dev_warn(dev, "Firmware update failed: %d\n", ret); + goto no_fw; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); + + return; + +no_fw: + ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, + ARRAY_SIZE(hx9023s_reg_init_list)); + if (ret) { + dev_err(dev, "Error loading default configuration\n"); + return; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); +} + +static int hx9023s_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct hx9023s_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + mutex_init(&data->mutex); + + data->regmap = devm_regmap_init_i2c(client, &hx9023s_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap init failed\n"); + + ret = hx9023s_property_get(data); + if (ret) + return dev_err_probe(dev, ret, "dts phase failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "regulator get failed\n"); + + ret = hx9023s_id_check(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "id check failed\n"); + + indio_dev->name = "hx9023s"; + indio_dev->channels = hx9023s_channels; + indio_dev->num_channels = ARRAY_SIZE(hx9023s_channels); + indio_dev->info = &hx9023s_info; + indio_dev->modes = INDIO_DIRECT_MODE; + i2c_set_clientdata(client, indio_dev); + + ret = hx9023s_ch_cfg(data); + if (ret) + return dev_err_probe(dev, ret, "channel config failed\n"); + + ret = request_firmware_nowait(THIS_MODULE, true, "hx9023s.bin", dev, + GFP_KERNEL, data, hx9023s_cfg_update); + if (ret) + return dev_err_probe(dev, ret, "reg config failed\n"); + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, + hx9023s_irq_handler, + hx9023s_irq_thread_handler, + IRQF_ONESHOT, + "hx9023s_event", indio_dev); + if (ret) + return dev_err_probe(dev, ret, "irq request failed\n"); + + data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return dev_err_probe(dev, -ENOMEM, + "iio trigger alloc failed\n"); + + data->trig->ops = &hx9023s_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + + ret = devm_iio_trigger_register(dev, data->trig); + if (ret) + return dev_err_probe(dev, ret, + "iio trigger register failed\n"); + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + hx9023s_trigger_handler, + &hx9023s_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "iio triggered buffer setup failed\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int hx9023s_suspend(struct device *dev) +{ + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); + + guard(mutex)(&data->mutex); + hx9023s_interrupt_disable(data); + + return 0; +} + +static int hx9023s_resume(struct device *dev) +{ + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); + + guard(mutex)(&data->mutex); + if (data->trigger_enabled) + hx9023s_interrupt_enable(data); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hx9023s_pm_ops, hx9023s_suspend, + hx9023s_resume); + +static const struct of_device_id hx9023s_of_match[] = { + { .compatible = "tyhx,hx9023s" }, + {} +}; +MODULE_DEVICE_TABLE(of, hx9023s_of_match); + +static const struct i2c_device_id hx9023s_id[] = { + { "hx9023s" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, hx9023s_id); + +static struct i2c_driver hx9023s_driver = { + .driver = { + .name = "hx9023s", + .of_match_table = hx9023s_of_match, + .pm = &hx9023s_pm_ops, + + /* + * The I2C operations in hx9023s_reg_init() and hx9023s_ch_cfg() + * are time-consuming. Prefer async so we don't delay boot + * if we're builtin to the kernel. + */ + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = hx9023s_probe, + .id_table = hx9023s_id, +}; +module_i2c_driver(hx9023s_driver); + +MODULE_AUTHOR("Yasin Lee <yasin.lee.x@gmail.com>"); +MODULE_DESCRIPTION("Driver for TYHX HX9023S SAR sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 323ac6dac90e..b09d15230111 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -5,7 +5,7 @@ * Copyright (C) 2023 Axis Communications AB */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/bitfield.h> #include <linux/i2c.h> #include <linux/module.h> @@ -648,7 +648,8 @@ static int irsd200_read_event_config(struct iio_dev *indio_dev, static int irsd200_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, + bool state) { struct irsd200_data *data = iio_priv(indio_dev); unsigned int tmp; @@ -662,7 +663,7 @@ static int irsd200_write_event_config(struct iio_dev *indio_dev, return ret; return regmap_field_write( - data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state); + data->regfields[IRS_REGF_INTR_COUNT_THR_OR], state); default: return -EINVAL; } diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index bcebacaf3dab..dc66ca9bba6b 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -989,23 +989,22 @@ static int isl29501_probe(struct i2c_client *client) } static const struct i2c_device_id isl29501_id[] = { - {"isl29501", 0}, + { "isl29501" }, {} }; MODULE_DEVICE_TABLE(i2c, isl29501_id); -#if defined(CONFIG_OF) static const struct of_device_id isl29501_i2c_matches[] = { { .compatible = "renesas,isl29501" }, { } }; MODULE_DEVICE_TABLE(of, isl29501_i2c_matches); -#endif static struct i2c_driver isl29501_driver = { .driver = { .name = "isl29501", + .of_match_table = isl29501_i2c_matches, }, .id_table = isl29501_id, .probe = isl29501_probe, diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index 614e65cb9d42..cfc75d001f20 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -45,7 +45,7 @@ struct mb1232_data { /* Ensure correct alignment of data to push to IIO buffer */ struct { s16 distance; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 2913d5e0fe4f..f3d054b06b4c 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -47,7 +47,7 @@ struct lidar_data { /* Ensure timestamp is naturally aligned */ struct { u16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -322,9 +322,9 @@ static void lidar_remove(struct i2c_client *client) } static const struct i2c_device_id lidar_id[] = { - {"lidar-lite-v2", 0}, - {"lidar-lite-v3", 0}, - { }, + { "lidar-lite-v2" }, + { "lidar-lite-v3" }, + { } }; MODULE_DEVICE_TABLE(i2c, lidar_id); diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c index f02e83e3f15f..aff60a3c1a6f 100644 --- a/drivers/iio/proximity/rfd77402.c +++ b/drivers/iio/proximity/rfd77402.c @@ -308,7 +308,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend, rfd77402_resume); static const struct i2c_device_id rfd77402_id[] = { - { "rfd77402", 0 }, + { "rfd77402" }, { } }; MODULE_DEVICE_TABLE(i2c, rfd77402_id); diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 86c57672fc7e..71ad29e441b2 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -389,7 +389,7 @@ static const struct dev_pm_ops srf04_pm_ops = { static struct platform_driver srf04_driver = { .probe = srf04_probe, - .remove_new = srf04_remove, + .remove = srf04_remove, .driver = { .name = "srf04-gpio", .of_match_table = of_srf04_match, diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index a75ea5042876..86cab113ef3d 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -66,7 +66,7 @@ struct srf08_data { /* Ensure timestamp is naturally aligned */ struct { s16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; /* Sensor-Type */ diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 0d230a0dff56..0d7f0518d4fb 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -337,28 +337,19 @@ static int sx9310_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); - int ret; if (chan->type != IIO_PROXIMITY) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx_common_read_proximity(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx_common_read_proximity(data, chan, val); + unreachable(); case IIO_CHAN_INFO_HARDWAREGAIN: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx9310_read_gain(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx9310_read_gain(data, chan, val); + unreachable(); case IIO_CHAN_INFO_SAMP_FREQ: return sx9310_read_samp_freq(data, val, val2); default: @@ -546,12 +537,10 @@ static int sx9310_write_thresh(struct sx_common_data *data, return -EINVAL; regval = FIELD_PREP(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, reg, - SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); - mutex_unlock(&data->mutex); - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, reg, + SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval); } static int sx9310_write_hysteresis(struct sx_common_data *data, @@ -576,17 +565,14 @@ static int sx9310_write_hysteresis(struct sx_common_data *data, return -EINVAL; hyst = FIELD_PREP(SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, - SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); - mutex_unlock(&data->mutex); - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, + SX9310_REG_PROX_CTRL10_HYST_MASK, hyst); } static int sx9310_write_far_debounce(struct sx_common_data *data, int val) { - int ret; unsigned int regval; if (val > 0) @@ -596,18 +582,14 @@ static int sx9310_write_far_debounce(struct sx_common_data *data, int val) regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, - SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, + SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, + regval); } static int sx9310_write_close_debounce(struct sx_common_data *data, int val) { - int ret; unsigned int regval; if (val > 0) @@ -617,13 +599,10 @@ static int sx9310_write_close_debounce(struct sx_common_data *data, int val) regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, - SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10, + SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, + regval); } static int sx9310_write_event_val(struct iio_dev *indio_dev, @@ -658,7 +637,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev, static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2) { - int i, ret; + int i; for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++) if (val == sx9310_samp_freq_table[i].val && @@ -668,23 +647,17 @@ static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2) if (i == ARRAY_SIZE(sx9310_samp_freq_table)) return -EINVAL; - mutex_lock(&data->mutex); - - ret = regmap_update_bits( + guard(mutex)(&data->mutex); + return regmap_update_bits( data->regmap, SX9310_REG_PROX_CTRL0, SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, FIELD_PREP(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, i)); - - mutex_unlock(&data->mutex); - - return ret; } static int sx9310_write_gain(struct sx_common_data *data, const struct iio_chan_spec *chan, int val) { unsigned int gain, mask; - int ret; gain = ilog2(val); @@ -703,12 +676,9 @@ static int sx9310_write_gain(struct sx_common_data *data, return -EINVAL; } - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask, - gain); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask, + gain); } static int sx9310_write_raw(struct iio_dev *indio_dev, @@ -969,22 +939,18 @@ static int sx9310_suspend(struct device *dev) disable_irq_nosync(data->client->irq); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0, &data->suspend_ctrl); if (ret) - goto out; + return ret; ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK; ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0); if (ret) - goto out; - - ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 0); + return ret; -out: - mutex_unlock(&data->mutex); - return ret; + return regmap_write(data->regmap, SX9310_REG_PAUSE, 0); } static int sx9310_resume(struct device *dev) @@ -992,18 +958,16 @@ static int sx9310_resume(struct device *dev) struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); int ret; - mutex_lock(&data->mutex); - ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1); - if (ret) - goto out; - - ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, - data->suspend_ctrl); + scoped_guard(mutex, &data->mutex) { + ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1); + if (ret) + return ret; -out: - mutex_unlock(&data->mutex); - if (ret) - return ret; + ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, + data->suspend_ctrl); + if (ret) + return ret; + } enable_irq(data->client->irq); return 0; @@ -1065,4 +1029,4 @@ MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); MODULE_AUTHOR("Daniel Campello <campello@chromium.org>"); MODULE_DESCRIPTION("Driver for Semtech SX9310/SX9311 proximity sensor"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(SEMTECH_PROX); +MODULE_IMPORT_NS("SEMTECH_PROX"); diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index ac2ed2da21cc..f7819dd2775c 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -429,25 +429,16 @@ static int sx9324_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); - int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx_common_read_proximity(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx_common_read_proximity(data, chan, val); + unreachable(); case IIO_CHAN_INFO_HARDWAREGAIN: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx9324_read_gain(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx9324_read_gain(data, chan, val); + unreachable(); case IIO_CHAN_INFO_SAMP_FREQ: return sx9324_read_samp_freq(data, val, val2); default: @@ -484,7 +475,7 @@ static int sx9324_read_avail(struct iio_dev *indio_dev, static int sx9324_set_samp_freq(struct sx_common_data *data, int val, int val2) { - int i, ret; + int i; for (i = 0; i < ARRAY_SIZE(sx9324_samp_freq_table); i++) if (val == sx9324_samp_freq_table[i].val && @@ -494,15 +485,11 @@ static int sx9324_set_samp_freq(struct sx_common_data *data, if (i == ARRAY_SIZE(sx9324_samp_freq_table)) return -EINVAL; - mutex_lock(&data->mutex); - - ret = regmap_update_bits(data->regmap, - SX9324_REG_GNRL_CTRL0, - SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i); + guard(mutex)(&data->mutex); - mutex_unlock(&data->mutex); - - return ret; + return regmap_update_bits(data->regmap, + SX9324_REG_GNRL_CTRL0, + SX9324_REG_GNRL_CTRL0_SCANPERIOD_MASK, i); } static int sx9324_read_thresh(struct sx_common_data *data, @@ -623,7 +610,6 @@ static int sx9324_write_thresh(struct sx_common_data *data, const struct iio_chan_spec *chan, int _val) { unsigned int reg, val = _val; - int ret; reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2; @@ -633,11 +619,9 @@ static int sx9324_write_thresh(struct sx_common_data *data, if (val > 0xff) return -EINVAL; - mutex_lock(&data->mutex); - ret = regmap_write(data->regmap, reg, val); - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return ret; + return regmap_write(data->regmap, reg, val); } static int sx9324_write_hysteresis(struct sx_common_data *data, @@ -662,18 +646,15 @@ static int sx9324_write_hysteresis(struct sx_common_data *data, return -EINVAL; hyst = FIELD_PREP(SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, - SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return ret; + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_HYST_MASK, hyst); } static int sx9324_write_far_debounce(struct sx_common_data *data, int _val) { unsigned int regval, val = _val; - int ret; if (val > 0) val = ilog2(val); @@ -682,19 +663,16 @@ static int sx9324_write_far_debounce(struct sx_common_data *data, int _val) regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, - SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return ret; + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_FAR_DEBOUNCE_MASK, + regval); } static int sx9324_write_close_debounce(struct sx_common_data *data, int _val) { unsigned int regval, val = _val; - int ret; if (val > 0) val = ilog2(val); @@ -703,13 +681,11 @@ static int sx9324_write_close_debounce(struct sx_common_data *data, int _val) regval = FIELD_PREP(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, - SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return ret; + return regmap_update_bits(data->regmap, SX9324_REG_PROX_CTRL5, + SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, + regval); } static int sx9324_write_event_val(struct iio_dev *indio_dev, @@ -746,7 +722,6 @@ static int sx9324_write_gain(struct sx_common_data *data, const struct iio_chan_spec *chan, int val) { unsigned int gain, reg; - int ret; reg = SX9324_REG_PROX_CTRL0 + chan->channel / 2; @@ -756,13 +731,11 @@ static int sx9324_write_gain(struct sx_common_data *data, gain = FIELD_PREP(SX9324_REG_PROX_CTRL0_GAIN_MASK, gain); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, reg, - SX9324_REG_PROX_CTRL0_GAIN_MASK, - gain); - mutex_unlock(&data->mutex); + guard(mutex)(&data->mutex); - return ret; + return regmap_update_bits(data->regmap, reg, + SX9324_REG_PROX_CTRL0_GAIN_MASK, + gain); } static int sx9324_write_raw(struct iio_dev *indio_dev, @@ -862,9 +835,8 @@ static int sx9324_init_compensation(struct iio_dev *indio_dev) int ret; /* run the compensation phase on all channels */ - ret = regmap_update_bits(data->regmap, SX9324_REG_STAT2, - SX9324_REG_STAT2_COMPSTAT_MASK, - SX9324_REG_STAT2_COMPSTAT_MASK); + ret = regmap_set_bits(data->regmap, SX9324_REG_STAT2, + SX9324_REG_STAT2_COMPSTAT_MASK); if (ret) return ret; @@ -873,6 +845,49 @@ static int sx9324_init_compensation(struct iio_dev *indio_dev) 20000, 2000000); } +static u8 sx9324_parse_phase_prop(struct device *dev, + struct sx_common_reg_default *reg_def, + const char *prop) +{ + unsigned int pin_defs[SX9324_NUM_PINS]; + int count, ret, pin; + u32 raw = 0; + + count = device_property_count_u32(dev, prop); + if (count != ARRAY_SIZE(pin_defs)) + return reg_def->def; + ret = device_property_read_u32_array(dev, prop, pin_defs, + ARRAY_SIZE(pin_defs)); + if (ret) + return reg_def->def; + + for (pin = 0; pin < SX9324_NUM_PINS; pin++) + raw |= (pin_defs[pin] << (2 * pin)) & + SX9324_REG_AFE_PH0_PIN_MASK(pin); + + return raw; +} + +static void sx_common_get_raw_register_config(struct device *dev, + struct sx_common_reg_default *reg_def) +{ +#ifdef CONFIG_ACPI + struct acpi_device *adev = ACPI_COMPANION(dev); + u32 raw = 0, ret; + char prop[80]; + + if (!reg_def->property || !adev) + return; + + snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); + ret = device_property_read_u32(dev, prop, &raw); + if (ret) + return; + + reg_def->def = raw; +#endif +} + static const struct sx_common_reg_default * sx9324_get_default_reg(struct device *dev, int idx, struct sx_common_reg_default *reg_def) @@ -881,37 +896,29 @@ sx9324_get_default_reg(struct device *dev, int idx, "highest" }; static const char * const sx9324_csidle[] = { "hi-z", "hi-z", "gnd", "vdd" }; -#define SX9324_PIN_DEF "semtech,ph0-pin" -#define SX9324_RESOLUTION_DEF "semtech,ph01-resolution" -#define SX9324_PROXRAW_DEF "semtech,ph01-proxraw-strength" - unsigned int pin_defs[SX9324_NUM_PINS]; - char prop[] = SX9324_PROXRAW_DEF; u32 start = 0, raw = 0, pos = 0; - int ret, count, ph, pin; + const char *prop; + int ret; memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def)); sx_common_get_raw_register_config(dev, reg_def); switch (reg_def->reg) { case SX9324_REG_AFE_PH0: + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, + "semtech,ph0-pin"); + break; case SX9324_REG_AFE_PH1: + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, + "semtech,ph1-pin"); + break; case SX9324_REG_AFE_PH2: + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, + "semtech,ph2-pin"); + break; case SX9324_REG_AFE_PH3: - ph = reg_def->reg - SX9324_REG_AFE_PH0; - snprintf(prop, ARRAY_SIZE(prop), "semtech,ph%d-pin", ph); - - count = device_property_count_u32(dev, prop); - if (count != ARRAY_SIZE(pin_defs)) - break; - ret = device_property_read_u32_array(dev, prop, pin_defs, - ARRAY_SIZE(pin_defs)); - if (ret) - break; - - for (pin = 0; pin < SX9324_NUM_PINS; pin++) - raw |= (pin_defs[pin] << (2 * pin)) & - SX9324_REG_AFE_PH0_PIN_MASK(pin); - reg_def->def = raw; + reg_def->def = sx9324_parse_phase_prop(dev, reg_def, + "semtech,ph3-pin"); break; case SX9324_REG_AFE_CTRL0: ret = device_property_match_property_string(dev, "semtech,cs-idle-sleep", @@ -933,11 +940,9 @@ sx9324_get_default_reg(struct device *dev, int idx, case SX9324_REG_AFE_CTRL4: case SX9324_REG_AFE_CTRL7: if (reg_def->reg == SX9324_REG_AFE_CTRL4) - strncpy(prop, "semtech,ph01-resolution", - ARRAY_SIZE(prop)); + prop = "semtech,ph01-resolution"; else - strncpy(prop, "semtech,ph23-resolution", - ARRAY_SIZE(prop)); + prop = "semtech,ph23-resolution"; ret = device_property_read_u32(dev, prop, &raw); if (ret) @@ -1008,11 +1013,9 @@ sx9324_get_default_reg(struct device *dev, int idx, case SX9324_REG_PROX_CTRL0: case SX9324_REG_PROX_CTRL1: if (reg_def->reg == SX9324_REG_PROX_CTRL0) - strncpy(prop, "semtech,ph01-proxraw-strength", - ARRAY_SIZE(prop)); + prop = "semtech,ph01-proxraw-strength"; else - strncpy(prop, "semtech,ph23-proxraw-strength", - ARRAY_SIZE(prop)); + prop = "semtech,ph23-proxraw-strength"; ret = device_property_read_u32(dev, prop, &raw); if (ret) break; @@ -1081,34 +1084,30 @@ static int sx9324_suspend(struct device *dev) disable_irq_nosync(data->client->irq); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL1, ®val); + if (ret < 0) + return ret; data->suspend_ctrl = FIELD_GET(SX9324_REG_GNRL_CTRL1_PHEN_MASK, regval); - if (ret < 0) - goto out; /* Disable all phases, send the device to sleep. */ - ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0); - -out: - mutex_unlock(&data->mutex); - return ret; + return regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0); } static int sx9324_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); - int ret; - mutex_lock(&data->mutex); - ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, - data->suspend_ctrl | SX9324_REG_GNRL_CTRL1_PAUSECTRL); - mutex_unlock(&data->mutex); - if (ret) - return ret; + scoped_guard(mutex, &data->mutex) { + int ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, + data->suspend_ctrl | + SX9324_REG_GNRL_CTRL1_PAUSECTRL); + if (ret) + return ret; + } enable_irq(data->client->irq); return 0; @@ -1156,4 +1155,4 @@ module_i2c_driver(sx9324_driver); MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); MODULE_DESCRIPTION("Driver for Semtech SX9324 proximity sensor"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(SEMTECH_PROX); +MODULE_IMPORT_NS("SEMTECH_PROX"); diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index 2c4e14a4fe9f..a6ff16e33c1e 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -7,7 +7,6 @@ * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf */ -#include <linux/acpi.h> #include <linux/bits.h> #include <linux/bitfield.h> #include <linux/delay.h> @@ -322,25 +321,16 @@ static int sx9360_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); - int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx_common_read_proximity(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx_common_read_proximity(data, chan, val); + unreachable(); case IIO_CHAN_INFO_HARDWAREGAIN: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - ret = sx9360_read_gain(data, chan, val); - iio_device_release_direct_mode(indio_dev); - return ret; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) + return sx9360_read_gain(data, chan, val); + unreachable(); case IIO_CHAN_INFO_SAMP_FREQ: return sx9360_read_samp_freq(data, val, val2); default: @@ -387,19 +377,15 @@ static int sx9360_read_avail(struct iio_dev *indio_dev, static int sx9360_set_samp_freq(struct sx_common_data *data, int val, int val2) { - int ret, reg; + int reg; __be16 buf; reg = val * 8192 / SX9360_FOSC_HZ + val2 * 8192 / (SX9360_FOSC_MHZ); buf = cpu_to_be16(reg); - mutex_lock(&data->mutex); - - ret = regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf, - sizeof(buf)); + guard(mutex)(&data->mutex); - mutex_unlock(&data->mutex); - - return ret; + return regmap_bulk_write(data->regmap, SX9360_REG_GNRL_CTRL1, &buf, + sizeof(buf)); } static int sx9360_read_thresh(struct sx_common_data *data, int *val) @@ -510,7 +496,6 @@ static int sx9360_read_event_val(struct iio_dev *indio_dev, static int sx9360_write_thresh(struct sx_common_data *data, int _val) { unsigned int val = _val; - int ret; if (val >= 1) val = int_sqrt(2 * val); @@ -518,11 +503,8 @@ static int sx9360_write_thresh(struct sx_common_data *data, int _val) if (val > 0xff) return -EINVAL; - mutex_lock(&data->mutex); - ret = regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_write(data->regmap, SX9360_REG_PROX_CTRL5, val); } static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) @@ -546,18 +528,14 @@ static int sx9360_write_hysteresis(struct sx_common_data *data, int _val) return -EINVAL; hyst = FIELD_PREP(SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, - SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_HYST_MASK, hyst); } static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) { unsigned int regval, val = _val; - int ret; if (val > 0) val = ilog2(val); @@ -566,19 +544,15 @@ static int sx9360_write_far_debounce(struct sx_common_data *data, int _val) regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, - SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_FAR_DEBOUNCE_MASK, + regval); } static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) { unsigned int regval, val = _val; - int ret; if (val > 0) val = ilog2(val); @@ -587,13 +561,10 @@ static int sx9360_write_close_debounce(struct sx_common_data *data, int _val) regval = FIELD_PREP(SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, val); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, - SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, - regval); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, SX9360_REG_PROX_CTRL4, + SX9360_REG_PROX_CTRL4_CLOSE_DEBOUNCE_MASK, + regval); } static int sx9360_write_event_val(struct iio_dev *indio_dev, @@ -630,19 +601,15 @@ static int sx9360_write_gain(struct sx_common_data *data, const struct iio_chan_spec *chan, int val) { unsigned int gain, reg; - int ret; gain = ilog2(val); reg = SX9360_REG_PROX_CTRL0_PHR + chan->channel; gain = FIELD_PREP(SX9360_REG_PROX_CTRL0_GAIN_MASK, gain); - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, reg, - SX9360_REG_PROX_CTRL0_GAIN_MASK, - gain); - mutex_unlock(&data->mutex); - - return ret; + guard(mutex)(&data->mutex); + return regmap_update_bits(data->regmap, reg, + SX9360_REG_PROX_CTRL0_GAIN_MASK, + gain); } static int sx9360_write_raw(struct iio_dev *indio_dev, @@ -704,9 +671,8 @@ static int sx9360_init_compensation(struct iio_dev *indio_dev) int ret; /* run the compensation phase on all channels */ - ret = regmap_update_bits(data->regmap, SX9360_REG_STAT, - SX9360_REG_STAT_COMPSTAT_MASK, - SX9360_REG_STAT_COMPSTAT_MASK); + ret = regmap_set_bits(data->regmap, SX9360_REG_STAT, + SX9360_REG_STAT_COMPSTAT_MASK); if (ret) return ret; @@ -827,36 +793,31 @@ static int sx9360_suspend(struct device *dev) disable_irq_nosync(data->client->irq); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, SX9360_REG_GNRL_CTRL0, ®val); + if (ret < 0) + return ret; data->suspend_ctrl = FIELD_GET(SX9360_REG_GNRL_CTRL0_PHEN_MASK, regval); - if (ret < 0) - goto out; /* Disable all phases, send the device to sleep. */ - ret = regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0); - -out: - mutex_unlock(&data->mutex); - return ret; + return regmap_write(data->regmap, SX9360_REG_GNRL_CTRL0, 0); } static int sx9360_resume(struct device *dev) { struct sx_common_data *data = iio_priv(dev_get_drvdata(dev)); - int ret; - - mutex_lock(&data->mutex); - ret = regmap_update_bits(data->regmap, SX9360_REG_GNRL_CTRL0, - SX9360_REG_GNRL_CTRL0_PHEN_MASK, - data->suspend_ctrl); - mutex_unlock(&data->mutex); - if (ret) - return ret; + scoped_guard(mutex, &data->mutex) { + int ret = regmap_update_bits(data->regmap, + SX9360_REG_GNRL_CTRL0, + SX9360_REG_GNRL_CTRL0_PHEN_MASK, + data->suspend_ctrl); + if (ret) + return ret; + } enable_irq(data->client->irq); return 0; } @@ -904,4 +865,4 @@ module_i2c_driver(sx9360_driver); MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); MODULE_DESCRIPTION("Driver for Semtech SX9360 proximity sensor"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(SEMTECH_PROX); +MODULE_IMPORT_NS("SEMTECH_PROX"); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 550e7d3cd5ee..c4e94d0fb163 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -209,7 +209,7 @@ static int sx9500_inc_users(struct sx9500_data *data, int *counter, /* Bit is already active, nothing to do. */ return 0; - return regmap_update_bits(data->regmap, reg, bitmask, bitmask); + return regmap_set_bits(data->regmap, reg, bitmask); } static int sx9500_dec_users(struct sx9500_data *data, int *counter, @@ -220,7 +220,7 @@ static int sx9500_dec_users(struct sx9500_data *data, int *counter, /* There are more users, do not deactivate. */ return 0; - return regmap_update_bits(data->regmap, reg, bitmask, 0); + return regmap_clear_bits(data->regmap, reg, bitmask); } static int sx9500_inc_chan_users(struct sx9500_data *data, int chan) @@ -540,7 +540,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { struct sx9500_data *data = iio_priv(indio_dev); int ret; @@ -551,7 +551,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, mutex_lock(&data->mutex); - if (state == 1) { + if (state) { ret = sx9500_inc_chan_users(data, chan->channel); if (ret < 0) goto out_unlock; @@ -571,7 +571,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev, goto out_unlock; out_undo_chan: - if (state == 1) + if (state) sx9500_dec_chan_users(data, chan->channel); else sx9500_inc_chan_users(data, chan->channel); @@ -654,8 +654,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = sx9500_read_prox_data(data, &indio_dev->channels[bit], &val); if (ret < 0) @@ -795,8 +794,8 @@ static int sx9500_init_compensation(struct iio_dev *indio_dev) int i, ret; unsigned int val; - ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0, - SX9500_CHAN_MASK, SX9500_CHAN_MASK); + ret = regmap_set_bits(data->regmap, SX9500_REG_PROX_CTRL0, + SX9500_CHAN_MASK); if (ret < 0) return ret; @@ -815,8 +814,8 @@ static int sx9500_init_compensation(struct iio_dev *indio_dev) } out: - regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0, - SX9500_CHAN_MASK, 0); + regmap_clear_bits(data->regmap, SX9500_REG_PROX_CTRL0, + SX9500_CHAN_MASK); return ret; } @@ -1043,8 +1042,8 @@ static const struct of_device_id sx9500_of_match[] = { MODULE_DEVICE_TABLE(of, sx9500_of_match); static const struct i2c_device_id sx9500_id[] = { - {"sx9500", 0}, - { }, + { "sx9500" }, + { } }; MODULE_DEVICE_TABLE(i2c, sx9500_id); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index fe07d1444ac3..f70198a1f0d1 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -53,7 +53,7 @@ const struct iio_event_spec sx_common_events[3] = { BIT(IIO_EV_INFO_VALUE), }, }; -EXPORT_SYMBOL_NS_GPL(sx_common_events, SEMTECH_PROX); +EXPORT_SYMBOL_NS_GPL(sx_common_events, "SEMTECH_PROX"); static irqreturn_t sx_common_irq_handler(int irq, void *private) { @@ -111,17 +111,16 @@ static int sx_common_enable_irq(struct sx_common_data *data, unsigned int irq) { if (!data->client->irq) return 0; - return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk, - irq << data->chip_info->irq_msk_offset, - irq << data->chip_info->irq_msk_offset); + return regmap_set_bits(data->regmap, data->chip_info->reg_irq_msk, + irq << data->chip_info->irq_msk_offset); } static int sx_common_disable_irq(struct sx_common_data *data, unsigned int irq) { if (!data->client->irq) return 0; - return regmap_update_bits(data->regmap, data->chip_info->reg_irq_msk, - irq << data->chip_info->irq_msk_offset, 0); + return regmap_clear_bits(data->regmap, data->chip_info->reg_irq_msk, + irq << data->chip_info->irq_msk_offset); } static int sx_common_update_chan_en(struct sx_common_data *data, @@ -234,7 +233,7 @@ out: return ret; } -EXPORT_SYMBOL_NS_GPL(sx_common_read_proximity, SEMTECH_PROX); +EXPORT_SYMBOL_NS_GPL(sx_common_read_proximity, "SEMTECH_PROX"); /** * sx_common_read_event_config() - Configure event setting. @@ -254,7 +253,7 @@ int sx_common_read_event_config(struct iio_dev *indio_dev, return !!(data->chan_event & BIT(chan->channel)); } -EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX); +EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, "SEMTECH_PROX"); /** * sx_common_write_event_config() - Configure event setting. @@ -269,7 +268,7 @@ EXPORT_SYMBOL_NS_GPL(sx_common_read_event_config, SEMTECH_PROX); int sx_common_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct sx_common_data *data = iio_priv(indio_dev); unsigned int eventirq = SX_COMMON_FAR_IRQ | SX_COMMON_CLOSE_IRQ; @@ -304,7 +303,7 @@ out_unlock: mutex_unlock(&data->mutex); return ret; } -EXPORT_SYMBOL_NS_GPL(sx_common_write_event_config, SEMTECH_PROX); +EXPORT_SYMBOL_NS_GPL(sx_common_write_event_config, "SEMTECH_PROX"); static int sx_common_set_trigger_state(struct iio_trigger *trig, bool state) { @@ -370,8 +369,7 @@ static irqreturn_t sx_common_trigger_handler(int irq, void *private) mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, bit) { ret = data->chip_info->ops.read_prox_data(data, &indio_dev->channels[bit], &val); @@ -399,8 +397,7 @@ static int sx_common_buffer_preenable(struct iio_dev *indio_dev) int bit, ret; mutex_lock(&data->mutex); - for_each_set_bit(bit, indio_dev->active_scan_mask, - indio_dev->masklength) + iio_for_each_active_channel(indio_dev, bit) __set_bit(indio_dev->channels[bit].channel, &channels); ret = sx_common_update_chan_en(data, channels, data->chan_event); @@ -424,27 +421,6 @@ static const struct iio_buffer_setup_ops sx_common_buffer_setup_ops = { .postdisable = sx_common_buffer_postdisable, }; -void sx_common_get_raw_register_config(struct device *dev, - struct sx_common_reg_default *reg_def) -{ -#ifdef CONFIG_ACPI - struct acpi_device *adev = ACPI_COMPANION(dev); - u32 raw = 0, ret; - char prop[80]; - - if (!reg_def->property || !adev) - return; - - snprintf(prop, ARRAY_SIZE(prop), "%s,reg_%s", acpi_device_hid(adev), reg_def->property); - ret = device_property_read_u32(dev, prop, &raw); - if (ret) - return; - - reg_def->def = raw; -#endif -} -EXPORT_SYMBOL_NS_GPL(sx_common_get_raw_register_config, SEMTECH_PROX); - #define SX_COMMON_SOFT_RESET 0xde static int sx_common_init_device(struct device *dev, struct iio_dev *indio_dev) @@ -566,7 +542,7 @@ int sx_common_probe(struct i2c_client *client, return devm_iio_device_register(dev, indio_dev); } -EXPORT_SYMBOL_NS_GPL(sx_common_probe, SEMTECH_PROX); +EXPORT_SYMBOL_NS_GPL(sx_common_probe, "SEMTECH_PROX"); MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); MODULE_DESCRIPTION("Common functions and structures for Semtech sensor"); diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index 101b90e52ff2..259b5c695233 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -8,7 +8,6 @@ #ifndef IIO_SX_COMMON_H #define IIO_SX_COMMON_H -#include <linux/acpi.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> #include <linux/regulator/consumer.h> @@ -126,7 +125,7 @@ struct sx_common_data { /* Ensure correct alignment of timestamp when present. */ struct { __be16 channels[SX_COMMON_MAX_NUM_CHANNELS]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; unsigned int suspend_ctrl; @@ -144,15 +143,12 @@ int sx_common_read_event_config(struct iio_dev *indio_dev, int sx_common_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state); + enum iio_event_direction dir, bool state); int sx_common_probe(struct i2c_client *client, const struct sx_common_chip_info *chip_info, const struct regmap_config *regmap_config); -void sx_common_get_raw_register_config(struct device *dev, - struct sx_common_reg_default *reg_def); - /* 3 is the number of events defined by a single phase. */ extern const struct iio_event_spec sx_common_events[3]; diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index d1ddf85f5383..bb6c9cc88b35 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -449,7 +449,7 @@ static int vcnl3020_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, - int state) + bool state) { switch (chan->type) { case IIO_PROXIMITY: diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index 2cea64bea909..87d10faaff9b 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -20,8 +20,13 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/unaligned.h> #include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #define VL_REG_SYSRANGE_START 0x00 @@ -39,21 +44,74 @@ #define VL_REG_RESULT_INT_STATUS 0x13 #define VL_REG_RESULT_RANGE_STATUS 0x14 +#define VL_REG_IDENTIFICATION_MODEL_ID 0xC0 #define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0) +#define VL53L0X_MODEL_ID_VAL 0xEE +#define VL53L0X_CONTINUOUS_MODE 0x02 +#define VL53L0X_SINGLE_MODE 0x01 + struct vl53l0x_data { struct i2c_client *client; struct completion completion; struct regulator *vdd_supply; struct gpio_desc *reset_gpio; + struct iio_trigger *trig; + + struct { + u16 chan; + aligned_s64 timestamp; + } scan; }; -static irqreturn_t vl53l0x_handle_irq(int irq, void *priv) +static int vl53l0x_clear_irq(struct vl53l0x_data *data) +{ int ret; + + ret = i2c_smbus_write_byte_data(data->client, + VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); + if (ret < 0) { + dev_err(&data->client->dev, "failed to clear irq: %d\n", ret); + return -EINVAL; + } + + return 0; +} + +static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) +{ + struct iio_poll_func *pf = priv; + struct iio_dev *indio_dev = pf->indio_dev; + struct vl53l0x_data *data = iio_priv(indio_dev); + u8 buffer[12]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(data->client, + VL_REG_RESULT_RANGE_STATUS, + sizeof(buffer), buffer); + if (ret < 0) + return ret; + else if (ret != 12) + return -EREMOTEIO; + + data->scan.chan = get_unaligned_be16(&buffer[10]); + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + vl53l0x_clear_irq(data); + + return IRQ_HANDLED; +} + +static irqreturn_t vl53l0x_threaded_irq(int irq, void *priv) { struct iio_dev *indio_dev = priv; struct vl53l0x_data *data = iio_priv(indio_dev); - complete(&data->completion); + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll_nested(indio_dev->trig); + else + complete(&data->completion); return IRQ_HANDLED; } @@ -68,8 +126,9 @@ static int vl53l0x_configure_irq(struct i2c_client *client, if (!irq_flags) irq_flags = IRQF_TRIGGER_FALLING; - ret = devm_request_irq(&client->dev, client->irq, vl53l0x_handle_irq, - irq_flags, indio_dev->name, indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, vl53l0x_threaded_irq, + irq_flags | IRQF_ONESHOT, indio_dev->name, indio_dev); if (ret) { dev_err(&client->dev, "devm_request_irq error: %d\n", ret); return ret; @@ -84,26 +143,6 @@ static int vl53l0x_configure_irq(struct i2c_client *client, return ret; } -static void vl53l0x_clear_irq(struct vl53l0x_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - - ret = i2c_smbus_write_byte_data(data->client, - VL_REG_SYSTEM_INTERRUPT_CLEAR, 1); - if (ret < 0) - dev_err(dev, "failed to clear error irq: %d\n", ret); - - ret = i2c_smbus_write_byte_data(data->client, - VL_REG_SYSTEM_INTERRUPT_CLEAR, 0); - if (ret < 0) - dev_err(dev, "failed to clear range irq: %d\n", ret); - - ret = i2c_smbus_read_byte_data(data->client, VL_REG_RESULT_INT_STATUS); - if (ret < 0 || ret & 0x07) - dev_err(dev, "failed to clear irq: %d\n", ret); -} - static int vl53l0x_read_proximity(struct vl53l0x_data *data, const struct iio_chan_spec *chan, int *val) @@ -125,7 +164,9 @@ static int vl53l0x_read_proximity(struct vl53l0x_data *data, if (time_left == 0) return -ETIMEDOUT; - vl53l0x_clear_irq(data); + ret = vl53l0x_clear_irq(data); + if (ret < 0) + return ret; } else { do { ret = i2c_smbus_read_byte_data(client, @@ -150,7 +191,7 @@ static int vl53l0x_read_proximity(struct vl53l0x_data *data, return -EREMOTEIO; /* Values should be between 30~1200 in millimeters. */ - *val = (buffer[10] << 8) + buffer[11]; + *val = get_unaligned_be16(&buffer[10]); return 0; } @@ -160,7 +201,14 @@ static const struct iio_chan_spec vl53l0x_channels[] = { .type = IIO_DISTANCE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(1), }; static int vl53l0x_read_raw(struct iio_dev *indio_dev, @@ -190,8 +238,16 @@ static int vl53l0x_read_raw(struct iio_dev *indio_dev, } } +static int vl53l0x_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + + return data->trig == trig ? 0 : -EINVAL; +} + static const struct iio_info vl53l0x_info = { .read_raw = vl53l0x_read_raw, + .validate_trigger = vl53l0x_validate_trigger, }; static void vl53l0x_power_off(void *_data) @@ -218,11 +274,46 @@ static int vl53l0x_power_on(struct vl53l0x_data *data) return 0; } +static int vl53l0x_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + + return i2c_smbus_write_byte_data(data->client, VL_REG_SYSRANGE_START, + VL53L0X_CONTINUOUS_MODE); +} + +static int vl53l0x_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct vl53l0x_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, VL_REG_SYSRANGE_START, + VL53L0X_SINGLE_MODE); + if (ret < 0) + return ret; + + /* Let the ongoing reading finish */ + reinit_completion(&data->completion); + wait_for_completion_timeout(&data->completion, HZ / 10); + + return vl53l0x_clear_irq(data); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vl53l0x_buffer_postenable, + .postdisable = &vl53l0x_buffer_postdisable, +}; + +static const struct iio_trigger_ops vl53l0x_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + static int vl53l0x_probe(struct i2c_client *client) { struct vl53l0x_data *data; struct iio_dev *indio_dev; int error; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -237,6 +328,13 @@ static int vl53l0x_probe(struct i2c_client *client) I2C_FUNC_SMBUS_BYTE_DATA)) return -EOPNOTSUPP; + ret = i2c_smbus_read_byte_data(data->client, VL_REG_IDENTIFICATION_MODEL_ID); + if (ret < 0) + return -EINVAL; + + if (ret != VL53L0X_MODEL_ID_VAL) + dev_info(&client->dev, "Unknown model id: 0x%x", ret); + data->vdd_supply = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(data->vdd_supply)) return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply), @@ -265,20 +363,40 @@ static int vl53l0x_probe(struct i2c_client *client) /* usage of interrupt is optional */ if (client->irq) { - int ret; - init_completion(&data->completion); + data->trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &vl53l0x_trigger_ops; + iio_trigger_set_drvdata(data->trig, indio_dev); + ret = devm_iio_trigger_register(&client->dev, data->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(data->trig); + ret = vl53l0x_configure_irq(client, indio_dev); if (ret) return ret; + + ret = devm_iio_triggered_buffer_setup(&client->dev, + indio_dev, + NULL, + &vl53l0x_trigger_handler, + &iio_triggered_buffer_setup_ops); + if (ret) + return ret; } return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id vl53l0x_id[] = { - { "vl53l0x", 0 }, + { "vl53l0x" }, { } }; MODULE_DEVICE_TABLE(i2c, vl53l0x_id); |