1067 lines · 24.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/*- * Finger Sensing Pad PS/2 mouse driver. * * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. * Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation. */ #include <linux/module.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/ctype.h>#include <linux/libps2.h>#include <linux/serio.h>#include <linux/jiffies.h>#include <linux/slab.h> #include "psmouse.h"#include "sentelic.h" /* * Timeout for FSP PS/2 command only (in milliseconds). */#define	FSP_CMD_TIMEOUT		200#define	FSP_CMD_TIMEOUT2	30 #define	GET_ABS_X(packet)	((packet[1] << 2) | ((packet[3] >> 2) & 0x03))#define	GET_ABS_Y(packet)	((packet[2] << 2) | (packet[3] & 0x03)) /** Driver version. */static const char fsp_drv_ver[] = "1.1.0-K"; /* * Make sure that the value being sent to FSP will not conflict with * possible sample rate values. */static unsigned char fsp_test_swap_cmd(unsigned char reg_val){	switch (reg_val) {	case 10: case 20: case 40: case 60: case 80: case 100: case 200:		/*		 * The requested value being sent to FSP matched to possible		 * sample rates, swap the given value such that the hardware		 * wouldn't get confused.		 */		return (reg_val >> 4) | (reg_val << 4);	default:		return reg_val;	/* swap isn't necessary */	}} /* * Make sure that the value being sent to FSP will not conflict with certain * commands. */static unsigned char fsp_test_invert_cmd(unsigned char reg_val){	switch (reg_val) {	case 0xe9: case 0xee: case 0xf2: case 0xff:		/*		 * The requested value being sent to FSP matched to certain		 * commands, inverse the given value such that the hardware		 * wouldn't get confused.		 */		return ~reg_val;	default:		return reg_val;	/* inversion isn't necessary */	}} static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val){	struct ps2dev *ps2dev = &psmouse->ps2dev;	unsigned char param[3];	unsigned char addr;	int rc = -1; 	/*	 * We need to shut off the device and switch it into command	 * mode so we don't confuse our protocol handler. We don't need	 * to do that for writes because sysfs set helper does this for	 * us.	 */	psmouse_deactivate(psmouse); 	ps2_begin_command(ps2dev); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	/* should return 0xfe(request for resending) */	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);	/* should return 0xfc(failed) */	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {		ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);	} else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {		/* swapping is required */		ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);		/* expect 0xfe */	} else {		/* swapping isn't necessary */		ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);		/* expect 0xfe */	}	/* should return 0xfc(failed) */	ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); 	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)		goto out; 	*reg_val = param[2];	rc = 0;  out:	ps2_end_command(ps2dev);	psmouse_activate(psmouse);	psmouse_dbg(psmouse,		    "READ REG: 0x%02x is 0x%02x (rc = %d)\n",		    reg_addr, *reg_val, rc);	return rc;} static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val){	struct ps2dev *ps2dev = &psmouse->ps2dev;	unsigned char v;	int rc = -1; 	ps2_begin_command(ps2dev); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) {		/* inversion is required */		ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2);	} else {		if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) {			/* swapping is required */			ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2);		} else {			/* swapping isn't necessary */			ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2);		}	}	/* write the register address in correct order */	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {		/* inversion is required */		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {		/* swapping is required */		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);	} else {		/* swapping isn't necessary */		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);	} 	/* write the register value in correct order */	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);	rc = 0;  out:	ps2_end_command(ps2dev);	psmouse_dbg(psmouse,		    "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",		    reg_addr, reg_val, rc);	return rc;} /* Enable register clock gating for writing certain registers */static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable){	int v, nv; 	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1)		return -1; 	if (enable)		nv = v | FSP_BIT_EN_REG_CLK;	else		nv = v & ~FSP_BIT_EN_REG_CLK; 	/* only write if necessary */	if (nv != v)		if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1)			return -1; 	return 0;} static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val){	struct ps2dev *ps2dev = &psmouse->ps2dev;	unsigned char param[3];	int rc = -1; 	psmouse_deactivate(psmouse); 	ps2_begin_command(ps2dev); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2);	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); 	/* get the returned result */	if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))		goto out; 	*reg_val = param[2];	rc = 0;  out:	ps2_end_command(ps2dev);	psmouse_activate(psmouse);	psmouse_dbg(psmouse,		    "READ PAGE REG: 0x%02x (rc = %d)\n",		    *reg_val, rc);	return rc;} static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val){	struct ps2dev *ps2dev = &psmouse->ps2dev;	unsigned char v;	int rc = -1; 	ps2_begin_command(ps2dev); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2);	ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); 	if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)		goto out; 	if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {		ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);	} else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {		/* swapping is required */		ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);	} else {		/* swapping isn't necessary */		ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);	} 	ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);	rc = 0;  out:	ps2_end_command(ps2dev);	psmouse_dbg(psmouse,		    "WRITE PAGE REG: to 0x%02x (rc = %d)\n",		    reg_val, rc);	return rc;} static int fsp_get_version(struct psmouse *psmouse, int *version){	if (fsp_reg_read(psmouse, FSP_REG_VERSION, version))		return -EIO; 	return 0;} static int fsp_get_revision(struct psmouse *psmouse, int *rev){	if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev))		return -EIO; 	return 0;} static int fsp_get_sn(struct psmouse *psmouse, int *sn){	int v0, v1, v2;	int rc = -EIO; 	/* production number since Cx is available at: 0x0b40 ~ 0x0b42 */	if (fsp_page_reg_write(psmouse, FSP_PAGE_0B))		goto out;	if (fsp_reg_read(psmouse, FSP_REG_SN0, &v0))		goto out;	if (fsp_reg_read(psmouse, FSP_REG_SN1, &v1))		goto out;	if (fsp_reg_read(psmouse, FSP_REG_SN2, &v2))		goto out;	*sn = (v0 << 16) | (v1 << 8) | v2;	rc = 0;out:	fsp_page_reg_write(psmouse, FSP_PAGE_DEFAULT);	return rc;} static int fsp_get_buttons(struct psmouse *psmouse, int *btn){	static const int buttons[] = {		0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */		0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */		0x04, /* Left/Middle/Right & Scroll Up/Down */		0x02, /* Left/Middle/Right */	};	int val; 	if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS, &val) == -1)		return -EIO; 	*btn = buttons[(val & 0x30) >> 4];	return 0;} /* Enable on-pad command tag output */static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable){	int v, nv;	int res = 0; 	if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {		psmouse_err(psmouse, "Unable get OPC state.\n");		return -EIO;	} 	if (enable)		nv = v | FSP_BIT_EN_OPC_TAG;	else		nv = v & ~FSP_BIT_EN_OPC_TAG; 	/* only write if necessary */	if (nv != v) {		fsp_reg_write_enable(psmouse, true);		res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv);		fsp_reg_write_enable(psmouse, false);	} 	if (res != 0) {		psmouse_err(psmouse, "Unable to enable OPC tag.\n");		res = -EIO;	} 	return res;} static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable){	struct fsp_data *pad = psmouse->private;	int val; 	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))		return -EIO; 	pad->vscroll = enable; 	if (enable)		val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE);	else		val &= ~FSP_BIT_FIX_VSCR; 	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))		return -EIO; 	return 0;} static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable){	struct fsp_data *pad = psmouse->private;	int val, v2; 	if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))		return -EIO; 	if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2))		return -EIO; 	pad->hscroll = enable; 	if (enable) {		val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE);		v2 |= FSP_BIT_EN_MSID6;	} else {		val &= ~FSP_BIT_FIX_HSCR;		v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8);	} 	if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))		return -EIO; 	/* reconfigure horizontal scrolling packet output */	if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2))		return -EIO; 	return 0;} /* * Write device specific initial parameters. * * ex: 0xab 0xcd - write oxcd into register 0xab */static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,				   const char *buf, size_t count){	unsigned int reg, val;	char *rest;	ssize_t retval; 	reg = simple_strtoul(buf, &rest, 16);	if (rest == buf || *rest != ' ' || reg > 0xff)		return -EINVAL; 	retval = kstrtouint(rest + 1, 16, &val);	if (retval)		return retval; 	if (val > 0xff)		return -EINVAL; 	if (fsp_reg_write_enable(psmouse, true))		return -EIO; 	retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count; 	fsp_reg_write_enable(psmouse, false); 	return retval;} PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse,					void *data, char *buf){	struct fsp_data *pad = psmouse->private; 	return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val);} /* * Read a register from device. * * ex: 0xab -- read content from register 0xab */static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,					const char *buf, size_t count){	struct fsp_data *pad = psmouse->private;	unsigned int reg, val;	int err; 	err = kstrtouint(buf, 16, &reg);	if (err)		return err; 	if (reg > 0xff)		return -EINVAL; 	if (fsp_reg_read(psmouse, reg, &val))		return -EIO; 	pad->last_reg = reg;	pad->last_val = val; 	return count;} PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL,			fsp_attr_show_getreg, fsp_attr_set_getreg); static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,					void *data, char *buf){	int val = 0; 	if (fsp_page_reg_read(psmouse, &val))		return -EIO; 	return sprintf(buf, "%02x\n", val);} static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,					const char *buf, size_t count)

Lines 1–500 of 1067.