1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Freescale's 3-Axis Accelerometer MMA8450 |
4 | * |
5 | * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/input.h> |
14 | #include <linux/mod_devicetable.h> |
15 | |
16 | #define MMA8450_DRV_NAME "mma8450" |
17 | |
18 | #define MODE_CHANGE_DELAY_MS 100 |
19 | #define POLL_INTERVAL 100 |
20 | #define POLL_INTERVAL_MAX 500 |
21 | |
22 | /* register definitions */ |
23 | #define MMA8450_STATUS 0x00 |
24 | #define MMA8450_STATUS_ZXYDR 0x08 |
25 | |
26 | #define MMA8450_OUT_X8 0x01 |
27 | #define MMA8450_OUT_Y8 0x02 |
28 | #define MMA8450_OUT_Z8 0x03 |
29 | |
30 | #define MMA8450_OUT_X_LSB 0x05 |
31 | #define MMA8450_OUT_X_MSB 0x06 |
32 | #define MMA8450_OUT_Y_LSB 0x07 |
33 | #define MMA8450_OUT_Y_MSB 0x08 |
34 | #define MMA8450_OUT_Z_LSB 0x09 |
35 | #define MMA8450_OUT_Z_MSB 0x0a |
36 | |
37 | #define MMA8450_XYZ_DATA_CFG 0x16 |
38 | |
39 | #define MMA8450_CTRL_REG1 0x38 |
40 | #define MMA8450_CTRL_REG2 0x39 |
41 | |
42 | static int mma8450_read(struct i2c_client *c, unsigned int off) |
43 | { |
44 | int ret; |
45 | |
46 | ret = i2c_smbus_read_byte_data(client: c, command: off); |
47 | if (ret < 0) |
48 | dev_err(&c->dev, |
49 | "failed to read register 0x%02x, error %d\n" , |
50 | off, ret); |
51 | |
52 | return ret; |
53 | } |
54 | |
55 | static int mma8450_write(struct i2c_client *c, unsigned int off, u8 v) |
56 | { |
57 | int error; |
58 | |
59 | error = i2c_smbus_write_byte_data(client: c, command: off, value: v); |
60 | if (error < 0) { |
61 | dev_err(&c->dev, |
62 | "failed to write to register 0x%02x, error %d\n" , |
63 | off, error); |
64 | return error; |
65 | } |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static int mma8450_read_block(struct i2c_client *c, unsigned int off, |
71 | u8 *buf, size_t size) |
72 | { |
73 | int err; |
74 | |
75 | err = i2c_smbus_read_i2c_block_data(client: c, command: off, length: size, values: buf); |
76 | if (err < 0) { |
77 | dev_err(&c->dev, |
78 | "failed to read block data at 0x%02x, error %d\n" , |
79 | MMA8450_OUT_X_LSB, err); |
80 | return err; |
81 | } |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static void mma8450_poll(struct input_dev *input) |
87 | { |
88 | struct i2c_client *c = input_get_drvdata(dev: input); |
89 | int x, y, z; |
90 | int ret; |
91 | u8 buf[6]; |
92 | |
93 | ret = mma8450_read(c, MMA8450_STATUS); |
94 | if (ret < 0) |
95 | return; |
96 | |
97 | if (!(ret & MMA8450_STATUS_ZXYDR)) |
98 | return; |
99 | |
100 | ret = mma8450_read_block(c, MMA8450_OUT_X_LSB, buf, size: sizeof(buf)); |
101 | if (ret < 0) |
102 | return; |
103 | |
104 | x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf); |
105 | y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf); |
106 | z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf); |
107 | |
108 | input_report_abs(dev: input, ABS_X, value: x); |
109 | input_report_abs(dev: input, ABS_Y, value: y); |
110 | input_report_abs(dev: input, ABS_Z, value: z); |
111 | input_sync(dev: input); |
112 | } |
113 | |
114 | /* Initialize the MMA8450 chip */ |
115 | static int mma8450_open(struct input_dev *input) |
116 | { |
117 | struct i2c_client *c = input_get_drvdata(dev: input); |
118 | int err; |
119 | |
120 | /* enable all events from X/Y/Z, no FIFO */ |
121 | err = mma8450_write(c, MMA8450_XYZ_DATA_CFG, v: 0x07); |
122 | if (err) |
123 | return err; |
124 | |
125 | /* |
126 | * Sleep mode poll rate - 50Hz |
127 | * System output data rate - 400Hz |
128 | * Full scale selection - Active, +/- 2G |
129 | */ |
130 | err = mma8450_write(c, MMA8450_CTRL_REG1, v: 0x01); |
131 | if (err) |
132 | return err; |
133 | |
134 | msleep(MODE_CHANGE_DELAY_MS); |
135 | return 0; |
136 | } |
137 | |
138 | static void mma8450_close(struct input_dev *input) |
139 | { |
140 | struct i2c_client *c = input_get_drvdata(dev: input); |
141 | |
142 | mma8450_write(c, MMA8450_CTRL_REG1, v: 0x00); |
143 | mma8450_write(c, MMA8450_CTRL_REG2, v: 0x01); |
144 | } |
145 | |
146 | /* |
147 | * I2C init/probing/exit functions |
148 | */ |
149 | static int mma8450_probe(struct i2c_client *c) |
150 | { |
151 | struct input_dev *input; |
152 | int err; |
153 | |
154 | input = devm_input_allocate_device(&c->dev); |
155 | if (!input) |
156 | return -ENOMEM; |
157 | |
158 | input_set_drvdata(dev: input, data: c); |
159 | |
160 | input->name = MMA8450_DRV_NAME; |
161 | input->id.bustype = BUS_I2C; |
162 | |
163 | input->open = mma8450_open; |
164 | input->close = mma8450_close; |
165 | |
166 | input_set_abs_params(dev: input, ABS_X, min: -2048, max: 2047, fuzz: 32, flat: 32); |
167 | input_set_abs_params(dev: input, ABS_Y, min: -2048, max: 2047, fuzz: 32, flat: 32); |
168 | input_set_abs_params(dev: input, ABS_Z, min: -2048, max: 2047, fuzz: 32, flat: 32); |
169 | |
170 | err = input_setup_polling(dev: input, poll_fn: mma8450_poll); |
171 | if (err) { |
172 | dev_err(&c->dev, "failed to set up polling\n" ); |
173 | return err; |
174 | } |
175 | |
176 | input_set_poll_interval(dev: input, POLL_INTERVAL); |
177 | input_set_max_poll_interval(dev: input, POLL_INTERVAL_MAX); |
178 | |
179 | err = input_register_device(input); |
180 | if (err) { |
181 | dev_err(&c->dev, "failed to register input device\n" ); |
182 | return err; |
183 | } |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static const struct i2c_device_id mma8450_id[] = { |
189 | { MMA8450_DRV_NAME, 0 }, |
190 | { }, |
191 | }; |
192 | MODULE_DEVICE_TABLE(i2c, mma8450_id); |
193 | |
194 | static const struct of_device_id mma8450_dt_ids[] = { |
195 | { .compatible = "fsl,mma8450" , }, |
196 | { /* sentinel */ } |
197 | }; |
198 | MODULE_DEVICE_TABLE(of, mma8450_dt_ids); |
199 | |
200 | static struct i2c_driver mma8450_driver = { |
201 | .driver = { |
202 | .name = MMA8450_DRV_NAME, |
203 | .of_match_table = mma8450_dt_ids, |
204 | }, |
205 | .probe = mma8450_probe, |
206 | .id_table = mma8450_id, |
207 | }; |
208 | |
209 | module_i2c_driver(mma8450_driver); |
210 | |
211 | MODULE_AUTHOR("Freescale Semiconductor, Inc." ); |
212 | MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver" ); |
213 | MODULE_LICENSE("GPL" ); |
214 | |