1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * IIO driver for the 3-axis accelerometer Domintech DMARD09. |
4 | * |
5 | * Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl> |
6 | */ |
7 | |
8 | #include <asm/unaligned.h> |
9 | #include <linux/module.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/iio/iio.h> |
12 | |
13 | #define DMARD09_DRV_NAME "dmard09" |
14 | |
15 | #define DMARD09_REG_CHIPID 0x18 |
16 | #define DMARD09_REG_STAT 0x0A |
17 | #define DMARD09_REG_X 0x0C |
18 | #define DMARD09_REG_Y 0x0E |
19 | #define DMARD09_REG_Z 0x10 |
20 | #define DMARD09_CHIPID 0x95 |
21 | |
22 | #define DMARD09_BUF_LEN 8 |
23 | #define DMARD09_AXIS_X 0 |
24 | #define DMARD09_AXIS_Y 1 |
25 | #define DMARD09_AXIS_Z 2 |
26 | #define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2) |
27 | #define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1) * 2) |
28 | #define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2) |
29 | |
30 | struct dmard09_data { |
31 | struct i2c_client *client; |
32 | }; |
33 | |
34 | #define DMARD09_CHANNEL(_axis, offset) { \ |
35 | .type = IIO_ACCEL, \ |
36 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
37 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
38 | .modified = 1, \ |
39 | .address = offset, \ |
40 | .channel2 = IIO_MOD_##_axis, \ |
41 | } |
42 | |
43 | static const struct iio_chan_spec dmard09_channels[] = { |
44 | DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET), |
45 | DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET), |
46 | DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET), |
47 | }; |
48 | |
49 | static int dmard09_read_raw(struct iio_dev *indio_dev, |
50 | struct iio_chan_spec const *chan, |
51 | int *val, int *val2, long mask) |
52 | { |
53 | struct dmard09_data *data = iio_priv(indio_dev); |
54 | u8 buf[DMARD09_BUF_LEN]; |
55 | int ret; |
56 | s16 accel; |
57 | |
58 | switch (mask) { |
59 | case IIO_CHAN_INFO_RAW: |
60 | /* |
61 | * Read from the DMAR09_REG_STAT register, since the chip |
62 | * caches reads from the individual X, Y, Z registers. |
63 | */ |
64 | ret = i2c_smbus_read_i2c_block_data(client: data->client, |
65 | DMARD09_REG_STAT, |
66 | DMARD09_BUF_LEN, values: buf); |
67 | if (ret < 0) { |
68 | dev_err(&data->client->dev, "Error reading reg %d\n" , |
69 | DMARD09_REG_STAT); |
70 | return ret; |
71 | } |
72 | |
73 | accel = get_unaligned_le16(p: &buf[chan->address]); |
74 | |
75 | /* Remove lower 3 bits and sign extend */ |
76 | accel <<= 4; |
77 | accel >>= 7; |
78 | |
79 | *val = accel; |
80 | |
81 | return IIO_VAL_INT; |
82 | default: |
83 | return -EINVAL; |
84 | } |
85 | } |
86 | |
87 | static const struct iio_info dmard09_info = { |
88 | .read_raw = dmard09_read_raw, |
89 | }; |
90 | |
91 | static int dmard09_probe(struct i2c_client *client) |
92 | { |
93 | int ret; |
94 | struct iio_dev *indio_dev; |
95 | struct dmard09_data *data; |
96 | |
97 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*data)); |
98 | if (!indio_dev) { |
99 | dev_err(&client->dev, "iio allocation failed\n" ); |
100 | return -ENOMEM; |
101 | } |
102 | |
103 | data = iio_priv(indio_dev); |
104 | data->client = client; |
105 | |
106 | ret = i2c_smbus_read_byte_data(client: data->client, DMARD09_REG_CHIPID); |
107 | if (ret < 0) { |
108 | dev_err(&client->dev, "Error reading chip id %d\n" , ret); |
109 | return ret; |
110 | } |
111 | |
112 | if (ret != DMARD09_CHIPID) { |
113 | dev_err(&client->dev, "Invalid chip id %d\n" , ret); |
114 | return -ENODEV; |
115 | } |
116 | |
117 | i2c_set_clientdata(client, data: indio_dev); |
118 | indio_dev->name = DMARD09_DRV_NAME; |
119 | indio_dev->modes = INDIO_DIRECT_MODE; |
120 | indio_dev->channels = dmard09_channels; |
121 | indio_dev->num_channels = ARRAY_SIZE(dmard09_channels); |
122 | indio_dev->info = &dmard09_info; |
123 | |
124 | return devm_iio_device_register(&client->dev, indio_dev); |
125 | } |
126 | |
127 | static const struct i2c_device_id dmard09_id[] = { |
128 | { "dmard09" , 0 }, |
129 | { }, |
130 | }; |
131 | |
132 | MODULE_DEVICE_TABLE(i2c, dmard09_id); |
133 | |
134 | static struct i2c_driver dmard09_driver = { |
135 | .driver = { |
136 | .name = DMARD09_DRV_NAME |
137 | }, |
138 | .probe = dmard09_probe, |
139 | .id_table = dmard09_id, |
140 | }; |
141 | |
142 | module_i2c_driver(dmard09_driver); |
143 | |
144 | MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>" ); |
145 | MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver" ); |
146 | MODULE_LICENSE("GPL" ); |
147 | |