aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>2014-12-05 17:52:09 -0500
committerJonathan Cameron <jic23@kernel.org>2014-12-12 07:28:26 -0500
commit3a2ecc3d2dce6e051b6afc319bb380c829e4e4fd (patch)
treef5f29a7b9af315541411304b839f8d56d2c3f94c
parent0f8994b18afc47fd0cda20ba6162c21a093173da (diff)
iio: imu: inv_mpu6050: Add i2c mux for by pass
This chip allows some limited number of sensors connected to it as slaves, which can be directly accessed by register interface of this driver.But the current upstream driver doesn't support such mode. To attach such slaves to main processor i2c bus, chip has to be set up in bypass mode. This change adds i2c mux, which will enable/disable this mode for transaction to/from such slave devices. This was discussed for a while in mailing list, this was the outcome: Reference: http://www.spinics.net/lists/linux-iio/msg12126.html http://comments.gmane.org/gmane.linux.kernel.iio/11470 Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Reviewed-by: Wolfram Sang <wsa@the-dreams.de> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--drivers/iio/imu/inv_mpu6050/Kconfig1
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c115
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h6
3 files changed, 116 insertions, 6 deletions
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig
index 2d0608ba88d7..48fbc0bc7e2a 100644
--- a/drivers/iio/imu/inv_mpu6050/Kconfig
+++ b/drivers/iio/imu/inv_mpu6050/Kconfig
@@ -7,6 +7,7 @@ config INV_MPU6050_IIO
7 depends on I2C && SYSFS 7 depends on I2C && SYSFS
8 select IIO_BUFFER 8 select IIO_BUFFER
9 select IIO_TRIGGERED_BUFFER 9 select IIO_TRIGGERED_BUFFER
10 select I2C_MUX
10 help 11 help
11 This driver supports the Invensense MPU6050 devices. 12 This driver supports the Invensense MPU6050 devices.
12 This driver can also support MPU6500 in MPU6050 compatibility mode 13 This driver can also support MPU6500 in MPU6050 compatibility mode
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index b75519deac1a..6d2c115f39b4 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -23,6 +23,7 @@
23#include <linux/kfifo.h> 23#include <linux/kfifo.h>
24#include <linux/spinlock.h> 24#include <linux/spinlock.h>
25#include <linux/iio/iio.h> 25#include <linux/iio/iio.h>
26#include <linux/i2c-mux.h>
26#include "inv_mpu_iio.h" 27#include "inv_mpu_iio.h"
27 28
28/* 29/*
@@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
52 .int_enable = INV_MPU6050_REG_INT_ENABLE, 53 .int_enable = INV_MPU6050_REG_INT_ENABLE,
53 .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, 54 .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
54 .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, 55 .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
56 .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
55}; 57};
56 58
57static const struct inv_mpu6050_chip_config chip_config_6050 = { 59static const struct inv_mpu6050_chip_config chip_config_6050 = {
@@ -77,6 +79,83 @@ int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
77 return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d); 79 return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
78} 80}
79 81
82/*
83 * The i2c read/write needs to happen in unlocked mode. As the parent
84 * adapter is common. If we use locked versions, it will fail as
85 * the mux adapter will lock the parent i2c adapter, while calling
86 * select/deselect functions.
87 */
88static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st,
89 u8 reg, u8 d)
90{
91 int ret;
92 u8 buf[2];
93 struct i2c_msg msg[1] = {
94 {
95 .addr = st->client->addr,
96 .flags = 0,
97 .len = sizeof(buf),
98 .buf = buf,
99 }
100 };
101
102 buf[0] = reg;
103 buf[1] = d;
104 ret = __i2c_transfer(st->client->adapter, msg, 1);
105 if (ret != 1)
106 return ret;
107
108 return 0;
109}
110
111static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
112 u32 chan_id)
113{
114 struct iio_dev *indio_dev = mux_priv;
115 struct inv_mpu6050_state *st = iio_priv(indio_dev);
116 int ret = 0;
117
118 /* Use the same mutex which was used everywhere to protect power-op */
119 mutex_lock(&indio_dev->mlock);
120 if (!st->powerup_count) {
121 ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
122 0);
123 if (ret)
124 goto write_error;
125
126 msleep(INV_MPU6050_REG_UP_TIME);
127 }
128 if (!ret) {
129 st->powerup_count++;
130 ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
131 st->client->irq |
132 INV_MPU6050_BIT_BYPASS_EN);
133 }
134write_error:
135 mutex_unlock(&indio_dev->mlock);
136
137 return ret;
138}
139
140static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
141 void *mux_priv, u32 chan_id)
142{
143 struct iio_dev *indio_dev = mux_priv;
144 struct inv_mpu6050_state *st = iio_priv(indio_dev);
145
146 mutex_lock(&indio_dev->mlock);
147 /* It doesn't really mattter, if any of the calls fails */
148 inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
149 st->client->irq);
150 st->powerup_count--;
151 if (!st->powerup_count)
152 inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
153 INV_MPU6050_BIT_SLEEP);
154 mutex_unlock(&indio_dev->mlock);
155
156 return 0;
157}
158
80int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) 159int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
81{ 160{
82 u8 d, mgmt_1; 161 u8 d, mgmt_1;
@@ -133,13 +212,22 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
133 212
134int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) 213int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
135{ 214{
136 int result; 215 int result = 0;
216
217 if (power_on) {
218 /* Already under indio-dev->mlock mutex */
219 if (!st->powerup_count)
220 result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
221 0);
222 if (!result)
223 st->powerup_count++;
224 } else {
225 st->powerup_count--;
226 if (!st->powerup_count)
227 result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
228 INV_MPU6050_BIT_SLEEP);
229 }
137 230
138 if (power_on)
139 result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
140 else
141 result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
142 INV_MPU6050_BIT_SLEEP);
143 if (result) 231 if (result)
144 return result; 232 return result;
145 233
@@ -673,6 +761,7 @@ static int inv_mpu_probe(struct i2c_client *client,
673 761
674 st = iio_priv(indio_dev); 762 st = iio_priv(indio_dev);
675 st->client = client; 763 st->client = client;
764 st->powerup_count = 0;
676 pdata = dev_get_platdata(&client->dev); 765 pdata = dev_get_platdata(&client->dev);
677 if (pdata) 766 if (pdata)
678 st->plat_data = *pdata; 767 st->plat_data = *pdata;
@@ -720,8 +809,21 @@ static int inv_mpu_probe(struct i2c_client *client,
720 goto out_remove_trigger; 809 goto out_remove_trigger;
721 } 810 }
722 811
812 st->mux_adapter = i2c_add_mux_adapter(client->adapter,
813 &client->dev,
814 indio_dev,
815 0, 0, 0,
816 inv_mpu6050_select_bypass,
817 inv_mpu6050_deselect_bypass);
818 if (!st->mux_adapter) {
819 result = -ENODEV;
820 goto out_unreg_device;
821 }
822
723 return 0; 823 return 0;
724 824
825out_unreg_device:
826 iio_device_unregister(indio_dev);
725out_remove_trigger: 827out_remove_trigger:
726 inv_mpu6050_remove_trigger(st); 828 inv_mpu6050_remove_trigger(st);
727out_unreg_ring: 829out_unreg_ring:
@@ -734,6 +836,7 @@ static int inv_mpu_remove(struct i2c_client *client)
734 struct iio_dev *indio_dev = i2c_get_clientdata(client); 836 struct iio_dev *indio_dev = i2c_get_clientdata(client);
735 struct inv_mpu6050_state *st = iio_priv(indio_dev); 837 struct inv_mpu6050_state *st = iio_priv(indio_dev);
736 838
839 i2c_del_mux_adapter(st->mux_adapter);
737 iio_device_unregister(indio_dev); 840 iio_device_unregister(indio_dev);
738 inv_mpu6050_remove_trigger(st); 841 inv_mpu6050_remove_trigger(st);
739 iio_triggered_buffer_cleanup(indio_dev); 842 iio_triggered_buffer_cleanup(indio_dev);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index e7799315d4dc..aa837de57079 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -54,6 +54,7 @@ struct inv_mpu6050_reg_map {
54 u8 int_enable; 54 u8 int_enable;
55 u8 pwr_mgmt_1; 55 u8 pwr_mgmt_1;
56 u8 pwr_mgmt_2; 56 u8 pwr_mgmt_2;
57 u8 int_pin_cfg;
57}; 58};
58 59
59/*device enum */ 60/*device enum */
@@ -119,6 +120,8 @@ struct inv_mpu6050_state {
119 enum inv_devices chip_type; 120 enum inv_devices chip_type;
120 spinlock_t time_stamp_lock; 121 spinlock_t time_stamp_lock;
121 struct i2c_client *client; 122 struct i2c_client *client;
123 struct i2c_adapter *mux_adapter;
124 unsigned int powerup_count;
122 struct inv_mpu6050_platform_data plat_data; 125 struct inv_mpu6050_platform_data plat_data;
123 DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); 126 DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
124}; 127};
@@ -179,6 +182,9 @@ struct inv_mpu6050_state {
179/* 6 + 6 round up and plus 8 */ 182/* 6 + 6 round up and plus 8 */
180#define INV_MPU6050_OUTPUT_DATA_SIZE 24 183#define INV_MPU6050_OUTPUT_DATA_SIZE 24
181 184
185#define INV_MPU6050_REG_INT_PIN_CFG 0x37
186#define INV_MPU6050_BIT_BYPASS_EN 0x2
187
182/* init parameters */ 188/* init parameters */
183#define INV_MPU6050_INIT_FIFO_RATE 50 189#define INV_MPU6050_INIT_FIFO_RATE 50
184#define INV_MPU6050_TIME_STAMP_TOR 5 190#define INV_MPU6050_TIME_STAMP_TOR 5