1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and |
4 | * IIO driver for the MiraMEMS DA226 2-axis accelerometer |
5 | * |
6 | * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/acpi.h> |
12 | #include <linux/iio/iio.h> |
13 | #include <linux/iio/sysfs.h> |
14 | #include <linux/byteorder/generic.h> |
15 | |
16 | #define DA280_REG_CHIP_ID 0x01 |
17 | #define DA280_REG_ACC_X_LSB 0x02 |
18 | #define DA280_REG_ACC_Y_LSB 0x04 |
19 | #define DA280_REG_ACC_Z_LSB 0x06 |
20 | #define DA280_REG_MODE_BW 0x11 |
21 | |
22 | #define DA280_CHIP_ID 0x13 |
23 | #define DA280_MODE_ENABLE 0x1e |
24 | #define DA280_MODE_DISABLE 0x9e |
25 | |
26 | enum da280_chipset { da217, da226, da280 }; |
27 | |
28 | /* |
29 | * a value of + or -4096 corresponds to + or - 1G |
30 | * scale = 9.81 / 4096 = 0.002395019 |
31 | */ |
32 | |
33 | static const int da280_nscale = 2395019; |
34 | |
35 | #define DA280_CHANNEL(reg, axis) { \ |
36 | .type = IIO_ACCEL, \ |
37 | .address = reg, \ |
38 | .modified = 1, \ |
39 | .channel2 = IIO_MOD_##axis, \ |
40 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
41 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
42 | } |
43 | |
44 | static const struct iio_chan_spec da280_channels[] = { |
45 | DA280_CHANNEL(DA280_REG_ACC_X_LSB, X), |
46 | DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y), |
47 | DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z), |
48 | }; |
49 | |
50 | struct da280_data { |
51 | struct i2c_client *client; |
52 | }; |
53 | |
54 | static int da280_enable(struct i2c_client *client, bool enable) |
55 | { |
56 | u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE; |
57 | |
58 | return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, value: data); |
59 | } |
60 | |
61 | static int da280_read_raw(struct iio_dev *indio_dev, |
62 | struct iio_chan_spec const *chan, |
63 | int *val, int *val2, long mask) |
64 | { |
65 | struct da280_data *data = iio_priv(indio_dev); |
66 | int ret; |
67 | |
68 | switch (mask) { |
69 | case IIO_CHAN_INFO_RAW: |
70 | ret = i2c_smbus_read_word_data(client: data->client, command: chan->address); |
71 | if (ret < 0) |
72 | return ret; |
73 | /* |
74 | * Values are 14 bits, stored as 16 bits with the 2 |
75 | * least significant bits always 0. |
76 | */ |
77 | *val = (short)ret >> 2; |
78 | return IIO_VAL_INT; |
79 | case IIO_CHAN_INFO_SCALE: |
80 | *val = 0; |
81 | *val2 = da280_nscale; |
82 | return IIO_VAL_INT_PLUS_NANO; |
83 | default: |
84 | return -EINVAL; |
85 | } |
86 | } |
87 | |
88 | static const struct iio_info da280_info = { |
89 | .read_raw = da280_read_raw, |
90 | }; |
91 | |
92 | static enum da280_chipset da280_match_acpi_device(struct device *dev) |
93 | { |
94 | const struct acpi_device_id *id; |
95 | |
96 | id = acpi_match_device(ids: dev->driver->acpi_match_table, dev); |
97 | if (!id) |
98 | return -EINVAL; |
99 | |
100 | return (enum da280_chipset) id->driver_data; |
101 | } |
102 | |
103 | static void da280_disable(void *client) |
104 | { |
105 | da280_enable(client, enable: false); |
106 | } |
107 | |
108 | static int da280_probe(struct i2c_client *client) |
109 | { |
110 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
111 | int ret; |
112 | struct iio_dev *indio_dev; |
113 | struct da280_data *data; |
114 | enum da280_chipset chip; |
115 | |
116 | ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID); |
117 | if (ret != DA280_CHIP_ID) |
118 | return (ret < 0) ? ret : -ENODEV; |
119 | |
120 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
121 | if (!indio_dev) |
122 | return -ENOMEM; |
123 | |
124 | data = iio_priv(indio_dev); |
125 | data->client = client; |
126 | |
127 | indio_dev->info = &da280_info; |
128 | indio_dev->modes = INDIO_DIRECT_MODE; |
129 | indio_dev->channels = da280_channels; |
130 | |
131 | if (ACPI_HANDLE(&client->dev)) { |
132 | chip = da280_match_acpi_device(dev: &client->dev); |
133 | } else { |
134 | chip = id->driver_data; |
135 | } |
136 | |
137 | if (chip == da217) { |
138 | indio_dev->name = "da217" ; |
139 | indio_dev->num_channels = 3; |
140 | } else if (chip == da226) { |
141 | indio_dev->name = "da226" ; |
142 | indio_dev->num_channels = 2; |
143 | } else { |
144 | indio_dev->name = "da280" ; |
145 | indio_dev->num_channels = 3; |
146 | } |
147 | |
148 | ret = da280_enable(client, enable: true); |
149 | if (ret < 0) |
150 | return ret; |
151 | |
152 | ret = devm_add_action_or_reset(&client->dev, da280_disable, client); |
153 | if (ret) |
154 | return ret; |
155 | |
156 | return devm_iio_device_register(&client->dev, indio_dev); |
157 | } |
158 | |
159 | static int da280_suspend(struct device *dev) |
160 | { |
161 | return da280_enable(to_i2c_client(dev), enable: false); |
162 | } |
163 | |
164 | static int da280_resume(struct device *dev) |
165 | { |
166 | return da280_enable(to_i2c_client(dev), enable: true); |
167 | } |
168 | |
169 | static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); |
170 | |
171 | static const struct acpi_device_id da280_acpi_match[] = { |
172 | {"NSA2513" , da217}, |
173 | {"MIRAACC" , da280}, |
174 | {}, |
175 | }; |
176 | MODULE_DEVICE_TABLE(acpi, da280_acpi_match); |
177 | |
178 | static const struct i2c_device_id da280_i2c_id[] = { |
179 | { "da217" , da217 }, |
180 | { "da226" , da226 }, |
181 | { "da280" , da280 }, |
182 | {} |
183 | }; |
184 | MODULE_DEVICE_TABLE(i2c, da280_i2c_id); |
185 | |
186 | static struct i2c_driver da280_driver = { |
187 | .driver = { |
188 | .name = "da280" , |
189 | .acpi_match_table = ACPI_PTR(da280_acpi_match), |
190 | .pm = pm_sleep_ptr(&da280_pm_ops), |
191 | }, |
192 | .probe = da280_probe, |
193 | .id_table = da280_i2c_id, |
194 | }; |
195 | |
196 | module_i2c_driver(da280_driver); |
197 | |
198 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
199 | MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver" ); |
200 | MODULE_LICENSE("GPL v2" ); |
201 | |