xref: /linux/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c (revision 0d5ec7919f3747193f051036b2301734a4b5e1d6)
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  */
inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state * st)20 static 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 
72 error_stop_i2c:
73 	regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
74 error_restore_rate:
75 	regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider);
76 error_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  */
inv_mpu_aux_init(const struct inv_mpu6050_state * st)87 int 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  */
inv_mpu_aux_read(const struct inv_mpu6050_state * st,uint8_t addr,uint8_t reg,uint8_t * val,size_t size)135 int 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  */
inv_mpu_aux_write(const struct inv_mpu6050_state * st,uint8_t addr,uint8_t reg,uint8_t val)175 int 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 }
202