brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · 4de0a41 Raw
425 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ADXL313 3-Axis Digital Accelerometer4 *5 * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com>6 *7 * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf8 */9 10#include <linux/bitfield.h>11#include <linux/module.h>12#include <linux/regmap.h>13 14#include "adxl313.h"15 16static const struct regmap_range adxl312_readable_reg_range[] = {17	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),18	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),19	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),20	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),21};22 23static const struct regmap_range adxl313_readable_reg_range[] = {24	regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),25	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),26	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),27	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),28	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),29};30 31const struct regmap_access_table adxl312_readable_regs_table = {32	.yes_ranges = adxl312_readable_reg_range,33	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),34};35EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313);36 37const struct regmap_access_table adxl313_readable_regs_table = {38	.yes_ranges = adxl313_readable_reg_range,39	.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),40};41EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);42 43const struct regmap_access_table adxl314_readable_regs_table = {44	.yes_ranges = adxl312_readable_reg_range,45	.n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),46};47EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313);48 49static int adxl312_check_id(struct device *dev,50			    struct adxl313_data *data)51{52	unsigned int regval;53	int ret;54 55	ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);56	if (ret)57		return ret;58 59	if (regval != ADXL313_DEVID0_ADXL312_314)60		dev_warn(dev, "Invalid manufacturer ID: %#02x\n", regval);61 62	return 0;63}64 65static int adxl313_check_id(struct device *dev,66			    struct adxl313_data *data)67{68	unsigned int regval;69	int ret;70 71	ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);72	if (ret)73		return ret;74 75	if (regval != ADXL313_DEVID0)76		dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n", regval);77 78	/* Check DEVID1 and PARTID */79	if (regval == ADXL313_DEVID0) {80		ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);81		if (ret)82			return ret;83 84		if (regval != ADXL313_DEVID1)85			dev_warn(dev, "Invalid mems ID: 0x%02x\n", regval);86 87		ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);88		if (ret)89			return ret;90 91		if (regval != ADXL313_PARTID)92			dev_warn(dev, "Invalid device ID: 0x%02x\n", regval);93	}94 95	return 0;96}97 98const struct adxl313_chip_info adxl31x_chip_info[] = {99	[ADXL312] = {100		.name = "adxl312",101		.type = ADXL312,102		.scale_factor = 28425072,103		.variable_range = true,104		.soft_reset = false,105		.check_id = &adxl312_check_id,106	},107	[ADXL313] = {108		.name = "adxl313",109		.type = ADXL313,110		.scale_factor = 9576806,111		.variable_range = true,112		.soft_reset = true,113		.check_id = &adxl313_check_id,114	},115	[ADXL314] = {116		.name = "adxl314",117		.type = ADXL314,118		.scale_factor = 478858719,119		.variable_range = false,120		.soft_reset = false,121		.check_id = &adxl312_check_id,122	},123};124EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313);125 126static const struct regmap_range adxl312_writable_reg_range[] = {127	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),128	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),129	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),130	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),131	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),132};133 134static const struct regmap_range adxl313_writable_reg_range[] = {135	regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),136	regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),137	regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),138	regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),139	regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),140	regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),141};142 143const struct regmap_access_table adxl312_writable_regs_table = {144	.yes_ranges = adxl312_writable_reg_range,145	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),146};147EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313);148 149const struct regmap_access_table adxl313_writable_regs_table = {150	.yes_ranges = adxl313_writable_reg_range,151	.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),152};153EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);154 155const struct regmap_access_table adxl314_writable_regs_table = {156	.yes_ranges = adxl312_writable_reg_range,157	.n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),158};159EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313);160 161static const int adxl313_odr_freqs[][2] = {162	[0] = { 6, 250000 },163	[1] = { 12, 500000 },164	[2] = { 25, 0 },165	[3] = { 50, 0 },166	[4] = { 100, 0 },167	[5] = { 200, 0 },168	[6] = { 400, 0 },169	[7] = { 800, 0 },170	[8] = { 1600, 0 },171	[9] = { 3200, 0 },172};173 174#define ADXL313_ACCEL_CHANNEL(index, axis) {				\175	.type = IIO_ACCEL,						\176	.address = index,						\177	.modified = 1,							\178	.channel2 = IIO_MOD_##axis,					\179	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\180			      BIT(IIO_CHAN_INFO_CALIBBIAS),		\181	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |		\182				    BIT(IIO_CHAN_INFO_SAMP_FREQ),	\183	.info_mask_shared_by_type_available =				\184		BIT(IIO_CHAN_INFO_SAMP_FREQ),				\185	.scan_type = {							\186		.realbits = 13,						\187	},								\188}189 190static const struct iio_chan_spec adxl313_channels[] = {191	ADXL313_ACCEL_CHANNEL(0, X),192	ADXL313_ACCEL_CHANNEL(1, Y),193	ADXL313_ACCEL_CHANNEL(2, Z),194};195 196static int adxl313_set_odr(struct adxl313_data *data,197			   unsigned int freq1, unsigned int freq2)198{199	unsigned int i;200 201	for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) {202		if (adxl313_odr_freqs[i][0] == freq1 &&203		    adxl313_odr_freqs[i][1] == freq2)204			break;205	}206 207	if (i == ARRAY_SIZE(adxl313_odr_freqs))208		return -EINVAL;209 210	return regmap_update_bits(data->regmap, ADXL313_REG_BW_RATE,211				  ADXL313_RATE_MSK,212				  FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i));213}214 215static int adxl313_read_axis(struct adxl313_data *data,216			     struct iio_chan_spec const *chan)217{218	int ret;219 220	mutex_lock(&data->lock);221 222	ret = regmap_bulk_read(data->regmap,223			       ADXL313_REG_DATA_AXIS(chan->address),224			       &data->transf_buf, sizeof(data->transf_buf));225	if (ret)226		goto unlock_ret;227 228	ret = le16_to_cpu(data->transf_buf);229 230unlock_ret:231	mutex_unlock(&data->lock);232	return ret;233}234 235static int adxl313_read_freq_avail(struct iio_dev *indio_dev,236				   struct iio_chan_spec const *chan,237				   const int **vals, int *type, int *length,238				   long mask)239{240	switch (mask) {241	case IIO_CHAN_INFO_SAMP_FREQ:242		*vals = (const int *)adxl313_odr_freqs;243		*length = ARRAY_SIZE(adxl313_odr_freqs) * 2;244		*type = IIO_VAL_INT_PLUS_MICRO;245		return IIO_AVAIL_LIST;246	default:247		return -EINVAL;248	}249}250 251static int adxl313_read_raw(struct iio_dev *indio_dev,252			    struct iio_chan_spec const *chan,253			    int *val, int *val2, long mask)254{255	struct adxl313_data *data = iio_priv(indio_dev);256	unsigned int regval;257	int ret;258 259	switch (mask) {260	case IIO_CHAN_INFO_RAW:261		ret = adxl313_read_axis(data, chan);262		if (ret < 0)263			return ret;264 265		*val = sign_extend32(ret, chan->scan_type.realbits - 1);266		return IIO_VAL_INT;267	case IIO_CHAN_INFO_SCALE:268		*val = 0;269 270		*val2 = data->chip_info->scale_factor;271 272		return IIO_VAL_INT_PLUS_NANO;273	case IIO_CHAN_INFO_CALIBBIAS:274		ret = regmap_read(data->regmap,275				  ADXL313_REG_OFS_AXIS(chan->address), &regval);276		if (ret)277			return ret;278 279		/*280		 * 8-bit resolution at minimum range, that is 4x accel data scale281		 * factor at full resolution282		 */283		*val = sign_extend32(regval, 7) * 4;284		return IIO_VAL_INT;285	case IIO_CHAN_INFO_SAMP_FREQ:286		ret = regmap_read(data->regmap, ADXL313_REG_BW_RATE, &regval);287		if (ret)288			return ret;289 290		ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE;291		*val = adxl313_odr_freqs[ret][0];292		*val2 = adxl313_odr_freqs[ret][1];293		return IIO_VAL_INT_PLUS_MICRO;294	default:295		return -EINVAL;296	}297}298 299static int adxl313_write_raw(struct iio_dev *indio_dev,300			     struct iio_chan_spec const *chan,301			     int val, int val2, long mask)302{303	struct adxl313_data *data = iio_priv(indio_dev);304 305	switch (mask) {306	case IIO_CHAN_INFO_CALIBBIAS:307		/*308		 * 8-bit resolution at minimum range, that is 4x accel data scale309		 * factor at full resolution310		 */311		if (clamp_val(val, -128 * 4, 127 * 4) != val)312			return -EINVAL;313 314		return regmap_write(data->regmap,315				    ADXL313_REG_OFS_AXIS(chan->address),316				    val / 4);317	case IIO_CHAN_INFO_SAMP_FREQ:318		return adxl313_set_odr(data, val, val2);319	default:320		return -EINVAL;321	}322}323 324static const struct iio_info adxl313_info = {325	.read_raw	= adxl313_read_raw,326	.write_raw	= adxl313_write_raw,327	.read_avail	= adxl313_read_freq_avail,328};329 330static int adxl313_setup(struct device *dev, struct adxl313_data *data,331			 int (*setup)(struct device *, struct regmap *))332{333	int ret;334 335	/*336	 * If sw reset available, ensures the device is in a consistent337	 * state after start up338	 */339	if (data->chip_info->soft_reset) {340		ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,341				   ADXL313_SOFT_RESET);342		if (ret)343			return ret;344	}345 346	if (setup) {347		ret = setup(dev, data->regmap);348		if (ret)349			return ret;350	}351 352	ret = data->chip_info->check_id(dev, data);353	if (ret)354		return ret;355 356	/* Sets the range to maximum, full resolution, if applicable */357	if (data->chip_info->variable_range) {358		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,359					 ADXL313_RANGE_MSK,360					 FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX));361		if (ret)362			return ret;363 364		/* Enables full resolution */365		ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,366					 ADXL313_FULL_RES, ADXL313_FULL_RES);367		if (ret)368			return ret;369	}370 371	/* Enables measurement mode */372	return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,373				  ADXL313_POWER_CTL_MSK,374				  ADXL313_MEASUREMENT_MODE);375}376 377/**378 * adxl313_core_probe() - probe and setup for adxl313 accelerometer379 * @dev:	Driver model representation of the device380 * @regmap:	Register map of the device381 * @chip_info:	Structure containing device specific data382 * @setup:	Setup routine to be executed right before the standard device383 *		setup, can also be set to NULL if not required384 *385 * Return: 0 on success, negative errno on error cases386 */387int adxl313_core_probe(struct device *dev,388		       struct regmap *regmap,389		       const struct adxl313_chip_info *chip_info,390		       int (*setup)(struct device *, struct regmap *))391{392	struct adxl313_data *data;393	struct iio_dev *indio_dev;394	int ret;395 396	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));397	if (!indio_dev)398		return -ENOMEM;399 400	data = iio_priv(indio_dev);401	data->regmap = regmap;402	data->chip_info = chip_info;403 404	mutex_init(&data->lock);405 406	indio_dev->name = chip_info->name;407	indio_dev->info = &adxl313_info;408	indio_dev->modes = INDIO_DIRECT_MODE;409	indio_dev->channels = adxl313_channels;410	indio_dev->num_channels = ARRAY_SIZE(adxl313_channels);411 412	ret = adxl313_setup(dev, data, setup);413	if (ret) {414		dev_err(dev, "ADXL313 setup failed\n");415		return ret;416	}417 418	return devm_iio_device_register(dev, indio_dev);419}420EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313);421 422MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>");423MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver");424MODULE_LICENSE("GPL v2");425