From: Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxxxxx> The accelerometer can report precise values for x, y and z accelerations but it can also simply report its orientation on XY plane and Z axis. Since the orientation of the device may be enough information for userspace and allows to avoid expensive fusion algorithms, let's add support for it. The orientation register stores a 2b value for XY plane orientation: between 225° and 315°, returns 0, between 315° and 45°, 1, between 45° and 135°, 2 and between 135° and 225°, 3. We "round" those to 270°, 0°, 90° and 180° degrees. For Z axis, the register bit returns 0 if facing the user, 1 otherwise, which the driver translates to 0° and 180° respectively. Those values are proper if the accelerometer is mounted such that the XYZ axes are as follows when the device is facing the user in portrait mode (respecting the right-hand rule): y ^ | | | +----------> x / / / L z Since this information is very basic, imprecise (only 4 values for XY plane and 2 for Z axis) and can be extrapolated from the actual, precise, x, y and z acceleration values, it is not made available through buffers. A change in XY plane or Z axis orientation can also trigger an interrupt but this feature is not added in this commit. Signed-off-by: Quentin Schulz <quentin.schulz@xxxxxxxxxxxxxxxxxxxxx> --- drivers/iio/accel/mxc4005.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index b3afbf064915..61f24058d239 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -20,6 +20,11 @@ #define MXC4005_IRQ_NAME "mxc4005_event" #define MXC4005_REGMAP_NAME "mxc4005_regmap" +#define MXC4005_REG_TILT_ORIENT 0x01 +#define MXC4005_REG_TILT_ORIENT_Z_MASK BIT(6) +#define MXC4005_REG_TILT_ORIENT_XY_MASK GENMASK(5, 4) +#define MXC4005_REG_TILT_ORIENT_XY_SHIFT 4 + #define MXC4005_REG_XOUT_UPPER 0x03 #define MXC4005_REG_XOUT_LOWER 0x04 #define MXC4005_REG_YOUT_UPPER 0x05 @@ -96,6 +101,7 @@ static const struct attribute_group mxc4005_attrs_group = { static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { + case MXC4005_REG_TILT_ORIENT: case MXC4005_REG_XOUT_UPPER: case MXC4005_REG_XOUT_LOWER: case MXC4005_REG_YOUT_UPPER: @@ -214,6 +220,28 @@ static int mxc4005_read_raw(struct iio_dev *indio_dev, int ret; switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_ROT: + ret = regmap_read(data->regmap, chan->address, val); + if (ret < 0) { + dev_err(data->dev, "failed to read rotation\n"); + return ret; + } + + if (chan->channel2 == IIO_MOD_X_AND_Y) { + *val &= MXC4005_REG_TILT_ORIENT_XY_MASK; + *val >>= MXC4005_REG_TILT_ORIENT_XY_SHIFT; + /* 00 = 270°; 01 = 0°; 10 = 90°; 11 = 180° */ + *val = (360 + (*val - 1) * 90) % 360; + } else { + *val &= MXC4005_REG_TILT_ORIENT_Z_MASK; + *val = *val ? 180 : 0; + } + return IIO_VAL_INT; + default: + return -EINVAL; + } case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_ACCEL: @@ -287,11 +315,22 @@ static const unsigned long mxc4005_scan_masks[] = { }, \ } +#define MXC4005_CHANNEL_ORIENTATION(_axis) { \ + .type = IIO_ROT, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = MXC4005_REG_TILT_ORIENT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = -1, \ +} + static const struct iio_chan_spec mxc4005_channels[] = { MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER), MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER), MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER), IIO_CHAN_SOFT_TIMESTAMP(3), + MXC4005_CHANNEL_ORIENTATION(X_AND_Y), + MXC4005_CHANNEL_ORIENTATION(Z), }; static irqreturn_t mxc4005_trigger_handler(int irq, void *private) -- 2.36.1