cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bmi088-accel-core.c (14518B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
      4 *  - BMI088
      5 *
      6 * Copyright (c) 2018-2021, Topic Embedded Products
      7 */
      8
      9#include <linux/delay.h>
     10#include <linux/iio/iio.h>
     11#include <linux/iio/sysfs.h>
     12#include <linux/interrupt.h>
     13#include <linux/module.h>
     14#include <linux/pm.h>
     15#include <linux/pm_runtime.h>
     16#include <linux/regmap.h>
     17#include <linux/slab.h>
     18#include <asm/unaligned.h>
     19
     20#include "bmi088-accel.h"
     21
     22#define BMI088_ACCEL_REG_CHIP_ID			0x00
     23#define BMI088_ACCEL_REG_ERROR				0x02
     24
     25#define BMI088_ACCEL_REG_INT_STATUS			0x1D
     26#define BMI088_ACCEL_INT_STATUS_BIT_DRDY		BIT(7)
     27
     28#define BMI088_ACCEL_REG_RESET				0x7E
     29#define BMI088_ACCEL_RESET_VAL				0xB6
     30
     31#define BMI088_ACCEL_REG_PWR_CTRL			0x7D
     32#define BMI088_ACCEL_REG_PWR_CONF			0x7C
     33
     34#define BMI088_ACCEL_REG_INT_MAP_DATA			0x58
     35#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY		BIT(2)
     36#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM		BIT(5)
     37
     38#define BMI088_ACCEL_REG_INT1_IO_CONF			0x53
     39#define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT	BIT(3)
     40#define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL		BIT(1)
     41
     42#define BMI088_ACCEL_REG_INT2_IO_CONF			0x54
     43#define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT	BIT(3)
     44#define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL		BIT(1)
     45
     46#define BMI088_ACCEL_REG_ACC_CONF			0x40
     47#define BMI088_ACCEL_MODE_ODR_MASK			0x0f
     48
     49#define BMI088_ACCEL_REG_ACC_RANGE			0x41
     50#define BMI088_ACCEL_RANGE_3G				0x00
     51#define BMI088_ACCEL_RANGE_6G				0x01
     52#define BMI088_ACCEL_RANGE_12G				0x02
     53#define BMI088_ACCEL_RANGE_24G				0x03
     54
     55#define BMI088_ACCEL_REG_TEMP				0x22
     56#define BMI088_ACCEL_REG_TEMP_SHIFT			5
     57#define BMI088_ACCEL_TEMP_UNIT				125
     58#define BMI088_ACCEL_TEMP_OFFSET			23000
     59
     60#define BMI088_ACCEL_REG_XOUT_L				0x12
     61#define BMI088_ACCEL_AXIS_TO_REG(axis) \
     62	(BMI088_ACCEL_REG_XOUT_L + (axis * 2))
     63
     64#define BMI088_ACCEL_MAX_STARTUP_TIME_US		1000
     65#define BMI088_AUTO_SUSPEND_DELAY_MS			2000
     66
     67#define BMI088_ACCEL_REG_FIFO_STATUS			0x0E
     68#define BMI088_ACCEL_REG_FIFO_CONFIG0			0x48
     69#define BMI088_ACCEL_REG_FIFO_CONFIG1			0x49
     70#define BMI088_ACCEL_REG_FIFO_DATA			0x3F
     71#define BMI088_ACCEL_FIFO_LENGTH			100
     72
     73#define BMI088_ACCEL_FIFO_MODE_FIFO			0x40
     74#define BMI088_ACCEL_FIFO_MODE_STREAM			0x80
     75
     76enum bmi088_accel_axis {
     77	AXIS_X,
     78	AXIS_Y,
     79	AXIS_Z,
     80};
     81
     82static const int bmi088_sample_freqs[] = {
     83	12, 500000,
     84	25, 0,
     85	50, 0,
     86	100, 0,
     87	200, 0,
     88	400, 0,
     89	800, 0,
     90	1600, 0,
     91};
     92
     93/* Available OSR (over sampling rate) sets the 3dB cut-off frequency */
     94enum bmi088_osr_modes {
     95	BMI088_ACCEL_MODE_OSR_NORMAL = 0xA,
     96	BMI088_ACCEL_MODE_OSR_2 = 0x9,
     97	BMI088_ACCEL_MODE_OSR_4 = 0x8,
     98};
     99
    100/* Available ODR (output data rates) in Hz */
    101enum bmi088_odr_modes {
    102	BMI088_ACCEL_MODE_ODR_12_5 = 0x5,
    103	BMI088_ACCEL_MODE_ODR_25 = 0x6,
    104	BMI088_ACCEL_MODE_ODR_50 = 0x7,
    105	BMI088_ACCEL_MODE_ODR_100 = 0x8,
    106	BMI088_ACCEL_MODE_ODR_200 = 0x9,
    107	BMI088_ACCEL_MODE_ODR_400 = 0xa,
    108	BMI088_ACCEL_MODE_ODR_800 = 0xb,
    109	BMI088_ACCEL_MODE_ODR_1600 = 0xc,
    110};
    111
    112struct bmi088_scale_info {
    113	int scale;
    114	u8 reg_range;
    115};
    116
    117struct bmi088_accel_chip_info {
    118	const char *name;
    119	u8 chip_id;
    120	const struct iio_chan_spec *channels;
    121	int num_channels;
    122};
    123
    124struct bmi088_accel_data {
    125	struct regmap *regmap;
    126	const struct bmi088_accel_chip_info *chip_info;
    127	u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */
    128};
    129
    130static const struct regmap_range bmi088_volatile_ranges[] = {
    131	/* All registers below 0x40 are volatile, except the CHIP ID. */
    132	regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f),
    133	/* Mark the RESET as volatile too, it is self-clearing */
    134	regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET),
    135};
    136
    137static const struct regmap_access_table bmi088_volatile_table = {
    138	.yes_ranges	= bmi088_volatile_ranges,
    139	.n_yes_ranges	= ARRAY_SIZE(bmi088_volatile_ranges),
    140};
    141
    142const struct regmap_config bmi088_regmap_conf = {
    143	.reg_bits = 8,
    144	.val_bits = 8,
    145	.max_register = 0x7E,
    146	.volatile_table = &bmi088_volatile_table,
    147	.cache_type = REGCACHE_RBTREE,
    148};
    149EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088);
    150
    151static int bmi088_accel_power_up(struct bmi088_accel_data *data)
    152{
    153	int ret;
    154
    155	/* Enable accelerometer and temperature sensor */
    156	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4);
    157	if (ret)
    158		return ret;
    159
    160	/* Datasheet recommends to wait at least 5ms before communication */
    161	usleep_range(5000, 6000);
    162
    163	/* Disable suspend mode */
    164	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0);
    165	if (ret)
    166		return ret;
    167
    168	/* Recommended at least 1ms before further communication */
    169	usleep_range(1000, 1200);
    170
    171	return 0;
    172}
    173
    174static int bmi088_accel_power_down(struct bmi088_accel_data *data)
    175{
    176	int ret;
    177
    178	/* Enable suspend mode */
    179	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3);
    180	if (ret)
    181		return ret;
    182
    183	/* Recommended at least 1ms before further communication */
    184	usleep_range(1000, 1200);
    185
    186	/* Disable accelerometer and temperature sensor */
    187	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0);
    188	if (ret)
    189		return ret;
    190
    191	/* Datasheet recommends to wait at least 5ms before communication */
    192	usleep_range(5000, 6000);
    193
    194	return 0;
    195}
    196
    197static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data,
    198					int *val, int *val2)
    199{
    200	unsigned int value;
    201	int ret;
    202
    203	ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
    204			  &value);
    205	if (ret)
    206		return ret;
    207
    208	value &= BMI088_ACCEL_MODE_ODR_MASK;
    209	value -= BMI088_ACCEL_MODE_ODR_12_5;
    210	value <<= 1;
    211
    212	if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1)
    213		return -EINVAL;
    214
    215	*val = bmi088_sample_freqs[value];
    216	*val2 = bmi088_sample_freqs[value + 1];
    217
    218	return IIO_VAL_INT_PLUS_MICRO;
    219}
    220
    221static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val)
    222{
    223	unsigned int regval;
    224	int index = 0;
    225
    226	while (index < ARRAY_SIZE(bmi088_sample_freqs) &&
    227	       bmi088_sample_freqs[index] != val)
    228		index += 2;
    229
    230	if (index >= ARRAY_SIZE(bmi088_sample_freqs))
    231		return -EINVAL;
    232
    233	regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5;
    234
    235	return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
    236				  BMI088_ACCEL_MODE_ODR_MASK, regval);
    237}
    238
    239static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val)
    240{
    241	int ret;
    242	s16 temp;
    243
    244	ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP,
    245			       &data->buffer, sizeof(__be16));
    246	if (ret)
    247		return ret;
    248
    249	/* data->buffer is cacheline aligned */
    250	temp = be16_to_cpu(*(__be16 *)data->buffer);
    251
    252	*val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT;
    253
    254	return IIO_VAL_INT;
    255}
    256
    257static int bmi088_accel_get_axis(struct bmi088_accel_data *data,
    258				 struct iio_chan_spec const *chan,
    259				 int *val)
    260{
    261	int ret;
    262	s16 raw_val;
    263
    264	ret = regmap_bulk_read(data->regmap,
    265			       BMI088_ACCEL_AXIS_TO_REG(chan->scan_index),
    266			       data->buffer, sizeof(__le16));
    267	if (ret)
    268		return ret;
    269
    270	raw_val = le16_to_cpu(*(__le16 *)data->buffer);
    271	*val = raw_val;
    272
    273	return IIO_VAL_INT;
    274}
    275
    276static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
    277				 struct iio_chan_spec const *chan,
    278				 int *val, int *val2, long mask)
    279{
    280	struct bmi088_accel_data *data = iio_priv(indio_dev);
    281	struct device *dev = regmap_get_device(data->regmap);
    282	int ret;
    283
    284	switch (mask) {
    285	case IIO_CHAN_INFO_RAW:
    286		switch (chan->type) {
    287		case IIO_TEMP:
    288			ret = pm_runtime_resume_and_get(dev);
    289			if (ret)
    290				return ret;
    291
    292			ret = bmi088_accel_get_temp(data, val);
    293			goto out_read_raw_pm_put;
    294		case IIO_ACCEL:
    295			ret = pm_runtime_resume_and_get(dev);
    296			if (ret)
    297				return ret;
    298
    299			ret = iio_device_claim_direct_mode(indio_dev);
    300			if (ret)
    301				goto out_read_raw_pm_put;
    302
    303			ret = bmi088_accel_get_axis(data, chan, val);
    304			iio_device_release_direct_mode(indio_dev);
    305			if (!ret)
    306				ret = IIO_VAL_INT;
    307
    308			goto out_read_raw_pm_put;
    309		default:
    310			return -EINVAL;
    311		}
    312	case IIO_CHAN_INFO_OFFSET:
    313		switch (chan->type) {
    314		case IIO_TEMP:
    315			/* Offset applies before scale */
    316			*val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT;
    317			return IIO_VAL_INT;
    318		default:
    319			return -EINVAL;
    320		}
    321	case IIO_CHAN_INFO_SCALE:
    322		switch (chan->type) {
    323		case IIO_TEMP:
    324			/* 0.125 degrees per LSB */
    325			*val = BMI088_ACCEL_TEMP_UNIT;
    326			return IIO_VAL_INT;
    327		case IIO_ACCEL:
    328			ret = pm_runtime_resume_and_get(dev);
    329			if (ret)
    330				return ret;
    331
    332			ret = regmap_read(data->regmap,
    333					  BMI088_ACCEL_REG_ACC_RANGE, val);
    334			if (ret)
    335				goto out_read_raw_pm_put;
    336
    337			*val2 = 15 - (*val & 0x3);
    338			*val = 3 * 980;
    339			ret = IIO_VAL_FRACTIONAL_LOG2;
    340
    341			goto out_read_raw_pm_put;
    342		default:
    343			return -EINVAL;
    344		}
    345	case IIO_CHAN_INFO_SAMP_FREQ:
    346		ret = pm_runtime_resume_and_get(dev);
    347		if (ret)
    348			return ret;
    349
    350		ret = bmi088_accel_get_sample_freq(data, val, val2);
    351		goto out_read_raw_pm_put;
    352	default:
    353		break;
    354	}
    355
    356	return -EINVAL;
    357
    358out_read_raw_pm_put:
    359	pm_runtime_mark_last_busy(dev);
    360	pm_runtime_put_autosuspend(dev);
    361
    362	return ret;
    363}
    364
    365static int bmi088_accel_read_avail(struct iio_dev *indio_dev,
    366			     struct iio_chan_spec const *chan,
    367			     const int **vals, int *type, int *length,
    368			     long mask)
    369{
    370	switch (mask) {
    371	case IIO_CHAN_INFO_SAMP_FREQ:
    372		*type = IIO_VAL_INT_PLUS_MICRO;
    373		*vals = bmi088_sample_freqs;
    374		*length = ARRAY_SIZE(bmi088_sample_freqs);
    375		return IIO_AVAIL_LIST;
    376	default:
    377		return -EINVAL;
    378	}
    379}
    380
    381static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
    382				  struct iio_chan_spec const *chan,
    383				  int val, int val2, long mask)
    384{
    385	struct bmi088_accel_data *data = iio_priv(indio_dev);
    386	struct device *dev = regmap_get_device(data->regmap);
    387	int ret;
    388
    389	switch (mask) {
    390	case IIO_CHAN_INFO_SAMP_FREQ:
    391		ret = pm_runtime_resume_and_get(dev);
    392		if (ret)
    393			return ret;
    394
    395		ret = bmi088_accel_set_sample_freq(data, val);
    396		pm_runtime_mark_last_busy(dev);
    397		pm_runtime_put_autosuspend(dev);
    398		return ret;
    399	default:
    400		return -EINVAL;
    401	}
    402}
    403
    404#define BMI088_ACCEL_CHANNEL(_axis) { \
    405	.type = IIO_ACCEL, \
    406	.modified = 1, \
    407	.channel2 = IIO_MOD_##_axis, \
    408	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
    409	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
    410				BIT(IIO_CHAN_INFO_SAMP_FREQ), \
    411	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
    412	.scan_index = AXIS_##_axis, \
    413}
    414
    415static const struct iio_chan_spec bmi088_accel_channels[] = {
    416	{
    417		.type = IIO_TEMP,
    418		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
    419				      BIT(IIO_CHAN_INFO_SCALE) |
    420				      BIT(IIO_CHAN_INFO_OFFSET),
    421		.scan_index = -1,
    422	},
    423	BMI088_ACCEL_CHANNEL(X),
    424	BMI088_ACCEL_CHANNEL(Y),
    425	BMI088_ACCEL_CHANNEL(Z),
    426	IIO_CHAN_SOFT_TIMESTAMP(3),
    427};
    428
    429static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = {
    430	[0] = {
    431		.name = "bmi088a",
    432		.chip_id = 0x1E,
    433		.channels = bmi088_accel_channels,
    434		.num_channels = ARRAY_SIZE(bmi088_accel_channels),
    435	},
    436};
    437
    438static const struct iio_info bmi088_accel_info = {
    439	.read_raw	= bmi088_accel_read_raw,
    440	.write_raw	= bmi088_accel_write_raw,
    441	.read_avail	= bmi088_accel_read_avail,
    442};
    443
    444static const unsigned long bmi088_accel_scan_masks[] = {
    445	BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
    446	0
    447};
    448
    449static int bmi088_accel_chip_init(struct bmi088_accel_data *data)
    450{
    451	struct device *dev = regmap_get_device(data->regmap);
    452	int ret, i;
    453	unsigned int val;
    454
    455	/* Do a dummy read to enable SPI interface, won't harm I2C */
    456	regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
    457
    458	/*
    459	 * Reset chip to get it in a known good state. A delay of 1ms after
    460	 * reset is required according to the data sheet
    461	 */
    462	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET,
    463			   BMI088_ACCEL_RESET_VAL);
    464	if (ret)
    465		return ret;
    466
    467	usleep_range(1000, 2000);
    468
    469	/* Do a dummy read again after a reset to enable the SPI interface */
    470	regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
    471
    472	/* Read chip ID */
    473	ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val);
    474	if (ret) {
    475		dev_err(dev, "Error: Reading chip id\n");
    476		return ret;
    477	}
    478
    479	/* Validate chip ID */
    480	for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) {
    481		if (bmi088_accel_chip_info_tbl[i].chip_id == val) {
    482			data->chip_info = &bmi088_accel_chip_info_tbl[i];
    483			break;
    484		}
    485	}
    486	if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) {
    487		dev_err(dev, "Invalid chip %x\n", val);
    488		return -ENODEV;
    489	}
    490
    491	return 0;
    492}
    493
    494int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
    495	int irq, const char *name, bool block_supported)
    496{
    497	struct bmi088_accel_data *data;
    498	struct iio_dev *indio_dev;
    499	int ret;
    500
    501	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    502	if (!indio_dev)
    503		return -ENOMEM;
    504
    505	data = iio_priv(indio_dev);
    506	dev_set_drvdata(dev, indio_dev);
    507
    508	data->regmap = regmap;
    509
    510	ret = bmi088_accel_chip_init(data);
    511	if (ret)
    512		return ret;
    513
    514	indio_dev->channels = data->chip_info->channels;
    515	indio_dev->num_channels = data->chip_info->num_channels;
    516	indio_dev->name = name ? name : data->chip_info->name;
    517	indio_dev->available_scan_masks = bmi088_accel_scan_masks;
    518	indio_dev->modes = INDIO_DIRECT_MODE;
    519	indio_dev->info = &bmi088_accel_info;
    520
    521	/* Enable runtime PM */
    522	pm_runtime_get_noresume(dev);
    523	pm_runtime_set_suspended(dev);
    524	pm_runtime_enable(dev);
    525	/* We need ~6ms to startup, so set the delay to 6 seconds */
    526	pm_runtime_set_autosuspend_delay(dev, 6000);
    527	pm_runtime_use_autosuspend(dev);
    528	pm_runtime_put(dev);
    529
    530	ret = iio_device_register(indio_dev);
    531	if (ret)
    532		dev_err(dev, "Unable to register iio device\n");
    533
    534	return ret;
    535}
    536EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088);
    537
    538
    539void bmi088_accel_core_remove(struct device *dev)
    540{
    541	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    542	struct bmi088_accel_data *data = iio_priv(indio_dev);
    543
    544	iio_device_unregister(indio_dev);
    545
    546	pm_runtime_disable(dev);
    547	pm_runtime_set_suspended(dev);
    548	bmi088_accel_power_down(data);
    549}
    550EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088);
    551
    552static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
    553{
    554	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    555	struct bmi088_accel_data *data = iio_priv(indio_dev);
    556
    557	return bmi088_accel_power_down(data);
    558}
    559
    560static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev)
    561{
    562	struct iio_dev *indio_dev = dev_get_drvdata(dev);
    563	struct bmi088_accel_data *data = iio_priv(indio_dev);
    564
    565	return bmi088_accel_power_up(data);
    566}
    567
    568const struct dev_pm_ops bmi088_accel_pm_ops = {
    569	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
    570				pm_runtime_force_resume)
    571	SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend,
    572			   bmi088_accel_runtime_resume, NULL)
    573};
    574EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088);
    575
    576MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
    577MODULE_LICENSE("GPL v2");
    578MODULE_DESCRIPTION("BMI088 accelerometer driver (core)");