On Wed, 14 Aug 2024 03:18:08 +0000 wangshuaijie@xxxxxxxxxx wrote: > From: shuaijie wang <wangshuaijie@xxxxxxxxxx> > > AW96103 is a low power consumption capacitive touch and proximity controller. > Each channel can be independently config as sensor input, shield output. > > Channel Information: > aw96103: 3-channel > aw96105: 5-channel > > Signed-off-by: shuaijie wang <wangshuaijie@xxxxxxxxxx> > --- > drivers/iio/proximity/Kconfig | 11 + > drivers/iio/proximity/Makefile | 1 + > drivers/iio/proximity/aw96103.c | 814 ++++++++++++++++++++++++++++++++ > drivers/iio/proximity/aw96103.h | 116 +++++ I've lost track on whether we discussed this earlier, but the header is an unnecessary addition of complexity. It would be better to have those definitions all in the c file. Various other comments inline. Jonathan > 4 files changed, 942 insertions(+) > create mode 100644 drivers/iio/proximity/aw96103.c > create mode 100644 drivers/iio/proximity/aw96103.h > > diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c > new file mode 100644 > index 000000000000..d8ca138de46e > --- /dev/null > +++ b/drivers/iio/proximity/aw96103.c > @@ -0,0 +1,814 @@ > + > +static void aw96103_parsing_bin_file(struct aw_bin *bin) > +{ > + int i; > + > + 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; > + for (i = 0; i < AW96103_BIN_CHIP_TYPE_SIZE; i++) { > + bin->chip_type[i] = > + *(bin->data + AW96103_BIN_CHIP_TYPE_OFFSET + i); looks like an opencoded memcpy, use memcpy instead. > + } > +} > + > + > +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; struct aw96103 *aw96103 = iio_priv(indio_dev); > + int ret; > + > + aw96103 = iio_priv(indio_dev); > + 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_write_hysteresis(struct aw96103 *aw96103, > + const struct iio_chan_spec *chan, int val) > +{ > + unsigned int reg, reg_val; > + int ret; > + > + reg = AW96103_REG_PROXCTRL_CH0 + chan->channel * AW96103_PROXTH_CH_STEP; Perhaps worth a macro #define AW96103_REG_PROXCTRL_CH(x) \ AW96103_REG_PROXCTRL_CH0 + (x) * AW96103_PROXTH_CH_STEP > + reg_val = FIELD_PREP(AW96103_THHYST_MASK, val); > + mutex_lock(&aw96103->mutex); guard(mutex)(&aw96103->mutex); return regmap_update_bits(aw96103->regmap, AS96103_REG_PROXCTRL_CH(chan->channel), AW96103_THHYST_MASK, FIELD_PREP(AW96103_THHYST_MASK, val)); Same for all other cod that looks like this. > + ret = regmap_update_bits(aw96103->regmap, reg, > + AW96103_THHYST_MASK, reg_val); > + mutex_unlock(&aw96103->mutex); > + > + return ret; > +} ... > +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, int state) > +{ > + struct aw96103 *aw96103 = iio_priv(indio_dev); > + > + aw96103->channels_arr[chan->channel].used = !!state; > + > + if (!!state) > + return regmap_update_bits(aw96103->regmap, > + AW96103_REG_SCANCTRL0, > + BIT(chan->channel), BIT(chan->channel)); > + else > + return regmap_update_bits(aw96103->regmap, > + AW96103_REG_SCANCTRL0, > + BIT(chan->channel), 0); unsigned int val = !!state; ad96103->channels_ar[chan->channel].used = val; return regmap_update_bits(aw96103->regmap, AW96103_REG_SCAN_CTRL0, val); > +} ... > + > +static int aw96103_iio_init(struct iio_dev *indio_dev) > +{ > + struct aw96103 *aw96103 = iio_priv(indio_dev); > + struct iio_chan_spec *aw96103_channels; > + unsigned int i; > + > + /* > + * 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. > + */ > + aw96103->channels_arr = devm_kcalloc(aw96103->dev, > + aw96103->max_channels + 1, > + sizeof(struct aw_channels_info), GFP_KERNEL); > + if (!aw96103->channels_arr) > + return -ENOMEM; Simpler to just embed an array of the maximum size. Given overheads of allocator etc and small size of structure that may be more efficient as well as reduce amount of code. > + > + aw96103_channels = devm_kcalloc(aw96103->dev, aw96103->max_channels + 1, > + sizeof(*aw96103_channels), GFP_KERNEL); > + if (!aw96103_channels) > + return -ENOMEM; Same for this one. Just embed the maximum size you might need in the iio_priv() structure rather than allocating fresh. However, max_channels is only either 3 or 5. Better to just have to static const arrays of channels and pick between them thus removing need to do any dynamic instantiating here. See a driver like adc/max1363 for an extreme example of this. > + > + for (i = 0; i <= aw96103->max_channels; i++) { > + aw96103_channels[i].type = IIO_PROXIMITY; > + aw96103_channels[i].info_mask_separate = > + BIT(IIO_CHAN_INFO_RAW); > + aw96103_channels[i].indexed = 1; > + aw96103_channels[i].channel = i; > + aw96103_channels[i].event_spec = aw_common_events; > + aw96103_channels[i].num_event_specs = > + ARRAY_SIZE(aw_common_events); > + } With all the above as a simple one line pick of data from the per device type structure you'll be adding (See below) this function will do very little. So just have the code inline at the caller. > + > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->num_channels = aw96103->max_channels + 1; > + indio_dev->channels = aw96103_channels; > + indio_dev->info = &iio_info; > + indio_dev->name = "aw96103_sensor"; > + indio_dev->dev.parent = aw96103->dev; > + > + return devm_iio_device_register(aw96103->dev, indio_dev); > +} > + > +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, blfilt1_tmp; > + unsigned char i; > + int 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 ((aw96103->vers == AW96103A) && (aw_bin->chip_type[7] == '\0')) { Pull the version register read to here so that you don't need to store the value in your iio_priv() structure. > + 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; > + blfilt1_tmp = FIELD_GET(AW96103_BLERRTRIG_MASK, > + blfilt1_data); > + if (blfilt1_tmp == 1) { > + 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) Align to just after ( > +{ > + 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 = *(u16 *)(aw_bin_data_s->data + start_addr); > + reg_data = *(u32 *)(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 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, "%s: No firmware.\n", __func__); The func isn't really useful, so drop it. The error message + device is enough for debugging. > + return; > + } > + > + ret = aw96103_cfg_all_loaded(fw, aw96103); > + if (ret) Add a comment here on why this makes sense on error. > + ret = aw96103_para_loaded(aw96103); If this returns an error handle it. > + release_firmware(fw); > + > + 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); > + msleep(20); Add a comment / reference for why 20 msecs. > + > + return ret; > +} > + > +static void aw96103_irq_handle(struct iio_dev *indio_dev) > +{ > + struct aw96103 *aw96103 = iio_priv(indio_dev); > + u32 curr_status_val; > + u32 curr_status; > + unsigned char i; > + int ret; > + > + ret = regmap_read(aw96103->regmap, AW96103_REG_STAT0, &curr_status_val); > + if (ret) > + return; > + > + /* > + * Iteratively analyze the interrupt status of different channels, > + * with each channel having 4 interrupt states. > + */ > + for (i = 0; i < AW_CHANNEL_MAX; i++) { > + 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].used || I'd do this check before computing curr_status as no point in looking at data that we don't care about. > + (aw96103->channels_arr[i].last_channel_info == curr_status)) and keep this one here. > + 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; > + } > + aw96103->channels_arr[i].last_channel_info = curr_status; > + } > +} > + > +static void aw96103_interrupt_clear(struct iio_dev *indio_dev) > +{ > + struct aw96103 *aw96103 = iio_priv(indio_dev); > + int ret; > + > + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, > + &aw96103->irq_status); > + if (ret) > + return; As below. Use a local variable for irq_status here as nothing else ever reads what you are storing into it here > + > + aw96103_irq_handle(indio_dev); Even this nesting doesn't add much. I'd bring that code inline as well as the suggestion below. > +} > + > +static irqreturn_t aw96103_irq(int irq, void *data) > +{ > + struct iio_dev *indio_dev = (struct iio_dev *)data; Never need to cast explicitly from a void * to another pointer type (the C specification defines this as always fine). > + > + aw96103_interrupt_clear(indio_dev); Very little code in interrupt_clear() just have it inline here instead. > + > + return IRQ_HANDLED; > +} > +static int aw96103_wait_chip_init(struct aw96103 *aw96103) > +{ > + unsigned int cnt = 20; > + u32 reg_data; > + int ret; > + > + while (cnt--) { Document why this retry count etc. > + ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, > + ®_data); > + if (ret) > + return ret; > + > + if (FIELD_GET(AW96103_INITOVERIRQ_MASK, reg_data)) > + return 0; > + mdelay(1); > + } > + > + return -EINVAL; > +} > + > +static int aw96103_read_chipid(struct aw96103 *aw96103) > +{ > + unsigned char cnt = 0; > + u32 reg_val = 0; > + int ret; > + > + while (cnt < AW_READ_CHIPID_RETRIES) { As below. The retry number and sleep length needs documenting. If it's just 'this works' not something from the datasheet that is fine, but say it. > + ret = regmap_read(aw96103->regmap, AW96103_REG_CHIPID, > + ®_val); > + if (ret < 0) { > + cnt++; > + usleep_range(2000, 3000); > + } else { > + reg_val = FIELD_GET(AW96103_CHIPID_MASK, reg_val); > + break; > + } > + } Two things here and a failure to read anything should be an error. if (cnt == AW_READ_CHIPID_RETRIES) return -ETIMEDOUT; if (FIELD_GET(AW...) != AW6103_CHIP_ID) dev_info(...) return 0; > + > + if (reg_val != AW96103_CHIP_ID) dev_info() only as this may be a DT fallback compatible situation which is not an err. > + dev_err(aw96103->dev, > + "chipid error, error_id=0x%08X\n", reg_val); Also change comment to "unexpected chipid" > + > + return 0; > +} > + > +static int aw96103_version_init(struct aw96103 *aw96103) > +{ > + u32 fw_ver; > + int ret; > + > + ret = regmap_read(aw96103->regmap, AW96103_REG_FWVER2, &fw_ver); > + if (ret) As above, just read this when you need it and match directly on the field rather than converting to another representation. > + return ret; > + if (fw_ver == AW_CHIP_AW96103A) > + aw96103->vers = AW96103A; > + else > + aw96103->vers = AW96103; > + > + return 0; > +} > + > +static int aw96103_i2c_probe(struct i2c_client *i2c) > +{ > + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); This is fragile. Fix the enum vs pointer thing (see below) and use i2c_get_match_data() to get the structure. > + enum aw96103_sensor_type sensor_type; > + struct iio_dev *aw_iio_dev; > + struct aw96103 *aw96103; > + int ret; > + > + aw_iio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*aw96103)); > + if (!aw_iio_dev) > + return -ENOMEM; > + > + aw96103 = iio_priv(aw_iio_dev); > + aw96103->dev = &i2c->dev; > + aw96103->i2c = i2c; > + mutex_init(&aw96103->mutex); > + i2c_set_clientdata(i2c, aw96103); Is this used? If not don't set it. > + > + sensor_type = (enum aw96103_sensor_type)id->driver_data; > + switch (sensor_type) { > + case AW96103_VAL: > + aw96103->max_channels = AW_CHANNEL3; Encode this in the per type structure you are going to use to replace sensor_type in the match tables below. Where possible it is better to use data than code to differentiate particular device variants. As you add more parts it scales much better than thsi. > + break; > + case AW96105_VAL: > + aw96103->max_channels = AW_CHANNEL5; > + break; > + default: > + return -EINVAL; > + } > + > + 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 = aw96103_version_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(aw_iio_dev); > + if (ret) > + return ret; > + > + return aw96103_iio_init(aw_iio_dev); > +} > + > +static const struct of_device_id aw96103_dt_match[] = { > + { .compatible = "awinic,aw96103", (void *)AW96103_VAL }, > + { .compatible = "awinic,aw96105", (void *)AW96105_VAL }, Don't use enums in here. Define a structure for each one with all relevant data in it. > + { } > +}; > +MODULE_DEVICE_TABLE(of, aw96103_dt_match); > + > +static const struct i2c_device_id aw96103_i2c_id[] = { > + { "aw96103", AW96103_VAL }, > + { "aw96105", 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@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Driver for Awinic AW96103 proximity sensor"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/iio/proximity/aw96103.h b/drivers/iio/proximity/aw96103.h > new file mode 100644 > index 000000000000..78716e34b54c > --- /dev/null > +++ b/drivers/iio/proximity/aw96103.h > @@ -0,0 +1,116 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +#ifndef _AW96103_H_ > +#define _AW96103_H_ > + > +#define AW_DATA_PROCESS_FACTOR 1024 > +#define AW_CHIP_AW96103A 0x03000b00 > +#define AW_READ_CHIPID_RETRIES 3 Why? If you are going to do a retry count to allow a chip to finish booting or similar add a comment on why you chose this value. May be better to get rid of define and document the behaviour at the one place it is used. There the delay per retry is also visible. > +#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) > + > +/** > + * 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, I would specify all values given they don't start at 0. > + AW96103_SLEEP_MODE, > + AW96103_DEEPSLEEP_MODE, > + AW96103B_DEEPSLEEP_MODE, > +}; > + > +enum aw96103_channel { > + AW_CHANNEL0, > + AW_CHANNEL1, > + AW_CHANNEL2, > + AW_CHANNEL3, > + AW_CHANNEL4, > + AW_CHANNEL5, > + AW_CHANNEL_MAX This is just encodeing 0 == 0. Drop this and use the values directly they are not magic numebrs! > +}; > + > +enum aw96103_irq_trigger_position { > + FAR, > + TRIGGER_TH0, Given non obvious later values, better to specify them all This is only used in one place and isn't directly related to device state (due to the prior bit manipulations applied to the register), so I'd put the definition right above the code. > + TRIGGER_TH1 = 0x03, > + TRIGGER_TH2 = 0x07, > + TRIGGER_TH3 = 0x0f, > +}; > + > +enum aw96103_sensor_type { > + AW96103_VAL, > + AW96105_VAL, > +}; > + > +struct aw_channels_info { > + bool used; > + unsigned int last_channel_info; Add a comment on this or rename - name doesn't make it sufficiently obvious what info is being stored in here. > +}; > + > +struct aw96103 { > + unsigned char vers; As mentioned above, I'd just read the register when ever you need this information. No point in storing a copy - it's not in a high performance path. > + unsigned int irq_status; This is only ever read into as part of clearing interrupts. Just use a local variable > + unsigned int hostirqen; > + struct delayed_work cfg_work; > + struct i2c_client *i2c; Unused (or certainly should be as you have regmap). > + struct regmap *regmap; > + struct device *dev; > + struct aw_bin *aw_bin; Not used? > + struct aw_channels_info *channels_arr; > + unsigned char chip_type[9]; I think this is only used locally in parsing code. Better to keep the data in a local variable that you pass to relevant functions. > + unsigned int max_channels; > + unsigned int chan_en; > + struct mutex mutex; Any lock must have documentation comment. What data is this protecting? The scope of many locks is non obvious and failing to document them can lead to future bugs as a driver is modified over time. > +}; > + > +#endif > +