diff options
author | Hans de Goede <hdegoede@redhat.com> | 2016-09-12 03:43:50 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2016-09-18 08:25:38 -0400 |
commit | 063e3303a93fcd64554730145361f102236724cb (patch) | |
tree | 21f0baff78adcc3a30f56f9e6ca510cf45bef245 /drivers | |
parent | 5620318828dce80748c0a8e5fa2775957cf28347 (diff) |
iio: accel: Add driver for the mCube MC3230 3-axis accelerometer
Add an IIO driver for the mCube MC3230 3-axis accelerometer.
A datasheet for the mCube MC3230 can be found here:
http://www.mcubemems.com/wp-content/uploads/2014/10/MC3230_2-Datasheet-APS-048-0007v1.6.pdf
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/iio/accel/Kconfig | 10 | ||||
-rw-r--r-- | drivers/iio/accel/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/accel/mc3230.c | 211 |
3 files changed, 222 insertions, 0 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index a8e9ed47bbb4..2b791fe1e2bc 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig | |||
@@ -161,6 +161,16 @@ config KXCJK1013 | |||
161 | To compile this driver as a module, choose M here: the module will | 161 | To compile this driver as a module, choose M here: the module will |
162 | be called kxcjk-1013. | 162 | be called kxcjk-1013. |
163 | 163 | ||
164 | config MC3230 | ||
165 | tristate "mCube MC3230 Digital Accelerometer Driver" | ||
166 | depends on I2C | ||
167 | help | ||
168 | Say yes here to build support for the mCube MC3230 low-g tri-axial | ||
169 | digital accelerometer. | ||
170 | |||
171 | To compile this driver as a module, choose M here: the | ||
172 | module will be called mc3230. | ||
173 | |||
164 | config MMA7455 | 174 | config MMA7455 |
165 | tristate | 175 | tristate |
166 | select IIO_BUFFER | 176 | select IIO_BUFFER |
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index b5c2a0b5550f..f5d3ddee619e 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o | |||
15 | obj-$(CONFIG_KXSD9) += kxsd9.o | 15 | obj-$(CONFIG_KXSD9) += kxsd9.o |
16 | obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o | 16 | obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o |
17 | obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o | 17 | obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o |
18 | obj-$(CONFIG_MC3230) += mc3230.o | ||
18 | 19 | ||
19 | obj-$(CONFIG_MMA7455) += mma7455_core.o | 20 | obj-$(CONFIG_MMA7455) += mma7455_core.o |
20 | obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o | 21 | obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o |
diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c new file mode 100644 index 000000000000..4ea2ff623a6d --- /dev/null +++ b/drivers/iio/accel/mc3230.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /** | ||
2 | * mCube MC3230 3-Axis Accelerometer | ||
3 | * | ||
4 | * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c. | ||
12 | */ | ||
13 | |||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/iio/iio.h> | ||
17 | #include <linux/iio/sysfs.h> | ||
18 | |||
19 | #define MC3230_REG_XOUT 0x00 | ||
20 | #define MC3230_REG_YOUT 0x01 | ||
21 | #define MC3230_REG_ZOUT 0x02 | ||
22 | |||
23 | #define MC3230_REG_MODE 0x07 | ||
24 | #define MC3230_MODE_OPCON_MASK 0x03 | ||
25 | #define MC3230_MODE_OPCON_WAKE 0x01 | ||
26 | #define MC3230_MODE_OPCON_STANDBY 0x03 | ||
27 | |||
28 | #define MC3230_REG_CHIP_ID 0x18 | ||
29 | #define MC3230_CHIP_ID 0x01 | ||
30 | |||
31 | #define MC3230_REG_PRODUCT_CODE 0x3b | ||
32 | #define MC3230_PRODUCT_CODE 0x19 | ||
33 | |||
34 | /* | ||
35 | * The accelerometer has one measurement range: | ||
36 | * | ||
37 | * -1.5g - +1.5g (8-bit, signed) | ||
38 | * | ||
39 | * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765 | ||
40 | */ | ||
41 | |||
42 | static const int mc3230_nscale = 115411765; | ||
43 | |||
44 | #define MC3230_CHANNEL(reg, axis) { \ | ||
45 | .type = IIO_ACCEL, \ | ||
46 | .address = reg, \ | ||
47 | .modified = 1, \ | ||
48 | .channel2 = IIO_MOD_##axis, \ | ||
49 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
50 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | ||
51 | } | ||
52 | |||
53 | static const struct iio_chan_spec mc3230_channels[] = { | ||
54 | MC3230_CHANNEL(MC3230_REG_XOUT, X), | ||
55 | MC3230_CHANNEL(MC3230_REG_YOUT, Y), | ||
56 | MC3230_CHANNEL(MC3230_REG_ZOUT, Z), | ||
57 | }; | ||
58 | |||
59 | struct mc3230_data { | ||
60 | struct i2c_client *client; | ||
61 | }; | ||
62 | |||
63 | static int mc3230_set_opcon(struct mc3230_data *data, int opcon) | ||
64 | { | ||
65 | int ret; | ||
66 | struct i2c_client *client = data->client; | ||
67 | |||
68 | ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE); | ||
69 | if (ret < 0) { | ||
70 | dev_err(&client->dev, "failed to read mode reg: %d\n", ret); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | ret &= ~MC3230_MODE_OPCON_MASK; | ||
75 | ret |= opcon; | ||
76 | |||
77 | ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret); | ||
78 | if (ret < 0) { | ||
79 | dev_err(&client->dev, "failed to write mode reg: %d\n", ret); | ||
80 | return ret; | ||
81 | } | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int mc3230_read_raw(struct iio_dev *indio_dev, | ||
87 | struct iio_chan_spec const *chan, | ||
88 | int *val, int *val2, long mask) | ||
89 | { | ||
90 | struct mc3230_data *data = iio_priv(indio_dev); | ||
91 | int ret; | ||
92 | |||
93 | switch (mask) { | ||
94 | case IIO_CHAN_INFO_RAW: | ||
95 | ret = i2c_smbus_read_byte_data(data->client, chan->address); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | *val = sign_extend32(ret, 7); | ||
99 | return IIO_VAL_INT; | ||
100 | case IIO_CHAN_INFO_SCALE: | ||
101 | *val = 0; | ||
102 | *val2 = mc3230_nscale; | ||
103 | return IIO_VAL_INT_PLUS_NANO; | ||
104 | default: | ||
105 | return -EINVAL; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static const struct iio_info mc3230_info = { | ||
110 | .driver_module = THIS_MODULE, | ||
111 | .read_raw = mc3230_read_raw, | ||
112 | }; | ||
113 | |||
114 | static int mc3230_probe(struct i2c_client *client, | ||
115 | const struct i2c_device_id *id) | ||
116 | { | ||
117 | int ret; | ||
118 | struct iio_dev *indio_dev; | ||
119 | struct mc3230_data *data; | ||
120 | |||
121 | /* First check chip-id and product-id */ | ||
122 | ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID); | ||
123 | if (ret != MC3230_CHIP_ID) | ||
124 | return (ret < 0) ? ret : -ENODEV; | ||
125 | |||
126 | ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE); | ||
127 | if (ret != MC3230_PRODUCT_CODE) | ||
128 | return (ret < 0) ? ret : -ENODEV; | ||
129 | |||
130 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
131 | if (!indio_dev) { | ||
132 | dev_err(&client->dev, "iio allocation failed!\n"); | ||
133 | return -ENOMEM; | ||
134 | } | ||
135 | |||
136 | data = iio_priv(indio_dev); | ||
137 | data->client = client; | ||
138 | i2c_set_clientdata(client, indio_dev); | ||
139 | |||
140 | indio_dev->dev.parent = &client->dev; | ||
141 | indio_dev->info = &mc3230_info; | ||
142 | indio_dev->name = "mc3230"; | ||
143 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
144 | indio_dev->channels = mc3230_channels; | ||
145 | indio_dev->num_channels = ARRAY_SIZE(mc3230_channels); | ||
146 | |||
147 | ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); | ||
148 | if (ret < 0) | ||
149 | return ret; | ||
150 | |||
151 | ret = iio_device_register(indio_dev); | ||
152 | if (ret < 0) { | ||
153 | dev_err(&client->dev, "device_register failed\n"); | ||
154 | mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY); | ||
155 | } | ||
156 | |||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | static int mc3230_remove(struct i2c_client *client) | ||
161 | { | ||
162 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
163 | |||
164 | iio_device_unregister(indio_dev); | ||
165 | |||
166 | return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY); | ||
167 | } | ||
168 | |||
169 | #ifdef CONFIG_PM_SLEEP | ||
170 | static int mc3230_suspend(struct device *dev) | ||
171 | { | ||
172 | struct mc3230_data *data; | ||
173 | |||
174 | data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | ||
175 | |||
176 | return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY); | ||
177 | } | ||
178 | |||
179 | static int mc3230_resume(struct device *dev) | ||
180 | { | ||
181 | struct mc3230_data *data; | ||
182 | |||
183 | data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | ||
184 | |||
185 | return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); | ||
186 | } | ||
187 | #endif | ||
188 | |||
189 | static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); | ||
190 | |||
191 | static const struct i2c_device_id mc3230_i2c_id[] = { | ||
192 | {"mc3230", 0}, | ||
193 | {} | ||
194 | }; | ||
195 | MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); | ||
196 | |||
197 | static struct i2c_driver mc3230_driver = { | ||
198 | .driver = { | ||
199 | .name = "mc3230", | ||
200 | .pm = &mc3230_pm_ops, | ||
201 | }, | ||
202 | .probe = mc3230_probe, | ||
203 | .remove = mc3230_remove, | ||
204 | .id_table = mc3230_i2c_id, | ||
205 | }; | ||
206 | |||
207 | module_i2c_driver(mc3230_driver); | ||
208 | |||
209 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | ||
210 | MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver"); | ||
211 | MODULE_LICENSE("GPL v2"); | ||