> + > + data_size = HX9023S_3BYTES; > + > + /* ch0~ch3 */ > + p = rx_buf; > + size = (HX9023S_CH_NUM - 1) * data_size; > + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH0_0, p, size); > + if (ret) > + return ret; > + > + /* ch4 */ > + p = rx_buf + size; > + size = data_size; > + ret = regmap_bulk_read(data->regmap, HX9023S_RAW_BL_CH4_0, p, size); > + if (ret) > + return ret; > + > + for (i = 0; i < HX9023S_CH_NUM; i++) { [1] Maybe use some per-device (example: indio_dev->num_channels) here (instead of HX9023S_CH_NUM)? If adding support for a part with fewer channels, this would crash. This comment is for all places where for (i = 0; i < HX9023S_CH_NUM; i++) is used > + value = get_unaligned_le16(&rx_buf[i * data_size + 1]); > + value = sign_extend32(value, 15); > + data->ch_data[i].raw = 0; > + data->ch_data[i].bl = 0; > + if (data->ch_data[i].sel_raw == true) > + data->ch_data[i].raw = value; > + if (data->ch_data[i].sel_bl == true) > + data->ch_data[i].bl = value; > + } > + > + /* ch0~ch3 */ > + p = rx_buf; > + size = (HX9023S_CH_NUM - 1) * data_size; > + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH0_0, p, size); > + if (ret) > + return ret; > + > + /* ch4 */ > + p = rx_buf + size; > + size = data_size; > + ret = regmap_bulk_read(data->regmap, HX9023S_LP_DIFF_CH4_0, p, size); > + if (ret) > + return ret; > + > + for (i = 0; i < HX9023S_CH_NUM; i++) { See comment [1] > + value = get_unaligned_le16(&rx_buf[i * data_size + 1]); > + value = sign_extend32(value, 15); > + data->ch_data[i].lp = 0; > + data->ch_data[i].diff = 0; > + if (data->ch_data[i].sel_lp == true) > + data->ch_data[i].lp = value; > + if (data->ch_data[i].sel_diff == true) > + data->ch_data[i].diff = value; > + } > + > + for (i = 0; i < HX9023S_CH_NUM; i++) { See comment [1] > + if (data->ch_data[i].sel_lp == true && data->ch_data[i].sel_bl == true) > + data->ch_data[i].diff = data->ch_data[i].lp - data->ch_data[i].bl; > + } > + > + /* offset DAC */ > + offset_data_size = HX9023S_2BYTES; > + p = rx_buf; > + size = HX9023S_CH_NUM * offset_data_size; > + ret = regmap_bulk_read(data->regmap, HX9023S_OFFSET_DAC0_7_0, p, size); > + if (ret) > + return ret; > + > + for (i = 0; i < HX9023S_CH_NUM; i++) { See comment [1] > + value = get_unaligned_le16(&rx_buf[i * offset_data_size]); > + value = FIELD_GET(GENMASK(11, 0), value); > + data->ch_data[i].dac = value; > + } > + > + ret = hx9023s_data_lock(data, false); > + if (ret) > + return ret; > + > + return 0; > +} > + > +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 fwnode_handle *child; > + struct device *dev = regmap_get_device(data->regmap); > + int ret; > + u32 i, reg, temp, array[2]; > + > + data->chan_in_use = 0; > + for (i = 0; i < HX9023S_CH_NUM; i++) { See comment [1] > + data->ch_data[i].channel_positive = HX9023S_NOT_CONNECTED; > + data->ch_data[i].channel_negative = HX9023S_NOT_CONNECTED; > + } > + > + device_for_each_child_node(dev, child) { > + ret = fwnode_property_read_u32(child, "reg", ®); Maybe add a protection for when reg >= num_channels (HX9023S_CH_NUM)? > + if (ret) { > + fwnode_handle_put(child); > + return dev_err_probe(dev, ret, "Failed to read reg\n"); > + } > + __set_bit(reg, &data->chan_in_use); > + > + if (fwnode_property_read_u32(child, "input-channel", &temp) == 0) { > + data->ch_data[reg].channel_positive = temp; > + data->ch_data[reg].channel_negative = HX9023S_NOT_CONNECTED; > + } else if (fwnode_property_read_u32_array(child, "diff-channels", > + array, sizeof(array)) == 0) { > + data->ch_data[reg].channel_positive = array[0]; > + data->ch_data[reg].channel_negative = array[1]; > + } else { > + fwnode_handle_put(child); > + return dev_err_probe(dev, ret, > + "Failed to read channel input for channel %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; > + > + hx9023s_sample(data); > + hx9023s_get_prox_state(data); > + > + 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, > + int 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); > + int ret; > + unsigned int bit, index, i = 0; > + > + 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; > + } > + > + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { > + 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); > + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) > + __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); > + int ret; > + unsigned int id; > + > + ret = regmap_read(data->regmap, HX9023S_DEVICE_ID, &id); > + if (ret) > + return ret; > + > + if (id == HX9023S_CHIP_ID) { > + indio_dev->name = "hx9023s"; This assignment is quirky here. Maybe move this into the probe function? The rest of the function looks fine. > + return 0; > + } > + > + return -ENODEV; > +} > + > +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(struct hx9023s_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->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_reg_init(data); > + if (ret) > + return dev_err_probe(dev, ret, "device init failed\n"); > + > + ret = hx9023s_ch_cfg(data); > + if (ret) > + return dev_err_probe(dev, ret, "channel config failed\n"); > + > + ret = regcache_sync(data->regmap); > + if (ret) > + return dev_err_probe(dev, ret, "regcache sync 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"); > + > + ret = devm_iio_device_register(dev, indio_dev); A direct return would also work: return devm_iio_device_register(dev, indio_dev); And it would get logged if it happens. > + if (ret) > + return dev_err_probe(dev, ret, "iio device register failed\n"); > + > + return 0; > +} > + > +static int hx9023s_suspend(struct device *dev) > +{ > + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); > + > + hx9023s_interrupt_disable(data); > + > + return 0; > +} > + > +static int hx9023s_resume(struct device *dev) > +{ > + struct hx9023s_data *data = iio_priv(dev_get_drvdata(dev)); > + > + 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@xxxxxxxxx>"); > +MODULE_DESCRIPTION("Driver for TYHX HX9023S SAR sensor"); > +MODULE_LICENSE("GPL"); > > -- > 2.25.1 > >