summaryrefslogtreecommitdiff
path: root/drivers/iio/accel/adxl355_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/accel/adxl355_core.c')
-rw-r--r--drivers/iio/accel/adxl355_core.c167
1 files changed, 122 insertions, 45 deletions
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 4f485909f459..5fc7f814b907 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -18,9 +18,11 @@
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
-#include <linux/of_irq.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <asm/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/unaligned.h>
#include "adxl355.h"
@@ -54,15 +56,15 @@
#define ADXL355_POWER_CTL_DRDY_MSK BIT(2)
#define ADXL355_SELF_TEST_REG 0x2E
#define ADXL355_RESET_REG 0x2F
+#define ADXL355_BASE_ADDR_SHADOW_REG 0x50
+#define ADXL355_SHADOW_REG_COUNT 5
#define ADXL355_DEVID_AD_VAL 0xAD
#define ADXL355_DEVID_MST_VAL 0x1D
#define ADXL355_PARTID_VAL 0xED
+#define ADXL359_PARTID_VAL 0xE9
#define ADXL355_RESET_CODE 0x52
-#define MEGA 1000000UL
-#define TERA 1000000000000ULL
-
static const struct regmap_range adxl355_read_reg_range[] = {
regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG),
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG),
@@ -72,7 +74,7 @@ const struct regmap_access_table adxl355_readable_regs_tbl = {
.yes_ranges = adxl355_read_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl);
+EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, "IIO_ADXL355");
static const struct regmap_range adxl355_write_reg_range[] = {
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG),
@@ -82,7 +84,61 @@ const struct regmap_access_table adxl355_writeable_regs_tbl = {
.yes_ranges = adxl355_write_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range),
};
-EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl);
+EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, "IIO_ADXL355");
+
+const struct adxl355_chip_info adxl35x_chip_info[] = {
+ [ADXL355] = {
+ .name = "adxl355",
+ .part_id = ADXL355_PARTID_VAL,
+ /*
+ * At +/- 2g with 20-bit resolution, scale is given in datasheet
+ * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2.
+ */
+ .accel_scale = {
+ .integer = 0,
+ .decimal = 38245,
+ },
+ /*
+ * The datasheet defines an intercept of 1885 LSB at 25 degC
+ * and a slope of -9.05 LSB/C. The following formula can be used
+ * to find the temperature:
+ * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow
+ * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE.
+ * Hence using some rearranging we get the scale as -110.497238
+ * and offset as -2111.25.
+ */
+ .temp_offset = {
+ .integer = -2111,
+ .decimal = 250000,
+ },
+ },
+ [ADXL359] = {
+ .name = "adxl359",
+ .part_id = ADXL359_PARTID_VAL,
+ /*
+ * At +/- 10g with 20-bit resolution, scale is given in datasheet
+ * as 19.5ug/LSB = 0.0000195 * 9.80665 = 0.0.00019122967 m/s^2.
+ */
+ .accel_scale = {
+ .integer = 0,
+ .decimal = 191229,
+ },
+ /*
+ * The datasheet defines an intercept of 1852 LSB at 25 degC
+ * and a slope of -9.05 LSB/C. The following formula can be used
+ * to find the temperature:
+ * Temp = ((RAW - 1852)/(-9.05)) + 25 but this doesn't follow
+ * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE.
+ * Hence using some rearranging we get the scale as -110.497238
+ * and offset as -2079.25.
+ */
+ .temp_offset = {
+ .integer = -2079,
+ .decimal = 250000,
+ },
+ },
+};
+EXPORT_SYMBOL_NS_GPL(adxl35x_chip_info, "IIO_ADXL355");
enum adxl355_op_mode {
ADXL355_MEASUREMENT,
@@ -163,6 +219,7 @@ static const struct adxl355_chan_info adxl355_chans[] = {
};
struct adxl355_data {
+ const struct adxl355_chip_info *chip_info;
struct regmap *regmap;
struct device *dev;
struct mutex lock; /* lock to protect op_mode */
@@ -176,9 +233,9 @@ struct adxl355_data {
u8 transf_buf[3];
struct {
u8 buf[14];
- s64 ts;
+ aligned_s64 ts;
} buffer;
- } ____cacheline_aligned;
+ } __aligned(IIO_DMA_MINALIGN);
};
static int adxl355_set_op_mode(struct adxl355_data *data,
@@ -239,7 +296,12 @@ static void adxl355_fill_3db_frequency_table(struct adxl355_data *data)
static int adxl355_setup(struct adxl355_data *data)
{
unsigned int regval;
+ int retries = 5; /* the number is chosen based on empirical reasons */
int ret;
+ u8 *shadow_regs __free(kfree) = kzalloc(ADXL355_SHADOW_REG_COUNT, GFP_KERNEL);
+
+ if (!shadow_regs)
+ return -ENOMEM;
ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, &regval);
if (ret)
@@ -263,19 +325,44 @@ static int adxl355_setup(struct adxl355_data *data)
if (ret)
return ret;
- if (regval != ADXL355_PARTID_VAL) {
- dev_err(data->dev, "Invalid DEV ID 0x%02x\n", regval);
- return -ENODEV;
- }
+ if (regval != ADXL355_PARTID_VAL)
+ dev_warn(data->dev, "Invalid DEV ID 0x%02x\n", regval);
- /*
- * Perform a software reset to make sure the device is in a consistent
- * state after start-up.
- */
- ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE);
+ /* Read shadow registers to be compared after reset */
+ ret = regmap_bulk_read(data->regmap,
+ ADXL355_BASE_ADDR_SHADOW_REG,
+ shadow_regs, ADXL355_SHADOW_REG_COUNT);
if (ret)
return ret;
+ do {
+ if (--retries == 0) {
+ dev_err(data->dev, "Shadow registers mismatch\n");
+ return -EIO;
+ }
+
+ /*
+ * Perform a software reset to make sure the device is in a consistent
+ * state after start-up.
+ */
+ ret = regmap_write(data->regmap, ADXL355_RESET_REG,
+ ADXL355_RESET_CODE);
+ if (ret)
+ return ret;
+
+ /* Wait at least 5ms after software reset */
+ usleep_range(5000, 10000);
+
+ /* Read shadow registers for comparison */
+ ret = regmap_bulk_read(data->regmap,
+ ADXL355_BASE_ADDR_SHADOW_REG,
+ data->buffer.buf,
+ ADXL355_SHADOW_REG_COUNT);
+ if (ret)
+ return ret;
+ } while (memcmp(shadow_regs, data->buffer.buf,
+ ADXL355_SHADOW_REG_COUNT));
+
ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG,
ADXL355_POWER_CTL_DRDY_MSK,
FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1));
@@ -459,33 +546,25 @@ static int adxl355_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
- /*
- * The datasheet defines an intercept of 1885 LSB at 25 degC
- * and a slope of -9.05 LSB/C. The following formula can be used
- * to find the temperature:
- * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow
- * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE.
- * Hence using some rearranging we get the scale as -110.497238
- * and offset as -2111.25.
- */
case IIO_TEMP:
+ /*
+ * Temperature scale is -110.497238.
+ * See the detailed explanation in adxl35x_chip_info
+ * definition above.
+ */
*val = -110;
*val2 = 497238;
return IIO_VAL_INT_PLUS_MICRO;
- /*
- * At +/- 2g with 20-bit resolution, scale is given in datasheet
- * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2.
- */
case IIO_ACCEL:
- *val = 0;
- *val2 = 38245;
+ *val = data->chip_info->accel_scale.integer;
+ *val2 = data->chip_info->accel_scale.decimal;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
- *val = -2111;
- *val2 = 250000;
+ *val = data->chip_info->temp_offset.integer;
+ *val2 = data->chip_info->temp_offset.decimal;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
*val = sign_extend32(data->calibbias[chan->address], 15);
@@ -598,7 +677,7 @@ static irqreturn_t adxl355_trigger_handler(int irq, void *p)
* The acceleration data is 24 bits and big endian. It has to be saved
* in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer.
* The buf array is 14 bytes as it includes 3x4=12 bytes for
- * accelaration data of x, y, and z axis. It also includes 2 bytes for
+ * acceleration data of x, y, and z axis. It also includes 2 bytes for
* temperature data.
*/
ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG,
@@ -621,8 +700,8 @@ static irqreturn_t adxl355_trigger_handler(int irq, void *p)
if (ret)
goto out_unlock_notify;
- iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
- pf->timestamp);
+ iio_push_to_buffers_with_ts(indio_dev, &data->buffer,
+ sizeof(data->buffer), pf->timestamp);
out_unlock_notify:
mutex_unlock(&data->lock);
@@ -708,7 +787,7 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
}
int adxl355_core_probe(struct device *dev, struct regmap *regmap,
- const char *name)
+ const struct adxl355_chip_info *chip_info)
{
struct adxl355_data *data;
struct iio_dev *indio_dev;
@@ -723,9 +802,10 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap,
data->regmap = regmap;
data->dev = dev;
data->op_mode = ADXL355_STANDBY;
+ data->chip_info = chip_info;
mutex_init(&data->lock);
- indio_dev->name = name;
+ indio_dev->name = chip_info->name;
indio_dev->info = &adxl355_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl355_channels;
@@ -746,10 +826,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap,
return ret;
}
- /*
- * TODO: Would be good to move it to the generic version.
- */
- irq = of_irq_get_byname(dev->of_node, "DRDY");
+ irq = fwnode_irq_get_byname(dev_fwnode(dev), "DRDY");
if (irq > 0) {
ret = adxl355_probe_trigger(indio_dev, irq);
if (ret)
@@ -758,7 +835,7 @@ int adxl355_core_probe(struct device *dev, struct regmap *regmap,
return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_GPL(adxl355_core_probe);
+EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, "IIO_ADXL355");
MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver");