Re: [PATCH v3 3/5] iio: proximity: Add SX9324 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Quoting Gwendal Grignou (2021-11-17 00:46:29)
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-sx9324 b/Documentation/ABI/testing/sysfs-bus-iio-sx9324
> new file mode 100644
> index 00000000000000..db38cb307244e1
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-sx9324
> @@ -0,0 +1,28 @@
> +What:          /sys/bus/iio/devices/iio:deviceX/in_proximity<id>_setup
> +Date:          November 2021
> +KernelVersion: 5.14

I suppose this needs an update.

> diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c
> new file mode 100644
> index 00000000000000..21423999a192ff
> --- /dev/null
> +++ b/drivers/iio/proximity/sx9324.c
> @@ -0,0 +1,936 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2021 Google LLC.
> + *
> + * Driver for Semtech's SX9324 capacitive proximity/button solution.
> + * Based on SX9324 driver and copy of datasheet at:
> + * https://edit.wpgdadawant.com/uploads/news_file/program/2019/30184/tech_files/program_30184_suggest_other_file.pdf
> + *
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/gpio/consumer.h>

Is this include used?

> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/pm.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/events.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include "sx_common.h"
> +
> +#define SX9324_DRIVER_NAME             "sx9324"
> +
[...]
> +
> +static int sx9324_read_thresh(struct sx_common_data *data,
> +                             const struct iio_chan_spec *chan, int *val)
> +{
> +       unsigned int regval;
> +       unsigned int reg;
> +       int ret;
> +
> +       /*
> +        * TODO(gwendal): Depending on the phase function
> +        * (proximity/table/body), retrieve the right threshold.

What is used right now? proximity threshold? Would be good to have that
clarified here for future readers.

> +        */
> +       reg = SX9324_REG_PROX_CTRL6 + chan->channel / 2;
> +       ret = regmap_read(data->regmap, reg, &regval);
> +       if (ret)
> +               return ret;
> +
> +       if (regval <= 1)
> +               *val = regval;
> +       else
> +               *val = (regval * regval) / 2;
> +
> +       return IIO_VAL_INT;
> +}
> +
[...]
> +
> +static int sx9324_write_close_debounce(struct sx_common_data *data, int val)
> +{
> +       unsigned int regval;
> +       int ret;
> +
> +       if (val > 0)
> +               val = ilog2(val);

If val is less than 0 does FIELD_FIT bail out? Seems like we should
ignore negative values and/or treat them as 0?

> +       if (!FIELD_FIT(SX9324_REG_PROX_CTRL5_CLOSE_DEBOUNCE_MASK, val))
> +               return -EINVAL;
> +
> +       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);
> +
> +       return ret;
> +}
> +
> +static int sx9324_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 sx_common_data *data = iio_priv(indio_dev);
> +
> +       if (chan->type != IIO_PROXIMITY)
> +               return -EINVAL;
> +
> +       switch (info) {
> +       case IIO_EV_INFO_VALUE:
> +               return sx9324_write_thresh(data, chan, val);
> +       case IIO_EV_INFO_PERIOD:
> +               switch (dir) {
> +               case IIO_EV_DIR_RISING:
> +                       return sx9324_write_far_debounce(data, val);
> +               case IIO_EV_DIR_FALLING:
> +                       return sx9324_write_close_debounce(data, val);
> +               default:
> +                       return -EINVAL;
> +               }
> +       case IIO_EV_INFO_HYSTERESIS:
> +               return sx9324_write_hysteresis(data, chan, val);
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int sx9324_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 = SX9324_REG_PROX_CTRL0 + chan->channel / 2;
> +       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);
> +
> +       return ret;
> +}
> +
> +static int sx9324_write_raw(struct iio_dev *indio_dev,
> +                           const struct iio_chan_spec *chan, int val, int val2,
> +                           long mask)
> +{
> +       struct sx_common_data *data = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_SAMP_FREQ:
> +               return sx9324_set_samp_freq(data, val, val2);
> +       case IIO_CHAN_INFO_HARDWAREGAIN:
> +               return sx9324_write_gain(data, chan, val);
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +/* Activate all channels and perform an initial compensation. */
> +static int sx9324_init_compensation(struct iio_dev *indio_dev)
> +{
> +       struct sx_common_data *data = iio_priv(indio_dev);
> +       unsigned int val;
> +       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);
> +       if (ret)
> +               return ret;
> +
> +       ret = regmap_read_poll_timeout(data->regmap, SX9324_REG_STAT2, val,
> +                                      !(val & SX9324_REG_STAT2_COMPSTAT_MASK),
> +                                      20000, 2000000);
> +       if (ret == -ETIMEDOUT)
> +               dev_err(&data->client->dev,
> +                       "initial compensation timed out: 0x%02x\n",
> +                       val);
> +       return ret;
> +}
> +
> +static int sx9324_check_whoami(struct device *dev,
> +                              struct iio_dev *indio_dev)
> +{
> +       struct sx_common_data *data = iio_priv(indio_dev);
> +       unsigned int long ddata;
> +       unsigned int whoami;
> +       int ret;
> +
> +       ret = regmap_read(data->regmap, SX9324_REG_WHOAMI, &whoami);
> +       if (ret) {
> +               dev_err(dev, "error in reading WHOAMI register: %d", ret);
> +               return ret;
> +       }
> +
> +       ddata = (uintptr_t)device_get_match_data(dev);
> +       if (ddata != whoami) {
> +               dev_err(dev, "WHOAMI does not match device data: %u\n", whoami);
> +               return -ENODEV;
> +       }
> +
> +       switch (whoami) {
> +       case SX9324_WHOAMI_VALUE:
> +               indio_dev->name = "sx9324";
> +               break;
> +       default:
> +               dev_err(dev, "unexpected WHOAMI response: %u\n", whoami);
> +               return -ENODEV;
> +       }

Is this necessary? There's only one match for this driver.

> +
> +       return 0;
> +}
> +
> +static const struct sx_common_chip_info sx9324_chip_info = {
> +       .reg_stat = SX9324_REG_STAT0,
> +       .reg_irq_msk = SX9324_REG_IRQ_MSK,
> +       .reg_enable_chan = SX9324_REG_GNRL_CTRL1,
> +       .reg_reset = SX9324_REG_RESET,
> +
> +       .mask_enable_chan = SX9324_REG_GNRL_CTRL1_PHEN_MASK,
> +       .irq_msk_offset = 3,
> +       .num_channels = SX9324_NUM_CHANNELS,
> +
> +       .ops = {
> +               .read_prox_data = sx9324_read_prox_data,
> +               .check_whoami = sx9324_check_whoami,
> +               .init_compensation = sx9324_init_compensation,
> +               .wait_for_sample = sx9324_wait_for_sample,
> +       },
> +
> +       .iio_channels = sx9324_channels,
> +       .num_iio_channels = ARRAY_SIZE(sx9324_channels),
> +       .iio_info =  {
> +               .read_raw = sx9324_read_raw,
> +               .read_avail = sx9324_read_avail,
> +               .read_event_value = sx9324_read_event_val,
> +               .write_event_value = sx9324_write_event_val,
> +               .write_raw = sx9324_write_raw,
> +               .read_event_config = sx_common_read_event_config,
> +               .write_event_config = sx_common_write_event_config,
> +       },
> +};
> +
> +static int sx9324_probe(struct i2c_client *client)
> +{
> +       return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config);
> +}
> +
> +static int __maybe_unused sx9324_suspend(struct device *dev)
> +{
> +       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +       struct sx_common_data *data = iio_priv(indio_dev);
> +       unsigned int regval;
> +       int ret;
> +
> +       disable_irq_nosync(data->client->irq);
> +
> +       mutex_lock(&data->mutex);
> +       ret = regmap_read(data->regmap, SX9324_REG_GNRL_CTRL1, &regval);
> +
> +       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

Please use /* comments like this */

> +       ret = regmap_write(data->regmap, SX9324_REG_GNRL_CTRL1, 0);
> +
> +out:
> +       mutex_unlock(&data->mutex);
> +       return ret;
> +}
> +
> +static int __maybe_unused sx9324_resume(struct device *dev)
> +{
> +       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +       struct sx_common_data *data = iio_priv(indio_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;
> +
> +       enable_irq(data->client->irq);
> +       return 0;
> +}
> +
> +static const struct dev_pm_ops sx9324_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(sx9324_suspend, sx9324_resume)
> +};
> +
> +static const struct acpi_device_id sx9324_acpi_match[] = {
> +       { "STH9324", SX9324_WHOAMI_VALUE},
> +       { },

Nitpick: Drop comma after sentinel to avoid adding something after
without compilation error.

> +};
> +MODULE_DEVICE_TABLE(acpi, sx9324_acpi_match);
> +
> +static const struct of_device_id sx9324_of_match[] = {
> +       { .compatible = "semtech,sx9324", (void *)SX9324_WHOAMI_VALUE},
> +       { }
> +};
> +MODULE_DEVICE_TABLE(of, sx9324_of_match);
> +
> +static const struct i2c_device_id sx9324_id[] = {
> +       {"sx9324", SX9324_WHOAMI_VALUE},
> +       { },

Nitpick: Drop comma after sentinel to avoid adding something after
without compilation error.

> +};
> +MODULE_DEVICE_TABLE(i2c, sx9324_id);
> +
> +static struct i2c_driver sx9324_driver = {
> +       .driver = {
> +               .name   = SX9324_DRIVER_NAME,

If it's only used one time it's better to drop the #define

> +               .acpi_match_table = ACPI_PTR(sx9324_acpi_match),
> +               .of_match_table = of_match_ptr(sx9324_of_match),
> +               .pm = &sx9324_pm_ops,
> +



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux