| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Driver for Nicera D3-323-AA PIR sensor. |
| * |
| * Copyright (C) 2025 Axis Communications AB |
| */ |
| |
| #include <linux/bitmap.h> |
| #include <linux/cleanup.h> |
| #include <linux/completion.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/interrupt.h> |
| #include <linux/jiffies.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/module.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/types.h> |
| |
| #include <linux/iio/events.h> |
| #include <linux/iio/iio.h> |
| |
| /* |
| * Register bitmap. |
| * For some reason the first bit is denoted as F37 in the datasheet, the second |
| * as F38 and so on. Note the gap between F60 and F64. |
| */ |
| #define D3323AA_REG_BIT_SLAVEA1 0 /* F37. */ |
| #define D3323AA_REG_BIT_SLAVEA2 1 /* F38. */ |
| #define D3323AA_REG_BIT_SLAVEA3 2 /* F39. */ |
| #define D3323AA_REG_BIT_SLAVEA4 3 /* F40. */ |
| #define D3323AA_REG_BIT_SLAVEA5 4 /* F41. */ |
| #define D3323AA_REG_BIT_SLAVEA6 5 /* F42. */ |
| #define D3323AA_REG_BIT_SLAVEA7 6 /* F43. */ |
| #define D3323AA_REG_BIT_SLAVEA8 7 /* F44. */ |
| #define D3323AA_REG_BIT_SLAVEA9 8 /* F45. */ |
| #define D3323AA_REG_BIT_SLAVEA10 9 /* F46. */ |
| #define D3323AA_REG_BIT_DETLVLABS0 10 /* F47. */ |
| #define D3323AA_REG_BIT_DETLVLABS1 11 /* F48. */ |
| #define D3323AA_REG_BIT_DETLVLABS2 12 /* F49. */ |
| #define D3323AA_REG_BIT_DETLVLABS3 13 /* F50. */ |
| #define D3323AA_REG_BIT_DETLVLABS4 14 /* F51. */ |
| #define D3323AA_REG_BIT_DETLVLABS5 15 /* F52. */ |
| #define D3323AA_REG_BIT_DETLVLABS6 16 /* F53. */ |
| #define D3323AA_REG_BIT_DETLVLABS7 17 /* F54. */ |
| #define D3323AA_REG_BIT_DSLP 18 /* F55. */ |
| #define D3323AA_REG_BIT_FSTEP0 19 /* F56. */ |
| #define D3323AA_REG_BIT_FSTEP1 20 /* F57. */ |
| #define D3323AA_REG_BIT_FILSEL0 21 /* F58. */ |
| #define D3323AA_REG_BIT_FILSEL1 22 /* F59. */ |
| #define D3323AA_REG_BIT_FILSEL2 23 /* F60. */ |
| #define D3323AA_REG_BIT_FDSET 24 /* F64. */ |
| #define D3323AA_REG_BIT_F65 25 |
| #define D3323AA_REG_BIT_F87 (D3323AA_REG_BIT_F65 + (87 - 65)) |
| |
| #define D3323AA_REG_NR_BITS (D3323AA_REG_BIT_F87 - D3323AA_REG_BIT_SLAVEA1 + 1) |
| #define D3323AA_THRESH_REG_NR_BITS \ |
| (D3323AA_REG_BIT_DETLVLABS7 - D3323AA_REG_BIT_DETLVLABS0 + 1) |
| #define D3323AA_FILTER_TYPE_NR_BITS \ |
| (D3323AA_REG_BIT_FILSEL2 - D3323AA_REG_BIT_FILSEL0 + 1) |
| #define D3323AA_FILTER_GAIN_REG_NR_BITS \ |
| (D3323AA_REG_BIT_FSTEP1 - D3323AA_REG_BIT_FSTEP0 + 1) |
| |
| #define D3323AA_THRESH_DEFAULT_VAL 56 |
| #define D3323AA_FILTER_GAIN_DEFAULT_IDX 1 |
| #define D3323AA_LP_FILTER_FREQ_DEFAULT_IDX 1 |
| |
| /* |
| * The pattern is 0b01101, but store it reversed (0b10110) due to writing from |
| * LSB on the wire (c.f. d3323aa_write_settings()). |
| */ |
| #define D3323AA_SETTING_END_PATTERN 0x16 |
| #define D3323AA_SETTING_END_PATTERN_NR_BITS 5 |
| |
| /* |
| * Device should be ready for configuration after this many milliseconds. |
| * Datasheet mentions "approx. 1.2 s". Measurements show around 1.23 s, |
| * therefore add 100 ms of slack. |
| */ |
| #define D3323AA_RESET_TIMEOUT (1200 + 100) |
| |
| /* |
| * The configuration of the device (write and read) should be done within this |
| * many milliseconds. |
| */ |
| #define D3323AA_CONFIG_TIMEOUT 1400 |
| |
| /* Number of IRQs needed for configuration stage after reset. */ |
| #define D3323AA_IRQ_RESET_COUNT 2 |
| |
| /* |
| * High-pass filter cutoff frequency for the band-pass filter. There is a |
| * corresponding low-pass cutoff frequency for each of the filter types |
| * (denoted A, B, C and D in the datasheet). The index in this array matches |
| * that corresponding value in d3323aa_lp_filter_freq. |
| * Note that this represents a fractional value (e.g. the first value |
| * corresponds to 40 / 100 = 0.4 Hz). |
| */ |
| static const int d3323aa_hp_filter_freq[][2] = { |
| { 40, 100 }, |
| { 30, 100 }, |
| { 30, 100 }, |
| { 1, 100 }, |
| }; |
| |
| /* |
| * Low-pass filter cutoff frequency for the band-pass filter. There is a |
| * corresponding high-pass cutoff frequency for each of the filter types |
| * (denoted A, B, C and D in the datasheet). The index in this array matches |
| * that corresponding value in d3323aa_hp_filter_freq. |
| * Note that this represents a fractional value (e.g. the first value |
| * corresponds to 27 / 10 = 2.7 Hz). |
| */ |
| static const int d3323aa_lp_filter_freq[][2] = { |
| { 27, 10 }, |
| { 15, 10 }, |
| { 5, 1 }, |
| { 100, 1 }, |
| }; |
| |
| /* |
| * Register bitmap values for filter types (denoted A, B, C and D in the |
| * datasheet). The index in this array matches the corresponding value in |
| * d3323aa_lp_filter_freq (which in turn matches d3323aa_hp_filter_freq). For |
| * example, the first value 7 corresponds to 2.7 Hz low-pass and 0.4 Hz |
| * high-pass cutoff frequency. |
| */ |
| static const int d3323aa_lp_filter_regval[] = { |
| 7, |
| 0, |
| 1, |
| 2, |
| }; |
| |
| /* |
| * This is denoted as "step" in datasheet and corresponds to the gain at peak |
| * for the band-pass filter. The index in this array is the corresponding index |
| * in d3323aa_filter_gain_regval for the register bitmap value. |
| */ |
| static const int d3323aa_filter_gain[] = { 1, 2, 3 }; |
| |
| /* |
| * Register bitmap values for the filter gain. The index in this array is the |
| * corresponding index in d3323aa_filter_gain for the gain value. |
| */ |
| static const u8 d3323aa_filter_gain_regval[] = { 1, 3, 0 }; |
| |
| struct d3323aa_data { |
| struct completion reset_completion; |
| /* |
| * Since the setup process always requires a complete write of _all_ |
| * the state variables, we need to synchronize them with a lock. |
| */ |
| struct mutex statevar_lock; |
| |
| struct device *dev; |
| |
| /* Supply voltage. */ |
| struct regulator *regulator_vdd; |
| /* Input clock or output detection signal (Vout). */ |
| struct gpio_desc *gpiod_clkin_detectout; |
| /* Input (setting) or output data. */ |
| struct gpio_desc *gpiod_data; |
| |
| /* |
| * We only need the low-pass cutoff frequency to unambiguously choose |
| * the type of band-pass filter. For example, both filter type B and C |
| * have 0.3 Hz as high-pass cutoff frequency (see |
| * d3323aa_hp_filter_freq). |
| */ |
| size_t lp_filter_freq_idx; |
| size_t filter_gain_idx; |
| u8 detect_thresh; |
| u8 irq_reset_count; |
| |
| /* Indicator for operational mode (configuring or detecting). */ |
| bool detecting; |
| }; |
| |
| static int d3323aa_read_settings(struct iio_dev *indio_dev, |
| unsigned long *regbitmap) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| size_t i; |
| int ret; |
| |
| /* Bit bang the clock and data pins. */ |
| ret = gpiod_direction_output(data->gpiod_clkin_detectout, 0); |
| if (ret) |
| return ret; |
| |
| ret = gpiod_direction_input(data->gpiod_data); |
| if (ret) |
| return ret; |
| |
| dev_dbg(data->dev, "Reading settings...\n"); |
| |
| for (i = 0; i < D3323AA_REG_NR_BITS; ++i) { |
| /* Clock frequency needs to be 1 kHz. */ |
| gpiod_set_value(data->gpiod_clkin_detectout, 1); |
| udelay(500); |
| |
| /* The data seems to change when clock signal is high. */ |
| if (gpiod_get_value(data->gpiod_data)) |
| set_bit(i, regbitmap); |
| |
| gpiod_set_value(data->gpiod_clkin_detectout, 0); |
| udelay(500); |
| } |
| |
| /* The first bit (F37) is just dummy data. Discard it. */ |
| clear_bit(0, regbitmap); |
| |
| /* Datasheet says to wait 30 ms after reading the settings. */ |
| msleep(30); |
| |
| return 0; |
| } |
| |
| static int d3323aa_write_settings(struct iio_dev *indio_dev, |
| unsigned long *written_regbitmap) |
| { |
| #define REGBITMAP_LEN \ |
| (D3323AA_REG_NR_BITS + D3323AA_SETTING_END_PATTERN_NR_BITS) |
| DECLARE_BITMAP(regbitmap, REGBITMAP_LEN); |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| size_t i; |
| int ret; |
| |
| /* Build the register bitmap. */ |
| bitmap_zero(regbitmap, REGBITMAP_LEN); |
| bitmap_write(regbitmap, data->detect_thresh, D3323AA_REG_BIT_DETLVLABS0, |
| D3323AA_REG_BIT_DETLVLABS7 - D3323AA_REG_BIT_DETLVLABS0 + |
| 1); |
| bitmap_write(regbitmap, |
| d3323aa_filter_gain_regval[data->filter_gain_idx], |
| D3323AA_REG_BIT_FSTEP0, |
| D3323AA_REG_BIT_FSTEP1 - D3323AA_REG_BIT_FSTEP0 + 1); |
| bitmap_write(regbitmap, |
| d3323aa_lp_filter_regval[data->lp_filter_freq_idx], |
| D3323AA_REG_BIT_FILSEL0, |
| D3323AA_REG_BIT_FILSEL2 - D3323AA_REG_BIT_FILSEL0 + 1); |
| /* Compulsory end pattern. */ |
| bitmap_write(regbitmap, D3323AA_SETTING_END_PATTERN, |
| D3323AA_REG_NR_BITS, D3323AA_SETTING_END_PATTERN_NR_BITS); |
| |
| /* Bit bang the clock and data pins. */ |
| ret = gpiod_direction_output(data->gpiod_clkin_detectout, 0); |
| if (ret) |
| return ret; |
| |
| ret = gpiod_direction_output(data->gpiod_data, 0); |
| if (ret) |
| return ret; |
| |
| dev_dbg(data->dev, "Writing settings...\n"); |
| |
| /* First bit (F37) is not used when writing the register bitmap. */ |
| for (i = 1; i < REGBITMAP_LEN; ++i) { |
| gpiod_set_value(data->gpiod_data, test_bit(i, regbitmap)); |
| |
| /* Clock frequency needs to be 1 kHz. */ |
| gpiod_set_value(data->gpiod_clkin_detectout, 1); |
| udelay(500); |
| gpiod_set_value(data->gpiod_clkin_detectout, 0); |
| udelay(500); |
| } |
| |
| /* Datasheet says to wait 30 ms after writing the settings. */ |
| msleep(30); |
| |
| bitmap_copy(written_regbitmap, regbitmap, D3323AA_REG_NR_BITS); |
| |
| return 0; |
| } |
| |
| static irqreturn_t d3323aa_irq_handler(int irq, void *dev_id) |
| { |
| struct iio_dev *indio_dev = dev_id; |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| enum iio_event_direction dir; |
| int val; |
| |
| val = gpiod_get_value(data->gpiod_clkin_detectout); |
| if (val < 0) { |
| dev_err_ratelimited(data->dev, |
| "Could not read from GPIO vout-clk (%d)\n", |
| val); |
| return IRQ_HANDLED; |
| } |
| |
| if (!data->detecting) { |
| /* Reset interrupt counting falling edges. */ |
| if (!val && ++data->irq_reset_count == D3323AA_IRQ_RESET_COUNT) |
| complete(&data->reset_completion); |
| |
| return IRQ_HANDLED; |
| } |
| |
| /* Detection interrupt. */ |
| dir = val ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; |
| iio_push_event(indio_dev, |
| IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, |
| IIO_EV_TYPE_THRESH, dir), |
| iio_get_time_ns(indio_dev)); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int d3323aa_reset(struct iio_dev *indio_dev) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| long time; |
| int ret; |
| |
| /* During probe() the regulator may already be disabled. */ |
| if (regulator_is_enabled(data->regulator_vdd)) { |
| ret = regulator_disable(data->regulator_vdd); |
| if (ret) |
| return ret; |
| } |
| |
| /* |
| * Datasheet says VDD needs to be low at least for 30 ms. Let's add a |
| * couple more to allow VDD to completely discharge as well. |
| */ |
| fsleep((30 + 5) * USEC_PER_MSEC); |
| |
| /* |
| * When later enabling VDD, the device will signal with |
| * D3323AA_IRQ_RESET_COUNT falling edges on Vout/CLK that it is now |
| * ready for configuration. Datasheet says that this should happen |
| * within D3323AA_RESET_TIMEOUT ms. Count these two edges within that |
| * timeout. |
| */ |
| data->irq_reset_count = 0; |
| reinit_completion(&data->reset_completion); |
| data->detecting = false; |
| |
| ret = gpiod_direction_input(data->gpiod_clkin_detectout); |
| if (ret) |
| return ret; |
| |
| dev_dbg(data->dev, "Resetting...\n"); |
| |
| ret = regulator_enable(data->regulator_vdd); |
| if (ret) |
| return ret; |
| |
| /* |
| * Wait for VDD to completely charge up. Measurements have shown that |
| * Vout/CLK signal slowly ramps up during this period. Thus, the digital |
| * signal will have bogus values. It is therefore necessary to wait |
| * before we can count the "real" falling edges. |
| */ |
| fsleep(2000); |
| |
| time = wait_for_completion_killable_timeout( |
| &data->reset_completion, |
| msecs_to_jiffies(D3323AA_RESET_TIMEOUT)); |
| if (time == 0) { |
| return -ETIMEDOUT; |
| } else if (time < 0) { |
| /* Got interrupted. */ |
| return time; |
| } |
| |
| dev_dbg(data->dev, "Reset completed\n"); |
| |
| return 0; |
| } |
| |
| static int d3323aa_setup(struct iio_dev *indio_dev, size_t lp_filter_freq_idx, |
| size_t filter_gain_idx, u8 detect_thresh) |
| { |
| DECLARE_BITMAP(write_regbitmap, D3323AA_REG_NR_BITS); |
| DECLARE_BITMAP(read_regbitmap, D3323AA_REG_NR_BITS); |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| unsigned long start_time; |
| int ret; |
| |
| ret = d3323aa_reset(indio_dev); |
| if (ret) { |
| if (ret != -ERESTARTSYS) |
| dev_err(data->dev, "Could not reset device (%d)\n", |
| ret); |
| |
| return ret; |
| } |
| |
| /* |
| * Datasheet says to wait 10 us before setting the configuration. |
| * Moreover, the total configuration should be done within |
| * D3323AA_CONFIG_TIMEOUT ms. Clock it. |
| */ |
| fsleep(10); |
| start_time = jiffies; |
| |
| ret = d3323aa_write_settings(indio_dev, write_regbitmap); |
| if (ret) { |
| dev_err(data->dev, "Could not write settings (%d)\n", ret); |
| return ret; |
| } |
| |
| ret = d3323aa_read_settings(indio_dev, read_regbitmap); |
| if (ret) { |
| dev_err(data->dev, "Could not read settings (%d)\n", ret); |
| return ret; |
| } |
| |
| if (time_is_before_jiffies(start_time + |
| msecs_to_jiffies(D3323AA_CONFIG_TIMEOUT))) { |
| dev_err(data->dev, "Could not set up configuration in time\n"); |
| return -EAGAIN; |
| } |
| |
| /* Check if settings were set successfully. */ |
| if (!bitmap_equal(write_regbitmap, read_regbitmap, |
| D3323AA_REG_NR_BITS)) { |
| dev_err(data->dev, "Settings data mismatch\n"); |
| return -EIO; |
| } |
| |
| /* Now in operational mode. */ |
| ret = gpiod_direction_input(data->gpiod_clkin_detectout); |
| if (ret) { |
| dev_err(data->dev, |
| "Could not set GPIO vout-clk as input (%d)\n", ret); |
| return ret; |
| } |
| |
| ret = gpiod_direction_input(data->gpiod_data); |
| if (ret) { |
| dev_err(data->dev, "Could not set GPIO data as input (%d)\n", |
| ret); |
| return ret; |
| } |
| |
| data->lp_filter_freq_idx = lp_filter_freq_idx; |
| data->filter_gain_idx = filter_gain_idx; |
| data->detect_thresh = detect_thresh; |
| data->detecting = true; |
| |
| dev_dbg(data->dev, "Setup done\n"); |
| |
| return 0; |
| } |
| |
| static int d3323aa_set_lp_filter_freq(struct iio_dev *indio_dev, const int val, |
| int val2) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| size_t idx; |
| |
| /* Truncate fractional part to one digit. */ |
| val2 /= 100000; |
| |
| for (idx = 0; idx < ARRAY_SIZE(d3323aa_lp_filter_freq); ++idx) { |
| int integer = d3323aa_lp_filter_freq[idx][0] / |
| d3323aa_lp_filter_freq[idx][1]; |
| int fract = d3323aa_lp_filter_freq[idx][0] % |
| d3323aa_lp_filter_freq[idx][1]; |
| |
| if (val == integer && val2 == fract) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(d3323aa_lp_filter_freq)) |
| return -EINVAL; |
| |
| return d3323aa_setup(indio_dev, idx, data->filter_gain_idx, |
| data->detect_thresh); |
| } |
| |
| static int d3323aa_set_hp_filter_freq(struct iio_dev *indio_dev, const int val, |
| int val2) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| size_t idx; |
| |
| /* Truncate fractional part to two digits. */ |
| val2 /= 10000; |
| |
| for (idx = 0; idx < ARRAY_SIZE(d3323aa_hp_filter_freq); ++idx) { |
| int integer = d3323aa_hp_filter_freq[idx][0] / |
| d3323aa_hp_filter_freq[idx][1]; |
| int fract = d3323aa_hp_filter_freq[idx][0] % |
| d3323aa_hp_filter_freq[idx][1]; |
| |
| if (val == integer && val2 == fract) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(d3323aa_hp_filter_freq)) |
| return -EINVAL; |
| |
| if (idx == data->lp_filter_freq_idx) { |
| /* Corresponding filter frequency already set. */ |
| return 0; |
| } |
| |
| if (idx == 1 && data->lp_filter_freq_idx == 2) { |
| /* |
| * The low-pass cutoff frequency is the only way to |
| * unambiguously choose the type of band-pass filter. For |
| * example, both filter type B (index 1) and C (index 2) have |
| * 0.3 Hz as high-pass cutoff frequency (see |
| * d3323aa_hp_filter_freq). Therefore, if one of these are |
| * requested _and_ the corresponding low-pass filter frequency |
| * is already set, we can't know which filter type is the wanted |
| * one. The low-pass filter frequency is the decider (i.e. in |
| * this case index 2). |
| */ |
| return 0; |
| } |
| |
| return d3323aa_setup(indio_dev, idx, data->filter_gain_idx, |
| data->detect_thresh); |
| } |
| |
| static int d3323aa_set_filter_gain(struct iio_dev *indio_dev, const int val) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| size_t idx; |
| |
| for (idx = 0; idx < ARRAY_SIZE(d3323aa_filter_gain); ++idx) { |
| if (d3323aa_filter_gain[idx] == val) |
| break; |
| } |
| |
| if (idx == ARRAY_SIZE(d3323aa_filter_gain)) |
| return -EINVAL; |
| |
| return d3323aa_setup(indio_dev, data->lp_filter_freq_idx, idx, |
| data->detect_thresh); |
| } |
| |
| static int d3323aa_set_threshold(struct iio_dev *indio_dev, const int val) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| |
| if (val > ((1 << D3323AA_THRESH_REG_NR_BITS) - 1)) |
| return -EINVAL; |
| |
| return d3323aa_setup(indio_dev, data->lp_filter_freq_idx, |
| data->filter_gain_idx, val); |
| } |
| |
| static int d3323aa_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_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| *vals = (int *)d3323aa_hp_filter_freq; |
| *type = IIO_VAL_FRACTIONAL; |
| *length = 2 * ARRAY_SIZE(d3323aa_hp_filter_freq); |
| return IIO_AVAIL_LIST; |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| *vals = (int *)d3323aa_lp_filter_freq; |
| *type = IIO_VAL_FRACTIONAL; |
| *length = 2 * ARRAY_SIZE(d3323aa_lp_filter_freq); |
| return IIO_AVAIL_LIST; |
| case IIO_CHAN_INFO_HARDWAREGAIN: |
| *vals = (int *)d3323aa_filter_gain; |
| *type = IIO_VAL_INT; |
| *length = ARRAY_SIZE(d3323aa_filter_gain); |
| return IIO_AVAIL_LIST; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int d3323aa_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int *val, |
| int *val2, long mask) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| |
| guard(mutex)(&data->statevar_lock); |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| *val = d3323aa_hp_filter_freq[data->lp_filter_freq_idx][0]; |
| *val2 = d3323aa_hp_filter_freq[data->lp_filter_freq_idx][1]; |
| return IIO_VAL_FRACTIONAL; |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| *val = d3323aa_lp_filter_freq[data->lp_filter_freq_idx][0]; |
| *val2 = d3323aa_lp_filter_freq[data->lp_filter_freq_idx][1]; |
| return IIO_VAL_FRACTIONAL; |
| case IIO_CHAN_INFO_HARDWAREGAIN: |
| *val = d3323aa_filter_gain[data->filter_gain_idx]; |
| return IIO_VAL_INT; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int d3323aa_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, int val, |
| int val2, long mask) |
| { |
| struct d3323aa_data *data = iio_priv(indio_dev); |
| |
| guard(mutex)(&data->statevar_lock); |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
| return d3323aa_set_hp_filter_freq(indio_dev, val, val2); |
| case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: |
| return d3323aa_set_lp_filter_freq(indio_dev, val, val2); |
| case IIO_CHAN_INFO_HARDWAREGAIN: |
| return d3323aa_set_filter_gain(indio_dev, val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int d3323aa_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 d3323aa_data *data = iio_priv(indio_dev); |
| |
| guard(mutex)(&data->statevar_lock); |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| *val = data->detect_thresh; |
| return IIO_VAL_INT; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int d3323aa_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 d3323aa_data *data = iio_priv(indio_dev); |
| |
| guard(mutex)(&data->statevar_lock); |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| return d3323aa_set_threshold(indio_dev, val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static const struct iio_info d3323aa_info = { |
| .read_avail = d3323aa_read_avail, |
| .read_raw = d3323aa_read_raw, |
| .write_raw = d3323aa_write_raw, |
| .read_event_value = d3323aa_read_event, |
| .write_event_value = d3323aa_write_event, |
| }; |
| |
| static const struct iio_event_spec d3323aa_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), |
| }, |
| }; |
| |
| static const struct iio_chan_spec d3323aa_channels[] = { |
| { |
| .type = IIO_PROXIMITY, |
| .info_mask_separate = |
| BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
| .info_mask_separate_available = |
| BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | |
| BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
| .event_spec = d3323aa_event_spec, |
| .num_event_specs = ARRAY_SIZE(d3323aa_event_spec), |
| }, |
| }; |
| |
| static void d3323aa_disable_regulator(void *indata) |
| { |
| struct d3323aa_data *data = indata; |
| int ret; |
| |
| /* |
| * During probe() the regulator may be disabled. It is enabled during |
| * device setup (in d3323aa_reset(), where it is also briefly disabled). |
| * The check is therefore needed in order to have balanced |
| * regulator_enable/disable() calls. |
| */ |
| if (!regulator_is_enabled(data->regulator_vdd)) |
| return; |
| |
| ret = regulator_disable(data->regulator_vdd); |
| if (ret) |
| dev_err(data->dev, "Could not disable regulator (%d)\n", ret); |
| } |
| |
| static int d3323aa_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct d3323aa_data *data; |
| struct iio_dev *indio_dev; |
| int ret; |
| |
| indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); |
| if (!indio_dev) |
| return dev_err_probe(dev, -ENOMEM, |
| "Could not allocate iio device\n"); |
| |
| data = iio_priv(indio_dev); |
| data->dev = dev; |
| |
| init_completion(&data->reset_completion); |
| |
| ret = devm_mutex_init(dev, &data->statevar_lock); |
| if (ret) |
| return dev_err_probe(dev, ret, "Could not initialize mutex\n"); |
| |
| data->regulator_vdd = devm_regulator_get_exclusive(dev, "vdd"); |
| if (IS_ERR(data->regulator_vdd)) |
| return dev_err_probe(dev, PTR_ERR(data->regulator_vdd), |
| "Could not get regulator\n"); |
| |
| /* |
| * The regulator will be enabled for the first time during the |
| * device setup below (in d3323aa_reset()). However parameter changes |
| * from userspace can require a temporary disable of the regulator. |
| * To avoid complex handling of state, use a callback that will disable |
| * the regulator if it happens to be enabled at time of devm unwind. |
| */ |
| ret = devm_add_action_or_reset(dev, d3323aa_disable_regulator, data); |
| if (ret) |
| return ret; |
| |
| data->gpiod_clkin_detectout = |
| devm_gpiod_get(dev, "vout-clk", GPIOD_OUT_LOW); |
| if (IS_ERR(data->gpiod_clkin_detectout)) |
| return dev_err_probe(dev, PTR_ERR(data->gpiod_clkin_detectout), |
| "Could not get GPIO vout-clk\n"); |
| |
| data->gpiod_data = devm_gpiod_get(dev, "data", GPIOD_OUT_LOW); |
| if (IS_ERR(data->gpiod_data)) |
| return dev_err_probe(dev, PTR_ERR(data->gpiod_data), |
| "Could not get GPIO data\n"); |
| |
| ret = gpiod_to_irq(data->gpiod_clkin_detectout); |
| if (ret < 0) |
| return dev_err_probe(dev, ret, "Could not get IRQ\n"); |
| |
| /* |
| * Device signals with a rising or falling detection signal when the |
| * proximity data is above or below the threshold, respectively. |
| */ |
| ret = devm_request_irq(dev, ret, d3323aa_irq_handler, |
| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
| dev_name(dev), indio_dev); |
| if (ret) |
| return dev_err_probe(dev, ret, "Could not request IRQ\n"); |
| |
| ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX, |
| D3323AA_FILTER_GAIN_DEFAULT_IDX, |
| D3323AA_THRESH_DEFAULT_VAL); |
| if (ret) |
| return ret; |
| |
| indio_dev->info = &d3323aa_info; |
| indio_dev->name = "d3323aa"; |
| indio_dev->channels = d3323aa_channels; |
| indio_dev->num_channels = ARRAY_SIZE(d3323aa_channels); |
| |
| ret = devm_iio_device_register(dev, indio_dev); |
| if (ret) |
| return dev_err_probe(dev, ret, |
| "Could not register iio device\n"); |
| |
| return 0; |
| } |
| |
| static const struct of_device_id d3323aa_of_match[] = { |
| { |
| .compatible = "nicera,d3323aa", |
| }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, d3323aa_of_match); |
| |
| static struct platform_driver d3323aa_driver = { |
| .probe = d3323aa_probe, |
| .driver = { |
| .name = "d3323aa", |
| .of_match_table = d3323aa_of_match, |
| }, |
| }; |
| module_platform_driver(d3323aa_driver); |
| |
| MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>"); |
| MODULE_DESCRIPTION("Nicera D3-323-AA PIR sensor driver"); |
| MODULE_LICENSE("GPL"); |