| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2012 Invensense, Inc. |
|---|
| 3 | | -* |
|---|
| 4 | | -* This software is licensed under the terms of the GNU General Public |
|---|
| 5 | | -* License version 2, as published by the Free Software Foundation, and |
|---|
| 6 | | -* may be copied, distributed, and modified under those terms. |
|---|
| 7 | | -* |
|---|
| 8 | | -* This program is distributed in the hope that it will be useful, |
|---|
| 9 | | -* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | | -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | | -* GNU General Public License for more details. |
|---|
| 12 | 4 | */ |
|---|
| 13 | 5 | |
|---|
| 14 | 6 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 18 | 10 | #include <linux/iio/iio.h> |
|---|
| 19 | 11 | #include <linux/module.h> |
|---|
| 20 | 12 | #include <linux/of_device.h> |
|---|
| 13 | +#include <linux/property.h> |
|---|
| 21 | 14 | #include "inv_mpu_iio.h" |
|---|
| 22 | 15 | |
|---|
| 23 | 16 | static const struct regmap_config inv_mpu_regmap_config = { |
|---|
| .. | .. |
|---|
| 27 | 20 | |
|---|
| 28 | 21 | static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) |
|---|
| 29 | 22 | { |
|---|
| 30 | | - struct iio_dev *indio_dev = i2c_mux_priv(muxc); |
|---|
| 31 | | - struct inv_mpu6050_state *st = iio_priv(indio_dev); |
|---|
| 32 | | - int ret; |
|---|
| 33 | | - |
|---|
| 34 | | - mutex_lock(&st->lock); |
|---|
| 35 | | - |
|---|
| 36 | | - ret = inv_mpu6050_set_power_itg(st, true); |
|---|
| 37 | | - if (ret) |
|---|
| 38 | | - goto error_unlock; |
|---|
| 39 | | - |
|---|
| 40 | | - ret = regmap_write(st->map, st->reg->int_pin_cfg, |
|---|
| 41 | | - st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); |
|---|
| 42 | | - |
|---|
| 43 | | -error_unlock: |
|---|
| 44 | | - mutex_unlock(&st->lock); |
|---|
| 45 | | - |
|---|
| 46 | | - return ret; |
|---|
| 47 | | -} |
|---|
| 48 | | - |
|---|
| 49 | | -static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) |
|---|
| 50 | | -{ |
|---|
| 51 | | - struct iio_dev *indio_dev = i2c_mux_priv(muxc); |
|---|
| 52 | | - struct inv_mpu6050_state *st = iio_priv(indio_dev); |
|---|
| 53 | | - |
|---|
| 54 | | - mutex_lock(&st->lock); |
|---|
| 55 | | - |
|---|
| 56 | | - /* It doesn't really matter if any of the calls fail */ |
|---|
| 57 | | - regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); |
|---|
| 58 | | - inv_mpu6050_set_power_itg(st, false); |
|---|
| 59 | | - |
|---|
| 60 | | - mutex_unlock(&st->lock); |
|---|
| 61 | | - |
|---|
| 62 | 23 | return 0; |
|---|
| 63 | 24 | } |
|---|
| 64 | 25 | |
|---|
| 65 | | -static const char *inv_mpu_match_acpi_device(struct device *dev, |
|---|
| 66 | | - enum inv_devices *chip_id) |
|---|
| 26 | +static bool inv_mpu_i2c_aux_bus(struct device *dev) |
|---|
| 67 | 27 | { |
|---|
| 68 | | - const struct acpi_device_id *id; |
|---|
| 28 | + struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev)); |
|---|
| 69 | 29 | |
|---|
| 70 | | - id = acpi_match_device(dev->driver->acpi_match_table, dev); |
|---|
| 71 | | - if (!id) |
|---|
| 72 | | - return NULL; |
|---|
| 30 | + switch (st->chip_type) { |
|---|
| 31 | + case INV_ICM20608: |
|---|
| 32 | + case INV_ICM20609: |
|---|
| 33 | + case INV_ICM20689: |
|---|
| 34 | + case INV_ICM20602: |
|---|
| 35 | + case INV_IAM20680: |
|---|
| 36 | + /* no i2c auxiliary bus on the chip */ |
|---|
| 37 | + return false; |
|---|
| 38 | + case INV_MPU9150: |
|---|
| 39 | + case INV_MPU9250: |
|---|
| 40 | + case INV_MPU9255: |
|---|
| 41 | + if (st->magn_disabled) |
|---|
| 42 | + return true; |
|---|
| 43 | + else |
|---|
| 44 | + return false; |
|---|
| 45 | + default: |
|---|
| 46 | + return true; |
|---|
| 47 | + } |
|---|
| 48 | +} |
|---|
| 73 | 49 | |
|---|
| 74 | | - *chip_id = (int)id->driver_data; |
|---|
| 50 | +static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev) |
|---|
| 51 | +{ |
|---|
| 52 | + struct inv_mpu6050_state *st = iio_priv(indio_dev); |
|---|
| 53 | + struct device *dev = indio_dev->dev.parent; |
|---|
| 54 | + struct device_node *mux_node; |
|---|
| 55 | + int ret; |
|---|
| 75 | 56 | |
|---|
| 76 | | - return dev_name(dev); |
|---|
| 57 | + /* |
|---|
| 58 | + * MPU9xxx magnetometer support requires to disable i2c auxiliary bus. |
|---|
| 59 | + * To ensure backward compatibility with existing setups, do not disable |
|---|
| 60 | + * i2c auxiliary bus if it used. |
|---|
| 61 | + * Check for i2c-gate node in devicetree and set magnetometer disabled. |
|---|
| 62 | + * Only MPU6500 is supported by ACPI, no need to check. |
|---|
| 63 | + */ |
|---|
| 64 | + switch (st->chip_type) { |
|---|
| 65 | + case INV_MPU9150: |
|---|
| 66 | + case INV_MPU9250: |
|---|
| 67 | + case INV_MPU9255: |
|---|
| 68 | + mux_node = of_get_child_by_name(dev->of_node, "i2c-gate"); |
|---|
| 69 | + if (mux_node != NULL) { |
|---|
| 70 | + st->magn_disabled = true; |
|---|
| 71 | + dev_warn(dev, "disable internal use of magnetometer\n"); |
|---|
| 72 | + } |
|---|
| 73 | + of_node_put(mux_node); |
|---|
| 74 | + break; |
|---|
| 75 | + default: |
|---|
| 76 | + break; |
|---|
| 77 | + } |
|---|
| 78 | + |
|---|
| 79 | + /* enable i2c bypass when using i2c auxiliary bus */ |
|---|
| 80 | + if (inv_mpu_i2c_aux_bus(dev)) { |
|---|
| 81 | + ret = regmap_write(st->map, st->reg->int_pin_cfg, |
|---|
| 82 | + st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); |
|---|
| 83 | + if (ret) |
|---|
| 84 | + return ret; |
|---|
| 85 | + } |
|---|
| 86 | + |
|---|
| 87 | + return 0; |
|---|
| 77 | 88 | } |
|---|
| 78 | 89 | |
|---|
| 79 | 90 | /** |
|---|
| .. | .. |
|---|
| 86 | 97 | static int inv_mpu_probe(struct i2c_client *client, |
|---|
| 87 | 98 | const struct i2c_device_id *id) |
|---|
| 88 | 99 | { |
|---|
| 100 | + const void *match; |
|---|
| 89 | 101 | struct inv_mpu6050_state *st; |
|---|
| 90 | 102 | int result; |
|---|
| 91 | 103 | enum inv_devices chip_type; |
|---|
| .. | .. |
|---|
| 96 | 108 | I2C_FUNC_SMBUS_I2C_BLOCK)) |
|---|
| 97 | 109 | return -EOPNOTSUPP; |
|---|
| 98 | 110 | |
|---|
| 99 | | - if (client->dev.of_node) { |
|---|
| 100 | | - chip_type = (enum inv_devices) |
|---|
| 101 | | - of_device_get_match_data(&client->dev); |
|---|
| 111 | + match = device_get_match_data(&client->dev); |
|---|
| 112 | + if (match) { |
|---|
| 113 | + chip_type = (enum inv_devices)match; |
|---|
| 102 | 114 | name = client->name; |
|---|
| 103 | 115 | } else if (id) { |
|---|
| 104 | 116 | chip_type = (enum inv_devices) |
|---|
| 105 | 117 | id->driver_data; |
|---|
| 106 | 118 | name = id->name; |
|---|
| 107 | | - } else if (ACPI_HANDLE(&client->dev)) { |
|---|
| 108 | | - name = inv_mpu_match_acpi_device(&client->dev, &chip_type); |
|---|
| 109 | | - if (!name) |
|---|
| 110 | | - return -ENODEV; |
|---|
| 111 | 119 | } else { |
|---|
| 112 | 120 | return -ENOSYS; |
|---|
| 113 | 121 | } |
|---|
| 114 | 122 | |
|---|
| 115 | 123 | regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config); |
|---|
| 116 | 124 | if (IS_ERR(regmap)) { |
|---|
| 117 | | - dev_err(&client->dev, "Failed to register i2c regmap %d\n", |
|---|
| 118 | | - (int)PTR_ERR(regmap)); |
|---|
| 125 | + dev_err(&client->dev, "Failed to register i2c regmap: %pe\n", |
|---|
| 126 | + regmap); |
|---|
| 119 | 127 | return PTR_ERR(regmap); |
|---|
| 120 | 128 | } |
|---|
| 121 | 129 | |
|---|
| 122 | 130 | result = inv_mpu_core_probe(regmap, client->irq, name, |
|---|
| 123 | | - NULL, chip_type); |
|---|
| 131 | + inv_mpu_i2c_aux_setup, chip_type); |
|---|
| 124 | 132 | if (result < 0) |
|---|
| 125 | 133 | return result; |
|---|
| 126 | 134 | |
|---|
| 127 | 135 | st = iio_priv(dev_get_drvdata(&client->dev)); |
|---|
| 128 | | - switch (st->chip_type) { |
|---|
| 129 | | - case INV_ICM20608: |
|---|
| 130 | | - case INV_ICM20602: |
|---|
| 131 | | - /* no i2c auxiliary bus on the chip */ |
|---|
| 132 | | - break; |
|---|
| 133 | | - default: |
|---|
| 136 | + if (inv_mpu_i2c_aux_bus(&client->dev)) { |
|---|
| 134 | 137 | /* declare i2c auxiliary bus */ |
|---|
| 135 | 138 | st->muxc = i2c_mux_alloc(client->adapter, &client->dev, |
|---|
| 136 | 139 | 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, |
|---|
| 137 | | - inv_mpu6050_select_bypass, |
|---|
| 138 | | - inv_mpu6050_deselect_bypass); |
|---|
| 140 | + inv_mpu6050_select_bypass, NULL); |
|---|
| 139 | 141 | if (!st->muxc) |
|---|
| 140 | 142 | return -ENOMEM; |
|---|
| 141 | 143 | st->muxc->priv = dev_get_drvdata(&client->dev); |
|---|
| .. | .. |
|---|
| 145 | 147 | result = inv_mpu_acpi_create_mux_client(client); |
|---|
| 146 | 148 | if (result) |
|---|
| 147 | 149 | goto out_del_mux; |
|---|
| 148 | | - break; |
|---|
| 149 | 150 | } |
|---|
| 150 | 151 | |
|---|
| 151 | 152 | return 0; |
|---|
| .. | .. |
|---|
| 180 | 181 | {"mpu9250", INV_MPU9250}, |
|---|
| 181 | 182 | {"mpu9255", INV_MPU9255}, |
|---|
| 182 | 183 | {"icm20608", INV_ICM20608}, |
|---|
| 184 | + {"icm20609", INV_ICM20609}, |
|---|
| 185 | + {"icm20689", INV_ICM20689}, |
|---|
| 183 | 186 | {"icm20602", INV_ICM20602}, |
|---|
| 187 | + {"icm20690", INV_ICM20690}, |
|---|
| 188 | + {"iam20680", INV_IAM20680}, |
|---|
| 184 | 189 | {} |
|---|
| 185 | 190 | }; |
|---|
| 186 | 191 | |
|---|
| .. | .. |
|---|
| 216 | 221 | .data = (void *)INV_ICM20608 |
|---|
| 217 | 222 | }, |
|---|
| 218 | 223 | { |
|---|
| 224 | + .compatible = "invensense,icm20609", |
|---|
| 225 | + .data = (void *)INV_ICM20609 |
|---|
| 226 | + }, |
|---|
| 227 | + { |
|---|
| 228 | + .compatible = "invensense,icm20689", |
|---|
| 229 | + .data = (void *)INV_ICM20689 |
|---|
| 230 | + }, |
|---|
| 231 | + { |
|---|
| 219 | 232 | .compatible = "invensense,icm20602", |
|---|
| 220 | 233 | .data = (void *)INV_ICM20602 |
|---|
| 221 | 234 | }, |
|---|
| 235 | + { |
|---|
| 236 | + .compatible = "invensense,icm20690", |
|---|
| 237 | + .data = (void *)INV_ICM20690 |
|---|
| 238 | + }, |
|---|
| 239 | + { |
|---|
| 240 | + .compatible = "invensense,iam20680", |
|---|
| 241 | + .data = (void *)INV_IAM20680 |
|---|
| 242 | + }, |
|---|
| 222 | 243 | { } |
|---|
| 223 | 244 | }; |
|---|
| 224 | 245 | MODULE_DEVICE_TABLE(of, inv_of_match); |
|---|