Commit e631f61f authored by Mehdi Djait's avatar Mehdi Djait Committed by Jonathan Cameron

iio: accel: Add support for Kionix/ROHM KX132-1211 accelerometer

Kionix KX132-1211 is a tri-axis 16-bit accelerometer that can support
ranges from ±2G to ±16G, digital output through I²C/SPI.
Add support for basic accelerometer features such as reading acceleration
via IIO using raw reads, triggered buffer (data-ready), or the WMI IRQ.

Datasheet: https://kionixfs.azureedge.net/en/document/KX132-1211-Technical-Reference-Manual-Rev-5.0.pdfAcked-by: default avatarMatti Vaittinen <mazziesaccount@gmail.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarMehdi Djait <mehdi.djait.k@gmail.com>
Link: https://lore.kernel.org/r/389a7d6100ff2e71b1c5d60bebe662d09435996a.1694867379.git.mehdi.djait.k@gmail.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 774c3fab
......@@ -418,8 +418,9 @@ config IIO_KX022A_SPI
select IIO_KX022A
select REGMAP_SPI
help
Enable support for the Kionix KX022A digital tri-axis
accelerometer connected to I2C interface.
Enable support for the Kionix digital tri-axis accelerometers
connected to SPI interface. Supported devices are:
KX022A, KX132-1211
config IIO_KX022A_I2C
tristate "Kionix KX022A tri-axis digital accelerometer I2C interface"
......@@ -427,8 +428,9 @@ config IIO_KX022A_I2C
select IIO_KX022A
select REGMAP_I2C
help
Enable support for the Kionix KX022A digital tri-axis
accelerometer connected to I2C interface.
Enable support for the Kionix digital tri-axis accelerometers
connected to I2C interface. Supported devices are:
KX022A, KX132-1211
config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver"
......
......@@ -37,12 +37,14 @@ static int kx022a_i2c_probe(struct i2c_client *i2c)
static const struct i2c_device_id kx022a_i2c_id[] = {
{ .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info },
{ .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id);
static const struct of_device_id kx022a_of_match[] = {
{ .compatible = "kionix,kx022a", .data = &kx022a_chip_info },
{ .compatible = "kionix,kx132-1211", .data = &kx132_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, kx022a_of_match);
......
......@@ -37,12 +37,14 @@ static int kx022a_spi_probe(struct spi_device *spi)
static const struct spi_device_id kx022a_id[] = {
{ .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info },
{ .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, kx022a_id);
static const struct of_device_id kx022a_of_match[] = {
{ .compatible = "kionix,kx022a", .data = &kx022a_chip_info },
{ .compatible = "kionix,kx132-1211", .data = &kx132_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, kx022a_of_match);
......
......@@ -150,6 +150,117 @@ static const struct regmap_config kx022a_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
/* Regmap configs kx132 */
static const struct regmap_range kx132_volatile_ranges[] = {
{
.range_min = KX132_REG_XADP_L,
.range_max = KX132_REG_COTR,
}, {
.range_min = KX132_REG_TSCP,
.range_max = KX132_REG_INT_REL,
}, {
/* The reset bit will be cleared by sensor */
.range_min = KX132_REG_CNTL2,
.range_max = KX132_REG_CNTL2,
}, {
.range_min = KX132_REG_CNTL5,
.range_max = KX132_REG_CNTL5,
}, {
.range_min = KX132_REG_BUF_STATUS_1,
.range_max = KX132_REG_BUF_READ,
},
};
static const struct regmap_access_table kx132_volatile_regs = {
.yes_ranges = &kx132_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(kx132_volatile_ranges),
};
static const struct regmap_range kx132_precious_ranges[] = {
{
.range_min = KX132_REG_INT_REL,
.range_max = KX132_REG_INT_REL,
},
};
static const struct regmap_access_table kx132_precious_regs = {
.yes_ranges = &kx132_precious_ranges[0],
.n_yes_ranges = ARRAY_SIZE(kx132_precious_ranges),
};
static const struct regmap_range kx132_read_only_ranges[] = {
{
.range_min = KX132_REG_XADP_L,
.range_max = KX132_REG_INT_REL,
}, {
.range_min = KX132_REG_BUF_STATUS_1,
.range_max = KX132_REG_BUF_STATUS_2,
}, {
.range_min = KX132_REG_BUF_READ,
.range_max = KX132_REG_BUF_READ,
}, {
/* Kionix reserved registers: should not be written */
.range_min = 0x28,
.range_max = 0x28,
}, {
.range_min = 0x35,
.range_max = 0x36,
}, {
.range_min = 0x3c,
.range_max = 0x48,
}, {
.range_min = 0x4e,
.range_max = 0x5c,
}, {
.range_min = 0x77,
.range_max = 0x7f,
},
};
static const struct regmap_access_table kx132_ro_regs = {
.no_ranges = &kx132_read_only_ranges[0],
.n_no_ranges = ARRAY_SIZE(kx132_read_only_ranges),
};
static const struct regmap_range kx132_write_only_ranges[] = {
{
.range_min = KX132_REG_SELF_TEST,
.range_max = KX132_REG_SELF_TEST,
}, {
.range_min = KX132_REG_BUF_CLEAR,
.range_max = KX132_REG_BUF_CLEAR,
},
};
static const struct regmap_access_table kx132_wo_regs = {
.no_ranges = &kx132_write_only_ranges[0],
.n_no_ranges = ARRAY_SIZE(kx132_write_only_ranges),
};
static const struct regmap_range kx132_noinc_read_ranges[] = {
{
.range_min = KX132_REG_BUF_READ,
.range_max = KX132_REG_BUF_READ,
},
};
static const struct regmap_access_table kx132_nir_regs = {
.yes_ranges = &kx132_noinc_read_ranges[0],
.n_yes_ranges = ARRAY_SIZE(kx132_noinc_read_ranges),
};
static const struct regmap_config kx132_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &kx132_volatile_regs,
.rd_table = &kx132_wo_regs,
.wr_table = &kx132_ro_regs,
.rd_noinc_table = &kx132_nir_regs,
.precious_table = &kx132_precious_regs,
.max_register = KX132_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
};
struct kx022a_data {
struct regmap *regmap;
const struct kx022a_chip_info *chip_info;
......@@ -239,6 +350,13 @@ static const struct iio_chan_spec kx022a_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec kx132_channels[] = {
KX022A_ACCEL_CHAN(X, KX132_REG_XOUT_L, 0),
KX022A_ACCEL_CHAN(Y, KX132_REG_YOUT_L, 1),
KX022A_ACCEL_CHAN(Z, KX132_REG_ZOUT_L, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
/*
* The sensor HW can support ODR up to 1600 Hz, which is beyond what most of the
* Linux CPUs can handle without dropping samples. Also, the low power mode is
......@@ -601,6 +719,26 @@ static int kx022a_get_fifo_bytes_available(struct kx022a_data *data)
return fifo_bytes;
}
static int kx132_get_fifo_bytes_available(struct kx022a_data *data)
{
__le16 buf_status;
int ret, fifo_bytes;
ret = regmap_bulk_read(data->regmap, data->chip_info->buf_status1,
&buf_status, sizeof(buf_status));
if (ret) {
dev_err(data->dev, "Error reading buffer status\n");
return ret;
}
fifo_bytes = le16_to_cpu(buf_status);
fifo_bytes &= data->chip_info->buf_smp_lvl_mask;
fifo_bytes = min((unsigned int)fifo_bytes, data->chip_info->fifo_length *
KX022A_FIFO_SAMPLES_SIZE_BYTES);
return fifo_bytes;
}
static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples,
bool irq)
{
......@@ -1024,6 +1162,32 @@ const struct kx022a_chip_info kx022a_chip_info = {
};
EXPORT_SYMBOL_NS_GPL(kx022a_chip_info, IIO_KX022A);
const struct kx022a_chip_info kx132_chip_info = {
.name = "kx132-1211",
.regmap_config = &kx132_regmap_config,
.channels = kx132_channels,
.num_channels = ARRAY_SIZE(kx132_channels),
.fifo_length = KX132_FIFO_LENGTH,
.who = KX132_REG_WHO,
.id = KX132_ID,
.cntl = KX132_REG_CNTL,
.cntl2 = KX132_REG_CNTL2,
.odcntl = KX132_REG_ODCNTL,
.buf_cntl1 = KX132_REG_BUF_CNTL1,
.buf_cntl2 = KX132_REG_BUF_CNTL2,
.buf_clear = KX132_REG_BUF_CLEAR,
.buf_status1 = KX132_REG_BUF_STATUS_1,
.buf_smp_lvl_mask = KX132_MASK_BUF_SMP_LVL,
.buf_read = KX132_REG_BUF_READ,
.inc1 = KX132_REG_INC1,
.inc4 = KX132_REG_INC4,
.inc5 = KX132_REG_INC5,
.inc6 = KX132_REG_INC6,
.xout_l = KX132_REG_XOUT_L,
.get_fifo_bytes_available = kx132_get_fifo_bytes_available,
};
EXPORT_SYMBOL_NS_GPL(kx132_chip_info, IIO_KX022A);
int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info)
{
static const char * const regulator_names[] = {"io-vdd", "vdd"};
......
......@@ -74,6 +74,57 @@
#define KX022A_REG_SELF_TEST 0x60
#define KX022A_MAX_REGISTER 0x60
#define KX132_REG_WHO 0x13
#define KX132_ID 0x3d
#define KX132_FIFO_LENGTH 86
#define KX132_REG_CNTL 0x1b
#define KX132_REG_CNTL2 0x1c
#define KX132_REG_CNTL5 0x1f
#define KX132_MASK_RES BIT(6)
#define KX132_GSEL_2 0x0
#define KX132_GSEL_4 BIT(3)
#define KX132_GSEL_8 BIT(4)
#define KX132_GSEL_16 GENMASK(4, 3)
#define KX132_REG_INS2 0x17
#define KX132_MASK_INS2_WMI BIT(5)
#define KX132_REG_XADP_L 0x02
#define KX132_REG_XOUT_L 0x08
#define KX132_REG_YOUT_L 0x0a
#define KX132_REG_ZOUT_L 0x0c
#define KX132_REG_COTR 0x12
#define KX132_REG_TSCP 0x14
#define KX132_REG_INT_REL 0x1a
#define KX132_REG_ODCNTL 0x21
#define KX132_REG_BTS_WUF_TH 0x4a
#define KX132_REG_BUF_CNTL1 0x5e
#define KX132_REG_BUF_CNTL2 0x5f
#define KX132_REG_BUF_STATUS_1 0x60
#define KX132_REG_BUF_STATUS_2 0x61
#define KX132_MASK_BUF_SMP_LVL GENMASK(9, 0)
#define KX132_REG_BUF_CLEAR 0x62
#define KX132_REG_BUF_READ 0x63
#define KX132_ODR_SHIFT 3
#define KX132_FIFO_MAX_WMI_TH 86
#define KX132_REG_INC1 0x22
#define KX132_REG_INC5 0x26
#define KX132_REG_INC6 0x27
#define KX132_IPOL_LOW 0
#define KX132_IPOL_HIGH KX022A_MASK_IPOL
#define KX132_ITYP_PULSE KX022A_MASK_ITYP
#define KX132_REG_INC4 0x25
#define KX132_REG_SELF_TEST 0x5d
#define KX132_MAX_REGISTER 0x76
struct device;
struct kx022a_data;
......@@ -86,6 +137,7 @@ struct kx022a_data;
* @channels: pointer to iio_chan_spec array
* @num_channels: number of iio_chan_spec channels
* @fifo_length: number of 16-bit samples in a full buffer
* @buf_smp_lvl_mask: buffer sample level mask
* @who: WHO_AM_I register
* @id: WHO_AM_I register value
* @cntl: control register 1
......@@ -111,6 +163,7 @@ struct kx022a_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
unsigned int fifo_length;
u16 buf_smp_lvl_mask;
u8 who;
u8 id;
u8 cntl;
......@@ -132,5 +185,6 @@ struct kx022a_chip_info {
int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info);
extern const struct kx022a_chip_info kx022a_chip_info;
extern const struct kx022a_chip_info kx132_chip_info;
#endif
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