1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com> |
4 | * Sponsored by ARMadeus Systems |
5 | * |
6 | * Driver for Austria Microsystems joysticks AS5011 |
7 | * |
8 | * TODO: |
9 | * - Power on the chip when open() and power down when close() |
10 | * - Manage power mode |
11 | */ |
12 | |
13 | #include <linux/i2c.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/input.h> |
16 | #include <linux/gpio.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/input/as5011.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/module.h> |
21 | |
22 | #define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick" |
23 | #define MODULE_DEVICE_ALIAS "as5011" |
24 | |
25 | MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>" ); |
26 | MODULE_DESCRIPTION(DRIVER_DESC); |
27 | MODULE_LICENSE("GPL" ); |
28 | |
29 | /* registers */ |
30 | #define AS5011_CTRL1 0x76 |
31 | #define AS5011_CTRL2 0x75 |
32 | #define AS5011_XP 0x43 |
33 | #define AS5011_XN 0x44 |
34 | #define AS5011_YP 0x53 |
35 | #define AS5011_YN 0x54 |
36 | #define AS5011_X_REG 0x41 |
37 | #define AS5011_Y_REG 0x42 |
38 | #define AS5011_X_RES_INT 0x51 |
39 | #define AS5011_Y_RES_INT 0x52 |
40 | |
41 | /* CTRL1 bits */ |
42 | #define AS5011_CTRL1_LP_PULSED 0x80 |
43 | #define AS5011_CTRL1_LP_ACTIVE 0x40 |
44 | #define AS5011_CTRL1_LP_CONTINUE 0x20 |
45 | #define AS5011_CTRL1_INT_WUP_EN 0x10 |
46 | #define AS5011_CTRL1_INT_ACT_EN 0x08 |
47 | #define AS5011_CTRL1_EXT_CLK_EN 0x04 |
48 | #define AS5011_CTRL1_SOFT_RST 0x02 |
49 | #define AS5011_CTRL1_DATA_VALID 0x01 |
50 | |
51 | /* CTRL2 bits */ |
52 | #define AS5011_CTRL2_EXT_SAMPLE_EN 0x08 |
53 | #define AS5011_CTRL2_RC_BIAS_ON 0x04 |
54 | #define AS5011_CTRL2_INV_SPINNING 0x02 |
55 | |
56 | #define AS5011_MAX_AXIS 80 |
57 | #define AS5011_MIN_AXIS (-80) |
58 | #define AS5011_FUZZ 8 |
59 | #define AS5011_FLAT 40 |
60 | |
61 | struct as5011_device { |
62 | struct input_dev *input_dev; |
63 | struct i2c_client *i2c_client; |
64 | unsigned int button_gpio; |
65 | unsigned int button_irq; |
66 | unsigned int axis_irq; |
67 | }; |
68 | |
69 | static int as5011_i2c_write(struct i2c_client *client, |
70 | uint8_t aregaddr, |
71 | uint8_t avalue) |
72 | { |
73 | uint8_t data[2] = { aregaddr, avalue }; |
74 | struct i2c_msg msg = { |
75 | .addr = client->addr, |
76 | .flags = I2C_M_IGNORE_NAK, |
77 | .len = 2, |
78 | .buf = (uint8_t *)data |
79 | }; |
80 | int error; |
81 | |
82 | error = i2c_transfer(adap: client->adapter, msgs: &msg, num: 1); |
83 | return error < 0 ? error : 0; |
84 | } |
85 | |
86 | static int as5011_i2c_read(struct i2c_client *client, |
87 | uint8_t aregaddr, signed char *value) |
88 | { |
89 | uint8_t data[2] = { aregaddr }; |
90 | struct i2c_msg msg_set[2] = { |
91 | { |
92 | .addr = client->addr, |
93 | .flags = I2C_M_REV_DIR_ADDR, |
94 | .len = 1, |
95 | .buf = (uint8_t *)data |
96 | }, |
97 | { |
98 | .addr = client->addr, |
99 | .flags = I2C_M_RD | I2C_M_NOSTART, |
100 | .len = 1, |
101 | .buf = (uint8_t *)data |
102 | } |
103 | }; |
104 | int error; |
105 | |
106 | error = i2c_transfer(adap: client->adapter, msgs: msg_set, num: 2); |
107 | if (error < 0) |
108 | return error; |
109 | |
110 | *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0]; |
111 | return 0; |
112 | } |
113 | |
114 | static irqreturn_t as5011_button_interrupt(int irq, void *dev_id) |
115 | { |
116 | struct as5011_device *as5011 = dev_id; |
117 | int val = gpio_get_value_cansleep(gpio: as5011->button_gpio); |
118 | |
119 | input_report_key(dev: as5011->input_dev, BTN_JOYSTICK, value: !val); |
120 | input_sync(dev: as5011->input_dev); |
121 | |
122 | return IRQ_HANDLED; |
123 | } |
124 | |
125 | static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id) |
126 | { |
127 | struct as5011_device *as5011 = dev_id; |
128 | int error; |
129 | signed char x, y; |
130 | |
131 | error = as5011_i2c_read(client: as5011->i2c_client, AS5011_X_RES_INT, value: &x); |
132 | if (error < 0) |
133 | goto out; |
134 | |
135 | error = as5011_i2c_read(client: as5011->i2c_client, AS5011_Y_RES_INT, value: &y); |
136 | if (error < 0) |
137 | goto out; |
138 | |
139 | input_report_abs(dev: as5011->input_dev, ABS_X, value: x); |
140 | input_report_abs(dev: as5011->input_dev, ABS_Y, value: y); |
141 | input_sync(dev: as5011->input_dev); |
142 | |
143 | out: |
144 | return IRQ_HANDLED; |
145 | } |
146 | |
147 | static int as5011_configure_chip(struct as5011_device *as5011, |
148 | const struct as5011_platform_data *plat_dat) |
149 | { |
150 | struct i2c_client *client = as5011->i2c_client; |
151 | int error; |
152 | signed char value; |
153 | |
154 | /* chip soft reset */ |
155 | error = as5011_i2c_write(client, AS5011_CTRL1, |
156 | AS5011_CTRL1_SOFT_RST); |
157 | if (error < 0) { |
158 | dev_err(&client->dev, "Soft reset failed\n" ); |
159 | return error; |
160 | } |
161 | |
162 | mdelay(10); |
163 | |
164 | error = as5011_i2c_write(client, AS5011_CTRL1, |
165 | AS5011_CTRL1_LP_PULSED | |
166 | AS5011_CTRL1_LP_ACTIVE | |
167 | AS5011_CTRL1_INT_ACT_EN); |
168 | if (error < 0) { |
169 | dev_err(&client->dev, "Power config failed\n" ); |
170 | return error; |
171 | } |
172 | |
173 | error = as5011_i2c_write(client, AS5011_CTRL2, |
174 | AS5011_CTRL2_INV_SPINNING); |
175 | if (error < 0) { |
176 | dev_err(&client->dev, "Can't invert spinning\n" ); |
177 | return error; |
178 | } |
179 | |
180 | /* write threshold */ |
181 | error = as5011_i2c_write(client, AS5011_XP, avalue: plat_dat->xp); |
182 | if (error < 0) { |
183 | dev_err(&client->dev, "Can't write threshold\n" ); |
184 | return error; |
185 | } |
186 | |
187 | error = as5011_i2c_write(client, AS5011_XN, avalue: plat_dat->xn); |
188 | if (error < 0) { |
189 | dev_err(&client->dev, "Can't write threshold\n" ); |
190 | return error; |
191 | } |
192 | |
193 | error = as5011_i2c_write(client, AS5011_YP, avalue: plat_dat->yp); |
194 | if (error < 0) { |
195 | dev_err(&client->dev, "Can't write threshold\n" ); |
196 | return error; |
197 | } |
198 | |
199 | error = as5011_i2c_write(client, AS5011_YN, avalue: plat_dat->yn); |
200 | if (error < 0) { |
201 | dev_err(&client->dev, "Can't write threshold\n" ); |
202 | return error; |
203 | } |
204 | |
205 | /* to free irq gpio in chip */ |
206 | error = as5011_i2c_read(client, AS5011_X_RES_INT, value: &value); |
207 | if (error < 0) { |
208 | dev_err(&client->dev, "Can't read i2c X resolution value\n" ); |
209 | return error; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static int as5011_probe(struct i2c_client *client) |
216 | { |
217 | const struct as5011_platform_data *plat_data; |
218 | struct as5011_device *as5011; |
219 | struct input_dev *input_dev; |
220 | int irq; |
221 | int error; |
222 | |
223 | plat_data = dev_get_platdata(dev: &client->dev); |
224 | if (!plat_data) |
225 | return -EINVAL; |
226 | |
227 | if (!plat_data->axis_irq) { |
228 | dev_err(&client->dev, "No axis IRQ?\n" ); |
229 | return -EINVAL; |
230 | } |
231 | |
232 | if (!i2c_check_functionality(adap: client->adapter, |
233 | I2C_FUNC_NOSTART | |
234 | I2C_FUNC_PROTOCOL_MANGLING)) { |
235 | dev_err(&client->dev, |
236 | "need i2c bus that supports protocol mangling\n" ); |
237 | return -ENODEV; |
238 | } |
239 | |
240 | as5011 = kmalloc(size: sizeof(struct as5011_device), GFP_KERNEL); |
241 | input_dev = input_allocate_device(); |
242 | if (!as5011 || !input_dev) { |
243 | dev_err(&client->dev, |
244 | "Can't allocate memory for device structure\n" ); |
245 | error = -ENOMEM; |
246 | goto err_free_mem; |
247 | } |
248 | |
249 | as5011->i2c_client = client; |
250 | as5011->input_dev = input_dev; |
251 | as5011->button_gpio = plat_data->button_gpio; |
252 | as5011->axis_irq = plat_data->axis_irq; |
253 | |
254 | input_dev->name = "Austria Microsystem as5011 joystick" ; |
255 | input_dev->id.bustype = BUS_I2C; |
256 | input_dev->dev.parent = &client->dev; |
257 | |
258 | input_set_capability(dev: input_dev, EV_KEY, BTN_JOYSTICK); |
259 | |
260 | input_set_abs_params(dev: input_dev, ABS_X, |
261 | AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); |
262 | input_set_abs_params(dev: as5011->input_dev, ABS_Y, |
263 | AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); |
264 | |
265 | error = gpio_request(gpio: as5011->button_gpio, label: "AS5011 button" ); |
266 | if (error < 0) { |
267 | dev_err(&client->dev, "Failed to request button gpio\n" ); |
268 | goto err_free_mem; |
269 | } |
270 | |
271 | irq = gpio_to_irq(gpio: as5011->button_gpio); |
272 | if (irq < 0) { |
273 | dev_err(&client->dev, |
274 | "Failed to get irq number for button gpio\n" ); |
275 | error = irq; |
276 | goto err_free_button_gpio; |
277 | } |
278 | |
279 | as5011->button_irq = irq; |
280 | |
281 | error = request_threaded_irq(irq: as5011->button_irq, |
282 | NULL, thread_fn: as5011_button_interrupt, |
283 | IRQF_TRIGGER_RISING | |
284 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
285 | name: "as5011_button" , dev: as5011); |
286 | if (error < 0) { |
287 | dev_err(&client->dev, |
288 | "Can't allocate button irq %d\n" , as5011->button_irq); |
289 | goto err_free_button_gpio; |
290 | } |
291 | |
292 | error = as5011_configure_chip(as5011, plat_dat: plat_data); |
293 | if (error) |
294 | goto err_free_button_irq; |
295 | |
296 | error = request_threaded_irq(irq: as5011->axis_irq, NULL, |
297 | thread_fn: as5011_axis_interrupt, |
298 | flags: plat_data->axis_irqflags | IRQF_ONESHOT, |
299 | name: "as5011_joystick" , dev: as5011); |
300 | if (error) { |
301 | dev_err(&client->dev, |
302 | "Can't allocate axis irq %d\n" , plat_data->axis_irq); |
303 | goto err_free_button_irq; |
304 | } |
305 | |
306 | error = input_register_device(as5011->input_dev); |
307 | if (error) { |
308 | dev_err(&client->dev, "Failed to register input device\n" ); |
309 | goto err_free_axis_irq; |
310 | } |
311 | |
312 | i2c_set_clientdata(client, data: as5011); |
313 | |
314 | return 0; |
315 | |
316 | err_free_axis_irq: |
317 | free_irq(as5011->axis_irq, as5011); |
318 | err_free_button_irq: |
319 | free_irq(as5011->button_irq, as5011); |
320 | err_free_button_gpio: |
321 | gpio_free(gpio: as5011->button_gpio); |
322 | err_free_mem: |
323 | input_free_device(dev: input_dev); |
324 | kfree(objp: as5011); |
325 | |
326 | return error; |
327 | } |
328 | |
329 | static void as5011_remove(struct i2c_client *client) |
330 | { |
331 | struct as5011_device *as5011 = i2c_get_clientdata(client); |
332 | |
333 | free_irq(as5011->axis_irq, as5011); |
334 | free_irq(as5011->button_irq, as5011); |
335 | gpio_free(gpio: as5011->button_gpio); |
336 | |
337 | input_unregister_device(as5011->input_dev); |
338 | kfree(objp: as5011); |
339 | } |
340 | |
341 | static const struct i2c_device_id as5011_id[] = { |
342 | { MODULE_DEVICE_ALIAS, 0 }, |
343 | { } |
344 | }; |
345 | MODULE_DEVICE_TABLE(i2c, as5011_id); |
346 | |
347 | static struct i2c_driver as5011_driver = { |
348 | .driver = { |
349 | .name = "as5011" , |
350 | }, |
351 | .probe = as5011_probe, |
352 | .remove = as5011_remove, |
353 | .id_table = as5011_id, |
354 | }; |
355 | |
356 | module_i2c_driver(as5011_driver); |
357 | |