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 | */ |
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(map: st->map, reg: st->reg->sample_rate_div, val: 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(map: st->map, reg: st->reg->user_ctrl, val: user_ctrl); |
36 | if (ret) |
37 | goto error_restore_rate; |
38 | |
39 | /* wait for xfer: 1 period + half-period margin */ |
40 | msleep(msecs: period_ms + period_ms / 2); |
41 | |
42 | /* stop i2c master */ |
43 | user_ctrl = st->chip_config.user_ctrl; |
44 | ret = regmap_write(map: st->map, reg: st->reg->user_ctrl, val: 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(map: st->map, reg: st->reg->sample_rate_div, val: d); |
51 | if (ret) |
52 | goto error_restore_rate; |
53 | |
54 | return 0; |
55 | |
56 | error_stop_i2c: |
57 | regmap_write(map: st->map, reg: st->reg->user_ctrl, val: st->chip_config.user_ctrl); |
58 | error_restore_rate: |
59 | regmap_write(map: st->map, reg: st->reg->sample_rate_div, val: 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 | */ |
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(map: st->map, reg: 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(map: 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(map: st->map, INV_MPU6050_REG_I2C_SLV4_CTRL, val: 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(map: 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 | */ |
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(map: 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(map: st->map, INV_MPU6050_REG_I2C_SLV_REG(0), val: reg); |
132 | if (ret) |
133 | return ret; |
134 | ret = regmap_write(map: 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(map: st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), val: 0); |
146 | if (ret) |
147 | goto error_disable_i2c; |
148 | |
149 | /* check i2c status */ |
150 | ret = regmap_read(map: st->map, INV_MPU6050_REG_I2C_MST_STATUS, val: &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(map: st->map, INV_MPU6050_REG_EXT_SENS_DATA, |
158 | val, val_count: size); |
159 | |
160 | error_disable_i2c: |
161 | regmap_write(map: st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), val: 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 | */ |
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(map: st->map, INV_MPU6050_REG_I2C_SLV_ADDR(0), val: addr); |
182 | if (ret) |
183 | return ret; |
184 | ret = regmap_write(map: st->map, INV_MPU6050_REG_I2C_SLV_REG(0), val: reg); |
185 | if (ret) |
186 | return ret; |
187 | ret = regmap_write(map: st->map, INV_MPU6050_REG_I2C_SLV_DO(0), val); |
188 | if (ret) |
189 | return ret; |
190 | ret = regmap_write(map: 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(map: st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), val: 0); |
202 | if (ret) |
203 | goto error_disable_i2c; |
204 | |
205 | /* check i2c status */ |
206 | ret = regmap_read(map: st->map, INV_MPU6050_REG_I2C_MST_STATUS, val: &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(map: st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), val: 0); |
216 | return ret; |
217 | } |
218 | |