1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 Invensense, Inc. |
4 | */ |
5 | |
6 | #include <linux/delay.h> |
7 | #include <linux/err.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/iio/iio.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/module.h> |
12 | #include <linux/property.h> |
13 | |
14 | #include "inv_mpu_iio.h" |
15 | |
16 | static const struct regmap_config inv_mpu_regmap_config = { |
17 | .reg_bits = 8, |
18 | .val_bits = 8, |
19 | }; |
20 | |
21 | static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) |
22 | { |
23 | return 0; |
24 | } |
25 | |
26 | static bool inv_mpu_i2c_aux_bus(struct device *dev) |
27 | { |
28 | struct inv_mpu6050_state *st = iio_priv(indio_dev: dev_get_drvdata(dev)); |
29 | |
30 | switch (st->chip_type) { |
31 | case INV_ICM20608: |
32 | case INV_ICM20608D: |
33 | case INV_ICM20609: |
34 | case INV_ICM20689: |
35 | case INV_ICM20600: |
36 | case INV_ICM20602: |
37 | case INV_IAM20680: |
38 | /* no i2c auxiliary bus on the chip */ |
39 | return false; |
40 | case INV_MPU9150: |
41 | case INV_MPU9250: |
42 | case INV_MPU9255: |
43 | if (st->magn_disabled) |
44 | return true; |
45 | else |
46 | return false; |
47 | default: |
48 | return true; |
49 | } |
50 | } |
51 | |
52 | static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) |
53 | { |
54 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
55 | struct device *dev = indio_dev->dev.parent; |
56 | struct fwnode_handle *mux_node; |
57 | int ret; |
58 | |
59 | /* |
60 | * MPU9xxx magnetometer support requires to disable i2c auxiliary bus. |
61 | * To ensure backward compatibility with existing setups, do not disable |
62 | * i2c auxiliary bus if it used. |
63 | * Check for i2c-gate node in devicetree and set magnetometer disabled. |
64 | * Only MPU6500 is supported by ACPI, no need to check. |
65 | */ |
66 | switch (st->chip_type) { |
67 | case INV_MPU9150: |
68 | case INV_MPU9250: |
69 | case INV_MPU9255: |
70 | mux_node = device_get_named_child_node(dev, childname: "i2c-gate" ); |
71 | if (mux_node != NULL) { |
72 | st->magn_disabled = true; |
73 | dev_warn(dev, "disable internal use of magnetometer\n" ); |
74 | } |
75 | fwnode_handle_put(fwnode: mux_node); |
76 | break; |
77 | default: |
78 | break; |
79 | } |
80 | |
81 | /* enable i2c bypass when using i2c auxiliary bus */ |
82 | if (inv_mpu_i2c_aux_bus(dev)) { |
83 | ret = regmap_write(map: st->map, reg: st->reg->int_pin_cfg, |
84 | val: st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); |
85 | if (ret) |
86 | return ret; |
87 | } |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | /** |
93 | * inv_mpu_probe() - probe function. |
94 | * @client: i2c client. |
95 | * |
96 | * Returns 0 on success, a negative error code otherwise. |
97 | */ |
98 | static int inv_mpu_probe(struct i2c_client *client) |
99 | { |
100 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
101 | const void *match; |
102 | struct inv_mpu6050_state *st; |
103 | int result; |
104 | enum inv_devices chip_type; |
105 | struct regmap *regmap; |
106 | const char *name; |
107 | |
108 | if (!i2c_check_functionality(adap: client->adapter, |
109 | I2C_FUNC_SMBUS_I2C_BLOCK)) |
110 | return -EOPNOTSUPP; |
111 | |
112 | match = device_get_match_data(dev: &client->dev); |
113 | if (match) { |
114 | chip_type = (uintptr_t)match; |
115 | name = client->name; |
116 | } else if (id) { |
117 | chip_type = (enum inv_devices) |
118 | id->driver_data; |
119 | name = id->name; |
120 | } else { |
121 | return -ENOSYS; |
122 | } |
123 | |
124 | regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config); |
125 | if (IS_ERR(ptr: regmap)) { |
126 | dev_err(&client->dev, "Failed to register i2c regmap: %pe\n" , |
127 | regmap); |
128 | return PTR_ERR(ptr: regmap); |
129 | } |
130 | |
131 | result = inv_mpu_core_probe(regmap, irq: client->irq, name, |
132 | inv_mpu_bus_setup: inv_mpu_i2c_aux_setup, chip_type); |
133 | if (result < 0) |
134 | return result; |
135 | |
136 | st = iio_priv(indio_dev: dev_get_drvdata(dev: &client->dev)); |
137 | if (inv_mpu_i2c_aux_bus(dev: &client->dev)) { |
138 | /* declare i2c auxiliary bus */ |
139 | st->muxc = i2c_mux_alloc(parent: client->adapter, dev: &client->dev, |
140 | max_adapters: 1, sizeof_priv: 0, I2C_MUX_LOCKED | I2C_MUX_GATE, |
141 | select: inv_mpu6050_select_bypass, NULL); |
142 | if (!st->muxc) |
143 | return -ENOMEM; |
144 | st->muxc->priv = dev_get_drvdata(dev: &client->dev); |
145 | result = i2c_mux_add_adapter(muxc: st->muxc, force_nr: 0, chan_id: 0, class: 0); |
146 | if (result) |
147 | return result; |
148 | result = inv_mpu_acpi_create_mux_client(client); |
149 | if (result) |
150 | goto out_del_mux; |
151 | } |
152 | |
153 | return 0; |
154 | |
155 | out_del_mux: |
156 | i2c_mux_del_adapters(muxc: st->muxc); |
157 | return result; |
158 | } |
159 | |
160 | static void inv_mpu_remove(struct i2c_client *client) |
161 | { |
162 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
163 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
164 | |
165 | if (st->muxc) { |
166 | inv_mpu_acpi_delete_mux_client(client); |
167 | i2c_mux_del_adapters(muxc: st->muxc); |
168 | } |
169 | } |
170 | |
171 | /* |
172 | * device id table is used to identify what device can be |
173 | * supported by this driver |
174 | */ |
175 | static const struct i2c_device_id inv_mpu_id[] = { |
176 | {"mpu6050" , INV_MPU6050}, |
177 | {"mpu6500" , INV_MPU6500}, |
178 | {"mpu6515" , INV_MPU6515}, |
179 | {"mpu6880" , INV_MPU6880}, |
180 | {"mpu9150" , INV_MPU9150}, |
181 | {"mpu9250" , INV_MPU9250}, |
182 | {"mpu9255" , INV_MPU9255}, |
183 | {"icm20608" , INV_ICM20608}, |
184 | {"icm20608d" , INV_ICM20608D}, |
185 | {"icm20609" , INV_ICM20609}, |
186 | {"icm20689" , INV_ICM20689}, |
187 | {"icm20600" , INV_ICM20600}, |
188 | {"icm20602" , INV_ICM20602}, |
189 | {"icm20690" , INV_ICM20690}, |
190 | {"iam20680" , INV_IAM20680}, |
191 | {} |
192 | }; |
193 | |
194 | MODULE_DEVICE_TABLE(i2c, inv_mpu_id); |
195 | |
196 | static const struct of_device_id inv_of_match[] = { |
197 | { |
198 | .compatible = "invensense,mpu6050" , |
199 | .data = (void *)INV_MPU6050 |
200 | }, |
201 | { |
202 | .compatible = "invensense,mpu6500" , |
203 | .data = (void *)INV_MPU6500 |
204 | }, |
205 | { |
206 | .compatible = "invensense,mpu6515" , |
207 | .data = (void *)INV_MPU6515 |
208 | }, |
209 | { |
210 | .compatible = "invensense,mpu6880" , |
211 | .data = (void *)INV_MPU6880 |
212 | }, |
213 | { |
214 | .compatible = "invensense,mpu9150" , |
215 | .data = (void *)INV_MPU9150 |
216 | }, |
217 | { |
218 | .compatible = "invensense,mpu9250" , |
219 | .data = (void *)INV_MPU9250 |
220 | }, |
221 | { |
222 | .compatible = "invensense,mpu9255" , |
223 | .data = (void *)INV_MPU9255 |
224 | }, |
225 | { |
226 | .compatible = "invensense,icm20608" , |
227 | .data = (void *)INV_ICM20608 |
228 | }, |
229 | { |
230 | .compatible = "invensense,icm20608d" , |
231 | .data = (void *)INV_ICM20608D |
232 | }, |
233 | { |
234 | .compatible = "invensense,icm20609" , |
235 | .data = (void *)INV_ICM20609 |
236 | }, |
237 | { |
238 | .compatible = "invensense,icm20689" , |
239 | .data = (void *)INV_ICM20689 |
240 | }, |
241 | { |
242 | .compatible = "invensense,icm20600" , |
243 | .data = (void *)INV_ICM20600 |
244 | }, |
245 | { |
246 | .compatible = "invensense,icm20602" , |
247 | .data = (void *)INV_ICM20602 |
248 | }, |
249 | { |
250 | .compatible = "invensense,icm20690" , |
251 | .data = (void *)INV_ICM20690 |
252 | }, |
253 | { |
254 | .compatible = "invensense,iam20680" , |
255 | .data = (void *)INV_IAM20680 |
256 | }, |
257 | { } |
258 | }; |
259 | MODULE_DEVICE_TABLE(of, inv_of_match); |
260 | |
261 | static const struct acpi_device_id inv_acpi_match[] = { |
262 | {"INVN6500" , INV_MPU6500}, |
263 | { }, |
264 | }; |
265 | MODULE_DEVICE_TABLE(acpi, inv_acpi_match); |
266 | |
267 | static struct i2c_driver inv_mpu_driver = { |
268 | .probe = inv_mpu_probe, |
269 | .remove = inv_mpu_remove, |
270 | .id_table = inv_mpu_id, |
271 | .driver = { |
272 | .of_match_table = inv_of_match, |
273 | .acpi_match_table = inv_acpi_match, |
274 | .name = "inv-mpu6050-i2c" , |
275 | .pm = pm_ptr(&inv_mpu_pmops), |
276 | }, |
277 | }; |
278 | |
279 | module_i2c_driver(inv_mpu_driver); |
280 | |
281 | MODULE_AUTHOR("Invensense Corporation" ); |
282 | MODULE_DESCRIPTION("Invensense device MPU6050 driver" ); |
283 | MODULE_LICENSE("GPL" ); |
284 | MODULE_IMPORT_NS(IIO_MPU6050); |
285 | |