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/consumer.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 | struct gpio_desc *button_gpiod; |
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 = gpiod_get_value_cansleep(desc: as5011->button_gpiod); |
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->axis_irq = plat_data->axis_irq; |
252 | |
253 | input_dev->name = "Austria Microsystem as5011 joystick" ; |
254 | input_dev->id.bustype = BUS_I2C; |
255 | input_dev->dev.parent = &client->dev; |
256 | |
257 | input_set_capability(dev: input_dev, EV_KEY, BTN_JOYSTICK); |
258 | |
259 | input_set_abs_params(dev: input_dev, ABS_X, |
260 | AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); |
261 | input_set_abs_params(dev: as5011->input_dev, ABS_Y, |
262 | AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); |
263 | |
264 | as5011->button_gpiod = devm_gpiod_get(dev: &client->dev, NULL, flags: GPIOD_IN); |
265 | if (IS_ERR(ptr: as5011->button_gpiod)) { |
266 | error = PTR_ERR(ptr: as5011->button_gpiod); |
267 | dev_err(&client->dev, "Failed to request button GPIO\n" ); |
268 | goto err_free_mem; |
269 | } |
270 | gpiod_set_consumer_name(desc: as5011->button_gpiod, name: "AS5011 button" ); |
271 | |
272 | irq = gpiod_to_irq(desc: as5011->button_gpiod); |
273 | if (irq < 0) { |
274 | dev_err(&client->dev, |
275 | "Failed to get irq number for button gpio\n" ); |
276 | error = irq; |
277 | goto err_free_mem; |
278 | } |
279 | |
280 | as5011->button_irq = irq; |
281 | |
282 | error = request_threaded_irq(irq: as5011->button_irq, |
283 | NULL, thread_fn: as5011_button_interrupt, |
284 | IRQF_TRIGGER_RISING | |
285 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
286 | name: "as5011_button" , dev: as5011); |
287 | if (error < 0) { |
288 | dev_err(&client->dev, |
289 | "Can't allocate button irq %d\n" , as5011->button_irq); |
290 | goto err_free_mem; |
291 | } |
292 | |
293 | error = as5011_configure_chip(as5011, plat_dat: plat_data); |
294 | if (error) |
295 | goto err_free_button_irq; |
296 | |
297 | error = request_threaded_irq(irq: as5011->axis_irq, NULL, |
298 | thread_fn: as5011_axis_interrupt, |
299 | flags: plat_data->axis_irqflags | IRQF_ONESHOT, |
300 | name: "as5011_joystick" , dev: as5011); |
301 | if (error) { |
302 | dev_err(&client->dev, |
303 | "Can't allocate axis irq %d\n" , plat_data->axis_irq); |
304 | goto err_free_button_irq; |
305 | } |
306 | |
307 | error = input_register_device(as5011->input_dev); |
308 | if (error) { |
309 | dev_err(&client->dev, "Failed to register input device\n" ); |
310 | goto err_free_axis_irq; |
311 | } |
312 | |
313 | i2c_set_clientdata(client, data: as5011); |
314 | |
315 | return 0; |
316 | |
317 | err_free_axis_irq: |
318 | free_irq(as5011->axis_irq, as5011); |
319 | err_free_button_irq: |
320 | free_irq(as5011->button_irq, as5011); |
321 | err_free_mem: |
322 | input_free_device(dev: input_dev); |
323 | kfree(objp: as5011); |
324 | |
325 | return error; |
326 | } |
327 | |
328 | static void as5011_remove(struct i2c_client *client) |
329 | { |
330 | struct as5011_device *as5011 = i2c_get_clientdata(client); |
331 | |
332 | free_irq(as5011->axis_irq, as5011); |
333 | free_irq(as5011->button_irq, as5011); |
334 | |
335 | input_unregister_device(as5011->input_dev); |
336 | kfree(objp: as5011); |
337 | } |
338 | |
339 | static const struct i2c_device_id as5011_id[] = { |
340 | { MODULE_DEVICE_ALIAS, 0 }, |
341 | { } |
342 | }; |
343 | MODULE_DEVICE_TABLE(i2c, as5011_id); |
344 | |
345 | static struct i2c_driver as5011_driver = { |
346 | .driver = { |
347 | .name = "as5011" , |
348 | }, |
349 | .probe = as5011_probe, |
350 | .remove = as5011_remove, |
351 | .id_table = as5011_id, |
352 | }; |
353 | |
354 | module_i2c_driver(as5011_driver); |
355 | |