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 | /* |
27 | * a value of + or -4096 corresponds to + or - 1G |
28 | * scale = 9.81 / 4096 = 0.002395019 |
29 | */ |
30 | |
31 | static const int da280_nscale = 2395019; |
32 | |
33 | #define DA280_CHANNEL(reg, axis) { \ |
34 | .type = IIO_ACCEL, \ |
35 | .address = reg, \ |
36 | .modified = 1, \ |
37 | .channel2 = IIO_MOD_##axis, \ |
38 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
39 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
40 | } |
41 | |
42 | static const struct iio_chan_spec da280_channels[] = { |
43 | DA280_CHANNEL(DA280_REG_ACC_X_LSB, X), |
44 | DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y), |
45 | DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z), |
46 | }; |
47 | |
48 | struct da280_match_data { |
49 | const char *name; |
50 | int num_channels; |
51 | }; |
52 | |
53 | struct da280_data { |
54 | struct i2c_client *client; |
55 | }; |
56 | |
57 | static int da280_enable(struct i2c_client *client, bool enable) |
58 | { |
59 | u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE; |
60 | |
61 | return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, value: data); |
62 | } |
63 | |
64 | static int da280_read_raw(struct iio_dev *indio_dev, |
65 | struct iio_chan_spec const *chan, |
66 | int *val, int *val2, long mask) |
67 | { |
68 | struct da280_data *data = iio_priv(indio_dev); |
69 | int ret; |
70 | |
71 | switch (mask) { |
72 | case IIO_CHAN_INFO_RAW: |
73 | ret = i2c_smbus_read_word_data(client: data->client, command: chan->address); |
74 | if (ret < 0) |
75 | return ret; |
76 | /* |
77 | * Values are 14 bits, stored as 16 bits with the 2 |
78 | * least significant bits always 0. |
79 | */ |
80 | *val = (short)ret >> 2; |
81 | return IIO_VAL_INT; |
82 | case IIO_CHAN_INFO_SCALE: |
83 | *val = 0; |
84 | *val2 = da280_nscale; |
85 | return IIO_VAL_INT_PLUS_NANO; |
86 | default: |
87 | return -EINVAL; |
88 | } |
89 | } |
90 | |
91 | static const struct iio_info da280_info = { |
92 | .read_raw = da280_read_raw, |
93 | }; |
94 | |
95 | static void da280_disable(void *client) |
96 | { |
97 | da280_enable(client, enable: false); |
98 | } |
99 | |
100 | static int da280_probe(struct i2c_client *client) |
101 | { |
102 | const struct da280_match_data *match_data; |
103 | struct iio_dev *indio_dev; |
104 | struct da280_data *data; |
105 | int ret; |
106 | |
107 | ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID); |
108 | if (ret != DA280_CHIP_ID) |
109 | return (ret < 0) ? ret : -ENODEV; |
110 | |
111 | match_data = i2c_get_match_data(client); |
112 | if (!match_data) { |
113 | dev_err(&client->dev, "Error match-data not set\n" ); |
114 | return -EINVAL; |
115 | } |
116 | |
117 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
118 | if (!indio_dev) |
119 | return -ENOMEM; |
120 | |
121 | data = iio_priv(indio_dev); |
122 | data->client = client; |
123 | |
124 | indio_dev->info = &da280_info; |
125 | indio_dev->modes = INDIO_DIRECT_MODE; |
126 | indio_dev->channels = da280_channels; |
127 | indio_dev->num_channels = match_data->num_channels; |
128 | indio_dev->name = match_data->name; |
129 | |
130 | ret = da280_enable(client, enable: true); |
131 | if (ret < 0) |
132 | return ret; |
133 | |
134 | ret = devm_add_action_or_reset(&client->dev, da280_disable, client); |
135 | if (ret) |
136 | return ret; |
137 | |
138 | return devm_iio_device_register(&client->dev, indio_dev); |
139 | } |
140 | |
141 | static int da280_suspend(struct device *dev) |
142 | { |
143 | return da280_enable(to_i2c_client(dev), enable: false); |
144 | } |
145 | |
146 | static int da280_resume(struct device *dev) |
147 | { |
148 | return da280_enable(to_i2c_client(dev), enable: true); |
149 | } |
150 | |
151 | static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); |
152 | |
153 | static const struct da280_match_data da217_match_data = { "da217" , 3 }; |
154 | static const struct da280_match_data da226_match_data = { "da226" , 2 }; |
155 | static const struct da280_match_data da280_match_data = { "da280" , 3 }; |
156 | |
157 | static const struct acpi_device_id da280_acpi_match[] = { |
158 | { "NSA2513" , (kernel_ulong_t)&da217_match_data }, |
159 | { "MIRAACC" , (kernel_ulong_t)&da280_match_data }, |
160 | {} |
161 | }; |
162 | MODULE_DEVICE_TABLE(acpi, da280_acpi_match); |
163 | |
164 | static const struct i2c_device_id da280_i2c_id[] = { |
165 | { "da217" , (kernel_ulong_t)&da217_match_data }, |
166 | { "da226" , (kernel_ulong_t)&da226_match_data }, |
167 | { "da280" , (kernel_ulong_t)&da280_match_data }, |
168 | {} |
169 | }; |
170 | MODULE_DEVICE_TABLE(i2c, da280_i2c_id); |
171 | |
172 | static struct i2c_driver da280_driver = { |
173 | .driver = { |
174 | .name = "da280" , |
175 | .acpi_match_table = da280_acpi_match, |
176 | .pm = pm_sleep_ptr(&da280_pm_ops), |
177 | }, |
178 | .probe = da280_probe, |
179 | .id_table = da280_i2c_id, |
180 | }; |
181 | |
182 | module_i2c_driver(da280_driver); |
183 | |
184 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
185 | MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver" ); |
186 | MODULE_LICENSE("GPL v2" ); |
187 | |