xref: /linux/drivers/input/misc/cma3000_d0x.c (revision e3966940559d52aa1800a008dcfeec218dd31f88)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * VTI CMA3000_D0x Accelerometer driver
4  *
5  * Copyright (C) 2010 Texas Instruments
6  * Author: Hemanth V <hemanthv@ti.com>
7  */
8 
9 #include <linux/export.h>
10 #include <linux/types.h>
11 #include <linux/interrupt.h>
12 #include <linux/delay.h>
13 #include <linux/slab.h>
14 #include <linux/input.h>
15 #include <linux/input/cma3000.h>
16 #include <linux/module.h>
17 
18 #include "cma3000_d0x.h"
19 
20 #define CMA3000_WHOAMI      0x00
21 #define CMA3000_REVID       0x01
22 #define CMA3000_CTRL        0x02
23 #define CMA3000_STATUS      0x03
24 #define CMA3000_RSTR        0x04
25 #define CMA3000_INTSTATUS   0x05
26 #define CMA3000_DOUTX       0x06
27 #define CMA3000_DOUTY       0x07
28 #define CMA3000_DOUTZ       0x08
29 #define CMA3000_MDTHR       0x09
30 #define CMA3000_MDFFTMR     0x0A
31 #define CMA3000_FFTHR       0x0B
32 
33 #define CMA3000_RANGE2G    (1 << 7)
34 #define CMA3000_RANGE8G    (0 << 7)
35 #define CMA3000_BUSI2C     (0 << 4)
36 #define CMA3000_MODEMASK   (7 << 1)
37 #define CMA3000_GRANGEMASK (1 << 7)
38 
39 #define CMA3000_STATUS_PERR    1
40 #define CMA3000_INTSTATUS_FFDET (1 << 2)
41 
42 /* Settling time delay in ms */
43 #define CMA3000_SETDELAY    30
44 
45 /* Delay for clearing interrupt in us */
46 #define CMA3000_INTDELAY    44
47 
48 
49 /*
50  * Bit weights in mg for bit 0, other bits need
51  * multiply factor 2^n. Eight bit is the sign bit.
52  */
53 #define BIT_TO_2G  18
54 #define BIT_TO_8G  71
55 
56 struct cma3000_accl_data {
57 	const struct cma3000_bus_ops *bus_ops;
58 	const struct cma3000_platform_data *pdata;
59 
60 	struct device *dev;
61 	struct input_dev *input_dev;
62 
63 	int bit_to_mg;
64 	int irq;
65 
66 	int g_range;
67 	u8 mode;
68 
69 	struct mutex mutex;
70 	bool opened;
71 	bool suspended;
72 };
73 
74 #define CMA3000_READ(data, reg, msg) \
75 	(data->bus_ops->read(data->dev, reg, msg))
76 #define CMA3000_SET(data, reg, val, msg) \
77 	((data)->bus_ops->write(data->dev, reg, val, msg))
78 
79 /*
80  * Conversion for each of the eight modes to g, depending
81  * on G range i.e 2G or 8G. Some modes always operate in
82  * 8G.
83  */
84 
85 static int mode_to_mg[8][2] = {
86 	{ 0, 0 },
87 	{ BIT_TO_8G, BIT_TO_2G },
88 	{ BIT_TO_8G, BIT_TO_2G },
89 	{ BIT_TO_8G, BIT_TO_8G },
90 	{ BIT_TO_8G, BIT_TO_8G },
91 	{ BIT_TO_8G, BIT_TO_2G },
92 	{ BIT_TO_8G, BIT_TO_2G },
93 	{ 0, 0},
94 };
95 
96 static void decode_mg(struct cma3000_accl_data *data, int *datax,
97 				int *datay, int *dataz)
98 {
99 	/* Data in 2's complement, convert to mg */
100 	*datax = ((s8)*datax) * data->bit_to_mg;
101 	*datay = ((s8)*datay) * data->bit_to_mg;
102 	*dataz = ((s8)*dataz) * data->bit_to_mg;
103 }
104 
105 static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
106 {
107 	struct cma3000_accl_data *data = dev_id;
108 	int datax, datay, dataz, intr_status;
109 	u8 ctrl, mode, range;
110 
111 	intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
112 	if (intr_status < 0)
113 		return IRQ_NONE;
114 
115 	/* Check if free fall is detected, report immediately */
116 	if (intr_status & CMA3000_INTSTATUS_FFDET) {
117 		input_report_abs(data->input_dev, ABS_MISC, 1);
118 		input_sync(data->input_dev);
119 	} else {
120 		input_report_abs(data->input_dev, ABS_MISC, 0);
121 	}
122 
123 	datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
124 	datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
125 	dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
126 
127 	ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
128 	mode = (ctrl & CMA3000_MODEMASK) >> 1;
129 	range = (ctrl & CMA3000_GRANGEMASK) >> 7;
130 
131 	data->bit_to_mg = mode_to_mg[mode][range];
132 
133 	/* Interrupt not for this device */
134 	if (data->bit_to_mg == 0)
135 		return IRQ_NONE;
136 
137 	/* Decode register values to milli g */
138 	decode_mg(data, &datax, &datay, &dataz);
139 
140 	input_report_abs(data->input_dev, ABS_X, datax);
141 	input_report_abs(data->input_dev, ABS_Y, datay);
142 	input_report_abs(data->input_dev, ABS_Z, dataz);
143 	input_sync(data->input_dev);
144 
145 	return IRQ_HANDLED;
146 }
147 
148 static int cma3000_reset(struct cma3000_accl_data *data)
149 {
150 	int val;
151 
152 	/* Reset sequence */
153 	CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
154 	CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
155 	CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
156 
157 	/* Settling time delay */
158 	mdelay(10);
159 
160 	val = CMA3000_READ(data, CMA3000_STATUS, "Status");
161 	if (val < 0) {
162 		dev_err(data->dev, "Reset failed\n");
163 		return val;
164 	}
165 
166 	if (val & CMA3000_STATUS_PERR) {
167 		dev_err(data->dev, "Parity Error\n");
168 		return -EIO;
169 	}
170 
171 	return 0;
172 }
173 
174 static int cma3000_poweron(struct cma3000_accl_data *data)
175 {
176 	const struct cma3000_platform_data *pdata = data->pdata;
177 	u8 ctrl = 0;
178 	int ret;
179 
180 	if (data->g_range == CMARANGE_2G) {
181 		ctrl = (data->mode << 1) | CMA3000_RANGE2G;
182 	} else if (data->g_range == CMARANGE_8G) {
183 		ctrl = (data->mode << 1) | CMA3000_RANGE8G;
184 	} else {
185 		dev_info(data->dev,
186 			 "Invalid G range specified, assuming 8G\n");
187 		ctrl = (data->mode << 1) | CMA3000_RANGE8G;
188 	}
189 
190 	ctrl |= data->bus_ops->ctrl_mod;
191 
192 	CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
193 		    "Motion Detect Threshold");
194 	CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
195 		    "Time register");
196 	CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
197 		    "Free fall threshold");
198 	ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
199 	if (ret < 0)
200 		return -EIO;
201 
202 	msleep(CMA3000_SETDELAY);
203 
204 	return 0;
205 }
206 
207 static int cma3000_poweroff(struct cma3000_accl_data *data)
208 {
209 	int ret;
210 
211 	ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
212 	msleep(CMA3000_SETDELAY);
213 
214 	return ret;
215 }
216 
217 static int cma3000_open(struct input_dev *input_dev)
218 {
219 	struct cma3000_accl_data *data = input_get_drvdata(input_dev);
220 
221 	guard(mutex)(&data->mutex);
222 
223 	if (!data->suspended)
224 		cma3000_poweron(data);
225 
226 	data->opened = true;
227 
228 	return 0;
229 }
230 
231 static void cma3000_close(struct input_dev *input_dev)
232 {
233 	struct cma3000_accl_data *data = input_get_drvdata(input_dev);
234 
235 	guard(mutex)(&data->mutex);
236 
237 	if (!data->suspended)
238 		cma3000_poweroff(data);
239 
240 	data->opened = false;
241 }
242 
243 void cma3000_suspend(struct cma3000_accl_data *data)
244 {
245 	guard(mutex)(&data->mutex);
246 
247 	if (!data->suspended && data->opened)
248 		cma3000_poweroff(data);
249 
250 	data->suspended = true;
251 }
252 EXPORT_SYMBOL(cma3000_suspend);
253 
254 
255 void cma3000_resume(struct cma3000_accl_data *data)
256 {
257 	guard(mutex)(&data->mutex);
258 
259 	if (data->suspended && data->opened)
260 		cma3000_poweron(data);
261 
262 	data->suspended = false;
263 }
264 EXPORT_SYMBOL(cma3000_resume);
265 
266 struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
267 				       const struct cma3000_bus_ops *bops)
268 {
269 	const struct cma3000_platform_data *pdata = dev_get_platdata(dev);
270 	struct cma3000_accl_data *data;
271 	struct input_dev *input_dev;
272 	int rev;
273 	int error;
274 
275 	if (!pdata) {
276 		dev_err(dev, "platform data not found\n");
277 		error = -EINVAL;
278 		goto err_out;
279 	}
280 
281 
282 	/* if no IRQ return error */
283 	if (irq == 0) {
284 		error = -EINVAL;
285 		goto err_out;
286 	}
287 
288 	data = kzalloc(sizeof(*data), GFP_KERNEL);
289 	input_dev = input_allocate_device();
290 	if (!data || !input_dev) {
291 		error = -ENOMEM;
292 		goto err_free_mem;
293 	}
294 
295 	data->dev = dev;
296 	data->input_dev = input_dev;
297 	data->bus_ops = bops;
298 	data->pdata = pdata;
299 	data->irq = irq;
300 	mutex_init(&data->mutex);
301 
302 	data->mode = pdata->mode;
303 	if (data->mode > CMAMODE_POFF) {
304 		data->mode = CMAMODE_MOTDET;
305 		dev_warn(dev,
306 			 "Invalid mode specified, assuming Motion Detect\n");
307 	}
308 
309 	data->g_range = pdata->g_range;
310 	if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
311 		dev_info(dev,
312 			 "Invalid G range specified, assuming 8G\n");
313 		data->g_range = CMARANGE_8G;
314 	}
315 
316 	input_dev->name = "cma3000-accelerometer";
317 	input_dev->id.bustype = bops->bustype;
318 	input_dev->open = cma3000_open;
319 	input_dev->close = cma3000_close;
320 
321 	input_set_abs_params(input_dev, ABS_X,
322 			-data->g_range, data->g_range, pdata->fuzz_x, 0);
323 	input_set_abs_params(input_dev, ABS_Y,
324 			-data->g_range, data->g_range, pdata->fuzz_y, 0);
325 	input_set_abs_params(input_dev, ABS_Z,
326 			-data->g_range, data->g_range, pdata->fuzz_z, 0);
327 	input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
328 
329 	input_set_drvdata(input_dev, data);
330 
331 	error = cma3000_reset(data);
332 	if (error)
333 		goto err_free_mem;
334 
335 	rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
336 	if (rev < 0) {
337 		error = rev;
338 		goto err_free_mem;
339 	}
340 
341 	pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
342 
343 	error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
344 				     pdata->irqflags | IRQF_ONESHOT,
345 				     "cma3000_d0x", data);
346 	if (error) {
347 		dev_err(dev, "request_threaded_irq failed\n");
348 		goto err_free_mem;
349 	}
350 
351 	error = input_register_device(data->input_dev);
352 	if (error) {
353 		dev_err(dev, "Unable to register input device\n");
354 		goto err_free_irq;
355 	}
356 
357 	return data;
358 
359 err_free_irq:
360 	free_irq(irq, data);
361 err_free_mem:
362 	input_free_device(input_dev);
363 	kfree(data);
364 err_out:
365 	return ERR_PTR(error);
366 }
367 EXPORT_SYMBOL(cma3000_init);
368 
369 void cma3000_exit(struct cma3000_accl_data *data)
370 {
371 	free_irq(data->irq, data);
372 	input_unregister_device(data->input_dev);
373 	kfree(data);
374 }
375 EXPORT_SYMBOL(cma3000_exit);
376 
377 MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
378 MODULE_LICENSE("GPL");
379 MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
380