1fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*-
2fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Finger Sensing Pad PS/2 mouse driver.
3fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
4fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
5a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
6fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
7fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   This program is free software; you can redistribute it and/or
8fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   modify it under the terms of the GNU General Public License
9fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   as published by the Free Software Foundation; either version 2
10fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   of the License, or (at your option) any later version.
11fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
12fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   This program is distributed in the hope that it will be useful,
13fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   GNU General Public License for more details.
16fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
17fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   You should have received a copy of the GNU General Public License
18fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   along with this program; if not, write to the Free Software
19fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
21fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
22fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/module.h>
23fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/input.h>
24a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang#include <linux/input/mt.h>
25fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/ctype.h>
26fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/libps2.h>
27fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/serio.h>
28fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include <linux/jiffies.h>
295a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
30fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
31fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include "psmouse.h"
32fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#include "sentelic.h"
33fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
34fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*
35fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Timeout for FSP PS/2 command only (in milliseconds).
36fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
37fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#define	FSP_CMD_TIMEOUT		200
38fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#define	FSP_CMD_TIMEOUT2	30
39fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
40727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa#define	GET_ABS_X(packet)	((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
41727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa#define	GET_ABS_Y(packet)	((packet[2] << 2) | (packet[3] & 0x03))
42727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa
43fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/** Driver version. */
44fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic const char fsp_drv_ver[] = "1.0.0-K";
45fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
46fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*
47fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Make sure that the value being sent to FSP will not conflict with
48fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * possible sample rate values.
49fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
50fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic unsigned char fsp_test_swap_cmd(unsigned char reg_val)
51fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
52fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	switch (reg_val) {
53fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	case 10: case 20: case 40: case 60: case 80: case 100: case 200:
54fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/*
55fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * The requested value being sent to FSP matched to possible
56fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * sample rates, swap the given value such that the hardware
57fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * wouldn't get confused.
58fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 */
59fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return (reg_val >> 4) | (reg_val << 4);
60fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	default:
61fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return reg_val;	/* swap isn't necessary */
62fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
63fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
64fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
65fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*
66fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Make sure that the value being sent to FSP will not conflict with certain
67fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * commands.
68fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
69fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic unsigned char fsp_test_invert_cmd(unsigned char reg_val)
70fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
71fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	switch (reg_val) {
72fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	case 0xe9: case 0xee: case 0xf2: case 0xff:
73fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/*
74fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * The requested value being sent to FSP matched to certain
75fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * commands, inverse the given value such that the hardware
76fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * wouldn't get confused.
77fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 */
78fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return ~reg_val;
79fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	default:
80fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return reg_val;	/* inversion isn't necessary */
81fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
82fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
83fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
84fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
85fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
86fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct ps2dev *ps2dev = &psmouse->ps2dev;
87fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char param[3];
88fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char addr;
89fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rc = -1;
90fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
91fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/*
92fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * We need to shut off the device and switch it into command
93fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * mode so we don't confuse our protocol handler. We don't need
94fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * to do that for writes because sysfs set helper does this for
95fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * us.
96fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 */
97c35c0e7d425c11f629d9d037df6c37a7ffebcd96Paul Fox	psmouse_deactivate(psmouse);
98181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
99181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
100fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
101fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
102fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
103fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
104fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* should return 0xfe(request for resending) */
105fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
106fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* should return 0xfc(failed) */
107fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
108fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
109fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
110fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
111fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
112fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
113fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);
114fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
115fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping is required */
116fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);
117fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* expect 0xfe */
118fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else {
119fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping isn't necessary */
120fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
121fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* expect 0xfe */
122fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
123fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* should return 0xfc(failed) */
124fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT);
125fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
126fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)
127fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
128fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
129fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	*reg_val = param[2];
130fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	rc = 0;
131fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
132fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang out:
133181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
134c35c0e7d425c11f629d9d037df6c37a7ffebcd96Paul Fox	psmouse_activate(psmouse);
1353ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	psmouse_dbg(psmouse,
1363ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
1373ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    reg_addr, *reg_val, rc);
138fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return rc;
139fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
140fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
141fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
142fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
143fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct ps2dev *ps2dev = &psmouse->ps2dev;
144fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char v;
145fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rc = -1;
146fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
147181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
148fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
149fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
150fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
151fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
152fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
153fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* inversion is required */
154fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2);
155fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else {
156fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
157fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			/* swapping is required */
158fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2);
159fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		} else {
160fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			/* swapping isn't necessary */
161fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2);
162fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		}
163fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
164fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* write the register address in correct order */
165fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
166fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
167fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
168d9bae67a7a91a6cc2e7a99d5ae72ada62abcc993Tai-hwa Liang		goto out;
169fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
170fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
171fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* inversion is required */
172fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
173fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
174fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping is required */
175fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
176fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else {
177fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping isn't necessary */
178fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
179fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
180fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
181fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* write the register value in correct order */
182fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
183fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	rc = 0;
184fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
185fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang out:
186181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
1873ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	psmouse_dbg(psmouse,
1883ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
1893ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    reg_addr, reg_val, rc);
190fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return rc;
191fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
192fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
193fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/* Enable register clock gating for writing certain registers */
194fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_reg_write_enable(struct psmouse *psmouse, bool enable)
195fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
196fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int v, nv;
197fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
198fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1)
199fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -1;
200fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
201fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (enable)
202fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		nv = v | FSP_BIT_EN_REG_CLK;
203fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	else
204fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		nv = v & ~FSP_BIT_EN_REG_CLK;
205fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
206fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* only write if necessary */
207fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (nv != v)
208fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1)
209fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			return -1;
210fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
211fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
212fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
213fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
214fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
215fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
216fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct ps2dev *ps2dev = &psmouse->ps2dev;
217fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char param[3];
218fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rc = -1;
219fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
220c35c0e7d425c11f629d9d037df6c37a7ffebcd96Paul Fox	psmouse_deactivate(psmouse);
221181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
222181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
223fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
224fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
225fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
226fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
227fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
228fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
229fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
230fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
231fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
232fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
233fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2);
234fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
235fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
236fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* get the returned result */
237fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
238fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
239fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
240fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	*reg_val = param[2];
241fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	rc = 0;
242fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
243fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang out:
244181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
245c35c0e7d425c11f629d9d037df6c37a7ffebcd96Paul Fox	psmouse_activate(psmouse);
2463ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	psmouse_dbg(psmouse,
2473ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    "READ PAGE REG: 0x%02x (rc = %d)\n",
2483ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    *reg_val, rc);
249fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return rc;
250fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
251fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
252fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
253fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
254fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct ps2dev *ps2dev = &psmouse->ps2dev;
255fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char v;
256fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rc = -1;
257fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
258181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
259fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
260fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
261fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto out;
262fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
263fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2);
264fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
265fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
266fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
267d9bae67a7a91a6cc2e7a99d5ae72ada62abcc993Tai-hwa Liang		goto out;
268fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
269fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
270fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
271fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
272fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping is required */
273fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
274fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else {
275fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* swapping isn't necessary */
276fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
277fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
278fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
279fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
280fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	rc = 0;
281fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
282fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang out:
283181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
2843ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	psmouse_dbg(psmouse,
2853ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
2863ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		    reg_val, rc);
287fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return rc;
288fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
289fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
290fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_get_version(struct psmouse *psmouse, int *version)
291fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
292fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_VERSION, version))
293fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
294fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
295fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
296fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
297fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
298fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_get_revision(struct psmouse *psmouse, int *rev)
299fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
300fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev))
301fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
302fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
303fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
304fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
305fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
306fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_get_buttons(struct psmouse *psmouse, int *btn)
307fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
308fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	static const int buttons[] = {
309fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */
310fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */
311fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		0x04, /* Left/Middle/Right & Scroll Up/Down */
312fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		0x02, /* Left/Middle/Right */
313fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	};
314fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int val;
315fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
3166ccbcf2cb41131f8d56ef0723bf3f7c1f8486076Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1)
317fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
318fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
319fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	*btn = buttons[(val & 0x30) >> 4];
320fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
321fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
322fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
323fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/* Enable on-pad command tag output */
324fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
325fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
326fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int v, nv;
327fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int res = 0;
328fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
329fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
3303ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		psmouse_err(psmouse, "Unable get OPC state.\n");
331fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
332fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
333fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
334fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (enable)
335fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		nv = v | FSP_BIT_EN_OPC_TAG;
336fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	else
337fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		nv = v & ~FSP_BIT_EN_OPC_TAG;
338fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
339fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* only write if necessary */
340fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (nv != v) {
341fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		fsp_reg_write_enable(psmouse, true);
342fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv);
343fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		fsp_reg_write_enable(psmouse, false);
344fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
345fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
346fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (res != 0) {
3473ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		psmouse_err(psmouse, "Unable to enable OPC tag.\n");
348fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		res = -EIO;
349fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
350fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
351fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return res;
352fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
353fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
354fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_onpad_vscr(struct psmouse *psmouse, bool enable)
355fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
356fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
357fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int val;
358fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
359fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
360fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
361fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
362fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	pad->vscroll = enable;
363fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
364fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (enable)
365fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE);
366fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	else
367fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		val &= ~FSP_BIT_FIX_VSCR;
368fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
369fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
370fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
371fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
372fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
373fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
374fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
375fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
376fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
377fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
378fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int val, v2;
379fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
380fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
381fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
382fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
383fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2))
384fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
385fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
386fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	pad->hscroll = enable;
387fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
388fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (enable) {
389fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE);
390fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		v2 |= FSP_BIT_EN_MSID6;
391fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	} else {
392fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		val &= ~FSP_BIT_FIX_HSCR;
393fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8);
394fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
395fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
396fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
397fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
398fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
399fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/* reconfigure horizontal scrolling packet output */
400fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2))
401fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
402fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
403fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
404fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
405fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
406fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*
407fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Write device specific initial parameters.
408fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
409fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * ex: 0xab 0xcd - write oxcd into register 0xab
410fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
411fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
412fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				   const char *buf, size_t count)
413fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
41476496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	int reg, val;
415fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	char *rest;
416fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ssize_t retval;
417fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
418fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	reg = simple_strtoul(buf, &rest, 16);
419fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (rest == buf || *rest != ' ' || reg > 0xff)
420fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
421fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
42276496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	retval = kstrtoint(rest + 1, 16, &val);
42376496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (retval)
42476496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding		return retval;
42576496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding
42676496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (val > 0xff)
427fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
428fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
429fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_write_enable(psmouse, true))
430fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
431fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
432fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count;
433fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
434fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_reg_write_enable(psmouse, false);
435fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
436fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
437fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
438fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
439fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
440fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
441fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_getreg(struct psmouse *psmouse,
442fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
443fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
444fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
445fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
446fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val);
447fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
448fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
449fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang/*
450fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * Read a register from device.
451fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang *
452fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang * ex: 0xab -- read content from register 0xab
453fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang */
454fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
455fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					const char *buf, size_t count)
456fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
457fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
45876496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	int reg, val, err;
45976496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding
46076496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	err = kstrtoint(buf, 16, &reg);
46176496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (err)
46276496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding		return err;
463fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
46476496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (reg > 0xff)
465fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
466fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
467fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, reg, &val))
468fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
469fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
470fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	pad->last_reg = reg;
471fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	pad->last_val = val;
472fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
473fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
474fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
475fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
476fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL,
477fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			fsp_attr_show_getreg, fsp_attr_set_getreg);
478fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
479fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
480fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
481fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
482fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int val = 0;
483fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
484fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_page_reg_read(psmouse, &val))
485fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
486fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
487fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "%02x\n", val);
488fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
489fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
490fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
491fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					const char *buf, size_t count)
492fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
49376496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	int val, err;
494fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
49576496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	err = kstrtoint(buf, 16, &val);
49676496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (err)
49776496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding		return err;
49876496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding
49976496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (val > 0xff)
500fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
501fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
502fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_page_reg_write(psmouse, val))
503fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
504fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
505fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
506fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
507fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
508fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL,
509fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			fsp_attr_show_pagereg, fsp_attr_set_pagereg);
510fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
511fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,
512fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
513fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
514fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
515fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
516fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "%d\n", pad->vscroll);
517fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
518fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
519fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
520fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					const char *buf, size_t count)
521fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
52276496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	unsigned int val;
52376496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	int err;
52476496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding
52576496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	err = kstrtouint(buf, 10, &val);
52676496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (err)
52776496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding		return err;
528fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
52976496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (val > 1)
530fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
531fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
532fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_onpad_vscr(psmouse, val);
533fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
534fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
535fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
536fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
537fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL,
538fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			fsp_attr_show_vscroll, fsp_attr_set_vscroll);
539fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
540fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,
541fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
542fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
543fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
544fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
545fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "%d\n", pad->hscroll);
546fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
547fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
548fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
549fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					const char *buf, size_t count)
550fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
55176496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	unsigned int val;
55276496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	int err;
55376496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding
55476496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	err = kstrtouint(buf, 10, &val);
55576496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (err)
55676496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding		return err;
557fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
55876496e7a02e99d42844f4fffa145b81e513e7acdJJ Ding	if (val > 1)
559fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EINVAL;
560fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
561fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_onpad_hscr(psmouse, val);
562fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
563fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
564fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
565fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
566fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL,
567fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			fsp_attr_show_hscroll, fsp_attr_set_hscroll);
568fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
569fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_flags(struct psmouse *psmouse,
570fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
571fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
572fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
573fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
574fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "%c\n",
575fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c');
576fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
577fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
578fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data,
579fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					const char *buf, size_t count)
580fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
581fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
582fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	size_t i;
583fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
584fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	for (i = 0; i < count; i++) {
585fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		switch (buf[i]) {
586fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		case 'C':
587fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			pad->flags |= FSPDRV_FLAG_EN_OPC;
588fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			break;
589fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		case 'c':
590fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			pad->flags &= ~FSPDRV_FLAG_EN_OPC;
591fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			break;
592fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		default:
593fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			return -EINVAL;
594fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		}
595fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
596fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return count;
597fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
598fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
599fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL,
600fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			fsp_attr_show_flags, fsp_attr_set_flags);
601fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
602fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic ssize_t fsp_attr_show_ver(struct psmouse *psmouse,
603fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang					void *data, char *buf)
604fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
605fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver);
606fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
607fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
608fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangPSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver);
609fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
610fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic struct attribute *fsp_attributes[] = {
611fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_setreg.dattr.attr,
612fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_getreg.dattr.attr,
613fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_page.dattr.attr,
614fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_vscroll.dattr.attr,
615fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_hscroll.dattr.attr,
616fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_flags.dattr.attr,
617fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	&psmouse_attr_ver.dattr.attr,
618fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	NULL
619fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang};
620fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
621fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic struct attribute_group fsp_attribute_group = {
622fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	.attrs = fsp_attributes,
623fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang};
624fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
625727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa#ifdef	FSP_DEBUG
626727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaastatic void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
627fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
628fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	static unsigned int ps2_packet_cnt;
629fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	static unsigned int ps2_last_second;
630fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned int jiffies_msec;
631727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	const char *packet_type = "UNKNOWN";
632727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	unsigned short abs_x = 0, abs_y = 0;
633727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa
634727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	/* Interpret & dump the packet data. */
635727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	switch (packet[0] >> FSP_PKT_TYPE_SHIFT) {
636727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	case FSP_PKT_TYPE_ABS:
637727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		packet_type = "Absolute";
638727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		abs_x = GET_ABS_X(packet);
639727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		abs_y = GET_ABS_Y(packet);
640727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		break;
641727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	case FSP_PKT_TYPE_NORMAL:
642727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		packet_type = "Normal";
643727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		break;
644727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	case FSP_PKT_TYPE_NOTIFY:
645727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		packet_type = "Notify";
646727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		break;
647727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	case FSP_PKT_TYPE_NORMAL_OPC:
648727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		packet_type = "Normal-OPC";
649727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		break;
650727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	}
651fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
652fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_packet_cnt++;
653fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	jiffies_msec = jiffies_to_msecs(jiffies);
654b5d21704361eefe337a36ebbb57a1d9927132511Dmitry Torokhov	psmouse_dbg(psmouse,
655727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		    "%08dms %s packets: %02x, %02x, %02x, %02x; "
656727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		    "abs_x: %d, abs_y: %d\n",
657727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		    jiffies_msec, packet_type,
658727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		    packet[0], packet[1], packet[2], packet[3], abs_x, abs_y);
659fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
660fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (jiffies_msec - ps2_last_second > 1000) {
661b5d21704361eefe337a36ebbb57a1d9927132511Dmitry Torokhov		psmouse_dbg(psmouse, "PS/2 packets/sec = %d\n", ps2_packet_cnt);
662fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_packet_cnt = 0;
663fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		ps2_last_second = jiffies_msec;
664fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
665fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
666fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#else
667727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaastatic void fsp_packet_debug(struct psmouse *psmouse, unsigned char packet[])
668fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
669fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
670fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang#endif
671fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
672a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liangstatic void fsp_set_slot(struct input_dev *dev, int slot, bool active,
673a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			 unsigned int x, unsigned int y)
674a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang{
675a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	input_mt_slot(dev, slot);
676a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
677a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	if (active) {
678a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_abs(dev, ABS_MT_POSITION_X, x);
679a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_abs(dev, ABS_MT_POSITION_Y, y);
680a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	}
681a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang}
682a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
683fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
684fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
685fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct input_dev *dev = psmouse->dev;
686fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *ad = psmouse->private;
687fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char *packet = psmouse->packet;
688fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char button_status = 0, lscroll = 0, rscroll = 0;
689a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	unsigned short abs_x, abs_y, fgrs = 0;
690fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rel_x, rel_y;
691fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
692fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (psmouse->pktcnt < 4)
693fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return PSMOUSE_GOOD_DATA;
694fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
695fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/*
696fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * Full packet accumulated, process it
697fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 */
698fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
699727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa	fsp_packet_debug(psmouse, packet);
700727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa
701fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
702fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	case FSP_PKT_TYPE_ABS:
703727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		abs_x = GET_ABS_X(packet);
704727f9b480754dfcb82e36d431e85984893011b79Oskari Saarenmaa		abs_y = GET_ABS_Y(packet);
705a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
706a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		if (packet[0] & FSP_PB0_MFMC) {
707a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			/*
708a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			 * MFMC packet: assume that there are two fingers on
709a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			 * pad
710a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			 */
711a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			fgrs = 2;
712a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
713a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			/* MFMC packet */
714a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			if (packet[0] & FSP_PB0_MFMC_FGR2) {
715a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				/* 2nd finger */
716a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				if (ad->last_mt_fgr == 2) {
717a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					/*
718a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * workaround for buggy firmware
719a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * which doesn't clear MFMC bit if
720a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * the 1st finger is up
721a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 */
722a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					fgrs = 1;
723a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					fsp_set_slot(dev, 0, false, 0, 0);
724a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				}
725a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				ad->last_mt_fgr = 2;
726a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
727a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				fsp_set_slot(dev, 1, fgrs == 2, abs_x, abs_y);
728a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			} else {
729a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				/* 1st finger */
730a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				if (ad->last_mt_fgr == 1) {
731a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					/*
732a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * workaround for buggy firmware
733a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * which doesn't clear MFMC bit if
734a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 * the 2nd finger is up
735a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					 */
736a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					fgrs = 1;
737a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang					fsp_set_slot(dev, 1, false, 0, 0);
738a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				}
739a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				ad->last_mt_fgr = 1;
740a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				fsp_set_slot(dev, 0, fgrs != 0, abs_x, abs_y);
741a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			}
742a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		} else {
743a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			/* SFAC packet */
744d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa			if ((packet[0] & (FSP_PB0_LBTN|FSP_PB0_PHY_BTN)) ==
745d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				FSP_PB0_LBTN) {
746d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				/* On-pad click in SFAC mode should be handled
747d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				 * by userspace.  On-pad clicks in MFMC mode
748d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				 * are real clickpad clicks, and not ignored.
749d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				 */
750d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa				packet[0] &= ~FSP_PB0_LBTN;
751d626dad58f02e13730ded6ac84d6a9e53123f0e8Oskari Saarenmaa			}
752a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
753a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			/* no multi-finger information */
754a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			ad->last_mt_fgr = 0;
755a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
756a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			if (abs_x != 0 && abs_y != 0)
757a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				fgrs = 1;
758a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
759a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			fsp_set_slot(dev, 0, fgrs > 0, abs_x, abs_y);
760a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			fsp_set_slot(dev, 1, false, 0, 0);
761a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		}
762a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		if (fgrs > 0) {
763a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			input_report_abs(dev, ABS_X, abs_x);
764a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			input_report_abs(dev, ABS_Y, abs_y);
765a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		}
766a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
767a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
768a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_key(dev, BTN_TOUCH, fgrs);
769a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_key(dev, BTN_TOOL_FINGER, fgrs == 1);
770a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_report_key(dev, BTN_TOOL_DOUBLETAP, fgrs == 2);
771fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		break;
772fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
773fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	case FSP_PKT_TYPE_NORMAL_OPC:
774fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* on-pad click, filter it if necessary */
775fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
7767b85f73d0461188aa397d428e6c53419ebfd86b4Tai-hwa Liang			packet[0] &= ~FSP_PB0_LBTN;
777fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* fall through */
778fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
779fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	case FSP_PKT_TYPE_NORMAL:
780fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* normal packet */
781fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/* special packet data translation from on-pad packets */
782fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		if (packet[3] != 0) {
783fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			if (packet[3] & BIT(0))
784fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				button_status |= 0x01;	/* wheel down */
785fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			if (packet[3] & BIT(1))
786fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				button_status |= 0x0f;	/* wheel up */
787fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			if (packet[3] & BIT(2))
788c332e9fcc5289698350d39d4d22c3ed5257d7a80Tai-hwa Liang				button_status |= BIT(4);/* horizontal left */
789fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			if (packet[3] & BIT(3))
790c332e9fcc5289698350d39d4d22c3ed5257d7a80Tai-hwa Liang				button_status |= BIT(5);/* horizontal right */
791fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			/* push back to packet queue */
792fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			if (button_status != 0)
793fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				packet[3] = button_status;
794fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			rscroll = (packet[3] >> 4) & 1;
795fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			lscroll = (packet[3] >> 5) & 1;
796fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		}
797fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/*
798fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * Processing wheel up/down and extra button events
799fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 */
800fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_rel(dev, REL_WHEEL,
801fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				 (int)(packet[3] & 8) - (int)(packet[3] & 7));
802fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_rel(dev, REL_HWHEEL, lscroll - rscroll);
803fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_key(dev, BTN_BACK, lscroll);
804fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_key(dev, BTN_FORWARD, rscroll);
805fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
806fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		/*
807fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 * Standard PS/2 Mouse
808fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		 */
809fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_key(dev, BTN_LEFT, packet[0] & 1);
810fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
811fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
812fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
813fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
814fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
815fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
816fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_rel(dev, REL_X, rel_x);
817fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		input_report_rel(dev, REL_Y, rel_y);
818fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		break;
819fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
820fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
821fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	input_sync(dev);
822fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
823fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return PSMOUSE_FULL_PACKET;
824fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
825fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
826fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_activate_protocol(struct psmouse *psmouse)
827fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
828fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
829fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct ps2dev *ps2dev = &psmouse->ps2dev;
830fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	unsigned char param[2];
831fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int val;
832fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
833fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	/*
834fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * Standard procedure to enter FSP Intellimouse mode
835fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 * (scrolling wheel, 4th and 5th buttons)
836fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	 */
837fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	param[0] = 200;
838fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
839fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	param[0] = 200;
840fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
841fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	param[0] =  80;
842fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
843fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
844fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
845fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (param[0] != 0x04) {
8463ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		psmouse_err(psmouse,
8473ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			    "Unable to enable 4 bytes packet format.\n");
848fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
849fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
850fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
8513ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	if (pad->ver < FSP_VER_STL3888_C0) {
8523ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		/* Preparing relative coordinates output for older hardware */
8533ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
8543ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			psmouse_err(psmouse,
8553ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang				    "Unable to read SYSCTL5 register.\n");
8563ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			return -EIO;
8573ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		}
858fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
8593ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		if (fsp_get_buttons(psmouse, &pad->buttons)) {
8603ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			psmouse_err(psmouse,
8613ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang				    "Unable to retrieve number of buttons.\n");
8623ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			return -EIO;
8633ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		}
864fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
8653ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
8663ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		/* Ensure we are not in absolute mode */
8673ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		val &= ~FSP_BIT_EN_PKT_G0;
8683ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		if (pad->buttons == 0x06) {
8693ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			/* Left/Middle/Right & Scroll Up/Down/Right/Left */
8703ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			val |= FSP_BIT_EN_MSID6;
8713ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		}
8723ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang
8733ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
8743ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			psmouse_err(psmouse,
8753ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang				    "Unable to set up required mode bits.\n");
8763ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			return -EIO;
8773ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		}
8783ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang
8793ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		/*
8803ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		 * Enable OPC tags such that driver can tell the difference
8813ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		 * between on-pad and real button click
8823ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		 */
8833ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		if (fsp_opc_tag_enable(psmouse, true))
8843ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			psmouse_warn(psmouse,
8853ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang				     "Failed to enable OPC tag mode.\n");
8863ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		/* enable on-pad click by default */
8873ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		pad->flags |= FSPDRV_FLAG_EN_OPC;
8883ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang
8893ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		/* Enable on-pad vertical and horizontal scrolling */
8903ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		fsp_onpad_vscr(psmouse, true);
8913ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		fsp_onpad_hscr(psmouse, true);
892a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	} else {
893a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		/* Enable absolute coordinates output for Cx/Dx hardware */
894a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		if (fsp_reg_write(psmouse, FSP_REG_SWC1,
895a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				  FSP_BIT_SWC1_EN_ABS_1F |
896a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				  FSP_BIT_SWC1_EN_ABS_2F |
897a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				  FSP_BIT_SWC1_EN_FUP_OUT |
898a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				  FSP_BIT_SWC1_EN_ABS_CON)) {
899a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			psmouse_err(psmouse,
900a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang				    "Unable to enable absolute coordinates output.\n");
901a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang			return -EIO;
902a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		}
903fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
904fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
9053ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	return 0;
9063ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang}
9073ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang
9083ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liangstatic int fsp_set_input_params(struct psmouse *psmouse)
9093ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang{
9103ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	struct input_dev *dev = psmouse->dev;
9113ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	struct fsp_data *pad = psmouse->private;
912fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
9133ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	if (pad->ver < FSP_VER_STL3888_C0) {
9143ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		__set_bit(BTN_MIDDLE, dev->keybit);
9153ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		__set_bit(BTN_BACK, dev->keybit);
9163ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		__set_bit(BTN_FORWARD, dev->keybit);
9173ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		__set_bit(REL_WHEEL, dev->relbit);
9183ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		__set_bit(REL_HWHEEL, dev->relbit);
919a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang	} else {
920a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		/*
921a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 * Hardware prior to Cx performs much better in relative mode;
922a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 * hence, only enable absolute coordinates output as well as
923a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 * multi-touch output for the newer hardware.
924a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 *
925a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 * Maximum coordinates can be computed as:
926a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 *
927a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 *	number of scanlines * 64 - 57
928a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 *
929a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 * where number of X/Y scanline lines are 16/12.
930a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		 */
931a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		int abs_x = 967, abs_y = 711;
932a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
933a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__set_bit(EV_ABS, dev->evbit);
934a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__clear_bit(EV_REL, dev->evbit);
935a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__set_bit(BTN_TOUCH, dev->keybit);
936a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__set_bit(BTN_TOOL_FINGER, dev->keybit);
937a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
938a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
939a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang
940a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
941a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
942a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_mt_init_slots(dev, 2);
943a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
944a4c85075f00d56b38f5c277ab89f9aaad69eb17bTai-hwa Liang		input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
9453ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	}
946fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
947fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
948fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
949fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
950b7802c5c1ea9563f3746bea09c214ccedc8600f4Dmitry Torokhovint fsp_detect(struct psmouse *psmouse, bool set_properties)
951fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
952fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int id;
953fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
954fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id))
955fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
956fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
957fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (id != 0x01)
958fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -ENODEV;
959fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
960fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (set_properties) {
961fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		psmouse->vendor = "Sentelic";
962fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		psmouse->name = "FingerSensingPad";
963fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
964fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
965fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
966fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
967fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
968fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic void fsp_reset(struct psmouse *psmouse)
969fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
970fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_opc_tag_enable(psmouse, false);
971fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_onpad_vscr(psmouse, false);
972fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_onpad_hscr(psmouse, false);
973fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
974fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
975fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic void fsp_disconnect(struct psmouse *psmouse)
976fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
977fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
978fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang			   &fsp_attribute_group);
979fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
980fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	fsp_reset(psmouse);
981fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	kfree(psmouse->private);
982fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
983fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
984fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangstatic int fsp_reconnect(struct psmouse *psmouse)
985fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
986fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int version;
987fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
988fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_detect(psmouse, 0))
989fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -ENODEV;
990fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
991fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_get_version(psmouse, &version))
992fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -ENODEV;
993fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
994fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_activate_protocol(psmouse))
995fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -EIO;
996fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
997fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
998fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
999fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1000fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangint fsp_init(struct psmouse *psmouse)
1001fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
1002fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	struct fsp_data *priv;
10033ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	int ver, rev;
1004fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int error;
1005fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1006fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (fsp_get_version(psmouse, &ver) ||
10073ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	    fsp_get_revision(psmouse, &rev)) {
1008fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -ENODEV;
1009fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
1010fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
10113ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n",
10123ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		     ver >> 4, ver & 0x0F, rev, fsp_drv_ver);
1013fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1014fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
1015fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (!priv)
1016fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		return -ENOMEM;
1017fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1018fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	priv->ver = ver;
1019fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	priv->rev = rev;
1020fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1021fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->protocol_handler = fsp_process_byte;
1022fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->disconnect = fsp_disconnect;
1023fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->reconnect = fsp_reconnect;
1024fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->cleanup = fsp_reset;
1025fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->pktsize = 4;
1026fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1027fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	error = fsp_activate_protocol(psmouse);
1028fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (error)
1029fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto err_out;
1030fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
10313ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	/* Set up various supported input event bits */
10323ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	error = fsp_set_input_params(psmouse);
10333ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang	if (error)
10343ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		goto err_out;
10353ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang
1036fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
1037fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang				   &fsp_attribute_group);
1038fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	if (error) {
10393ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang		psmouse_err(psmouse,
10403ac1780f9e6ed212e56d4132e997551297a97112Tai-hwa Liang			    "Failed to create sysfs attributes (%d)", error);
1041fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang		goto err_out;
1042fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	}
1043fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1044fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return 0;
1045fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
1046fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang err_out:
1047fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	kfree(psmouse->private);
1048fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	psmouse->private = NULL;
1049fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return error;
1050fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
1051