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