| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Driver for Murata IRS-D200 PIR sensor. |
| * |
| * Copyright (C) 2023 Axis Communications AB |
| */ |
| |
| #include <linux/unaligned.h> |
| #include <linux/bitfield.h> |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/regmap.h> |
| |
| #include <linux/iio/buffer.h> |
| #include <linux/iio/events.h> |
| #include <linux/iio/iio.h> |
| #include <linux/iio/trigger.h> |
| #include <linux/iio/trigger_consumer.h> |
| #include <linux/iio/triggered_buffer.h> |
| #include <linux/iio/types.h> |
| |
| #define IRS_DRV_NAME "irsd200" |
| |
| /* Registers. */ |
| #define IRS_REG_OP 0x00 /* Operation mode. */ |
| #define IRS_REG_DATA_LO 0x02 /* Sensor data LSB. */ |
| #define IRS_REG_DATA_HI 0x03 /* Sensor data MSB. */ |
| #define IRS_REG_STATUS 0x04 /* Interrupt status. */ |
| #define IRS_REG_COUNT 0x05 /* Count of exceeding threshold. */ |
| #define IRS_REG_DATA_RATE 0x06 /* Output data rate. */ |
| #define IRS_REG_FILTER 0x07 /* High-pass and low-pass filter. */ |
| #define IRS_REG_INTR 0x09 /* Interrupt mode. */ |
| #define IRS_REG_NR_COUNT 0x0a /* Number of counts before interrupt. */ |
| #define IRS_REG_THR_HI 0x0b /* Upper threshold. */ |
| #define IRS_REG_THR_LO 0x0c /* Lower threshold. */ |
| #define IRS_REG_TIMER_LO 0x0d /* Timer setting LSB. */ |
| #define IRS_REG_TIMER_HI 0x0e /* Timer setting MSB. */ |
| |
| /* Interrupt status bits. */ |
| #define IRS_INTR_DATA 0 /* Data update. */ |
| #define IRS_INTR_TIMER 1 /* Timer expiration. */ |
| #define IRS_INTR_COUNT_THR_AND 2 /* Count "AND" threshold. */ |
| #define IRS_INTR_COUNT_THR_OR 3 /* Count "OR" threshold. */ |
| |
| /* Operation states. */ |
| #define IRS_OP_ACTIVE 0x00 |
| #define IRS_OP_SLEEP 0x01 |
| |
| /* |
| * Quantization scale value for threshold. Used for conversion from/to register |
| * value. |
| */ |
| #define IRS_THR_QUANT_SCALE 128 |
| |
| #define IRS_UPPER_COUNT(count) FIELD_GET(GENMASK(7, 4), count) |
| #define IRS_LOWER_COUNT(count) FIELD_GET(GENMASK(3, 0), count) |
| |
| /* Index corresponds to the value of IRS_REG_DATA_RATE register. */ |
| static const int irsd200_data_rates[] = { |
| 50, |
| 100, |
| }; |
| |
| /* Index corresponds to the (field) value of IRS_REG_FILTER register. */ |
| static const unsigned int irsd200_lp_filter_freq[] = { |
| 10, |
| 7, |
| }; |
| |
| /* |
| * Index corresponds to the (field) value of IRS_REG_FILTER register. Note that |
| * this represents a fractional value (e.g the first value corresponds to 3 / 10 |
| * = 0.3 Hz). |
| */ |
| static const unsigned int irsd200_hp_filter_freq[][2] = { |
| { 3, 10 }, |
| { 5, 10 }, |
| }; |
| |
| /* Register fields. */ |
| enum irsd200_regfield { |
| /* Data interrupt. */ |
| IRS_REGF_INTR_DATA, |
| /* Timer interrupt. */ |
| IRS_REGF_INTR_TIMER, |
| /* AND count threshold interrupt. */ |
| IRS_REGF_INTR_COUNT_THR_AND, |
| /* OR count threshold interrupt. */ |
| IRS_REGF_INTR_COUNT_THR_OR, |
| |
| /* Low-pass filter frequency. */ |
| IRS_REGF_LP_FILTER, |
| /* High-pass filter frequency. */ |
| IRS_REGF_HP_FILTER, |
| |
| /* Sentinel value. */ |
| IRS_REGF_MAX |
| }; |
| |
| static const struct reg_field irsd200_regfields[] = { |
| [IRS_REGF_INTR_DATA] = |
| REG_FIELD(IRS_REG_INTR, IRS_INTR_DATA, IRS_INTR_DATA), |
| [IRS_REGF_INTR_TIMER] = |
| REG_FIELD(IRS_REG_INTR, IRS_INTR_TIMER, IRS_INTR_TIMER), |
| [IRS_REGF_INTR_COUNT_THR_AND] = REG_FIELD( |
| IRS_REG_INTR, IRS_INTR_COUNT_THR_AND, IRS_INTR_COUNT_THR_AND), |
| [IRS_REGF_INTR_COUNT_THR_OR] = REG_FIELD( |
| IRS_REG_INTR, IRS_INTR_COUNT_THR_OR, IRS_INTR_COUNT_THR_OR), |
| |
| [IRS_REGF_LP_FILTER] = REG_FIELD(IRS_REG_FILTER, 1, 1), |
| [IRS_REGF_HP_FILTER] = REG_FIELD(IRS_REG_FILTER, 0, 0), |
| }; |
| |
| static const struct regmap_config irsd200_regmap_config = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| .max_register = IRS_REG_TIMER_HI, |
| }; |
| |
| struct irsd200_data { |
| struct regmap *regmap; |
| struct regmap_field *regfields[IRS_REGF_MAX]; |
| struct device *dev; |
| }; |
| |
| static int irsd200_setup(struct irsd200_data *data) |
| { |
| unsigned int val; |
| int ret; |
| |
| /* Disable all interrupt sources. */ |
| ret = regmap_write(data->regmap, IRS_REG_INTR, 0); |
| if (ret) { |
| dev_err(data->dev, "Could not set interrupt sources (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| /* Set operation to active. */ |
| ret = regmap_write(data->regmap, IRS_REG_OP, IRS_OP_ACTIVE); |
| if (ret) { |
| dev_err(data->dev, "Could not set operation mode (%d)\n", ret); |
| return ret; |
| } |
| |
| /* Clear threshold count. */ |
| ret = regmap_read(data->regmap, IRS_REG_COUNT, &val); |
| if (ret) { |
| dev_err(data->dev, "Could not clear threshold count (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| /* Clear status. */ |
| ret = regmap_write(data->regmap, IRS_REG_STATUS, 0x0f); |
| if (ret) { |
| dev_err(data->dev, "Could not clear status (%d)\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_threshold(struct irsd200_data *data, |
| enum iio_event_direction dir, int *val) |
| { |
| unsigned int regval; |
| unsigned int reg; |
| int scale; |
| int ret; |
| |
| /* Set quantization scale. */ |
| if (dir == IIO_EV_DIR_RISING) { |
| scale = IRS_THR_QUANT_SCALE; |
| reg = IRS_REG_THR_HI; |
| } else if (dir == IIO_EV_DIR_FALLING) { |
| scale = -IRS_THR_QUANT_SCALE; |
| reg = IRS_REG_THR_LO; |
| } else { |
| return -EINVAL; |
| } |
| |
| ret = regmap_read(data->regmap, reg, ®val); |
| if (ret) { |
| dev_err(data->dev, "Could not read threshold (%d)\n", ret); |
| return ret; |
| } |
| |
| *val = ((int)regval) * scale; |
| |
| return 0; |
| } |
| |
| static int irsd200_write_threshold(struct irsd200_data *data, |
| enum iio_event_direction dir, int val) |
| { |
| unsigned int regval; |
| unsigned int reg; |
| int scale; |
| int ret; |
| |
| /* Set quantization scale. */ |
| if (dir == IIO_EV_DIR_RISING) { |
| if (val < 0) |
| return -ERANGE; |
| |
| scale = IRS_THR_QUANT_SCALE; |
| reg = IRS_REG_THR_HI; |
| } else if (dir == IIO_EV_DIR_FALLING) { |
| if (val > 0) |
| return -ERANGE; |
| |
| scale = -IRS_THR_QUANT_SCALE; |
| reg = IRS_REG_THR_LO; |
| } else { |
| return -EINVAL; |
| } |
| |
| regval = val / scale; |
| |
| if (regval >= BIT(8)) |
| return -ERANGE; |
| |
| ret = regmap_write(data->regmap, reg, regval); |
| if (ret) { |
| dev_err(data->dev, "Could not write threshold (%d)\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_data(struct irsd200_data *data, s16 *val) |
| { |
| __le16 buf; |
| int ret; |
| |
| ret = regmap_bulk_read(data->regmap, IRS_REG_DATA_LO, &buf, |
| sizeof(buf)); |
| if (ret) { |
| dev_err(data->dev, "Could not bulk read data (%d)\n", ret); |
| return ret; |
| } |
| |
| *val = le16_to_cpu(buf); |
| |
| return 0; |
| } |
| |
| static int irsd200_read_data_rate(struct irsd200_data *data, int *val) |
| { |
| unsigned int regval; |
| int ret; |
| |
| ret = regmap_read(data->regmap, IRS_REG_DATA_RATE, ®val); |
| if (ret) { |
| dev_err(data->dev, "Could not read data rate (%d)\n", ret); |
| return ret; |
| } |
| |
| if (regval >= ARRAY_SIZE(irsd200_data_rates)) |
| return -ERANGE; |
| |
| *val = irsd200_data_rates[regval]; |
| |
| return 0; |
| } |
| |
| static int irsd200_write_data_rate(struct irsd200_data *data, int val) |
| { |
| size_t idx; |
| int ret; |
| |
| for (idx = 0; idx < ARRAY_SIZE(irsd200_data_rates); ++idx) { |
| if (irsd200_data_rates[idx] == val) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(irsd200_data_rates)) |
| return -ERANGE; |
| |
| ret = regmap_write(data->regmap, IRS_REG_DATA_RATE, idx); |
| if (ret) { |
| dev_err(data->dev, "Could not write data rate (%d)\n", ret); |
| return ret; |
| } |
| |
| /* |
| * Data sheet says the device needs 3 seconds of settling time. The |
| * device operates normally during this period though. This is more of a |
| * "guarantee" than trying to prevent other user space reads/writes. |
| */ |
| ssleep(3); |
| |
| return 0; |
| } |
| |
| static int irsd200_read_timer(struct irsd200_data *data, int *val, int *val2) |
| { |
| __le16 buf; |
| int ret; |
| |
| ret = regmap_bulk_read(data->regmap, IRS_REG_TIMER_LO, &buf, |
| sizeof(buf)); |
| if (ret) { |
| dev_err(data->dev, "Could not bulk read timer (%d)\n", ret); |
| return ret; |
| } |
| |
| ret = irsd200_read_data_rate(data, val2); |
| if (ret) |
| return ret; |
| |
| *val = le16_to_cpu(buf); |
| |
| return 0; |
| } |
| |
| static int irsd200_write_timer(struct irsd200_data *data, int val, int val2) |
| { |
| unsigned int regval; |
| int data_rate; |
| __le16 buf; |
| int ret; |
| |
| if (val < 0 || val2 < 0) |
| return -ERANGE; |
| |
| ret = irsd200_read_data_rate(data, &data_rate); |
| if (ret) |
| return ret; |
| |
| /* Quantize from seconds. */ |
| regval = val * data_rate + (val2 * data_rate) / 1000000; |
| |
| /* Value is 10 bits. */ |
| if (regval >= BIT(10)) |
| return -ERANGE; |
| |
| buf = cpu_to_le16((u16)regval); |
| |
| ret = regmap_bulk_write(data->regmap, IRS_REG_TIMER_LO, &buf, |
| sizeof(buf)); |
| if (ret) { |
| dev_err(data->dev, "Could not bulk write timer (%d)\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_nr_count(struct irsd200_data *data, int *val) |
| { |
| unsigned int regval; |
| int ret; |
| |
| ret = regmap_read(data->regmap, IRS_REG_NR_COUNT, ®val); |
| if (ret) { |
| dev_err(data->dev, "Could not read nr count (%d)\n", ret); |
| return ret; |
| } |
| |
| *val = regval; |
| |
| return 0; |
| } |
| |
| static int irsd200_write_nr_count(struct irsd200_data *data, int val) |
| { |
| unsigned int regval; |
| int ret; |
| |
| /* A value of zero means that IRS_REG_STATUS is never set. */ |
| if (val <= 0 || val >= 8) |
| return -ERANGE; |
| |
| regval = val; |
| |
| if (regval >= 2) { |
| /* |
| * According to the data sheet, timer must be also set in this |
| * case (i.e. be non-zero). Check and enforce that. |
| */ |
| ret = irsd200_read_timer(data, &val, &val); |
| if (ret) |
| return ret; |
| |
| if (val == 0) { |
| dev_err(data->dev, |
| "Timer must be non-zero when nr count is %u\n", |
| regval); |
| return -EPERM; |
| } |
| } |
| |
| ret = regmap_write(data->regmap, IRS_REG_NR_COUNT, regval); |
| if (ret) { |
| dev_err(data->dev, "Could not write nr count (%d)\n", ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_lp_filter(struct irsd200_data *data, int *val) |
| { |
| unsigned int regval; |
| int ret; |
| |
| ret = regmap_field_read(data->regfields[IRS_REGF_LP_FILTER], ®val); |
| if (ret) { |
| dev_err(data->dev, "Could not read lp filter frequency (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| *val = irsd200_lp_filter_freq[regval]; |
| |
| return 0; |
| } |
| |
| static int irsd200_write_lp_filter(struct irsd200_data *data, int val) |
| { |
| size_t idx; |
| int ret; |
| |
| for (idx = 0; idx < ARRAY_SIZE(irsd200_lp_filter_freq); ++idx) { |
| if (irsd200_lp_filter_freq[idx] == val) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(irsd200_lp_filter_freq)) |
| return -ERANGE; |
| |
| ret = regmap_field_write(data->regfields[IRS_REGF_LP_FILTER], idx); |
| if (ret) { |
| dev_err(data->dev, "Could not write lp filter frequency (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_hp_filter(struct irsd200_data *data, int *val, |
| int *val2) |
| { |
| unsigned int regval; |
| int ret; |
| |
| ret = regmap_field_read(data->regfields[IRS_REGF_HP_FILTER], ®val); |
| if (ret) { |
| dev_err(data->dev, "Could not read hp filter frequency (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| *val = irsd200_hp_filter_freq[regval][0]; |
| *val2 = irsd200_hp_filter_freq[regval][1]; |
| |
| return 0; |
| } |
| |
| static int irsd200_write_hp_filter(struct irsd200_data *data, int val, int val2) |
| { |
| size_t idx; |
| int ret; |
| |
| /* Truncate fractional part to one digit. */ |
| val2 /= 100000; |
| |
| for (idx = 0; idx < ARRAY_SIZE(irsd200_hp_filter_freq); ++idx) { |
| if (irsd200_hp_filter_freq[idx][0] == val2) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(irsd200_hp_filter_freq) || val != 0) |
| return -ERANGE; |
| |
| ret = regmap_field_write(data->regfields[IRS_REGF_HP_FILTER], idx); |
| if (ret) { |
| dev_err(data->dev, "Could not write hp filter frequency (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static int irsd200_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int *val, |
| int *val2, long mask) |
| { |
| struct irsd200_data *data = iio_priv(indio_dev); |
| int ret; |
| s16 buf; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_RAW: |
| ret = irsd200_read_data(data, &buf); |
| if (ret) |
| return ret; |
| |
| *val = buf; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| ret = irsd200_read_data_rate(data, val); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| ret = irsd200_read_lp_filter(data, val); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| ret = irsd200_read_hp_filter(data, val, val2); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_FRACTIONAL; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int irsd200_read_avail(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| const int **vals, int *type, int *length, |
| long mask) |
| { |
| switch (mask) { |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| *vals = irsd200_data_rates; |
| *type = IIO_VAL_INT; |
| *length = ARRAY_SIZE(irsd200_data_rates); |
| return IIO_AVAIL_LIST; |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| *vals = irsd200_lp_filter_freq; |
| *type = IIO_VAL_INT; |
| *length = ARRAY_SIZE(irsd200_lp_filter_freq); |
| return IIO_AVAIL_LIST; |
| case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| *vals = (int *)irsd200_hp_filter_freq; |
| *type = IIO_VAL_FRACTIONAL; |
| *length = 2 * ARRAY_SIZE(irsd200_hp_filter_freq); |
| return IIO_AVAIL_LIST; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int irsd200_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int val, |
| int val2, long mask) |
| { |
| struct irsd200_data *data = iio_priv(indio_dev); |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_SAMP_FREQ: |
| return irsd200_write_data_rate(data, val); |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| return irsd200_write_lp_filter(data, val); |
| case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| return irsd200_write_hp_filter(data, val, val2); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int irsd200_read_event(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 irsd200_data *data = iio_priv(indio_dev); |
| int ret; |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| ret = irsd200_read_threshold(data, dir, val); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| case IIO_EV_INFO_RUNNING_PERIOD: |
| ret = irsd200_read_timer(data, val, val2); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_FRACTIONAL; |
| case IIO_EV_INFO_RUNNING_COUNT: |
| ret = irsd200_read_nr_count(data, val); |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int irsd200_write_event(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 irsd200_data *data = iio_priv(indio_dev); |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| return irsd200_write_threshold(data, dir, val); |
| case IIO_EV_INFO_RUNNING_PERIOD: |
| return irsd200_write_timer(data, val, val2); |
| case IIO_EV_INFO_RUNNING_COUNT: |
| return irsd200_write_nr_count(data, val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int irsd200_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 irsd200_data *data = iio_priv(indio_dev); |
| unsigned int val; |
| int ret; |
| |
| switch (type) { |
| case IIO_EV_TYPE_THRESH: |
| ret = regmap_field_read( |
| data->regfields[IRS_REGF_INTR_COUNT_THR_OR], &val); |
| if (ret) |
| return ret; |
| |
| return val; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| 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) |
| { |
| struct irsd200_data *data = iio_priv(indio_dev); |
| unsigned int tmp; |
| int ret; |
| |
| switch (type) { |
| case IIO_EV_TYPE_THRESH: |
| /* Clear the count register (by reading from it). */ |
| ret = regmap_read(data->regmap, IRS_REG_COUNT, &tmp); |
| if (ret) |
| return ret; |
| |
| return regmap_field_write( |
| data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static irqreturn_t irsd200_irq_thread(int irq, void *dev_id) |
| { |
| struct iio_dev *indio_dev = dev_id; |
| struct irsd200_data *data = iio_priv(indio_dev); |
| enum iio_event_direction dir; |
| unsigned int lower_count; |
| unsigned int upper_count; |
| unsigned int status = 0; |
| unsigned int source = 0; |
| unsigned int clear = 0; |
| unsigned int count = 0; |
| int ret; |
| |
| ret = regmap_read(data->regmap, IRS_REG_INTR, &source); |
| if (ret) { |
| dev_err(data->dev, "Could not read interrupt source (%d)\n", |
| ret); |
| return IRQ_HANDLED; |
| } |
| |
| ret = regmap_read(data->regmap, IRS_REG_STATUS, &status); |
| if (ret) { |
| dev_err(data->dev, "Could not acknowledge interrupt (%d)\n", |
| ret); |
| return IRQ_HANDLED; |
| } |
| |
| if (status & BIT(IRS_INTR_DATA) && iio_buffer_enabled(indio_dev)) { |
| iio_trigger_poll_nested(indio_dev->trig); |
| clear |= BIT(IRS_INTR_DATA); |
| } |
| |
| if (status & BIT(IRS_INTR_COUNT_THR_OR) && |
| source & BIT(IRS_INTR_COUNT_THR_OR)) { |
| /* |
| * The register value resets to zero after reading. We therefore |
| * need to read once and manually extract the lower and upper |
| * count register fields. |
| */ |
| ret = regmap_read(data->regmap, IRS_REG_COUNT, &count); |
| if (ret) |
| dev_err(data->dev, "Could not read count (%d)\n", ret); |
| |
| upper_count = IRS_UPPER_COUNT(count); |
| lower_count = IRS_LOWER_COUNT(count); |
| |
| /* |
| * We only check the OR mode to be able to push events for |
| * rising and falling thresholds. AND mode is covered when both |
| * upper and lower count is non-zero, and is signaled with |
| * IIO_EV_DIR_EITHER. |
| */ |
| if (upper_count && !lower_count) |
| dir = IIO_EV_DIR_RISING; |
| else if (!upper_count && lower_count) |
| dir = IIO_EV_DIR_FALLING; |
| else |
| dir = IIO_EV_DIR_EITHER; |
| |
| iio_push_event(indio_dev, |
| IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, |
| IIO_EV_TYPE_THRESH, dir), |
| iio_get_time_ns(indio_dev)); |
| |
| /* |
| * The OR mode will always trigger when the AND mode does, but |
| * not vice versa. However, it seems like the AND bit needs to |
| * be cleared if data capture _and_ threshold count interrupts |
| * are desirable, even though it hasn't explicitly been selected |
| * (with IRS_REG_INTR). Either way, it doesn't hurt... |
| */ |
| clear |= BIT(IRS_INTR_COUNT_THR_OR) | |
| BIT(IRS_INTR_COUNT_THR_AND); |
| } |
| |
| if (!clear) |
| return IRQ_NONE; |
| |
| ret = regmap_write(data->regmap, IRS_REG_STATUS, clear); |
| if (ret) |
| dev_err(data->dev, |
| "Could not clear interrupt status (%d)\n", ret); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t irsd200_trigger_handler(int irq, void *pollf) |
| { |
| struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev; |
| struct irsd200_data *data = iio_priv(indio_dev); |
| s64 buf[2] = {}; |
| int ret; |
| |
| ret = irsd200_read_data(data, (s16 *)buf); |
| if (ret) |
| goto end; |
| |
| iio_push_to_buffers_with_timestamp(indio_dev, buf, |
| iio_get_time_ns(indio_dev)); |
| |
| end: |
| iio_trigger_notify_done(indio_dev->trig); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state) |
| { |
| struct irsd200_data *data = iio_trigger_get_drvdata(trig); |
| int ret; |
| |
| ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state); |
| if (ret) { |
| dev_err(data->dev, "Could not %s data interrupt source (%d)\n", |
| state ? "enable" : "disable", ret); |
| } |
| |
| return ret; |
| } |
| |
| static const struct iio_info irsd200_info = { |
| .read_raw = irsd200_read_raw, |
| .read_avail = irsd200_read_avail, |
| .write_raw = irsd200_write_raw, |
| .read_event_value = irsd200_read_event, |
| .write_event_value = irsd200_write_event, |
| .read_event_config = irsd200_read_event_config, |
| .write_event_config = irsd200_write_event_config, |
| }; |
| |
| static const struct iio_trigger_ops irsd200_trigger_ops = { |
| .set_trigger_state = irsd200_set_trigger_state, |
| .validate_device = iio_trigger_validate_own_device, |
| }; |
| |
| static const struct iio_event_spec irsd200_event_spec[] = { |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_RISING, |
| .mask_separate = BIT(IIO_EV_INFO_VALUE), |
| }, |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_FALLING, |
| .mask_separate = BIT(IIO_EV_INFO_VALUE), |
| }, |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_EITHER, |
| .mask_separate = |
| BIT(IIO_EV_INFO_RUNNING_PERIOD) | |
| BIT(IIO_EV_INFO_RUNNING_COUNT) | |
| BIT(IIO_EV_INFO_ENABLE), |
| }, |
| }; |
| |
| static const struct iio_chan_spec irsd200_channels[] = { |
| { |
| .type = IIO_PROXIMITY, |
| .info_mask_separate = |
| BIT(IIO_CHAN_INFO_RAW) | |
| BIT(IIO_CHAN_INFO_SAMP_FREQ) | |
| BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), |
| .info_mask_separate_available = |
| BIT(IIO_CHAN_INFO_SAMP_FREQ) | |
| BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), |
| .event_spec = irsd200_event_spec, |
| .num_event_specs = ARRAY_SIZE(irsd200_event_spec), |
| .scan_type = { |
| .sign = 's', |
| .realbits = 16, |
| .storagebits = 16, |
| .endianness = IIO_CPU, |
| }, |
| }, |
| }; |
| |
| static int irsd200_probe(struct i2c_client *client) |
| { |
| struct iio_trigger *trigger; |
| struct irsd200_data *data; |
| struct iio_dev *indio_dev; |
| size_t i; |
| int ret; |
| |
| indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); |
| if (!indio_dev) |
| return dev_err_probe(&client->dev, -ENOMEM, |
| "Could not allocate iio device\n"); |
| |
| data = iio_priv(indio_dev); |
| data->dev = &client->dev; |
| |
| data->regmap = devm_regmap_init_i2c(client, &irsd200_regmap_config); |
| if (IS_ERR(data->regmap)) |
| return dev_err_probe(data->dev, PTR_ERR(data->regmap), |
| "Could not initialize regmap\n"); |
| |
| for (i = 0; i < IRS_REGF_MAX; ++i) { |
| data->regfields[i] = devm_regmap_field_alloc( |
| data->dev, data->regmap, irsd200_regfields[i]); |
| if (IS_ERR(data->regfields[i])) |
| return dev_err_probe( |
| data->dev, PTR_ERR(data->regfields[i]), |
| "Could not allocate register field %zu\n", i); |
| } |
| |
| ret = devm_regulator_get_enable(data->dev, "vdd"); |
| if (ret) |
| return dev_err_probe( |
| data->dev, ret, |
| "Could not get and enable regulator (%d)\n", ret); |
| |
| ret = irsd200_setup(data); |
| if (ret) |
| return ret; |
| |
| indio_dev->info = &irsd200_info; |
| indio_dev->name = IRS_DRV_NAME; |
| indio_dev->channels = irsd200_channels; |
| indio_dev->num_channels = ARRAY_SIZE(irsd200_channels); |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| |
| if (!client->irq) |
| return dev_err_probe(data->dev, -ENXIO, "No irq available\n"); |
| |
| ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL, |
| irsd200_trigger_handler, NULL); |
| if (ret) |
| return dev_err_probe( |
| data->dev, ret, |
| "Could not setup iio triggered buffer (%d)\n", ret); |
| |
| ret = devm_request_threaded_irq(data->dev, client->irq, NULL, |
| irsd200_irq_thread, |
| IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
| NULL, indio_dev); |
| if (ret) |
| return dev_err_probe(data->dev, ret, |
| "Could not request irq (%d)\n", ret); |
| |
| trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, |
| iio_device_id(indio_dev)); |
| if (!trigger) |
| return dev_err_probe(data->dev, -ENOMEM, |
| "Could not allocate iio trigger\n"); |
| |
| trigger->ops = &irsd200_trigger_ops; |
| iio_trigger_set_drvdata(trigger, data); |
| |
| ret = devm_iio_trigger_register(data->dev, trigger); |
| if (ret) |
| return dev_err_probe(data->dev, ret, |
| "Could not register iio trigger (%d)\n", |
| ret); |
| |
| ret = devm_iio_device_register(data->dev, indio_dev); |
| if (ret) |
| return dev_err_probe(data->dev, ret, |
| "Could not register iio device (%d)\n", |
| ret); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id irsd200_of_match[] = { |
| { |
| .compatible = "murata,irsd200", |
| }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, irsd200_of_match); |
| |
| static struct i2c_driver irsd200_driver = { |
| .driver = { |
| .name = IRS_DRV_NAME, |
| .of_match_table = irsd200_of_match, |
| }, |
| .probe = irsd200_probe, |
| }; |
| module_i2c_driver(irsd200_driver); |
| |
| MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>"); |
| MODULE_DESCRIPTION("Murata IRS-D200 PIR sensor driver"); |
| MODULE_LICENSE("GPL"); |