diff mbox series

[2/2] drivers: iio: accel adxl312 and adxl314 support

Message ID 20220816102828.182345-3-george.mois@analog.com (mailing list archive)
State Superseded
Headers show
Series Add support for ADXL312 and ADXL314 accelerometers | expand

Commit Message

George Mois Aug. 16, 2022, 10:28 a.m. UTC
ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
respectively.

Implement support for ADXL312 and ADXL314 by extending the ADXL313
driver.

Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL312.pdf
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL314.pdf

Signed-off-by: George Mois <george.mois@analog.com>
---
 drivers/iio/accel/adxl313.h      |  15 ++-
 drivers/iio/accel/adxl313_core.c | 164 +++++++++++++++++++++++--------
 drivers/iio/accel/adxl313_spi.c  |  40 +++++++-
 3 files changed, 173 insertions(+), 46 deletions(-)

Comments

Krzysztof Kozlowski Aug. 16, 2022, 11:16 a.m. UTC | #1
On 16/08/2022 13:28, George Mois wrote:
> ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
> with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
> respectively.
> 

Thank you for your patch. There is something to discuss/improve.

>  
>  static const struct spi_device_id adxl313_spi_id[] = {
> -	{ "adxl313" },
> +	{ "adxl312", ADXL312 },
> +	{ "adxl313", ADXL313 },
> +	{ "adxl314", ADXL314 },
>  	{ }
>  };
>  
>  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
>  
>  static const struct of_device_id adxl313_of_match[] = {
> +	{ .compatible = "adi,adxl312" },
>  	{ .compatible = "adi,adxl313" },
> +	{ .compatible = "adi,adxl314" },

You miss here driver data. I don't remember which driver matching takes
precedence (especially in various cases like DT platforms with device
instantiated by MFD), but for consistency I think both device id tables
should have same driver data.


Best regards,
Krzysztof
Jonathan Cameron Aug. 16, 2022, 12:44 p.m. UTC | #2
On Tue, 16 Aug 2022 14:16:13 +0300
Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:

> On 16/08/2022 13:28, George Mois wrote:
> > ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
> > with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
> > respectively.
> >   
> 
> Thank you for your patch. There is something to discuss/improve.
> 
> >  
> >  static const struct spi_device_id adxl313_spi_id[] = {
> > -	{ "adxl313" },
> > +	{ "adxl312", ADXL312 },
> > +	{ "adxl313", ADXL313 },
> > +	{ "adxl314", ADXL314 },
> >  	{ }
> >  };
> >  
> >  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
> >  
> >  static const struct of_device_id adxl313_of_match[] = {
> > +	{ .compatible = "adi,adxl312" },
> >  	{ .compatible = "adi,adxl313" },
> > +	{ .compatible = "adi,adxl314" },  
> 
> You miss here driver data. I don't remember which driver matching takes
> precedence (especially in various cases like DT platforms with device
> instantiated by MFD), but for consistency I think both device id tables
> should have same driver data.

You can set it up to try device_get_match_data() first then fallback
to the adxl313_spi_id[] table but there isn't a nice 'standard' way to
do it.

If that isn't done, then IIRC the match is against the compatible with
the vendor ID dropped and the table used is the spi_device_id one.
Which is just annoyingly complex and relies on the strings matching.

In the ideal world the spi_device_id table would go away but there are
still a few users (greybus - I think + remaining board files).
So for now something like

a = device_get_match_data(dev);
if (!a)
	a = &adxl31x_spi_regmap_config[id->data];

Provides a good way of ensuring the id tables don't need to remain
in sync.

Jonathan


> 
> 
> Best regards,
> Krzysztof
Krzysztof Kozlowski Aug. 16, 2022, 1:34 p.m. UTC | #3
On 16/08/2022 15:44, Jonathan Cameron wrote:
>>>  
>>>  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
>>>  
>>>  static const struct of_device_id adxl313_of_match[] = {
>>> +	{ .compatible = "adi,adxl312" },
>>>  	{ .compatible = "adi,adxl313" },
>>> +	{ .compatible = "adi,adxl314" },  
>>
>> You miss here driver data. I don't remember which driver matching takes
>> precedence (especially in various cases like DT platforms with device
>> instantiated by MFD), but for consistency I think both device id tables
>> should have same driver data.
> 
> You can set it up to try device_get_match_data() first then fallback
> to the adxl313_spi_id[] table but there isn't a nice 'standard' way to
> do it.
> 
> If that isn't done, then IIRC the match is against the compatible with
> the vendor ID dropped and the table used is the spi_device_id one.
> Which is just annoyingly complex and relies on the strings matching.
> 
> In the ideal world the spi_device_id table would go away but there are
> still a few users (greybus - I think + remaining board files).
> So for now something like
> 
> a = device_get_match_data(dev);
> if (!a)
> 	a = &adxl31x_spi_regmap_config[id->data];
> 
> Provides a good way of ensuring the id tables don't need to remain
> in sync.
> 

I guess the only minor issue is that first driver data - ADXL312 - is
equal to 0, so above code would make consider ADXL312 as missing data.

Best regards,
Krzysztof
Jonathan Cameron Aug. 16, 2022, 1:40 p.m. UTC | #4
On Tue, 16 Aug 2022 16:34:59 +0300
Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> wrote:

> On 16/08/2022 15:44, Jonathan Cameron wrote:
> >>>  
> >>>  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
> >>>  
> >>>  static const struct of_device_id adxl313_of_match[] = {
> >>> +	{ .compatible = "adi,adxl312" },
> >>>  	{ .compatible = "adi,adxl313" },
> >>> +	{ .compatible = "adi,adxl314" },    
> >>
> >> You miss here driver data. I don't remember which driver matching takes
> >> precedence (especially in various cases like DT platforms with device
> >> instantiated by MFD), but for consistency I think both device id tables
> >> should have same driver data.  
> > 
> > You can set it up to try device_get_match_data() first then fallback
> > to the adxl313_spi_id[] table but there isn't a nice 'standard' way to
> > do it.
> > 
> > If that isn't done, then IIRC the match is against the compatible with
> > the vendor ID dropped and the table used is the spi_device_id one.
> > Which is just annoyingly complex and relies on the strings matching.
> > 
> > In the ideal world the spi_device_id table would go away but there are
> > still a few users (greybus - I think + remaining board files).
> > So for now something like
> > 
> > a = device_get_match_data(dev);
> > if (!a)
> > 	a = &adxl31x_spi_regmap_config[id->data];
> > 
> > Provides a good way of ensuring the id tables don't need to remain
> > in sync.
> >   
> 
> I guess the only minor issue is that first driver data - ADXL312 - is
> equal to 0, so above code would make consider ADXL312 as missing data.
Should have given a type to a.

struct adxl31x_chip_info *a;

It would be a pointer not an enum.  Though we might run into some problems
with that clang issue of whether array lookups are const (I've not really
gotten my head around that yet). 

Jonathan


> 
> Best regards,
> Krzysztof
Jonathan Cameron Aug. 16, 2022, 2:09 p.m. UTC | #5
On Tue, 16 Aug 2022 13:28:28 +0300
George Mois <george.mois@analog.com> wrote:

> ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
> with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
> respectively.
> 
> Implement support for ADXL312 and ADXL314 by extending the ADXL313
> driver.
> 
> Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL312.pdf
> Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL314.pdf
> 
> Signed-off-by: George Mois <george.mois@analog.com>

Hi George,

Various comments inline.

Thanks,

Jonathan
> ---
>  drivers/iio/accel/adxl313.h      |  15 ++-
>  drivers/iio/accel/adxl313_core.c | 164 +++++++++++++++++++++++--------
>  drivers/iio/accel/adxl313_spi.c  |  40 +++++++-
>  3 files changed, 173 insertions(+), 46 deletions(-)
> 
> diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
> index 4415f2fc07e1..7428b1f7768f 100644
> --- a/drivers/iio/accel/adxl313.h
> +++ b/drivers/iio/accel/adxl313.h
> @@ -26,6 +26,7 @@
>  #define ADXL313_REG_FIFO_STATUS		0x39
>  
>  #define ADXL313_DEVID0			0xAD
> +#define ADXL313_DEVID0_ADXL312_314	0xE5
>  #define ADXL313_DEVID1			0x1D
>  #define ADXL313_PARTID			0xCB
>  #define ADXL313_SOFT_RESET		0x52
> @@ -37,18 +38,28 @@
>  #define ADXL313_MEASUREMENT_MODE	BIT(3)
>  
>  #define ADXL313_RANGE_MSK		GENMASK(1, 0)
> -#define ADXL313_RANGE_4G		3
> +#define ADXL313_RANGE_MAX		3
>  
>  #define ADXL313_FULL_RES		BIT(3)
>  #define ADXL313_SPI_3WIRE		BIT(6)
>  #define ADXL313_I2C_DISABLE		BIT(6)
>  
> +extern const struct regmap_access_table adxl312_readable_regs_table;
>  extern const struct regmap_access_table adxl313_readable_regs_table;
> +extern const struct regmap_access_table adxl314_readable_regs_table;
>  
> +extern const struct regmap_access_table adxl312_writable_regs_table;
>  extern const struct regmap_access_table adxl313_writable_regs_table;
> +extern const struct regmap_access_table adxl314_writable_regs_table;
> +
> +enum adxl313_device_type {
> +	ADXL312,
> +	ADXL313,
> +	ADXL314,
> +};
>  
>  int adxl313_core_probe(struct device *dev,
>  		       struct regmap *regmap,
> -		       const char *name,
> +		       const struct spi_device_id *id,
>  		       int (*setup)(struct device *, struct regmap *));
>  #endif /* _ADXL313_H_ */
> diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
> index afeef779e1d0..eea7d235950e 100644
> --- a/drivers/iio/accel/adxl313_core.c
> +++ b/drivers/iio/accel/adxl313_core.c
> @@ -11,9 +11,17 @@
>  #include <linux/iio/iio.h>
>  #include <linux/module.h>
>  #include <linux/regmap.h>
> +#include <linux/spi/spi.h>

Not in the core driver. This needs to be kept unware of the
buses that migh be used.

>  
>  #include "adxl313.h"
>  
> +static const struct regmap_range adxl312_readable_reg_range[] = {
> +	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
> +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
> +};
> +
>  static const struct regmap_range adxl313_readable_reg_range[] = {
>  	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
>  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
> @@ -22,12 +30,32 @@ static const struct regmap_range adxl313_readable_reg_range[] = {
>  	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
>  };
>  
> +const struct regmap_access_table adxl312_readable_regs_table = {
> +	.yes_ranges = adxl312_readable_reg_range,
> +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> +};
> +EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL312);
> +
>  const struct regmap_access_table adxl313_readable_regs_table = {
>  	.yes_ranges = adxl313_readable_reg_range,
>  	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
>  };
>  EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
>  
> +const struct regmap_access_table adxl314_readable_regs_table = {
> +	.yes_ranges = adxl312_readable_reg_range,
> +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> +};
> +EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL314);
> +
> +static const struct regmap_range adxl312_writable_reg_range[] = {
> +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
> +	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
> +	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
> +};
> +
>  static const struct regmap_range adxl313_writable_reg_range[] = {
>  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
>  	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> @@ -37,16 +65,30 @@ static const struct regmap_range adxl313_writable_reg_range[] = {
>  	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
>  };
>  
> +const struct regmap_access_table adxl312_writable_regs_table = {
> +	.yes_ranges = adxl312_writable_reg_range,
> +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> +};
> +EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL312);
> +
>  const struct regmap_access_table adxl313_writable_regs_table = {
>  	.yes_ranges = adxl313_writable_reg_range,
>  	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
>  };
>  EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
>  
> +const struct regmap_access_table adxl314_writable_regs_table = {
> +	.yes_ranges = adxl312_writable_reg_range,
> +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> +};
> +EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL314);
> +
>  struct adxl313_data {
>  	struct regmap	*regmap;
> +	const struct spi_device_id *id;
> +	int scale_factor;
>  	struct mutex	lock; /* lock to protect transf_buf */
> -	__le16		transf_buf __aligned(IIO_DMA_MINALIGN);
> +	__le16		transf_buf ____cacheline_aligned;
Check your patch for accidental reverts of other changes like this...

>  };
>  
>  static const int adxl313_odr_freqs[][2] = {
> @@ -156,12 +198,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
>  		*val = sign_extend32(ret, chan->scan_type.realbits - 1);
>  		return IIO_VAL_INT;
>  	case IIO_CHAN_INFO_SCALE:
> -		/*
> -		 * Scale for any g range is given in datasheet as
> -		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
> -		 */
>  		*val = 0;
> -		*val2 = 9576806;
> +
> +		*val2 = data->scale_factor;
I'd move this into a structure containing all the chip type specific data.
Hence this would be
data->chip.scale_factor.

> +
>  		return IIO_VAL_INT_PLUS_NANO;
>  	case IIO_CHAN_INFO_CALIBBIAS:
>  		ret = regmap_read(data->regmap,
> @@ -170,7 +210,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
>  			return ret;
>  
>  		/*
> -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> +		 * 8-bit resolution at minimum range, that is 4x accel data scale
>  		 * factor at full resolution
>  		 */
>  		*val = sign_extend32(regval, 7) * 4;
> @@ -198,7 +238,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
>  	switch (mask) {
>  	case IIO_CHAN_INFO_CALIBBIAS:
>  		/*
> -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> +		 * 8-bit resolution at minimum range, that is 4x accel data scale
>  		 * factor at full resolution
>  		 */
>  		if (clamp_val(val, -128 * 4, 127 * 4) != val)
> @@ -223,14 +263,17 @@ static const struct iio_info adxl313_info = {
>  static int adxl313_setup(struct device *dev, struct adxl313_data *data,
>  			 int (*setup)(struct device *, struct regmap *))
>  {
> +	enum adxl313_device_type dev_type = data->id->driver_data;
>  	unsigned int regval;
>  	int ret;
>  
> -	/* Ensures the device is in a consistent state after start up */
> -	ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> -			   ADXL313_SOFT_RESET);
> -	if (ret)
> -		return ret;
> +	/* If ADXL313, ensures the device is in a consistent state after start up */
> +	if (dev_type == ADXL313) {

Add a flag to the chip_info structure suggested below for 'soft_reset' and
base this decision on that.  The aim is to encode all the variations in chips
in one place so adding future chips is isolated there.

> +		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> +				   ADXL313_SOFT_RESET);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	if (setup) {
>  		ret = setup(dev, data->regmap);
> @@ -242,41 +285,54 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
>  	if (ret)
>  		return ret;
>  
> -	if (regval != ADXL313_DEVID0) {
> +	if (dev_type == ADXL313 && regval != ADXL313_DEVID0) {

Obviously this predates your changes...
Don't fail in this case.  Warn only.  We may be dealing with a newer compatible
device that falls back to an the ADXL313 compatible in dt. It's fine to express
we don't know what it is - particularly if ID matches something else, but
not to fail as a result.

>  		dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
>  		return -ENODEV;
>  	}
>  
> -	ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
> -	if (ret)
> -		return ret;
> +	/* If ADXL313, check DEVID1 and PARTID */
> +	if (regval == ADXL313_DEVID0) {
> +		ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
> +		if (ret)
> +			return ret;
>  
> -	if (regval != ADXL313_DEVID1) {
As above, should not fail - but just give a warning message (or dev_info perhaps).

> -		dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
> -		return -ENODEV;
> -	}
> +		if (regval != ADXL313_DEVID1) {
> +			dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
> +			return -ENODEV;
> +		}
>  
> -	ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
> -	if (ret)
> -		return ret;
> +		ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
> +		if (ret)
> +			return ret;
>  
> -	if (regval != ADXL313_PARTID) {
> -		dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
> +		if (regval != ADXL313_PARTID) {
> +			dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
> +			return -ENODEV;
> +		}
> +	}
> +
> +	/* If ADXL312 or ADXL314 device, check DEVID0 */
> +	if (dev_type != ADXL313 && regval != ADXL313_DEVID0_ADXL312_314) {
> +		dev_err(dev, "Invalid manufacturer ID: %#02x\n", regval);
>  		return -ENODEV;
>  	}
>  
> -	/* Sets the range to +/- 4g */
> -	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> -				 ADXL313_RANGE_MSK,
> -				 FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G));
> -	if (ret)
> -		return ret;
> +	dev_info(dev, "%s detected.\n", data->id->name);

Noise in the log. Drop this. It's easy to find out the name from sysfs.

>  
> -	/* Enables full resolution */
> -	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> -				 ADXL313_FULL_RES, ADXL313_FULL_RES);
> -	if (ret)
> -		return ret;
> +	/* Sets the range to maximum, full resolution, for ADXL312 and ADXL313 */
> +	if (dev_type == ADXL312 || dev_type == ADXL313) {
I'm reading this backwards btw.  As below, this sort of thing should depend on
contents of a chip_info structure.
In this case perhaps a flag called 'set max range').

> +		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> +					 ADXL313_RANGE_MSK,
> +					 FIELD_PREP(ADXL313_RANGE_MSK, 0x02));

0x02 looks like a magic numbers, you should ideally provide a define.

> +		if (ret)
> +			return ret;
> +
> +		/* Enables full resolution */
> +		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> +					 ADXL313_FULL_RES, 0);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	/* Enables measurement mode */
>  	return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
> @@ -296,7 +352,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
>   */
>  int adxl313_core_probe(struct device *dev,
>  		       struct regmap *regmap,
> -		       const char *name,
> +		       const struct spi_device_id *id,

A core probe function shouldn't be taking an SPI specific structure.
Fine to pass the id enum entry.  That can then index a chip_info structure
that includes the part name - we don't want to pass that through this interface
as well.

>  		       int (*setup)(struct device *, struct regmap *))
>  {
>  	struct adxl313_data *data;
> @@ -309,9 +365,35 @@ int adxl313_core_probe(struct device *dev,
>  
>  	data = iio_priv(indio_dev);
>  	data->regmap = regmap;
> +	data->id = id;
> +
> +	if (id->driver_data == ADXL312)

Whenever you hit a case like this, it almost always means you should be selecting
from an array of chip_info structures. One per type of chip.
If it is possible to convert code to data that is almost always a good idea.

> +		/*
> +		 * ADXL312
> +		 * Scale for any g range (full range) is given in datasheet as
> +		 * 345 LSB/g = 0.0028985507 * 9.80665 = 0.028425072222155 m/s^2
> +		 */
> +		data->scale_factor = 28425072;
> +
> +	if (id->driver_data == ADXL313)
> +		/*
> +		 * * ADXL313
> +		 * Scale for any g range is given in datasheet as
> +		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
> +		 */
> +		data->scale_factor = 9576806;
> +
> +	if (id->driver_data == ADXL314)
> +		/*
> +		 * ADXL314
> +		 * At +/-200g with 13-bit resolution, scale factor is given in datasheet as
> +		 * 48.83 mg/LSB = 0.0488300 * 9.80665 = 0.4788587195 m/s^2.
> +		 */
> +		data->scale_factor = 478858719;
> +
>  	mutex_init(&data->lock);
>  
> -	indio_dev->name = name;
> +	indio_dev->name = id->name;
>  	indio_dev->info = &adxl313_info;
>  	indio_dev->modes = INDIO_DIRECT_MODE;
>  	indio_dev->channels = adxl313_channels;
> @@ -319,13 +401,13 @@ int adxl313_core_probe(struct device *dev,
>  
>  	ret = adxl313_setup(dev, data, setup);
>  	if (ret) {
> -		dev_err(dev, "ADXL313 setup failed\n");
> +		dev_err(dev, "Device %s setup failed\n", id->name);

I doubt we'll ever care which part it was that failed.  I'd
just leave that as it waas.

>  		return ret;
>  	}
>  
>  	return devm_iio_device_register(dev, indio_dev);
>  }
> -EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
> +EXPORT_SYMBOL_GPL(adxl313_core_probe);

Please don't remove the namespacing. Add any new exports to the
existing IIO_ADXL313 NS.

>  
>  MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
>  MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");
> diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
> index a3c6d553462d..19cd096373b0 100644
> --- a/drivers/iio/accel/adxl313_spi.c
> +++ b/drivers/iio/accel/adxl313_spi.c
> @@ -14,6 +14,16 @@
>  
>  #include "adxl313.h"
>  
> +static const struct regmap_config adxl312_spi_regmap_config = {
> +	.reg_bits	= 8,
> +	.val_bits	= 8,
> +	.rd_table	= &adxl312_readable_regs_table,
> +	.wr_table	= &adxl312_writable_regs_table,
> +	.max_register	= 0x39,
> +	 /* Setting bits 7 and 6 enables multiple-byte read */
> +	.read_flag_mask	= BIT(7) | BIT(6),
> +};
> +
>  static const struct regmap_config adxl313_spi_regmap_config = {
>  	.reg_bits	= 8,
>  	.val_bits	= 8,
> @@ -24,6 +34,22 @@ static const struct regmap_config adxl313_spi_regmap_config = {
>  	.read_flag_mask	= BIT(7) | BIT(6),
>  };
>  
> +static const struct regmap_config adxl314_spi_regmap_config = {
> +	.reg_bits	= 8,
> +	.val_bits	= 8,
> +	.rd_table	= &adxl314_readable_regs_table,
> +	.wr_table	= &adxl314_writable_regs_table,
> +	.max_register	= 0x39,
> +	 /* Setting bits 7 and 6 enables multiple-byte read */
> +	.read_flag_mask	= BIT(7) | BIT(6),
> +};
> +
> +static const struct regmap_config adxl31x_spi_regmap_config[] = {
> +	adxl312_spi_regmap_config,
> +	adxl313_spi_regmap_config,
> +	adxl314_spi_regmap_config
> +};
> +
>  static int adxl313_spi_setup(struct device *dev, struct regmap *regmap)
>  {
>  	struct spi_device *spi = container_of(dev, struct spi_device, dev);
> @@ -51,26 +77,32 @@ static int adxl313_spi_probe(struct spi_device *spi)
>  	if (ret)
>  		return ret;
>  
> -	regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config);
> +	regmap = devm_regmap_init_spi(spi,
> +				      &adxl31x_spi_regmap_config[id->driver_data]);
> +
>  	if (IS_ERR(regmap)) {
>  		dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
>  			PTR_ERR(regmap));
>  		return PTR_ERR(regmap);
>  	}
>  
> -	return adxl313_core_probe(&spi->dev, regmap, id->name,
> +	return adxl313_core_probe(&spi->dev, regmap, id,
>  				  &adxl313_spi_setup);
>  }
>  
>  static const struct spi_device_id adxl313_spi_id[] = {
> -	{ "adxl313" },
> +	{ "adxl312", ADXL312 },
> +	{ "adxl313", ADXL313 },
> +	{ "adxl314", ADXL314 },
>  	{ }
>  };
>  
>  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
>  
>  static const struct of_device_id adxl313_of_match[] = {
> +	{ .compatible = "adi,adxl312" },
>  	{ .compatible = "adi,adxl313" },
> +	{ .compatible = "adi,adxl314" },
>  	{ }
As mentioned in other thread, there is a nicer way of handling
the different types of firmware ID.
>  };
>  
> @@ -90,4 +122,6 @@ module_spi_driver(adxl313_spi_driver);
>  MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
>  MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver");
>  MODULE_LICENSE("GPL v2");
> +MODULE_IMPORT_NS(IIO_ADXL312);
Take a look at what this is doing. There is only one namespace for the
driver, not one per chip type.

>  MODULE_IMPORT_NS(IIO_ADXL313);
> +MODULE_IMPORT_NS(IIO_ADXL314);
George Mois Aug. 18, 2022, 10:11 a.m. UTC | #6
Hi Jonathan,

Thank you for your review!

Regards,
George
> 
> On Tue, 16 Aug 2022 13:28:28 +0300
> George Mois <george.mois@analog.com> wrote:
> 
> > ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
> > with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
> > respectively.
> >
> > Implement support for ADXL312 and ADXL314 by extending the ADXL313
> > driver.
> >
> > Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL312.pdf
> > Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL314.pdf
> >
> > Signed-off-by: George Mois <george.mois@analog.com>
> 
> Hi George,
> 
> Various comments inline.
> 
> Thanks,
> 
> Jonathan
> > ---
> >  drivers/iio/accel/adxl313.h      |  15 ++-
> >  drivers/iio/accel/adxl313_core.c | 164 +++++++++++++++++++++++--------
> >  drivers/iio/accel/adxl313_spi.c  |  40 +++++++-
> >  3 files changed, 173 insertions(+), 46 deletions(-)
> >
> > diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
> > index 4415f2fc07e1..7428b1f7768f 100644
> > --- a/drivers/iio/accel/adxl313.h
> > +++ b/drivers/iio/accel/adxl313.h
> > @@ -26,6 +26,7 @@
> >  #define ADXL313_REG_FIFO_STATUS		0x39
> >
> >  #define ADXL313_DEVID0			0xAD
> > +#define ADXL313_DEVID0_ADXL312_314	0xE5
> >  #define ADXL313_DEVID1			0x1D
> >  #define ADXL313_PARTID			0xCB
> >  #define ADXL313_SOFT_RESET		0x52
> > @@ -37,18 +38,28 @@
> >  #define ADXL313_MEASUREMENT_MODE	BIT(3)
> >
> >  #define ADXL313_RANGE_MSK		GENMASK(1, 0)
> > -#define ADXL313_RANGE_4G		3
> > +#define ADXL313_RANGE_MAX		3
> >
> >  #define ADXL313_FULL_RES		BIT(3)
> >  #define ADXL313_SPI_3WIRE		BIT(6)
> >  #define ADXL313_I2C_DISABLE		BIT(6)
> >
> > +extern const struct regmap_access_table adxl312_readable_regs_table;
> >  extern const struct regmap_access_table adxl313_readable_regs_table;
> > +extern const struct regmap_access_table adxl314_readable_regs_table;
> >
> > +extern const struct regmap_access_table adxl312_writable_regs_table;
> >  extern const struct regmap_access_table adxl313_writable_regs_table;
> > +extern const struct regmap_access_table adxl314_writable_regs_table;
> > +
> > +enum adxl313_device_type {
> > +	ADXL312,
> > +	ADXL313,
> > +	ADXL314,
> > +};
> >
> >  int adxl313_core_probe(struct device *dev,
> >  		       struct regmap *regmap,
> > -		       const char *name,
> > +		       const struct spi_device_id *id,
> >  		       int (*setup)(struct device *, struct regmap *));
> >  #endif /* _ADXL313_H_ */
> > diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
> > index afeef779e1d0..eea7d235950e 100644
> > --- a/drivers/iio/accel/adxl313_core.c
> > +++ b/drivers/iio/accel/adxl313_core.c
> > @@ -11,9 +11,17 @@
> >  #include <linux/iio/iio.h>
> >  #include <linux/module.h>
> >  #include <linux/regmap.h>
> > +#include <linux/spi/spi.h>
> 
> Not in the core driver. This needs to be kept unware of the
> buses that migh be used.
> 
> >
> >  #include "adxl313.h"
> >
> > +static const struct regmap_range adxl312_readable_reg_range[] = {
> > +	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
> > +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> > +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
> > +};
> > +
> >  static const struct regmap_range adxl313_readable_reg_range[] = {
> >  	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
> >  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
> > @@ -22,12 +30,32 @@ static const struct regmap_range adxl313_readable_reg_range[] = {
> >  	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
> >  };
> >
> > +const struct regmap_access_table adxl312_readable_regs_table = {
> > +	.yes_ranges = adxl312_readable_reg_range,
> > +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> > +};
> > +EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL312);
> > +
> >  const struct regmap_access_table adxl313_readable_regs_table = {
> >  	.yes_ranges = adxl313_readable_reg_range,
> >  	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
> >  };
> >  EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
> >
> > +const struct regmap_access_table adxl314_readable_regs_table = {
> > +	.yes_ranges = adxl312_readable_reg_range,
> > +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> > +};
> > +EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL314);
> > +
> > +static const struct regmap_range adxl312_writable_reg_range[] = {
> > +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> > +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
> > +	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
> > +	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
> > +};
> > +
> >  static const struct regmap_range adxl313_writable_reg_range[] = {
> >  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
> >  	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > @@ -37,16 +65,30 @@ static const struct regmap_range adxl313_writable_reg_range[] = {
> >  	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
> >  };
> >
> > +const struct regmap_access_table adxl312_writable_regs_table = {
> > +	.yes_ranges = adxl312_writable_reg_range,
> > +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> > +};
> > +EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL312);
> > +
> >  const struct regmap_access_table adxl313_writable_regs_table = {
> >  	.yes_ranges = adxl313_writable_reg_range,
> >  	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
> >  };
> >  EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
> >
> > +const struct regmap_access_table adxl314_writable_regs_table = {
> > +	.yes_ranges = adxl312_writable_reg_range,
> > +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> > +};
> > +EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL314);
> > +
> >  struct adxl313_data {
> >  	struct regmap	*regmap;
> > +	const struct spi_device_id *id;
> > +	int scale_factor;
> >  	struct mutex	lock; /* lock to protect transf_buf */
> > -	__le16		transf_buf __aligned(IIO_DMA_MINALIGN);
> > +	__le16		transf_buf ____cacheline_aligned;
> Check your patch for accidental reverts of other changes like this...
> 
> >  };
> >
> >  static const int adxl313_odr_freqs[][2] = {
> > @@ -156,12 +198,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
> >  		*val = sign_extend32(ret, chan->scan_type.realbits - 1);
> >  		return IIO_VAL_INT;
> >  	case IIO_CHAN_INFO_SCALE:
> > -		/*
> > -		 * Scale for any g range is given in datasheet as
> > -		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
> > -		 */
> >  		*val = 0;
> > -		*val2 = 9576806;
> > +
> > +		*val2 = data->scale_factor;
> I'd move this into a structure containing all the chip type specific data.
> Hence this would be
> data->chip.scale_factor.
> 
> > +
> >  		return IIO_VAL_INT_PLUS_NANO;
> >  	case IIO_CHAN_INFO_CALIBBIAS:
> >  		ret = regmap_read(data->regmap,
> > @@ -170,7 +210,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
> >  			return ret;
> >
> >  		/*
> > -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> > +		 * 8-bit resolution at minimum range, that is 4x accel data scale
> >  		 * factor at full resolution
> >  		 */
> >  		*val = sign_extend32(regval, 7) * 4;
> > @@ -198,7 +238,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
> >  	switch (mask) {
> >  	case IIO_CHAN_INFO_CALIBBIAS:
> >  		/*
> > -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> > +		 * 8-bit resolution at minimum range, that is 4x accel data scale
> >  		 * factor at full resolution
> >  		 */
> >  		if (clamp_val(val, -128 * 4, 127 * 4) != val)
> > @@ -223,14 +263,17 @@ static const struct iio_info adxl313_info = {
> >  static int adxl313_setup(struct device *dev, struct adxl313_data *data,
> >  			 int (*setup)(struct device *, struct regmap *))
> >  {
> > +	enum adxl313_device_type dev_type = data->id->driver_data;
> >  	unsigned int regval;
> >  	int ret;
> >
> > -	/* Ensures the device is in a consistent state after start up */
> > -	ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> > -			   ADXL313_SOFT_RESET);
> > -	if (ret)
> > -		return ret;
> > +	/* If ADXL313, ensures the device is in a consistent state after start up */
> > +	if (dev_type == ADXL313) {
> 
> Add a flag to the chip_info structure suggested below for 'soft_reset' and
> base this decision on that.  The aim is to encode all the variations in chips
> in one place so adding future chips is isolated there.
> 
> > +		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> > +				   ADXL313_SOFT_RESET);
> > +		if (ret)
> > +			return ret;
> > +	}
> >
> >  	if (setup) {
> >  		ret = setup(dev, data->regmap);
> > @@ -242,41 +285,54 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
> >  	if (ret)
> >  		return ret;
> >
> > -	if (regval != ADXL313_DEVID0) {
> > +	if (dev_type == ADXL313 && regval != ADXL313_DEVID0) {
> 
> Obviously this predates your changes...
> Don't fail in this case.  Warn only.  We may be dealing with a newer compatible
> device that falls back to an the ADXL313 compatible in dt. It's fine to express
> we don't know what it is - particularly if ID matches something else, but
> not to fail as a result.
> 

Is this change mandatory? I am asking this because we have setups that require
the driver to fail if not the correct device is plugged in or if there is no device
attached to the system. 

Returning only warnings will cause the device  to probe, but abnormal values
to be produced. Moreover, most of the iio drivers perform a chip ID check and
return an error code if the physical device is not found.

Wouldn't it be better to treat the situation of new compatible devices when
they appear?

> >  		dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
> >  		return -ENODEV;
> >  	}
> >
> > -	ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
> > -	if (ret)
> > -		return ret;
> > +	/* If ADXL313, check DEVID1 and PARTID */
> > +	if (regval == ADXL313_DEVID0) {
> > +		ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
> > +		if (ret)
> > +			return ret;
> >
> > -	if (regval != ADXL313_DEVID1) {
> As above, should not fail - but just give a warning message (or dev_info perhaps).
> 
> > -		dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
> > -		return -ENODEV;
> > -	}
> > +		if (regval != ADXL313_DEVID1) {
> > +			dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
> > +			return -ENODEV;
> > +		}
> >
> > -	ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
> > -	if (ret)
> > -		return ret;
> > +		ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
> > +		if (ret)
> > +			return ret;
> >
> > -	if (regval != ADXL313_PARTID) {
> > -		dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
> > +		if (regval != ADXL313_PARTID) {
> > +			dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
> > +			return -ENODEV;
> > +		}
> > +	}
> > +
> > +	/* If ADXL312 or ADXL314 device, check DEVID0 */
> > +	if (dev_type != ADXL313 && regval != ADXL313_DEVID0_ADXL312_314) {
> > +		dev_err(dev, "Invalid manufacturer ID: %#02x\n", regval);
> >  		return -ENODEV;
> >  	}
> >
> > -	/* Sets the range to +/- 4g */
> > -	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> > -				 ADXL313_RANGE_MSK,
> > -				 FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G));
> > -	if (ret)
> > -		return ret;
> > +	dev_info(dev, "%s detected.\n", data->id->name);
> 
> Noise in the log. Drop this. It's easy to find out the name from sysfs.
> 
> >
> > -	/* Enables full resolution */
> > -	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> > -				 ADXL313_FULL_RES, ADXL313_FULL_RES);
> > -	if (ret)
> > -		return ret;
> > +	/* Sets the range to maximum, full resolution, for ADXL312 and ADXL313 */
> > +	if (dev_type == ADXL312 || dev_type == ADXL313) {
> I'm reading this backwards btw.  As below, this sort of thing should depend on
> contents of a chip_info structure.
> In this case perhaps a flag called 'set max range').
> 
> > +		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> > +					 ADXL313_RANGE_MSK,
> > +					 FIELD_PREP(ADXL313_RANGE_MSK, 0x02));
> 
> 0x02 looks like a magic numbers, you should ideally provide a define.
> 
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Enables full resolution */
> > +		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
> > +					 ADXL313_FULL_RES, 0);
> > +		if (ret)
> > +			return ret;
> > +	}
> >
> >  	/* Enables measurement mode */
> >  	return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
> > @@ -296,7 +352,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
> >   */
> >  int adxl313_core_probe(struct device *dev,
> >  		       struct regmap *regmap,
> > -		       const char *name,
> > +		       const struct spi_device_id *id,
> 
> A core probe function shouldn't be taking an SPI specific structure.
> Fine to pass the id enum entry.  That can then index a chip_info structure
> that includes the part name - we don't want to pass that through this interface
> as well.
> 
> >  		       int (*setup)(struct device *, struct regmap *))
> >  {
> >  	struct adxl313_data *data;
> > @@ -309,9 +365,35 @@ int adxl313_core_probe(struct device *dev,
> >
> >  	data = iio_priv(indio_dev);
> >  	data->regmap = regmap;
> > +	data->id = id;
> > +
> > +	if (id->driver_data == ADXL312)
> 
> Whenever you hit a case like this, it almost always means you should be selecting
> from an array of chip_info structures. One per type of chip.
> If it is possible to convert code to data that is almost always a good idea.
> 
> > +		/*
> > +		 * ADXL312
> > +		 * Scale for any g range (full range) is given in datasheet as
> > +		 * 345 LSB/g = 0.0028985507 * 9.80665 = 0.028425072222155 m/s^2
> > +		 */
> > +		data->scale_factor = 28425072;
> > +
> > +	if (id->driver_data == ADXL313)
> > +		/*
> > +		 * * ADXL313
> > +		 * Scale for any g range is given in datasheet as
> > +		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
> > +		 */
> > +		data->scale_factor = 9576806;
> > +
> > +	if (id->driver_data == ADXL314)
> > +		/*
> > +		 * ADXL314
> > +		 * At +/-200g with 13-bit resolution, scale factor is given in datasheet as
> > +		 * 48.83 mg/LSB = 0.0488300 * 9.80665 = 0.4788587195 m/s^2.
> > +		 */
> > +		data->scale_factor = 478858719;
> > +
> >  	mutex_init(&data->lock);
> >
> > -	indio_dev->name = name;
> > +	indio_dev->name = id->name;
> >  	indio_dev->info = &adxl313_info;
> >  	indio_dev->modes = INDIO_DIRECT_MODE;
> >  	indio_dev->channels = adxl313_channels;
> > @@ -319,13 +401,13 @@ int adxl313_core_probe(struct device *dev,
> >
> >  	ret = adxl313_setup(dev, data, setup);
> >  	if (ret) {
> > -		dev_err(dev, "ADXL313 setup failed\n");
> > +		dev_err(dev, "Device %s setup failed\n", id->name);
> 
> I doubt we'll ever care which part it was that failed.  I'd
> just leave that as it waas.
> 
> >  		return ret;
> >  	}
> >
> >  	return devm_iio_device_register(dev, indio_dev);
> >  }
> > -EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
> > +EXPORT_SYMBOL_GPL(adxl313_core_probe);
> 
> Please don't remove the namespacing. Add any new exports to the
> existing IIO_ADXL313 NS.
> 
> >
> >  MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
> >  MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");
> > diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
> > index a3c6d553462d..19cd096373b0 100644
> > --- a/drivers/iio/accel/adxl313_spi.c
> > +++ b/drivers/iio/accel/adxl313_spi.c
> > @@ -14,6 +14,16 @@
> >
> >  #include "adxl313.h"
> >
> > +static const struct regmap_config adxl312_spi_regmap_config = {
> > +	.reg_bits	= 8,
> > +	.val_bits	= 8,
> > +	.rd_table	= &adxl312_readable_regs_table,
> > +	.wr_table	= &adxl312_writable_regs_table,
> > +	.max_register	= 0x39,
> > +	 /* Setting bits 7 and 6 enables multiple-byte read */
> > +	.read_flag_mask	= BIT(7) | BIT(6),
> > +};
> > +
> >  static const struct regmap_config adxl313_spi_regmap_config = {
> >  	.reg_bits	= 8,
> >  	.val_bits	= 8,
> > @@ -24,6 +34,22 @@ static const struct regmap_config adxl313_spi_regmap_config = {
> >  	.read_flag_mask	= BIT(7) | BIT(6),
> >  };
> >
> > +static const struct regmap_config adxl314_spi_regmap_config = {
> > +	.reg_bits	= 8,
> > +	.val_bits	= 8,
> > +	.rd_table	= &adxl314_readable_regs_table,
> > +	.wr_table	= &adxl314_writable_regs_table,
> > +	.max_register	= 0x39,
> > +	 /* Setting bits 7 and 6 enables multiple-byte read */
> > +	.read_flag_mask	= BIT(7) | BIT(6),
> > +};
> > +
> > +static const struct regmap_config adxl31x_spi_regmap_config[] = {
> > +	adxl312_spi_regmap_config,
> > +	adxl313_spi_regmap_config,
> > +	adxl314_spi_regmap_config
> > +};
> > +
> >  static int adxl313_spi_setup(struct device *dev, struct regmap *regmap)
> >  {
> >  	struct spi_device *spi = container_of(dev, struct spi_device, dev);
> > @@ -51,26 +77,32 @@ static int adxl313_spi_probe(struct spi_device *spi)
> >  	if (ret)
> >  		return ret;
> >
> > -	regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config);
> > +	regmap = devm_regmap_init_spi(spi,
> > +				      &adxl31x_spi_regmap_config[id->driver_data]);
> > +
> >  	if (IS_ERR(regmap)) {
> >  		dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
> >  			PTR_ERR(regmap));
> >  		return PTR_ERR(regmap);
> >  	}
> >
> > -	return adxl313_core_probe(&spi->dev, regmap, id->name,
> > +	return adxl313_core_probe(&spi->dev, regmap, id,
> >  				  &adxl313_spi_setup);
> >  }
> >
> >  static const struct spi_device_id adxl313_spi_id[] = {
> > -	{ "adxl313" },
> > +	{ "adxl312", ADXL312 },
> > +	{ "adxl313", ADXL313 },
> > +	{ "adxl314", ADXL314 },
> >  	{ }
> >  };
> >
> >  MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
> >
> >  static const struct of_device_id adxl313_of_match[] = {
> > +	{ .compatible = "adi,adxl312" },
> >  	{ .compatible = "adi,adxl313" },
> > +	{ .compatible = "adi,adxl314" },
> >  	{ }
> As mentioned in other thread, there is a nicer way of handling
> the different types of firmware ID.
> >  };
> >
> > @@ -90,4 +122,6 @@ module_spi_driver(adxl313_spi_driver);
> >  MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
> >  MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver");
> >  MODULE_LICENSE("GPL v2");
> > +MODULE_IMPORT_NS(IIO_ADXL312);
> Take a look at what this is doing. There is only one namespace for the
> driver, not one per chip type.
> 
> >  MODULE_IMPORT_NS(IIO_ADXL313);
> > +MODULE_IMPORT_NS(IIO_ADXL314);
Jonathan Cameron Aug. 20, 2022, 10:34 a.m. UTC | #7
On Thu, 18 Aug 2022 10:11:16 +0000
"Mois, George" <George.Mois@analog.com> wrote:

> Hi Jonathan,
> 
> Thank you for your review!
> 
> Regards,
> George
> > 
> > On Tue, 16 Aug 2022 13:28:28 +0300
> > George Mois <george.mois@analog.com> wrote:
> >   
> > > ADXL312 and ADXL314 are small, thin, low power, 3-axis accelerometers
> > > with high resolution (13-bit) measurement up to +/-12 g and +/- 200 g
> > > respectively.
> > >
> > > Implement support for ADXL312 and ADXL314 by extending the ADXL313
> > > driver.
> > >
> > > Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL312.pdf
> > > Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL314.pdf
> > >
> > > Signed-off-by: George Mois <george.mois@analog.com>  
> > 
> > Hi George,
> > 
> > Various comments inline.
> > 
> > Thanks,
> > 
> > Jonathan  
> > > ---
> > >  drivers/iio/accel/adxl313.h      |  15 ++-
> > >  drivers/iio/accel/adxl313_core.c | 164 +++++++++++++++++++++++--------
> > >  drivers/iio/accel/adxl313_spi.c  |  40 +++++++-
> > >  3 files changed, 173 insertions(+), 46 deletions(-)
> > >
> > > diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
> > > index 4415f2fc07e1..7428b1f7768f 100644
> > > --- a/drivers/iio/accel/adxl313.h
> > > +++ b/drivers/iio/accel/adxl313.h
> > > @@ -26,6 +26,7 @@
> > >  #define ADXL313_REG_FIFO_STATUS		0x39
> > >
> > >  #define ADXL313_DEVID0			0xAD
> > > +#define ADXL313_DEVID0_ADXL312_314	0xE5
> > >  #define ADXL313_DEVID1			0x1D
> > >  #define ADXL313_PARTID			0xCB
> > >  #define ADXL313_SOFT_RESET		0x52
> > > @@ -37,18 +38,28 @@
> > >  #define ADXL313_MEASUREMENT_MODE	BIT(3)
> > >
> > >  #define ADXL313_RANGE_MSK		GENMASK(1, 0)
> > > -#define ADXL313_RANGE_4G		3
> > > +#define ADXL313_RANGE_MAX		3
> > >
> > >  #define ADXL313_FULL_RES		BIT(3)
> > >  #define ADXL313_SPI_3WIRE		BIT(6)
> > >  #define ADXL313_I2C_DISABLE		BIT(6)
> > >
> > > +extern const struct regmap_access_table adxl312_readable_regs_table;
> > >  extern const struct regmap_access_table adxl313_readable_regs_table;
> > > +extern const struct regmap_access_table adxl314_readable_regs_table;
> > >
> > > +extern const struct regmap_access_table adxl312_writable_regs_table;
> > >  extern const struct regmap_access_table adxl313_writable_regs_table;
> > > +extern const struct regmap_access_table adxl314_writable_regs_table;
> > > +
> > > +enum adxl313_device_type {
> > > +	ADXL312,
> > > +	ADXL313,
> > > +	ADXL314,
> > > +};
> > >
> > >  int adxl313_core_probe(struct device *dev,
> > >  		       struct regmap *regmap,
> > > -		       const char *name,
> > > +		       const struct spi_device_id *id,
> > >  		       int (*setup)(struct device *, struct regmap *));
> > >  #endif /* _ADXL313_H_ */
> > > diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
> > > index afeef779e1d0..eea7d235950e 100644
> > > --- a/drivers/iio/accel/adxl313_core.c
> > > +++ b/drivers/iio/accel/adxl313_core.c
> > > @@ -11,9 +11,17 @@
> > >  #include <linux/iio/iio.h>
> > >  #include <linux/module.h>
> > >  #include <linux/regmap.h>
> > > +#include <linux/spi/spi.h>  
> > 
> > Not in the core driver. This needs to be kept unware of the
> > buses that migh be used.
> >   
> > >
> > >  #include "adxl313.h"
> > >
> > > +static const struct regmap_range adxl312_readable_reg_range[] = {
> > > +	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
> > > +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > > +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> > > +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
> > > +};
> > > +
> > >  static const struct regmap_range adxl313_readable_reg_range[] = {
> > >  	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
> > >  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
> > > @@ -22,12 +30,32 @@ static const struct regmap_range adxl313_readable_reg_range[] = {
> > >  	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
> > >  };
> > >
> > > +const struct regmap_access_table adxl312_readable_regs_table = {
> > > +	.yes_ranges = adxl312_readable_reg_range,
> > > +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> > > +};
> > > +EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL312);
> > > +
> > >  const struct regmap_access_table adxl313_readable_regs_table = {
> > >  	.yes_ranges = adxl313_readable_reg_range,
> > >  	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
> > >  };
> > >  EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
> > >
> > > +const struct regmap_access_table adxl314_readable_regs_table = {
> > > +	.yes_ranges = adxl312_readable_reg_range,
> > > +	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
> > > +};
> > > +EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL314);
> > > +
> > > +static const struct regmap_range adxl312_writable_reg_range[] = {
> > > +	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > > +	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
> > > +	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
> > > +	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
> > > +	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
> > > +};
> > > +
> > >  static const struct regmap_range adxl313_writable_reg_range[] = {
> > >  	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
> > >  	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
> > > @@ -37,16 +65,30 @@ static const struct regmap_range adxl313_writable_reg_range[] = {
> > >  	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
> > >  };
> > >
> > > +const struct regmap_access_table adxl312_writable_regs_table = {
> > > +	.yes_ranges = adxl312_writable_reg_range,
> > > +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> > > +};
> > > +EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL312);
> > > +
> > >  const struct regmap_access_table adxl313_writable_regs_table = {
> > >  	.yes_ranges = adxl313_writable_reg_range,
> > >  	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
> > >  };
> > >  EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
> > >
> > > +const struct regmap_access_table adxl314_writable_regs_table = {
> > > +	.yes_ranges = adxl312_writable_reg_range,
> > > +	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
> > > +};
> > > +EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL314);
> > > +
> > >  struct adxl313_data {
> > >  	struct regmap	*regmap;
> > > +	const struct spi_device_id *id;
> > > +	int scale_factor;
> > >  	struct mutex	lock; /* lock to protect transf_buf */
> > > -	__le16		transf_buf __aligned(IIO_DMA_MINALIGN);
> > > +	__le16		transf_buf ____cacheline_aligned;  
> > Check your patch for accidental reverts of other changes like this...
> >   
> > >  };
> > >
> > >  static const int adxl313_odr_freqs[][2] = {
> > > @@ -156,12 +198,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
> > >  		*val = sign_extend32(ret, chan->scan_type.realbits - 1);
> > >  		return IIO_VAL_INT;
> > >  	case IIO_CHAN_INFO_SCALE:
> > > -		/*
> > > -		 * Scale for any g range is given in datasheet as
> > > -		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
> > > -		 */
> > >  		*val = 0;
> > > -		*val2 = 9576806;
> > > +
> > > +		*val2 = data->scale_factor;  
> > I'd move this into a structure containing all the chip type specific data.
> > Hence this would be
> > data->chip.scale_factor.
> >   
> > > +
> > >  		return IIO_VAL_INT_PLUS_NANO;
> > >  	case IIO_CHAN_INFO_CALIBBIAS:
> > >  		ret = regmap_read(data->regmap,
> > > @@ -170,7 +210,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
> > >  			return ret;
> > >
> > >  		/*
> > > -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> > > +		 * 8-bit resolution at minimum range, that is 4x accel data scale
> > >  		 * factor at full resolution
> > >  		 */
> > >  		*val = sign_extend32(regval, 7) * 4;
> > > @@ -198,7 +238,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
> > >  	switch (mask) {
> > >  	case IIO_CHAN_INFO_CALIBBIAS:
> > >  		/*
> > > -		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
> > > +		 * 8-bit resolution at minimum range, that is 4x accel data scale
> > >  		 * factor at full resolution
> > >  		 */
> > >  		if (clamp_val(val, -128 * 4, 127 * 4) != val)
> > > @@ -223,14 +263,17 @@ static const struct iio_info adxl313_info = {
> > >  static int adxl313_setup(struct device *dev, struct adxl313_data *data,
> > >  			 int (*setup)(struct device *, struct regmap *))
> > >  {
> > > +	enum adxl313_device_type dev_type = data->id->driver_data;
> > >  	unsigned int regval;
> > >  	int ret;
> > >
> > > -	/* Ensures the device is in a consistent state after start up */
> > > -	ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> > > -			   ADXL313_SOFT_RESET);
> > > -	if (ret)
> > > -		return ret;
> > > +	/* If ADXL313, ensures the device is in a consistent state after start up */
> > > +	if (dev_type == ADXL313) {  
> > 
> > Add a flag to the chip_info structure suggested below for 'soft_reset' and
> > base this decision on that.  The aim is to encode all the variations in chips
> > in one place so adding future chips is isolated there.
> >   
> > > +		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
> > > +				   ADXL313_SOFT_RESET);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > >
> > >  	if (setup) {
> > >  		ret = setup(dev, data->regmap);
> > > @@ -242,41 +285,54 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
> > >  	if (ret)
> > >  		return ret;
> > >
> > > -	if (regval != ADXL313_DEVID0) {
> > > +	if (dev_type == ADXL313 && regval != ADXL313_DEVID0) {  
> > 
> > Obviously this predates your changes...
> > Don't fail in this case.  Warn only.  We may be dealing with a newer compatible
> > device that falls back to an the ADXL313 compatible in dt. It's fine to express
> > we don't know what it is - particularly if ID matches something else, but
> > not to fail as a result.
> >   
> 
> Is this change mandatory? I am asking this because we have setups that require
> the driver to fail if not the correct device is plugged in or if there is no device
> attached to the system. 

Yes unfortunately.  There have been a couple of discussions with the device
tree maintainers on this recently.
The problem case is that a new compatible chip with a different device ID
is introduced.  The expectation is that

compatible = "newchip", "olderchip-with-otherwise identical software interfaces"
will work with an old kernel that knows nothing about new chip.
Note this extends to newer devices that add features but maintain backwards
compatibility with older devices as well.  The idea is we get partial support
in older kernels and the new compatible will be used only when new features
are added.

As a general rule new IIO drivers are tending to warn/info on unexpected IDs because
there is a non trivial risk of the part being wrong. If they know the ID but
it's for a different device they support, they should assume the device is
the one the ID matches for (and warn that the DT is probably wrong for the
board).

There are corner cases that are fine for detecting 'no device' such as
when we know the manufacturer doesn't use 0x00 or 0xff (likely false values on
SPI) for device IDs. In those cases it is fine to fail on basis device
isn't there.

> 
> Returning only warnings will cause the device  to probe, but abnormal values
> to be produced. Moreover, most of the iio drivers perform a chip ID check and
> return an error code if the physical device is not found.

We had this wrong the past unfortunately and haven't 'fixed' all drivers yet.
However, note that I expect we will do this over time. If the DT is wrong then
you are on your own. Correct solution is perform some form of detection in
an early boot firmware (uboot or similar) and patch the DT appropriately.
By the time Linux sees DT it should be correct for the board.

> 
> Wouldn't it be better to treat the situation of new compatible devices when
> they appear?

Unfortunately people insist on not upgrading their kernels as quick as new
devices turn up in device trees + the intent is that there is no need to
if the device is compatible with an older one (up to the ID).

Jonathan


> 
> > >  		dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
> > >  		return -ENODEV;
> > >  	}
diff mbox series

Patch

diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 4415f2fc07e1..7428b1f7768f 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -26,6 +26,7 @@ 
 #define ADXL313_REG_FIFO_STATUS		0x39
 
 #define ADXL313_DEVID0			0xAD
+#define ADXL313_DEVID0_ADXL312_314	0xE5
 #define ADXL313_DEVID1			0x1D
 #define ADXL313_PARTID			0xCB
 #define ADXL313_SOFT_RESET		0x52
@@ -37,18 +38,28 @@ 
 #define ADXL313_MEASUREMENT_MODE	BIT(3)
 
 #define ADXL313_RANGE_MSK		GENMASK(1, 0)
-#define ADXL313_RANGE_4G		3
+#define ADXL313_RANGE_MAX		3
 
 #define ADXL313_FULL_RES		BIT(3)
 #define ADXL313_SPI_3WIRE		BIT(6)
 #define ADXL313_I2C_DISABLE		BIT(6)
 
+extern const struct regmap_access_table adxl312_readable_regs_table;
 extern const struct regmap_access_table adxl313_readable_regs_table;
+extern const struct regmap_access_table adxl314_readable_regs_table;
 
+extern const struct regmap_access_table adxl312_writable_regs_table;
 extern const struct regmap_access_table adxl313_writable_regs_table;
+extern const struct regmap_access_table adxl314_writable_regs_table;
+
+enum adxl313_device_type {
+	ADXL312,
+	ADXL313,
+	ADXL314,
+};
 
 int adxl313_core_probe(struct device *dev,
 		       struct regmap *regmap,
-		       const char *name,
+		       const struct spi_device_id *id,
 		       int (*setup)(struct device *, struct regmap *));
 #endif /* _ADXL313_H_ */
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index afeef779e1d0..eea7d235950e 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -11,9 +11,17 @@ 
 #include <linux/iio/iio.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
+#include <linux/spi/spi.h>
 
 #include "adxl313.h"
 
+static const struct regmap_range adxl312_readable_reg_range[] = {
+	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
+	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
+	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
+	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
+};
+
 static const struct regmap_range adxl313_readable_reg_range[] = {
 	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
 	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
@@ -22,12 +30,32 @@  static const struct regmap_range adxl313_readable_reg_range[] = {
 	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
 };
 
+const struct regmap_access_table adxl312_readable_regs_table = {
+	.yes_ranges = adxl312_readable_reg_range,
+	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL312);
+
 const struct regmap_access_table adxl313_readable_regs_table = {
 	.yes_ranges = adxl313_readable_reg_range,
 	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
 };
 EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
 
+const struct regmap_access_table adxl314_readable_regs_table = {
+	.yes_ranges = adxl312_readable_reg_range,
+	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL314);
+
+static const struct regmap_range adxl312_writable_reg_range[] = {
+	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
+	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
+	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
+	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
+	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
+};
+
 static const struct regmap_range adxl313_writable_reg_range[] = {
 	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
 	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
@@ -37,16 +65,30 @@  static const struct regmap_range adxl313_writable_reg_range[] = {
 	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
 };
 
+const struct regmap_access_table adxl312_writable_regs_table = {
+	.yes_ranges = adxl312_writable_reg_range,
+	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL312);
+
 const struct regmap_access_table adxl313_writable_regs_table = {
 	.yes_ranges = adxl313_writable_reg_range,
 	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
 };
 EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
 
+const struct regmap_access_table adxl314_writable_regs_table = {
+	.yes_ranges = adxl312_writable_reg_range,
+	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL314);
+
 struct adxl313_data {
 	struct regmap	*regmap;
+	const struct spi_device_id *id;
+	int scale_factor;
 	struct mutex	lock; /* lock to protect transf_buf */
-	__le16		transf_buf __aligned(IIO_DMA_MINALIGN);
+	__le16		transf_buf ____cacheline_aligned;
 };
 
 static const int adxl313_odr_freqs[][2] = {
@@ -156,12 +198,10 @@  static int adxl313_read_raw(struct iio_dev *indio_dev,
 		*val = sign_extend32(ret, chan->scan_type.realbits - 1);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
-		/*
-		 * Scale for any g range is given in datasheet as
-		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
-		 */
 		*val = 0;
-		*val2 = 9576806;
+
+		*val2 = data->scale_factor;
+
 		return IIO_VAL_INT_PLUS_NANO;
 	case IIO_CHAN_INFO_CALIBBIAS:
 		ret = regmap_read(data->regmap,
@@ -170,7 +210,7 @@  static int adxl313_read_raw(struct iio_dev *indio_dev,
 			return ret;
 
 		/*
-		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
+		 * 8-bit resolution at minimum range, that is 4x accel data scale
 		 * factor at full resolution
 		 */
 		*val = sign_extend32(regval, 7) * 4;
@@ -198,7 +238,7 @@  static int adxl313_write_raw(struct iio_dev *indio_dev,
 	switch (mask) {
 	case IIO_CHAN_INFO_CALIBBIAS:
 		/*
-		 * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
+		 * 8-bit resolution at minimum range, that is 4x accel data scale
 		 * factor at full resolution
 		 */
 		if (clamp_val(val, -128 * 4, 127 * 4) != val)
@@ -223,14 +263,17 @@  static const struct iio_info adxl313_info = {
 static int adxl313_setup(struct device *dev, struct adxl313_data *data,
 			 int (*setup)(struct device *, struct regmap *))
 {
+	enum adxl313_device_type dev_type = data->id->driver_data;
 	unsigned int regval;
 	int ret;
 
-	/* Ensures the device is in a consistent state after start up */
-	ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
-			   ADXL313_SOFT_RESET);
-	if (ret)
-		return ret;
+	/* If ADXL313, ensures the device is in a consistent state after start up */
+	if (dev_type == ADXL313) {
+		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
+				   ADXL313_SOFT_RESET);
+		if (ret)
+			return ret;
+	}
 
 	if (setup) {
 		ret = setup(dev, data->regmap);
@@ -242,41 +285,54 @@  static int adxl313_setup(struct device *dev, struct adxl313_data *data,
 	if (ret)
 		return ret;
 
-	if (regval != ADXL313_DEVID0) {
+	if (dev_type == ADXL313 && regval != ADXL313_DEVID0) {
 		dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
 		return -ENODEV;
 	}
 
-	ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
-	if (ret)
-		return ret;
+	/* If ADXL313, check DEVID1 and PARTID */
+	if (regval == ADXL313_DEVID0) {
+		ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
+		if (ret)
+			return ret;
 
-	if (regval != ADXL313_DEVID1) {
-		dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
-		return -ENODEV;
-	}
+		if (regval != ADXL313_DEVID1) {
+			dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
+			return -ENODEV;
+		}
 
-	ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
-	if (ret)
-		return ret;
+		ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
+		if (ret)
+			return ret;
 
-	if (regval != ADXL313_PARTID) {
-		dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
+		if (regval != ADXL313_PARTID) {
+			dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
+			return -ENODEV;
+		}
+	}
+
+	/* If ADXL312 or ADXL314 device, check DEVID0 */
+	if (dev_type != ADXL313 && regval != ADXL313_DEVID0_ADXL312_314) {
+		dev_err(dev, "Invalid manufacturer ID: %#02x\n", regval);
 		return -ENODEV;
 	}
 
-	/* Sets the range to +/- 4g */
-	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
-				 ADXL313_RANGE_MSK,
-				 FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G));
-	if (ret)
-		return ret;
+	dev_info(dev, "%s detected.\n", data->id->name);
 
-	/* Enables full resolution */
-	ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
-				 ADXL313_FULL_RES, ADXL313_FULL_RES);
-	if (ret)
-		return ret;
+	/* Sets the range to maximum, full resolution, for ADXL312 and ADXL313 */
+	if (dev_type == ADXL312 || dev_type == ADXL313) {
+		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
+					 ADXL313_RANGE_MSK,
+					 FIELD_PREP(ADXL313_RANGE_MSK, 0x02));
+		if (ret)
+			return ret;
+
+		/* Enables full resolution */
+		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
+					 ADXL313_FULL_RES, 0);
+		if (ret)
+			return ret;
+	}
 
 	/* Enables measurement mode */
 	return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
@@ -296,7 +352,7 @@  static int adxl313_setup(struct device *dev, struct adxl313_data *data,
  */
 int adxl313_core_probe(struct device *dev,
 		       struct regmap *regmap,
-		       const char *name,
+		       const struct spi_device_id *id,
 		       int (*setup)(struct device *, struct regmap *))
 {
 	struct adxl313_data *data;
@@ -309,9 +365,35 @@  int adxl313_core_probe(struct device *dev,
 
 	data = iio_priv(indio_dev);
 	data->regmap = regmap;
+	data->id = id;
+
+	if (id->driver_data == ADXL312)
+		/*
+		 * ADXL312
+		 * Scale for any g range (full range) is given in datasheet as
+		 * 345 LSB/g = 0.0028985507 * 9.80665 = 0.028425072222155 m/s^2
+		 */
+		data->scale_factor = 28425072;
+
+	if (id->driver_data == ADXL313)
+		/*
+		 * * ADXL313
+		 * Scale for any g range is given in datasheet as
+		 * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
+		 */
+		data->scale_factor = 9576806;
+
+	if (id->driver_data == ADXL314)
+		/*
+		 * ADXL314
+		 * At +/-200g with 13-bit resolution, scale factor is given in datasheet as
+		 * 48.83 mg/LSB = 0.0488300 * 9.80665 = 0.4788587195 m/s^2.
+		 */
+		data->scale_factor = 478858719;
+
 	mutex_init(&data->lock);
 
-	indio_dev->name = name;
+	indio_dev->name = id->name;
 	indio_dev->info = &adxl313_info;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->channels = adxl313_channels;
@@ -319,13 +401,13 @@  int adxl313_core_probe(struct device *dev,
 
 	ret = adxl313_setup(dev, data, setup);
 	if (ret) {
-		dev_err(dev, "ADXL313 setup failed\n");
+		dev_err(dev, "Device %s setup failed\n", id->name);
 		return ret;
 	}
 
 	return devm_iio_device_register(dev, indio_dev);
 }
-EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);
+EXPORT_SYMBOL_GPL(adxl313_core_probe);
 
 MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
 MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");
diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
index a3c6d553462d..19cd096373b0 100644
--- a/drivers/iio/accel/adxl313_spi.c
+++ b/drivers/iio/accel/adxl313_spi.c
@@ -14,6 +14,16 @@ 
 
 #include "adxl313.h"
 
+static const struct regmap_config adxl312_spi_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.rd_table	= &adxl312_readable_regs_table,
+	.wr_table	= &adxl312_writable_regs_table,
+	.max_register	= 0x39,
+	 /* Setting bits 7 and 6 enables multiple-byte read */
+	.read_flag_mask	= BIT(7) | BIT(6),
+};
+
 static const struct regmap_config adxl313_spi_regmap_config = {
 	.reg_bits	= 8,
 	.val_bits	= 8,
@@ -24,6 +34,22 @@  static const struct regmap_config adxl313_spi_regmap_config = {
 	.read_flag_mask	= BIT(7) | BIT(6),
 };
 
+static const struct regmap_config adxl314_spi_regmap_config = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.rd_table	= &adxl314_readable_regs_table,
+	.wr_table	= &adxl314_writable_regs_table,
+	.max_register	= 0x39,
+	 /* Setting bits 7 and 6 enables multiple-byte read */
+	.read_flag_mask	= BIT(7) | BIT(6),
+};
+
+static const struct regmap_config adxl31x_spi_regmap_config[] = {
+	adxl312_spi_regmap_config,
+	adxl313_spi_regmap_config,
+	adxl314_spi_regmap_config
+};
+
 static int adxl313_spi_setup(struct device *dev, struct regmap *regmap)
 {
 	struct spi_device *spi = container_of(dev, struct spi_device, dev);
@@ -51,26 +77,32 @@  static int adxl313_spi_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config);
+	regmap = devm_regmap_init_spi(spi,
+				      &adxl31x_spi_regmap_config[id->driver_data]);
+
 	if (IS_ERR(regmap)) {
 		dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
 			PTR_ERR(regmap));
 		return PTR_ERR(regmap);
 	}
 
-	return adxl313_core_probe(&spi->dev, regmap, id->name,
+	return adxl313_core_probe(&spi->dev, regmap, id,
 				  &adxl313_spi_setup);
 }
 
 static const struct spi_device_id adxl313_spi_id[] = {
-	{ "adxl313" },
+	{ "adxl312", ADXL312 },
+	{ "adxl313", ADXL313 },
+	{ "adxl314", ADXL314 },
 	{ }
 };
 
 MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
 
 static const struct of_device_id adxl313_of_match[] = {
+	{ .compatible = "adi,adxl312" },
 	{ .compatible = "adi,adxl313" },
+	{ .compatible = "adi,adxl314" },
 	{ }
 };
 
@@ -90,4 +122,6 @@  module_spi_driver(adxl313_spi_driver);
 MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");
 MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver");
 MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADXL312);
 MODULE_IMPORT_NS(IIO_ADXL313);
+MODULE_IMPORT_NS(IIO_ADXL314);