File Index
Files and directories
Sign in
FILE

inv_mpu_aux.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2019 TDK-InvenSense, Inc.
  4 */
  5
  6#include <linux/kernel.h>
  7#include <linux/device.h>
  8#include <linux/regmap.h>
  9#include <linux/delay.h>
 10
 11#include "inv_mpu_aux.h"
 12#include "inv_mpu_iio.h"
 13
 14/*
 15 * i2c master auxiliary bus transfer function.
 16 * Requires the i2c operations to be correctly setup before.
 17 * Disables SLV0 and checks for NACK status internally.
 18 * Assumes that only SLV0 is used for transfers.
 19 */
 20static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st)
 21{
 22	/* use 50hz frequency for xfer */
 23	const unsigned int freq = 50;
 24	const unsigned int period_ms = 1000 / freq;
 25	uint8_t d;
 26	unsigned int user_ctrl;
 27	int ret;
 28	unsigned int status;
 29
 30	/* set sample rate */
 31	d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq);
 32	ret = regmap_write(st->map, st->reg->sample_rate_div, d);
 33	if (ret)
 34		return ret;
 35
 36	/* start i2c master */
 37	user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
 38	ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
 39	if (ret)
 40		goto error_restore_rate;
 41
 42	/* wait for xfer: 1 period + half-period margin */
 43	msleep(period_ms + period_ms / 2);
 44
 45	/* stop i2c master */
 46	user_ctrl = st->chip_config.user_ctrl;
 47	ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
 48	if (ret)
 49		goto error_stop_i2c;
 50
 51	/* restore sample rate */
 52	d = st->chip_config.divider;
 53	ret = regmap_write(st->map, st->reg->sample_rate_div, d);
 54	if (ret)
 55		goto error_restore_rate;
 56
 57	/* disable i2c slave */
 58	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
 59	if (ret)
 60		goto error_disable_i2c;
 61
 62	/* check i2c status */
 63	ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
 64	if (ret)
 65		return ret;
 66
 67	if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
 68		return -EIO;
 69
 70	return 0;
 71
 72error_stop_i2c:
 73	regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
 74error_restore_rate:
 75	regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider);
 76error_disable_i2c:
 77	regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
 78	return ret;
 79}
 80
 81/**
 82 * inv_mpu_aux_init() - init i2c auxiliary bus
 83 * @st: driver internal state
 84 *
 85 * Returns 0 on success, a negative error code otherwise.
 86 */
 87int inv_mpu_aux_init(const struct inv_mpu6050_state *st)
 88{
 89	unsigned int val;
 90	int ret;
 91
 92	/*
 93	 * Code based on the vendor Linux kernel v3.0,
 94	 * the exact meaning is unknown.
 95	 */
 96	if (st->chip_type == INV_MPU9150) {
 97		unsigned int mask = BIT(7);
 98
 99		val = st->level_shifter ? mask : 0;
100		ret = regmap_update_bits(st->map, 0x1, mask, val);
101		if (ret)
102			return ret;
103	}
104
105	/* configure i2c master */
106	val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ |
107			INV_MPU6050_BIT_WAIT_FOR_ES;
108	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val);
109	if (ret)
110		return ret;
111
112	/* configure i2c master delay */
113	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0);
114	if (ret)
115		return ret;
116
117	val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN |
118			INV_MPU6050_BIT_I2C_SLV1_DLY_EN |
119			INV_MPU6050_BIT_I2C_SLV2_DLY_EN |
120			INV_MPU6050_BIT_I2C_SLV3_DLY_EN |
121			INV_MPU6050_BIT_DELAY_ES_SHADOW;
122	return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val);
123}
124
125/**
126 * inv_mpu_aux_read() - read register function for i2c auxiliary bus
127 * @st: driver internal state.
128 * @addr: chip i2c Address
129 * @reg: chip register address
130 * @val: buffer for storing read bytes
131 * @size: number of bytes to read
132 *
133 *  Returns 0 on success, a negative error code otherwise.
134 */
135int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
136		     uint8_t reg, uint8_t *val, size_t size)
137{
138	int ret;
139
140	if (size > 0x0F)
141		return -EINVAL;
142
143	/* setup i2c SLV0 control: i2c addr, register, enable + size */
144	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0),
145			   INV_MPU6050_BIT_I2C_SLV_RNW | addr);
146	if (ret)
147		return ret;
148	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
149	if (ret)
150		return ret;
151	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
152			   INV_MPU6050_BIT_SLV_EN | size);
153	if (ret)
154		return ret;
155
156	/* do i2c xfer, disable i2c slave and check status*/
157	ret = inv_mpu_i2c_master_xfer(st);
158	if (ret)
159		return ret;
160
161	/* read data in registers */
162	return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
163				val, size);
164}
165
166/**
167 * inv_mpu_aux_write() - write register function for i2c auxiliary bus
168 * @st: driver internal state.
169 * @addr: chip i2c Address
170 * @reg: chip register address
171 * @val: 1 byte value to write
172 *
173 *  Returns 0 on success, a negative error code otherwise.
174 */
175int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
176		      uint8_t reg, uint8_t val)
177{
178	int ret;
179
180	/* setup i2c SLV0 control: i2c addr, register, value, enable + size */
181	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), addr);
182	if (ret)
183		return ret;
184	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
185	if (ret)
186		return ret;
187	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val);
188	if (ret)
189		return ret;
190	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
191			   INV_MPU6050_BIT_SLV_EN | 1);
192	if (ret)
193		return ret;
194
195	/* do i2c xfer, disable i2c slave and check status*/
196	ret = inv_mpu_i2c_master_xfer(st);
197	if (ret)
198		return ret;
199
200	return 0;
201}
~/ Read-only file preview.