xref: /linux/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
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  */
inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state * st)18 static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st)
19 {
20 	/* use 50hz frequency for xfer */
21 	const unsigned int freq = 50;
22 	const unsigned int period_ms = 1000 / freq;
23 	uint8_t d;
24 	unsigned int user_ctrl;
25 	int ret;
26 
27 	/* set sample rate */
28 	d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq);
29 	ret = regmap_write(st->map, st->reg->sample_rate_div, d);
30 	if (ret)
31 		return ret;
32 
33 	/* start i2c master */
34 	user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
35 	ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
36 	if (ret)
37 		goto error_restore_rate;
38 
39 	/* wait for xfer: 1 period + half-period margin */
40 	msleep(period_ms + period_ms / 2);
41 
42 	/* stop i2c master */
43 	user_ctrl = st->chip_config.user_ctrl;
44 	ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
45 	if (ret)
46 		goto error_stop_i2c;
47 
48 	/* restore sample rate */
49 	d = st->chip_config.divider;
50 	ret = regmap_write(st->map, st->reg->sample_rate_div, d);
51 	if (ret)
52 		goto error_restore_rate;
53 
54 	return 0;
55 
56 error_stop_i2c:
57 	regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
58 error_restore_rate:
59 	regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider);
60 	return ret;
61 }
62 
63 /**
64  * inv_mpu_aux_init() - init i2c auxiliary bus
65  * @st: driver internal state
66  *
67  * Returns 0 on success, a negative error code otherwise.
68  */
inv_mpu_aux_init(const struct inv_mpu6050_state * st)69 int inv_mpu_aux_init(const struct inv_mpu6050_state *st)
70 {
71 	unsigned int val;
72 	int ret;
73 
74 	/*
75 	 * Code based on the vendor Linux kernel v3.0,
76 	 * the exact meaning is unknown.
77 	 */
78 	if (st->chip_type == INV_MPU9150) {
79 		unsigned int mask = BIT(7);
80 
81 		val = st->level_shifter ? mask : 0;
82 		ret = regmap_update_bits(st->map, 0x1, mask, val);
83 		if (ret)
84 			return ret;
85 	}
86 
87 	/* configure i2c master */
88 	val = INV_MPU6050_BITS_I2C_MST_CLK_400KHZ |
89 			INV_MPU6050_BIT_WAIT_FOR_ES;
90 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_MST_CTRL, val);
91 	if (ret)
92 		return ret;
93 
94 	/* configure i2c master delay */
95 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, 0);
96 	if (ret)
97 		return ret;
98 
99 	val = INV_MPU6050_BIT_I2C_SLV0_DLY_EN |
100 			INV_MPU6050_BIT_I2C_SLV1_DLY_EN |
101 			INV_MPU6050_BIT_I2C_SLV2_DLY_EN |
102 			INV_MPU6050_BIT_I2C_SLV3_DLY_EN |
103 			INV_MPU6050_BIT_DELAY_ES_SHADOW;
104 	return regmap_write(st->map, INV_MPU6050_REG_I2C_MST_DELAY_CTRL, val);
105 }
106 
107 /**
108  * inv_mpu_aux_read() - read register function for i2c auxiliary bus
109  * @st: driver internal state.
110  * @addr: chip i2c Address
111  * @reg: chip register address
112  * @val: buffer for storing read bytes
113  * @size: number of bytes to read
114  *
115  *  Returns 0 on success, a negative error code otherwise.
116  */
inv_mpu_aux_read(const struct inv_mpu6050_state * st,uint8_t addr,uint8_t reg,uint8_t * val,size_t size)117 int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr,
118 		     uint8_t reg, uint8_t *val, size_t size)
119 {
120 	unsigned int status;
121 	int ret;
122 
123 	if (size > 0x0F)
124 		return -EINVAL;
125 
126 	/* setup i2c SLV0 control: i2c addr, register, enable + size */
127 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0),
128 			   INV_MPU6050_BIT_I2C_SLV_RNW | addr);
129 	if (ret)
130 		return ret;
131 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_REG(0), reg);
132 	if (ret)
133 		return ret;
134 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0),
135 			   INV_MPU6050_BIT_SLV_EN | size);
136 	if (ret)
137 		return ret;
138 
139 	/* do i2c xfer */
140 	ret = inv_mpu_i2c_master_xfer(st);
141 	if (ret)
142 		goto error_disable_i2c;
143 
144 	/* disable i2c slave */
145 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
146 	if (ret)
147 		goto error_disable_i2c;
148 
149 	/* check i2c status */
150 	ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
151 	if (ret)
152 		return ret;
153 	if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
154 		return -EIO;
155 
156 	/* read data in registers */
157 	return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
158 				val, size);
159 
160 error_disable_i2c:
161 	regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
162 	return ret;
163 }
164 
165 /**
166  * inv_mpu_aux_write() - write register function for i2c auxiliary bus
167  * @st: driver internal state.
168  * @addr: chip i2c Address
169  * @reg: chip register address
170  * @val: 1 byte value to write
171  *
172  *  Returns 0 on success, a negative error code otherwise.
173  */
inv_mpu_aux_write(const struct inv_mpu6050_state * st,uint8_t addr,uint8_t reg,uint8_t val)174 int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr,
175 		      uint8_t reg, uint8_t val)
176 {
177 	unsigned int status;
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 */
196 	ret = inv_mpu_i2c_master_xfer(st);
197 	if (ret)
198 		goto error_disable_i2c;
199 
200 	/* disable i2c slave */
201 	ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
202 	if (ret)
203 		goto error_disable_i2c;
204 
205 	/* check i2c status */
206 	ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
207 	if (ret)
208 		return ret;
209 	if (status & INV_MPU6050_BIT_I2C_SLV0_NACK)
210 		return -EIO;
211 
212 	return 0;
213 
214 error_disable_i2c:
215 	regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0);
216 	return ret;
217 }
218