brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 006ce66 Raw
256 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ADXL345 3-Axis Digital Accelerometer IIO core driver4 *5 * Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>6 *7 * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf8 */9 10#include <linux/module.h>11#include <linux/property.h>12#include <linux/regmap.h>13#include <linux/units.h>14 15#include <linux/iio/iio.h>16#include <linux/iio/sysfs.h>17 18#include "adxl345.h"19 20struct adxl345_data {21	const struct adxl345_chip_info *info;22	struct regmap *regmap;23};24 25#define ADXL345_CHANNEL(index, axis) {					\26	.type = IIO_ACCEL,						\27	.modified = 1,							\28	.channel2 = IIO_MOD_##axis,					\29	.address = index,						\30	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\31		BIT(IIO_CHAN_INFO_CALIBBIAS),				\32	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |		\33		BIT(IIO_CHAN_INFO_SAMP_FREQ),				\34}35 36static const struct iio_chan_spec adxl345_channels[] = {37	ADXL345_CHANNEL(0, X),38	ADXL345_CHANNEL(1, Y),39	ADXL345_CHANNEL(2, Z),40};41 42static int adxl345_read_raw(struct iio_dev *indio_dev,43			    struct iio_chan_spec const *chan,44			    int *val, int *val2, long mask)45{46	struct adxl345_data *data = iio_priv(indio_dev);47	__le16 accel;48	long long samp_freq_nhz;49	unsigned int regval;50	int ret;51 52	switch (mask) {53	case IIO_CHAN_INFO_RAW:54		/*55		 * Data is stored in adjacent registers:56		 * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte57		 * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte58		 */59		ret = regmap_bulk_read(data->regmap,60				       ADXL345_REG_DATA_AXIS(chan->address),61				       &accel, sizeof(accel));62		if (ret < 0)63			return ret;64 65		*val = sign_extend32(le16_to_cpu(accel), 12);66		return IIO_VAL_INT;67	case IIO_CHAN_INFO_SCALE:68		*val = 0;69		*val2 = data->info->uscale;70		return IIO_VAL_INT_PLUS_MICRO;71	case IIO_CHAN_INFO_CALIBBIAS:72		ret = regmap_read(data->regmap,73				  ADXL345_REG_OFS_AXIS(chan->address), &regval);74		if (ret < 0)75			return ret;76		/*77		 * 8-bit resolution at +/- 2g, that is 4x accel data scale78		 * factor79		 */80		*val = sign_extend32(regval, 7) * 4;81 82		return IIO_VAL_INT;83	case IIO_CHAN_INFO_SAMP_FREQ:84		ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, &regval);85		if (ret < 0)86			return ret;87 88		samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ <<89				(regval & ADXL345_BW_RATE);90		*val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2);91 92		return IIO_VAL_INT_PLUS_NANO;93	}94 95	return -EINVAL;96}97 98static int adxl345_write_raw(struct iio_dev *indio_dev,99			     struct iio_chan_spec const *chan,100			     int val, int val2, long mask)101{102	struct adxl345_data *data = iio_priv(indio_dev);103	s64 n;104 105	switch (mask) {106	case IIO_CHAN_INFO_CALIBBIAS:107		/*108		 * 8-bit resolution at +/- 2g, that is 4x accel data scale109		 * factor110		 */111		return regmap_write(data->regmap,112				    ADXL345_REG_OFS_AXIS(chan->address),113				    val / 4);114	case IIO_CHAN_INFO_SAMP_FREQ:115		n = div_s64(val * NANOHZ_PER_HZ + val2,116			    ADXL345_BASE_RATE_NANO_HZ);117 118		return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE,119					  ADXL345_BW_RATE,120					  clamp_val(ilog2(n), 0,121						    ADXL345_BW_RATE));122	}123 124	return -EINVAL;125}126 127static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,128				     struct iio_chan_spec const *chan,129				     long mask)130{131	switch (mask) {132	case IIO_CHAN_INFO_CALIBBIAS:133		return IIO_VAL_INT;134	case IIO_CHAN_INFO_SAMP_FREQ:135		return IIO_VAL_INT_PLUS_NANO;136	default:137		return -EINVAL;138	}139}140 141static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(142"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"143);144 145static struct attribute *adxl345_attrs[] = {146	&iio_const_attr_sampling_frequency_available.dev_attr.attr,147	NULL148};149 150static const struct attribute_group adxl345_attrs_group = {151	.attrs = adxl345_attrs,152};153 154static const struct iio_info adxl345_info = {155	.attrs		= &adxl345_attrs_group,156	.read_raw	= adxl345_read_raw,157	.write_raw	= adxl345_write_raw,158	.write_raw_get_fmt	= adxl345_write_raw_get_fmt,159};160 161static int adxl345_powerup(void *regmap)162{163	return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE);164}165 166static void adxl345_powerdown(void *regmap)167{168	regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);169}170 171/**172 * adxl345_core_probe() - probe and setup for the adxl345 accelerometer,173 *                        also covers the adlx375 accelerometer174 * @dev:	Driver model representation of the device175 * @regmap:	Regmap instance for the device176 * @setup:	Setup routine to be executed right before the standard device177 *		setup178 *179 * Return: 0 on success, negative errno on error180 */181int adxl345_core_probe(struct device *dev, struct regmap *regmap,182		       int (*setup)(struct device*, struct regmap*))183{184	struct adxl345_data *data;185	struct iio_dev *indio_dev;186	u32 regval;187	unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE |188					 ADXL345_DATA_FORMAT_JUSTIFY |189					 ADXL345_DATA_FORMAT_FULL_RES |190					 ADXL345_DATA_FORMAT_SELF_TEST);191	int ret;192 193	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));194	if (!indio_dev)195		return -ENOMEM;196 197	data = iio_priv(indio_dev);198	data->regmap = regmap;199	data->info = device_get_match_data(dev);200	if (!data->info)201		return -ENODEV;202 203	indio_dev->name = data->info->name;204	indio_dev->info = &adxl345_info;205	indio_dev->modes = INDIO_DIRECT_MODE;206	indio_dev->channels = adxl345_channels;207	indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);208 209	if (setup) {210		/* Perform optional initial bus specific configuration */211		ret = setup(dev, data->regmap);212		if (ret)213			return ret;214 215		/* Enable full-resolution mode */216		ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT,217					 data_format_mask,218					 ADXL345_DATA_FORMAT_FULL_RES);219		if (ret)220			return dev_err_probe(dev, ret,221					     "Failed to set data range\n");222 223	} else {224		/* Enable full-resolution mode (init all data_format bits) */225		ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,226				   ADXL345_DATA_FORMAT_FULL_RES);227		if (ret)228			return dev_err_probe(dev, ret,229					     "Failed to set data range\n");230	}231 232	ret = regmap_read(data->regmap, ADXL345_REG_DEVID, &regval);233	if (ret < 0)234		return dev_err_probe(dev, ret, "Error reading device ID\n");235 236	if (regval != ADXL345_DEVID)237		return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",238				     regval, ADXL345_DEVID);239 240	/* Enable measurement mode */241	ret = adxl345_powerup(data->regmap);242	if (ret < 0)243		return dev_err_probe(dev, ret, "Failed to enable measurement mode\n");244 245	ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap);246	if (ret < 0)247		return ret;248 249	return devm_iio_device_register(dev, indio_dev);250}251EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, IIO_ADXL345);252 253MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");254MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");255MODULE_LICENSE("GPL v2");256