Commit 45ae5561 authored by Jonathan Cameron's avatar Jonathan Cameron

staging:iio:accel:sca3000 use a 'fake' channel to handle freefall event registration.

This is an approach used in some newer drivers as it exposes the
compound channel events to the core rather than hiding their control
in sysfs attributes entirely via the driver.
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
Reviewed-by: default avatarLars-Peter Clausen <lars@metafoo.de>
parent 2600e12b
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#define SCA3000_MEAS_MODE_NORMAL 0x00 #define SCA3000_MEAS_MODE_NORMAL 0x00
#define SCA3000_MEAS_MODE_OP_1 0x01 #define SCA3000_MEAS_MODE_OP_1 0x01
#define SCA3000_MEAS_MODE_OP_2 0x02 #define SCA3000_MEAS_MODE_OP_2 0x02
#define SCA3000_MODE_MASK 0x03
/* /*
* In motion detection mode the accelerations are band pass filtered * In motion detection mode the accelerations are band pass filtered
...@@ -593,10 +594,25 @@ static const struct iio_event_spec sca3000_event = { ...@@ -593,10 +594,25 @@ static const struct iio_event_spec sca3000_event = {
.num_event_specs = 1, \ .num_event_specs = 1, \
} }
static const struct iio_event_spec sca3000_freefall_event_spec = {
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD),
};
static const struct iio_chan_spec sca3000_channels[] = { static const struct iio_chan_spec sca3000_channels[] = {
SCA3000_CHAN(0, IIO_MOD_X), SCA3000_CHAN(0, IIO_MOD_X),
SCA3000_CHAN(1, IIO_MOD_Y), SCA3000_CHAN(1, IIO_MOD_Y),
SCA3000_CHAN(2, IIO_MOD_Z), SCA3000_CHAN(2, IIO_MOD_Z),
{
.type = IIO_ACCEL,
.modified = 1,
.channel2 = IIO_MOD_X_AND_Y_AND_Z,
.scan_index = -1, /* Fake channel */
.event_spec = &sca3000_freefall_event_spec,
.num_event_specs = 1,
},
}; };
static const struct iio_chan_spec sca3000_channels_with_temp[] = { static const struct iio_chan_spec sca3000_channels_with_temp[] = {
...@@ -611,6 +627,14 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = { ...@@ -611,6 +627,14 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = {
/* No buffer support */ /* No buffer support */
.scan_index = -1, .scan_index = -1,
}, },
{
.type = IIO_ACCEL,
.modified = 1,
.channel2 = IIO_MOD_X_AND_Y_AND_Z,
.scan_index = -1, /* Fake channel */
.event_spec = &sca3000_freefall_event_spec,
.num_event_specs = 1,
},
}; };
static u8 sca3000_addresses[3][3] = { static u8 sca3000_addresses[3][3] = {
...@@ -854,9 +878,9 @@ static ssize_t sca3000_read_av_freq(struct device *dev, ...@@ -854,9 +878,9 @@ static ssize_t sca3000_read_av_freq(struct device *dev,
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
/** /**
* sca3000_read_thresh() - query of a threshold * sca3000_read_event_value() - query of a threshold or period
**/ **/
static int sca3000_read_thresh(struct iio_dev *indio_dev, static int sca3000_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, const struct iio_chan_spec *chan,
enum iio_event_type type, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_direction dir,
...@@ -866,7 +890,8 @@ static int sca3000_read_thresh(struct iio_dev *indio_dev, ...@@ -866,7 +890,8 @@ static int sca3000_read_thresh(struct iio_dev *indio_dev,
int ret, i; int ret, i;
struct sca3000_state *st = iio_priv(indio_dev); struct sca3000_state *st = iio_priv(indio_dev);
int num = chan->channel2; int num = chan->channel2;
switch (info) {
case IIO_EV_INFO_VALUE:
mutex_lock(&st->lock); mutex_lock(&st->lock);
ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]); ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]);
mutex_unlock(&st->lock); mutex_unlock(&st->lock);
...@@ -883,12 +908,19 @@ static int sca3000_read_thresh(struct iio_dev *indio_dev, ...@@ -883,12 +908,19 @@ static int sca3000_read_thresh(struct iio_dev *indio_dev,
*val += st->info->mot_det_mult_xz[i]; *val += st->info->mot_det_mult_xz[i];
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_EV_INFO_PERIOD:
*val = 0;
*val2 = 226000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
} }
/** /**
* sca3000_write_thresh() control of threshold * sca3000_write_value() control of threshold and period
**/ **/
static int sca3000_write_thresh(struct iio_dev *indio_dev, static int sca3000_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, const struct iio_chan_spec *chan,
enum iio_event_type type, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_direction dir,
...@@ -901,7 +933,7 @@ static int sca3000_write_thresh(struct iio_dev *indio_dev, ...@@ -901,7 +933,7 @@ static int sca3000_write_thresh(struct iio_dev *indio_dev,
int i; int i;
u8 nonlinear = 0; u8 nonlinear = 0;
if (num == 1) { if (num == IIO_MOD_Y) {
i = ARRAY_SIZE(st->info->mot_det_mult_y); i = ARRAY_SIZE(st->info->mot_det_mult_y);
while (i > 0) while (i > 0)
if (val >= st->info->mot_det_mult_y[--i]) { if (val >= st->info->mot_det_mult_y[--i]) {
...@@ -1088,179 +1120,155 @@ static int sca3000_read_event_config(struct iio_dev *indio_dev, ...@@ -1088,179 +1120,155 @@ static int sca3000_read_event_config(struct iio_dev *indio_dev,
/* read current value of mode register */ /* read current value of mode register */
mutex_lock(&st->lock); mutex_lock(&st->lock);
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
if (ret) if (ret)
goto error_ret; goto error_ret;
switch (chan->channel2) {
case IIO_MOD_X_AND_Y_AND_Z:
ret = !!(st->rx[0] & SCA3000_FREE_FALL_DETECT);
break;
case IIO_MOD_X:
case IIO_MOD_Y:
case IIO_MOD_Z:
/*
* Motion detection mode cannot run at the same time as
* acceleration data being read.
*/
if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET) { if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET) {
ret = 0; ret = 0;
} else { } else {
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); ret = sca3000_read_ctrl_reg(st,
SCA3000_REG_CTRL_SEL_MD_CTRL);
if (ret < 0) if (ret < 0)
goto error_ret; goto error_ret;
/* only supporting logical or's for now */ /* only supporting logical or's for now */
ret = !!(ret & sca3000_addresses[num][2]); ret = !!(ret & sca3000_addresses[num][2]);
} }
break;
default:
ret = -EINVAL;
}
error_ret: error_ret:
mutex_unlock(&st->lock); mutex_unlock(&st->lock);
return ret; return ret;
} }
/** static int sca3000_freefall_set_state(struct iio_dev *indio_dev, int state)
* sca3000_query_free_fall_mode() is free fall mode enabled
**/
static ssize_t sca3000_query_free_fall_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{ {
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sca3000_state *st = iio_priv(indio_dev); struct sca3000_state *st = iio_priv(indio_dev);
int val;
mutex_lock(&st->lock);
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
val = st->rx[0];
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", !!(val & SCA3000_FREE_FALL_DETECT));
}
/**
* sca3000_set_free_fall_mode() simple on off control for free fall int
*
* In these chips the free fall detector should send an interrupt if
* the device falls more than 25cm. This has not been tested due
* to fragile wiring.
**/
static ssize_t sca3000_set_free_fall_mode(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sca3000_state *st = iio_priv(indio_dev);
u8 val;
int ret; int ret;
u8 protect_mask = SCA3000_FREE_FALL_DETECT;
mutex_lock(&st->lock);
ret = kstrtou8(buf, 10, &val);
if (ret)
goto error_ret;
/* read current value of mode register */ /* read current value of mode register */
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
if (ret) if (ret)
goto error_ret; return ret;
/* if off and should be on */ /* if off and should be on */
if (val && !(st->rx[0] & protect_mask)) if (state && !(st->rx[0] & SCA3000_FREE_FALL_DETECT))
ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, return sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
(st->rx[0] | SCA3000_FREE_FALL_DETECT)); st->rx[0] | SCA3000_FREE_FALL_DETECT);
/* if on and should be off */ /* if on and should be off */
else if (!val && (st->rx[0] & protect_mask)) else if (!state && (st->rx[0] & SCA3000_FREE_FALL_DETECT))
ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, return sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
(st->rx[0] & ~protect_mask)); st->rx[0] & ~SCA3000_FREE_FALL_DETECT);
error_ret: else
mutex_unlock(&st->lock); return 0;
return ret ? ret : len;
} }
/** static int sca3000_motion_detect_set_state(struct iio_dev *indio_dev, int axis,
* sca3000_write_event_config() simple on off control for motion detector
*
* This is a per axis control, but enabling any will result in the
* motion detector unit being enabled.
* N.B. enabling motion detector stops normal data acquisition.
* There is a complexity in knowing which mode to return to when
* this mode is disabled. Currently normal mode is assumed.
**/
static int sca3000_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) int state)
{ {
struct sca3000_state *st = iio_priv(indio_dev); struct sca3000_state *st = iio_priv(indio_dev);
int ret, ctrlval; int ret, ctrlval;
u8 protect_mask = 0x03;
int num = chan->channel2;
mutex_lock(&st->lock);
/* /*
* First read the motion detector config to find out if * First read the motion detector config to find out if
* this axis is on * this axis is on
*/ */
ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
if (ret < 0) if (ret < 0)
goto exit_point; return ret;
ctrlval = ret; ctrlval = ret;
/* if off and should be on */ /* if off and should be on */
if (state && !(ctrlval & sca3000_addresses[num][2])) { if (state && !(ctrlval & sca3000_addresses[axis][2])) {
ret = sca3000_write_ctrl_reg(st, ret = sca3000_write_ctrl_reg(st,
SCA3000_REG_CTRL_SEL_MD_CTRL, SCA3000_REG_CTRL_SEL_MD_CTRL,
ctrlval | ctrlval |
sca3000_addresses[num][2]); sca3000_addresses[axis][2]);
if (ret) if (ret)
goto exit_point; return ret;
st->mo_det_use_count++; st->mo_det_use_count++;
} else if (!state && (ctrlval & sca3000_addresses[num][2])) { } else if (!state && (ctrlval & sca3000_addresses[axis][2])) {
ret = sca3000_write_ctrl_reg(st, ret = sca3000_write_ctrl_reg(st,
SCA3000_REG_CTRL_SEL_MD_CTRL, SCA3000_REG_CTRL_SEL_MD_CTRL,
ctrlval & ctrlval &
~(sca3000_addresses[num][2])); ~(sca3000_addresses[axis][2]));
if (ret) if (ret)
goto exit_point; return ret;
st->mo_det_use_count--; st->mo_det_use_count--;
} }
/* read current value of mode register */ /* read current value of mode register */
ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
if (ret) if (ret)
goto exit_point; return ret;
/* if off and should be on */ /* if off and should be on */
if ((st->mo_det_use_count) && if ((st->mo_det_use_count) &&
((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET)) ((st->rx[0] & SCA3000_MODE_MASK) != SCA3000_MEAS_MODE_MOT_DET))
ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, return sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
(st->rx[0] & ~protect_mask) (st->rx[0] & ~SCA3000_MODE_MASK)
| SCA3000_MEAS_MODE_MOT_DET); | SCA3000_MEAS_MODE_MOT_DET);
/* if on and should be off */ /* if on and should be off */
else if (!(st->mo_det_use_count) && else if (!(st->mo_det_use_count) &&
((st->rx[0] & protect_mask) == SCA3000_MEAS_MODE_MOT_DET)) ((st->rx[0] & SCA3000_MODE_MASK) == SCA3000_MEAS_MODE_MOT_DET))
ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, return sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
(st->rx[0] & ~protect_mask)); st->rx[0] & SCA3000_MODE_MASK);
exit_point: else
mutex_unlock(&st->lock); return 0;
return ret;
} }
/* Free fall detector related event attribute */ /**
static IIO_DEVICE_ATTR_NAMED(accel_xayaz_mag_falling_en, * sca3000_write_event_config() simple on off control for motion detector
in_accel_x & y & z_mag_falling_en, *
S_IRUGO | S_IWUSR, * This is a per axis control, but enabling any will result in the
sca3000_query_free_fall_mode, * motion detector unit being enabled.
sca3000_set_free_fall_mode, * N.B. enabling motion detector stops normal data acquisition.
0); * There is a complexity in knowing which mode to return to when
* this mode is disabled. Currently normal mode is assumed.
**/
static int sca3000_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 sca3000_state *st = iio_priv(indio_dev);
int ret;
static IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period, mutex_lock(&st->lock);
in_accel_x & y & z_mag_falling_period, switch (chan->channel2) {
"0.226"); case IIO_MOD_X_AND_Y_AND_Z:
ret = sca3000_freefall_set_state(indio_dev, state);
break;
static struct attribute *sca3000_event_attributes[] = { case IIO_MOD_X:
&iio_dev_attr_accel_xayaz_mag_falling_en.dev_attr.attr, case IIO_MOD_Y:
&iio_const_attr_accel_xayaz_mag_falling_period.dev_attr.attr, case IIO_MOD_Z:
NULL, ret = sca3000_motion_detect_set_state(indio_dev, chan->channel2,
}; state);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&st->lock);
static struct attribute_group sca3000_event_attribute_group = { return ret;
.attrs = sca3000_event_attributes, }
.name = "events",
};
static int sca3000_configure_ring(struct iio_dev *indio_dev) static int sca3000_configure_ring(struct iio_dev *indio_dev)
{ {
...@@ -1436,9 +1444,8 @@ static const struct iio_info sca3000_info = { ...@@ -1436,9 +1444,8 @@ static const struct iio_info sca3000_info = {
.attrs = &sca3000_attribute_group, .attrs = &sca3000_attribute_group,
.read_raw = &sca3000_read_raw, .read_raw = &sca3000_read_raw,
.write_raw = &sca3000_write_raw, .write_raw = &sca3000_write_raw,
.event_attrs = &sca3000_event_attribute_group, .read_event_value = &sca3000_read_event_value,
.read_event_value = &sca3000_read_thresh, .write_event_value = &sca3000_write_event_value,
.write_event_value = &sca3000_write_thresh,
.read_event_config = &sca3000_read_event_config, .read_event_config = &sca3000_read_event_config,
.write_event_config = &sca3000_write_event_config, .write_event_config = &sca3000_write_event_config,
.driver_module = THIS_MODULE, .driver_module = THIS_MODULE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment