// 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, ®); 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. Load more
// 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, ®); 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)