xref: /openbmc/linux/drivers/input/mouse/hgpk.c (revision b7802c5c1ea9563f3746bea09c214ccedc8600f4)
1df08ef27SAndres Salomon /*
2df08ef27SAndres Salomon  * OLPC HGPK (XO-1) touchpad PS/2 mouse driver
3df08ef27SAndres Salomon  *
4df08ef27SAndres Salomon  * Copyright (c) 2006-2008 One Laptop Per Child
5df08ef27SAndres Salomon  * Authors:
6df08ef27SAndres Salomon  *   Zephaniah E. Hull
7df08ef27SAndres Salomon  *   Andres Salomon <dilinger@debian.org>
8df08ef27SAndres Salomon  *
9df08ef27SAndres Salomon  * This driver is partly based on the ALPS driver, which is:
10df08ef27SAndres Salomon  *
11df08ef27SAndres Salomon  * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
12df08ef27SAndres Salomon  * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
13df08ef27SAndres Salomon  * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
14df08ef27SAndres Salomon  * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
15df08ef27SAndres Salomon  *
16df08ef27SAndres Salomon  * This program is free software; you can redistribute it and/or modify
17df08ef27SAndres Salomon  * it under the terms of the GNU General Public License version 2 as
18df08ef27SAndres Salomon  * published by the Free Software Foundation.
19df08ef27SAndres Salomon  */
20df08ef27SAndres Salomon 
21df08ef27SAndres Salomon /*
22df08ef27SAndres Salomon  * The spec from ALPS is available from
23df08ef27SAndres Salomon  * <http://wiki.laptop.org/go/Touch_Pad/Tablet>.  It refers to this
24df08ef27SAndres Salomon  * device as HGPK (Hybrid GS, PT, and Keymatrix).
25df08ef27SAndres Salomon  *
26df08ef27SAndres Salomon  * The earliest versions of the device had simultaneous reporting; that
27df08ef27SAndres Salomon  * was removed.  After that, the device used the Advanced Mode GS/PT streaming
28df08ef27SAndres Salomon  * stuff.  That turned out to be too buggy to support, so we've finally
29df08ef27SAndres Salomon  * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad).
30df08ef27SAndres Salomon  */
31df08ef27SAndres Salomon 
32df08ef27SAndres Salomon #define DEBUG
33df08ef27SAndres Salomon #include <linux/input.h>
34df08ef27SAndres Salomon #include <linux/serio.h>
35df08ef27SAndres Salomon #include <linux/libps2.h>
36df08ef27SAndres Salomon #include <linux/delay.h>
37df08ef27SAndres Salomon #include <asm/olpc.h>
38df08ef27SAndres Salomon 
39df08ef27SAndres Salomon #include "psmouse.h"
40df08ef27SAndres Salomon #include "hgpk.h"
41df08ef27SAndres Salomon 
42df08ef27SAndres Salomon static int tpdebug;
43df08ef27SAndres Salomon module_param(tpdebug, int, 0644);
44df08ef27SAndres Salomon MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
45df08ef27SAndres Salomon 
46df08ef27SAndres Salomon static int recalib_delta = 100;
47df08ef27SAndres Salomon module_param(recalib_delta, int, 0644);
48df08ef27SAndres Salomon MODULE_PARM_DESC(recalib_delta,
49df08ef27SAndres Salomon 	"packets containing a delta this large will cause a recalibration.");
50df08ef27SAndres Salomon 
518bbf2703SPaul Fox static int jumpy_delay = 1000;
528bbf2703SPaul Fox module_param(jumpy_delay, int, 0644);
538bbf2703SPaul Fox MODULE_PARM_DESC(jumpy_delay,
548bbf2703SPaul Fox 	"delay (ms) before recal after jumpiness detected");
558bbf2703SPaul Fox 
568bbf2703SPaul Fox static int spew_delay = 1000;
578bbf2703SPaul Fox module_param(spew_delay, int, 0644);
588bbf2703SPaul Fox MODULE_PARM_DESC(spew_delay,
598bbf2703SPaul Fox 	"delay (ms) before recal after packet spew detected");
608bbf2703SPaul Fox 
618bbf2703SPaul Fox static int recal_guard_time = 2000;
628bbf2703SPaul Fox module_param(recal_guard_time, int, 0644);
638bbf2703SPaul Fox MODULE_PARM_DESC(recal_guard_time,
648bbf2703SPaul Fox 	"interval (ms) during which recal will be restarted if packet received");
658bbf2703SPaul Fox 
668bbf2703SPaul Fox static int post_interrupt_delay = 1000;
678bbf2703SPaul Fox module_param(post_interrupt_delay, int, 0644);
688bbf2703SPaul Fox MODULE_PARM_DESC(post_interrupt_delay,
698bbf2703SPaul Fox 	"delay (ms) before recal after recal interrupt detected");
708bbf2703SPaul Fox 
718bbf2703SPaul Fox static int autorecal = 1;
728bbf2703SPaul Fox module_param(autorecal, int, 0644);
738bbf2703SPaul Fox MODULE_PARM_DESC(autorecal, "enable recalibration in the driver");
748bbf2703SPaul Fox 
75df08ef27SAndres Salomon /*
76df08ef27SAndres Salomon  * When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
77df08ef27SAndres Salomon  * above the pad and still have it send packets.  This causes a jump cursor
78df08ef27SAndres Salomon  * when one places their finger on the pad.  We can probably detect the
79df08ef27SAndres Salomon  * jump as we see a large deltas (>= 100px).  In mouse mode, I've been
80df08ef27SAndres Salomon  * unable to even come close to 100px deltas during normal usage, so I think
81df08ef27SAndres Salomon  * this threshold is safe.  If a large delta occurs, trigger a recalibration.
82df08ef27SAndres Salomon  */
83df08ef27SAndres Salomon static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
84df08ef27SAndres Salomon {
85df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
86df08ef27SAndres Salomon 
87df08ef27SAndres Salomon 	if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
88df08ef27SAndres Salomon 		hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
89df08ef27SAndres Salomon 				recalib_delta, x, y);
90df08ef27SAndres Salomon 		/* My car gets forty rods to the hogshead and that's the
91df08ef27SAndres Salomon 		 * way I likes it! */
92df08ef27SAndres Salomon 		psmouse_queue_work(psmouse, &priv->recalib_wq,
938bbf2703SPaul Fox 				msecs_to_jiffies(jumpy_delay));
94df08ef27SAndres Salomon 	}
95df08ef27SAndres Salomon }
96df08ef27SAndres Salomon 
97df08ef27SAndres Salomon /*
98df08ef27SAndres Salomon  * We have no idea why this particular hardware bug occurs.  The touchpad
99df08ef27SAndres Salomon  * will randomly start spewing packets without anything touching the
100df08ef27SAndres Salomon  * pad.  This wouldn't necessarily be bad, but it's indicative of a
101df08ef27SAndres Salomon  * severely miscalibrated pad; attempting to use the touchpad while it's
102df08ef27SAndres Salomon  * spewing means the cursor will jump all over the place, and act "drunk".
103df08ef27SAndres Salomon  *
104df08ef27SAndres Salomon  * The packets that are spewed tend to all have deltas between -2 and 2, and
105df08ef27SAndres Salomon  * the cursor will move around without really going very far.  It will
106df08ef27SAndres Salomon  * tend to end up in the same location; if we tally up the changes over
107df08ef27SAndres Salomon  * 100 packets, we end up w/ a final delta of close to 0.  This happens
108df08ef27SAndres Salomon  * pretty regularly when the touchpad is spewing, and is pretty hard to
109df08ef27SAndres Salomon  * manually trigger (at least for *my* fingers).  So, it makes a perfect
110df08ef27SAndres Salomon  * scheme for detecting spews.
111df08ef27SAndres Salomon  */
112df08ef27SAndres Salomon static void hgpk_spewing_hack(struct psmouse *psmouse,
113df08ef27SAndres Salomon 			      int l, int r, int x, int y)
114df08ef27SAndres Salomon {
115df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
116df08ef27SAndres Salomon 
117df08ef27SAndres Salomon 	/* ignore button press packets; many in a row could trigger
118df08ef27SAndres Salomon 	 * a false-positive! */
119df08ef27SAndres Salomon 	if (l || r)
120df08ef27SAndres Salomon 		return;
121df08ef27SAndres Salomon 
122df08ef27SAndres Salomon 	priv->x_tally += x;
123df08ef27SAndres Salomon 	priv->y_tally += y;
124df08ef27SAndres Salomon 
125df08ef27SAndres Salomon 	if (++priv->count > 100) {
126df08ef27SAndres Salomon 		if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
127df08ef27SAndres Salomon 			hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
128df08ef27SAndres Salomon 				 priv->x_tally, priv->y_tally);
129df08ef27SAndres Salomon 			psmouse_queue_work(psmouse, &priv->recalib_wq,
1308bbf2703SPaul Fox 					   msecs_to_jiffies(spew_delay));
131df08ef27SAndres Salomon 		}
132df08ef27SAndres Salomon 		/* reset every 100 packets */
133df08ef27SAndres Salomon 		priv->count = 0;
134df08ef27SAndres Salomon 		priv->x_tally = 0;
135df08ef27SAndres Salomon 		priv->y_tally = 0;
136df08ef27SAndres Salomon 	}
137df08ef27SAndres Salomon }
138df08ef27SAndres Salomon 
139df08ef27SAndres Salomon /*
140df08ef27SAndres Salomon  * HGPK Mouse Mode format (standard mouse format, sans middle button)
141df08ef27SAndres Salomon  *
142df08ef27SAndres Salomon  * byte 0:	y-over	x-over	y-neg	x-neg	1	0	swr	swl
143df08ef27SAndres Salomon  * byte 1:	x7	x6	x5	x4	x3	x2	x1	x0
144df08ef27SAndres Salomon  * byte 2:	y7	y6	y5	y4	y3	y2	y1	y0
145df08ef27SAndres Salomon  *
146df08ef27SAndres Salomon  * swr/swl are the left/right buttons.
147df08ef27SAndres Salomon  * x-neg/y-neg are the x and y delta negative bits
148df08ef27SAndres Salomon  * x-over/y-over are the x and y overflow bits
149df08ef27SAndres Salomon  */
150df08ef27SAndres Salomon static int hgpk_validate_byte(unsigned char *packet)
151df08ef27SAndres Salomon {
1525fb17fd9SAndres Salomon 	return (packet[0] & 0x0C) != 0x08;
153df08ef27SAndres Salomon }
154df08ef27SAndres Salomon 
155df08ef27SAndres Salomon static void hgpk_process_packet(struct psmouse *psmouse)
156df08ef27SAndres Salomon {
157df08ef27SAndres Salomon 	struct input_dev *dev = psmouse->dev;
158df08ef27SAndres Salomon 	unsigned char *packet = psmouse->packet;
159df08ef27SAndres Salomon 	int x, y, left, right;
160df08ef27SAndres Salomon 
161df08ef27SAndres Salomon 	left = packet[0] & 1;
162df08ef27SAndres Salomon 	right = (packet[0] >> 1) & 1;
163df08ef27SAndres Salomon 
164df08ef27SAndres Salomon 	x = packet[1] - ((packet[0] << 4) & 0x100);
165df08ef27SAndres Salomon 	y = ((packet[0] << 3) & 0x100) - packet[2];
166df08ef27SAndres Salomon 
167df08ef27SAndres Salomon 	hgpk_jumpy_hack(psmouse, x, y);
168df08ef27SAndres Salomon 	hgpk_spewing_hack(psmouse, left, right, x, y);
169df08ef27SAndres Salomon 
170df08ef27SAndres Salomon 	if (tpdebug)
171df08ef27SAndres Salomon 		hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
172df08ef27SAndres Salomon 
173df08ef27SAndres Salomon 	input_report_key(dev, BTN_LEFT, left);
174df08ef27SAndres Salomon 	input_report_key(dev, BTN_RIGHT, right);
175df08ef27SAndres Salomon 
176df08ef27SAndres Salomon 	input_report_rel(dev, REL_X, x);
177df08ef27SAndres Salomon 	input_report_rel(dev, REL_Y, y);
178df08ef27SAndres Salomon 
179df08ef27SAndres Salomon 	input_sync(dev);
180df08ef27SAndres Salomon }
181df08ef27SAndres Salomon 
182df08ef27SAndres Salomon static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
183df08ef27SAndres Salomon {
184df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
185df08ef27SAndres Salomon 
186df08ef27SAndres Salomon 	if (hgpk_validate_byte(psmouse->packet)) {
187df08ef27SAndres Salomon 		hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n",
188df08ef27SAndres Salomon 				__func__, psmouse->pktcnt, psmouse->packet[0],
189df08ef27SAndres Salomon 				psmouse->packet[1], psmouse->packet[2]);
190df08ef27SAndres Salomon 		return PSMOUSE_BAD_DATA;
191df08ef27SAndres Salomon 	}
192df08ef27SAndres Salomon 
193df08ef27SAndres Salomon 	if (psmouse->pktcnt >= psmouse->pktsize) {
194df08ef27SAndres Salomon 		hgpk_process_packet(psmouse);
195df08ef27SAndres Salomon 		return PSMOUSE_FULL_PACKET;
196df08ef27SAndres Salomon 	}
197df08ef27SAndres Salomon 
198df08ef27SAndres Salomon 	if (priv->recalib_window) {
199df08ef27SAndres Salomon 		if (time_before(jiffies, priv->recalib_window)) {
200df08ef27SAndres Salomon 			/*
201df08ef27SAndres Salomon 			 * ugh, got a packet inside our recalibration
202df08ef27SAndres Salomon 			 * window, schedule another recalibration.
203df08ef27SAndres Salomon 			 */
204df08ef27SAndres Salomon 			hgpk_dbg(psmouse,
205df08ef27SAndres Salomon 				 "packet inside calibration window, "
206df08ef27SAndres Salomon 				 "queueing another recalibration\n");
207df08ef27SAndres Salomon 			psmouse_queue_work(psmouse, &priv->recalib_wq,
2088bbf2703SPaul Fox 					msecs_to_jiffies(post_interrupt_delay));
209df08ef27SAndres Salomon 		}
210df08ef27SAndres Salomon 		priv->recalib_window = 0;
211df08ef27SAndres Salomon 	}
212df08ef27SAndres Salomon 
213df08ef27SAndres Salomon 	return PSMOUSE_GOOD_DATA;
214df08ef27SAndres Salomon }
215df08ef27SAndres Salomon 
216df08ef27SAndres Salomon static int hgpk_force_recalibrate(struct psmouse *psmouse)
217df08ef27SAndres Salomon {
218df08ef27SAndres Salomon 	struct ps2dev *ps2dev = &psmouse->ps2dev;
219df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
220df08ef27SAndres Salomon 
221df08ef27SAndres Salomon 	/* C-series touchpads added the recalibrate command */
222df08ef27SAndres Salomon 	if (psmouse->model < HGPK_MODEL_C)
223df08ef27SAndres Salomon 		return 0;
224df08ef27SAndres Salomon 
225df08ef27SAndres Salomon 	/* we don't want to race with the irq handler, nor with resyncs */
226df08ef27SAndres Salomon 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
227df08ef27SAndres Salomon 
228df08ef27SAndres Salomon 	/* start by resetting the device */
229df08ef27SAndres Salomon 	psmouse_reset(psmouse);
230df08ef27SAndres Salomon 
231df08ef27SAndres Salomon 	/* send the recalibrate request */
232df08ef27SAndres Salomon 	if (ps2_command(ps2dev, NULL, 0xf5) ||
233df08ef27SAndres Salomon 	    ps2_command(ps2dev, NULL, 0xf5) ||
234df08ef27SAndres Salomon 	    ps2_command(ps2dev, NULL, 0xe6) ||
235df08ef27SAndres Salomon 	    ps2_command(ps2dev, NULL, 0xf5)) {
236df08ef27SAndres Salomon 		return -1;
237df08ef27SAndres Salomon 	}
238df08ef27SAndres Salomon 
239df08ef27SAndres Salomon 	/* according to ALPS, 150mS is required for recalibration */
240df08ef27SAndres Salomon 	msleep(150);
241df08ef27SAndres Salomon 
242df08ef27SAndres Salomon 	/* XXX: If a finger is down during this delay, recalibration will
243df08ef27SAndres Salomon 	 * detect capacitance incorrectly.  This is a hardware bug, and
244df08ef27SAndres Salomon 	 * we don't have a good way to deal with it.  The 2s window stuff
245df08ef27SAndres Salomon 	 * (below) is our best option for now.
246df08ef27SAndres Salomon 	 */
247df08ef27SAndres Salomon 
248df08ef27SAndres Salomon 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
249df08ef27SAndres Salomon 		return -1;
250df08ef27SAndres Salomon 
251df08ef27SAndres Salomon 	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
252df08ef27SAndres Salomon 
253df08ef27SAndres Salomon 	/* After we recalibrate, we shouldn't get any packets for 2s.  If
254df08ef27SAndres Salomon 	 * we do, it's likely that someone's finger was on the touchpad.
255df08ef27SAndres Salomon 	 * If someone's finger *was* on the touchpad, it's probably
256df08ef27SAndres Salomon 	 * miscalibrated.  So, we should schedule another recalibration
257df08ef27SAndres Salomon 	 */
2588bbf2703SPaul Fox 	priv->recalib_window = jiffies +  msecs_to_jiffies(recal_guard_time);
259df08ef27SAndres Salomon 
260df08ef27SAndres Salomon 	return 0;
261df08ef27SAndres Salomon }
262df08ef27SAndres Salomon 
263df08ef27SAndres Salomon /*
264df08ef27SAndres Salomon  * This kills power to the touchpad; according to ALPS, current consumption
265df08ef27SAndres Salomon  * goes down to 50uA after running this.  To turn power back on, we drive
266df08ef27SAndres Salomon  * MS-DAT low.
267df08ef27SAndres Salomon  */
268df08ef27SAndres Salomon static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
269df08ef27SAndres Salomon {
270df08ef27SAndres Salomon 	struct ps2dev *ps2dev = &psmouse->ps2dev;
271df08ef27SAndres Salomon 	int timeo;
272df08ef27SAndres Salomon 
273df08ef27SAndres Salomon 	/* Added on D-series touchpads */
274df08ef27SAndres Salomon 	if (psmouse->model < HGPK_MODEL_D)
275df08ef27SAndres Salomon 		return 0;
276df08ef27SAndres Salomon 
277df08ef27SAndres Salomon 	if (enable) {
278df08ef27SAndres Salomon 		psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
279df08ef27SAndres Salomon 
280df08ef27SAndres Salomon 		/*
281df08ef27SAndres Salomon 		 * Sending a byte will drive MS-DAT low; this will wake up
282df08ef27SAndres Salomon 		 * the controller.  Once we get an ACK back from it, it
283df08ef27SAndres Salomon 		 * means we can continue with the touchpad re-init.  ALPS
284df08ef27SAndres Salomon 		 * tells us that 1s should be long enough, so set that as
285df08ef27SAndres Salomon 		 * the upper bound.
286df08ef27SAndres Salomon 		 */
287df08ef27SAndres Salomon 		for (timeo = 20; timeo > 0; timeo--) {
288df08ef27SAndres Salomon 			if (!ps2_sendbyte(&psmouse->ps2dev,
289df08ef27SAndres Salomon 					PSMOUSE_CMD_DISABLE, 20))
290df08ef27SAndres Salomon 				break;
291df08ef27SAndres Salomon 			msleep(50);
292df08ef27SAndres Salomon 		}
293df08ef27SAndres Salomon 
294df08ef27SAndres Salomon 		psmouse_reset(psmouse);
295df08ef27SAndres Salomon 
296df08ef27SAndres Salomon 		/* should be all set, enable the touchpad */
297df08ef27SAndres Salomon 		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
298df08ef27SAndres Salomon 		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
299df08ef27SAndres Salomon 
300df08ef27SAndres Salomon 	} else {
301df08ef27SAndres Salomon 		hgpk_dbg(psmouse, "Powering off touchpad.\n");
302df08ef27SAndres Salomon 		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
303df08ef27SAndres Salomon 
304df08ef27SAndres Salomon 		if (ps2_command(ps2dev, NULL, 0xec) ||
305df08ef27SAndres Salomon 		    ps2_command(ps2dev, NULL, 0xec) ||
306df08ef27SAndres Salomon 		    ps2_command(ps2dev, NULL, 0xea)) {
307df08ef27SAndres Salomon 			return -1;
308df08ef27SAndres Salomon 		}
309df08ef27SAndres Salomon 
310df08ef27SAndres Salomon 		/* probably won't see an ACK, the touchpad will be off */
311df08ef27SAndres Salomon 		ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
312df08ef27SAndres Salomon 	}
313df08ef27SAndres Salomon 
314df08ef27SAndres Salomon 	return 0;
315df08ef27SAndres Salomon }
316df08ef27SAndres Salomon 
317df08ef27SAndres Salomon static int hgpk_poll(struct psmouse *psmouse)
318df08ef27SAndres Salomon {
319df08ef27SAndres Salomon 	/* We can't poll, so always return failure. */
320df08ef27SAndres Salomon 	return -1;
321df08ef27SAndres Salomon }
322df08ef27SAndres Salomon 
323df08ef27SAndres Salomon static int hgpk_reconnect(struct psmouse *psmouse)
324df08ef27SAndres Salomon {
325df08ef27SAndres Salomon 	/* During suspend/resume the ps2 rails remain powered.  We don't want
326df08ef27SAndres Salomon 	 * to do a reset because it's flush data out of buffers; however,
327df08ef27SAndres Salomon 	 * earlier prototypes (B1) had some brokenness that required a reset. */
328df08ef27SAndres Salomon 	if (olpc_board_at_least(olpc_board(0xb2)))
329df08ef27SAndres Salomon 		if (psmouse->ps2dev.serio->dev.power.power_state.event !=
330df08ef27SAndres Salomon 				PM_EVENT_ON)
331df08ef27SAndres Salomon 			return 0;
332df08ef27SAndres Salomon 
333df08ef27SAndres Salomon 	psmouse_reset(psmouse);
334df08ef27SAndres Salomon 
335df08ef27SAndres Salomon 	return 0;
336df08ef27SAndres Salomon }
337df08ef27SAndres Salomon 
338df08ef27SAndres Salomon static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
339df08ef27SAndres Salomon {
340df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
341df08ef27SAndres Salomon 
342df08ef27SAndres Salomon 	return sprintf(buf, "%d\n", priv->powered);
343df08ef27SAndres Salomon }
344df08ef27SAndres Salomon 
345df08ef27SAndres Salomon static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
346df08ef27SAndres Salomon 				const char *buf, size_t count)
347df08ef27SAndres Salomon {
348df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
349df08ef27SAndres Salomon 	unsigned long value;
350df08ef27SAndres Salomon 	int err;
351df08ef27SAndres Salomon 
352df08ef27SAndres Salomon 	err = strict_strtoul(buf, 10, &value);
353df08ef27SAndres Salomon 	if (err || value > 1)
354df08ef27SAndres Salomon 		return -EINVAL;
355df08ef27SAndres Salomon 
356df08ef27SAndres Salomon 	if (value != priv->powered) {
357df08ef27SAndres Salomon 		/*
358df08ef27SAndres Salomon 		 * hgpk_toggle_power will deal w/ state so
359df08ef27SAndres Salomon 		 * we're not racing w/ irq
360df08ef27SAndres Salomon 		 */
361df08ef27SAndres Salomon 		err = hgpk_toggle_power(psmouse, value);
362df08ef27SAndres Salomon 		if (!err)
363df08ef27SAndres Salomon 			priv->powered = value;
364df08ef27SAndres Salomon 	}
365df08ef27SAndres Salomon 
366df08ef27SAndres Salomon 	return err ? err : count;
367df08ef27SAndres Salomon }
368df08ef27SAndres Salomon 
369df08ef27SAndres Salomon __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
370*b7802c5cSDmitry Torokhov 		      hgpk_show_powered, hgpk_set_powered, false);
371df08ef27SAndres Salomon 
372c46dd1ebSPaul Fox static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
373c46dd1ebSPaul Fox 		void *data, char *buf)
374c46dd1ebSPaul Fox {
375c46dd1ebSPaul Fox 	return -EINVAL;
376c46dd1ebSPaul Fox }
377c46dd1ebSPaul Fox 
378c46dd1ebSPaul Fox static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
379c46dd1ebSPaul Fox 				const char *buf, size_t count)
380c46dd1ebSPaul Fox {
381c46dd1ebSPaul Fox 	struct hgpk_data *priv = psmouse->private;
382c46dd1ebSPaul Fox 	unsigned long value;
383c46dd1ebSPaul Fox 	int err;
384c46dd1ebSPaul Fox 
385c46dd1ebSPaul Fox 	err = strict_strtoul(buf, 10, &value);
386c46dd1ebSPaul Fox 	if (err || value != 1)
387c46dd1ebSPaul Fox 		return -EINVAL;
388c46dd1ebSPaul Fox 
389c46dd1ebSPaul Fox 	/*
390c46dd1ebSPaul Fox 	 * We queue work instead of doing recalibration right here
391c46dd1ebSPaul Fox 	 * to avoid adding locking to to hgpk_force_recalibrate()
392c46dd1ebSPaul Fox 	 * since workqueue provides serialization.
393c46dd1ebSPaul Fox 	 */
394c46dd1ebSPaul Fox 	psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
395c46dd1ebSPaul Fox 	return count;
396c46dd1ebSPaul Fox }
397c46dd1ebSPaul Fox 
398c46dd1ebSPaul Fox __PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL,
399*b7802c5cSDmitry Torokhov 		      hgpk_trigger_recal_show, hgpk_trigger_recal, false);
400c46dd1ebSPaul Fox 
401df08ef27SAndres Salomon static void hgpk_disconnect(struct psmouse *psmouse)
402df08ef27SAndres Salomon {
403df08ef27SAndres Salomon 	struct hgpk_data *priv = psmouse->private;
404df08ef27SAndres Salomon 
405df08ef27SAndres Salomon 	device_remove_file(&psmouse->ps2dev.serio->dev,
406df08ef27SAndres Salomon 			   &psmouse_attr_powered.dattr);
407c46dd1ebSPaul Fox 
408c46dd1ebSPaul Fox 	if (psmouse->model >= HGPK_MODEL_C)
409c46dd1ebSPaul Fox 		device_remove_file(&psmouse->ps2dev.serio->dev,
410c46dd1ebSPaul Fox 				   &psmouse_attr_recalibrate.dattr);
411c46dd1ebSPaul Fox 
412df08ef27SAndres Salomon 	psmouse_reset(psmouse);
413df08ef27SAndres Salomon 	kfree(priv);
414df08ef27SAndres Salomon }
415df08ef27SAndres Salomon 
416df08ef27SAndres Salomon static void hgpk_recalib_work(struct work_struct *work)
417df08ef27SAndres Salomon {
418bf6aede7SJean Delvare 	struct delayed_work *w = to_delayed_work(work);
419df08ef27SAndres Salomon 	struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
420df08ef27SAndres Salomon 	struct psmouse *psmouse = priv->psmouse;
421df08ef27SAndres Salomon 
422df08ef27SAndres Salomon 	hgpk_dbg(psmouse, "recalibrating touchpad..\n");
423df08ef27SAndres Salomon 
424df08ef27SAndres Salomon 	if (hgpk_force_recalibrate(psmouse))
425df08ef27SAndres Salomon 		hgpk_err(psmouse, "recalibration failed!\n");
426df08ef27SAndres Salomon }
427df08ef27SAndres Salomon 
428df08ef27SAndres Salomon static int hgpk_register(struct psmouse *psmouse)
429df08ef27SAndres Salomon {
430df08ef27SAndres Salomon 	struct input_dev *dev = psmouse->dev;
431df08ef27SAndres Salomon 	int err;
432df08ef27SAndres Salomon 
433df08ef27SAndres Salomon 	/* unset the things that psmouse-base sets which we don't have */
434df08ef27SAndres Salomon 	__clear_bit(BTN_MIDDLE, dev->keybit);
435df08ef27SAndres Salomon 
436df08ef27SAndres Salomon 	/* set the things we do have */
437df08ef27SAndres Salomon 	__set_bit(EV_KEY, dev->evbit);
438df08ef27SAndres Salomon 	__set_bit(EV_REL, dev->evbit);
439df08ef27SAndres Salomon 
440df08ef27SAndres Salomon 	__set_bit(REL_X, dev->relbit);
441df08ef27SAndres Salomon 	__set_bit(REL_Y, dev->relbit);
442df08ef27SAndres Salomon 
443df08ef27SAndres Salomon 	__set_bit(BTN_LEFT, dev->keybit);
444df08ef27SAndres Salomon 	__set_bit(BTN_RIGHT, dev->keybit);
445df08ef27SAndres Salomon 
446df08ef27SAndres Salomon 	/* register handlers */
447df08ef27SAndres Salomon 	psmouse->protocol_handler = hgpk_process_byte;
448df08ef27SAndres Salomon 	psmouse->poll = hgpk_poll;
449df08ef27SAndres Salomon 	psmouse->disconnect = hgpk_disconnect;
450df08ef27SAndres Salomon 	psmouse->reconnect = hgpk_reconnect;
451df08ef27SAndres Salomon 	psmouse->pktsize = 3;
452df08ef27SAndres Salomon 
453df08ef27SAndres Salomon 	/* Disable the idle resync. */
454df08ef27SAndres Salomon 	psmouse->resync_time = 0;
455df08ef27SAndres Salomon 	/* Reset after a lot of bad bytes. */
456df08ef27SAndres Salomon 	psmouse->resetafter = 1024;
457df08ef27SAndres Salomon 
458df08ef27SAndres Salomon 	err = device_create_file(&psmouse->ps2dev.serio->dev,
459df08ef27SAndres Salomon 				 &psmouse_attr_powered.dattr);
460c46dd1ebSPaul Fox 	if (err) {
461c46dd1ebSPaul Fox 		hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n");
462df08ef27SAndres Salomon 		return err;
463df08ef27SAndres Salomon 	}
464df08ef27SAndres Salomon 
465c46dd1ebSPaul Fox 	/* C-series touchpads added the recalibrate command */
466c46dd1ebSPaul Fox 	if (psmouse->model >= HGPK_MODEL_C) {
467c46dd1ebSPaul Fox 		err = device_create_file(&psmouse->ps2dev.serio->dev,
468c46dd1ebSPaul Fox 					 &psmouse_attr_recalibrate.dattr);
469c46dd1ebSPaul Fox 		if (err) {
470c46dd1ebSPaul Fox 			hgpk_err(psmouse,
471c46dd1ebSPaul Fox 				"Failed creating 'recalibrate' sysfs node\n");
472c46dd1ebSPaul Fox 			device_remove_file(&psmouse->ps2dev.serio->dev,
473c46dd1ebSPaul Fox 					&psmouse_attr_powered.dattr);
474c46dd1ebSPaul Fox 			return err;
475c46dd1ebSPaul Fox 		}
476c46dd1ebSPaul Fox 	}
477c46dd1ebSPaul Fox 
478c46dd1ebSPaul Fox 	return 0;
479c46dd1ebSPaul Fox }
480c46dd1ebSPaul Fox 
481df08ef27SAndres Salomon int hgpk_init(struct psmouse *psmouse)
482df08ef27SAndres Salomon {
483df08ef27SAndres Salomon 	struct hgpk_data *priv;
484df08ef27SAndres Salomon 	int err = -ENOMEM;
485df08ef27SAndres Salomon 
486df08ef27SAndres Salomon 	priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
487df08ef27SAndres Salomon 	if (!priv)
488df08ef27SAndres Salomon 		goto alloc_fail;
489df08ef27SAndres Salomon 
490df08ef27SAndres Salomon 	psmouse->private = priv;
491df08ef27SAndres Salomon 	priv->psmouse = psmouse;
492*b7802c5cSDmitry Torokhov 	priv->powered = true;
493df08ef27SAndres Salomon 	INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
494df08ef27SAndres Salomon 
495df08ef27SAndres Salomon 	err = psmouse_reset(psmouse);
496df08ef27SAndres Salomon 	if (err)
497df08ef27SAndres Salomon 		goto init_fail;
498df08ef27SAndres Salomon 
499df08ef27SAndres Salomon 	err = hgpk_register(psmouse);
500df08ef27SAndres Salomon 	if (err)
501df08ef27SAndres Salomon 		goto init_fail;
502df08ef27SAndres Salomon 
503df08ef27SAndres Salomon 	return 0;
504df08ef27SAndres Salomon 
505df08ef27SAndres Salomon init_fail:
506df08ef27SAndres Salomon 	kfree(priv);
507df08ef27SAndres Salomon alloc_fail:
508df08ef27SAndres Salomon 	return err;
509df08ef27SAndres Salomon }
510df08ef27SAndres Salomon 
511df08ef27SAndres Salomon static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
512df08ef27SAndres Salomon {
513df08ef27SAndres Salomon 	struct ps2dev *ps2dev = &psmouse->ps2dev;
514df08ef27SAndres Salomon 	unsigned char param[3];
515df08ef27SAndres Salomon 
516df08ef27SAndres Salomon 	/* E7, E7, E7, E9 gets us a 3 byte identifier */
517df08ef27SAndres Salomon 	if (ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
518df08ef27SAndres Salomon 	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
519df08ef27SAndres Salomon 	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
520df08ef27SAndres Salomon 	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
521df08ef27SAndres Salomon 		return -EIO;
522df08ef27SAndres Salomon 	}
523df08ef27SAndres Salomon 
5240f495481SAndy Whitcroft 	hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]);
525df08ef27SAndres Salomon 
526df08ef27SAndres Salomon 	/* HGPK signature: 0x67, 0x00, 0x<model> */
527df08ef27SAndres Salomon 	if (param[0] != 0x67 || param[1] != 0x00)
528df08ef27SAndres Salomon 		return -ENODEV;
529df08ef27SAndres Salomon 
530df08ef27SAndres Salomon 	hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
531df08ef27SAndres Salomon 
532df08ef27SAndres Salomon 	return param[2];
533df08ef27SAndres Salomon }
534df08ef27SAndres Salomon 
535*b7802c5cSDmitry Torokhov int hgpk_detect(struct psmouse *psmouse, bool set_properties)
536df08ef27SAndres Salomon {
537df08ef27SAndres Salomon 	int version;
538df08ef27SAndres Salomon 
539df08ef27SAndres Salomon 	version = hgpk_get_model(psmouse);
540df08ef27SAndres Salomon 	if (version < 0)
541df08ef27SAndres Salomon 		return version;
542df08ef27SAndres Salomon 
543df08ef27SAndres Salomon 	if (set_properties) {
544df08ef27SAndres Salomon 		psmouse->vendor = "ALPS";
545df08ef27SAndres Salomon 		psmouse->name = "HGPK";
546df08ef27SAndres Salomon 		psmouse->model = version;
547df08ef27SAndres Salomon 	}
548df08ef27SAndres Salomon 
549df08ef27SAndres Salomon 	return 0;
550df08ef27SAndres Salomon }
551