#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>
#define D3323AA_REG_BIT_SLAVEA1 0
#define D3323AA_REG_BIT_SLAVEA2 1
#define D3323AA_REG_BIT_SLAVEA3 2
#define D3323AA_REG_BIT_SLAVEA4 3
#define D3323AA_REG_BIT_SLAVEA5 4
#define D3323AA_REG_BIT_SLAVEA6 5
#define D3323AA_REG_BIT_SLAVEA7 6
#define D3323AA_REG_BIT_SLAVEA8 7
#define D3323AA_REG_BIT_SLAVEA9 8
#define D3323AA_REG_BIT_SLAVEA10 9
#define D3323AA_REG_BIT_DETLVLABS0 10
#define D3323AA_REG_BIT_DETLVLABS1 11
#define D3323AA_REG_BIT_DETLVLABS2 12
#define D3323AA_REG_BIT_DETLVLABS3 13
#define D3323AA_REG_BIT_DETLVLABS4 14
#define D3323AA_REG_BIT_DETLVLABS5 15
#define D3323AA_REG_BIT_DETLVLABS6 16
#define D3323AA_REG_BIT_DETLVLABS7 17
#define D3323AA_REG_BIT_DSLP 18
#define D3323AA_REG_BIT_FSTEP0 19
#define D3323AA_REG_BIT_FSTEP1 20
#define D3323AA_REG_BIT_FILSEL0 21
#define D3323AA_REG_BIT_FILSEL1 22
#define D3323AA_REG_BIT_FILSEL2 23
#define D3323AA_REG_BIT_FDSET 24
#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
#define D3323AA_SETTING_END_PATTERN 0x16
#define D3323AA_SETTING_END_PATTERN_NR_BITS 5
#define D3323AA_RESET_TIMEOUT (1200 + 100)
#define D3323AA_CONFIG_TIMEOUT 1400
#define D3323AA_IRQ_RESET_COUNT 2
static const int d3323aa_hp_filter_freq[][2] = {
{ 40, 100 },
{ 30, 100 },
{ 30, 100 },
{ 1, 100 },
};
static const int d3323aa_lp_filter_freq[][2] = {
{ 27, 10 },
{ 15, 10 },
{ 5, 1 },
{ 100, 1 },
};
static const int d3323aa_lp_filter_regval[] = {
7,
0,
1,
2,
};
static const int d3323aa_filter_gain[] = { 1, 2, 3 };
static const u8 d3323aa_filter_gain_regval[] = { 1, 3, 0 };
struct d3323aa_data {
struct completion reset_completion;
struct mutex statevar_lock;
struct device *dev;
struct regulator *regulator_vdd;
struct gpio_desc *gpiod_clkin_detectout;
struct gpio_desc *gpiod_data;
size_t lp_filter_freq_idx;
size_t filter_gain_idx;
u8 detect_thresh;
u8 irq_reset_count;
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;
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) {
gpiod_set_value(data->gpiod_clkin_detectout, 1);
udelay(500);
if (gpiod_get_value(data->gpiod_data))
set_bit(i, regbitmap);
gpiod_set_value(data->gpiod_clkin_detectout, 0);
udelay(500);
}
clear_bit(0, regbitmap);
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;
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);
bitmap_write(regbitmap, D3323AA_SETTING_END_PATTERN,
D3323AA_REG_NR_BITS, D3323AA_SETTING_END_PATTERN_NR_BITS);
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");
for (i = 1; i < REGBITMAP_LEN; ++i) {
gpiod_set_value(data->gpiod_data, test_bit(i, regbitmap));
gpiod_set_value(data->gpiod_clkin_detectout, 1);
udelay(500);
gpiod_set_value(data->gpiod_clkin_detectout, 0);
udelay(500);
}
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) {
if (!val && ++data->irq_reset_count == D3323AA_IRQ_RESET_COUNT)
complete(&data->reset_completion);
return IRQ_HANDLED;
}
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;
if (regulator_is_enabled(data->regulator_vdd)) {
ret = regulator_disable(data->regulator_vdd);
if (ret)
return ret;
}
fsleep((30 + 5) * USEC_PER_MSEC);
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;
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) {
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;
}
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;
}
if (!bitmap_equal(write_regbitmap, read_regbitmap,
D3323AA_REG_NR_BITS)) {
dev_err(data->dev, "Settings data mismatch\n");
return -EIO;
}
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;
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;
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) {
return 0;
}
if (idx == 1 && data->lp_filter_freq_idx == 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;
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 -ENOMEM;
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");
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");
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");