aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2018-05-22 20:28:23 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2018-05-23 19:49:22 -0400
commit21c48dbde0944e3d52468008321900af3bdafcf1 (patch)
tree076300cb80f45799824b1f550fe04bdf9a3c8983
parent80212ed743d7d6618a33cf4d37fc625a061faf22 (diff)
Input: elantech - add support for SMBus devices
Many of the Elantech devices are connected through PS/2 and a different bus (SMBus or plain I2C). To not break any existing device, we only enable SMBus based on a module parameter. If some laptops require the quirk to be set, we will have to rely on a list of PNPIds or MDI matching to individually expose those hardware over SMBus. the parameter mentioned above is elantech_smbus from the psmouse module. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Acked-by: KT Liao <kt.liao@emc.com.tw> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--drivers/input/mouse/Kconfig12
-rw-r--r--drivers/input/mouse/elantech.c188
-rw-r--r--drivers/input/mouse/elantech.h24
-rw-r--r--drivers/input/mouse/psmouse-base.c21
-rw-r--r--drivers/input/mouse/psmouse-smbus.c11
-rw-r--r--drivers/input/mouse/psmouse.h1
6 files changed, 246 insertions, 11 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 89ebb8f39fee..f27f23f2d99a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH
133 133
134 If unsure, say N. 134 If unsure, say N.
135 135
136config MOUSE_PS2_ELANTECH_SMBUS
137 bool "Elantech PS/2 SMbus companion" if EXPERT
138 default y
139 depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
140 depends on I2C=y || I2C=MOUSE_PS2
141 select MOUSE_PS2_SMBUS
142 help
143 Say Y here if you have a Elantech touchpad connected to
144 to an SMBus, but enumerated through PS/2.
145
146 If unsure, say Y.
147
136config MOUSE_PS2_SENTELIC 148config MOUSE_PS2_SENTELIC
137 bool "Sentelic Finger Sensing Pad PS/2 protocol extension" 149 bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
138 depends on MOUSE_PS2 150 depends on MOUSE_PS2
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index a2a14a31edb5..510e7c0622d3 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -14,13 +14,16 @@
14#include <linux/dmi.h> 14#include <linux/dmi.h>
15#include <linux/slab.h> 15#include <linux/slab.h>
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/i2c.h>
17#include <linux/input.h> 18#include <linux/input.h>
18#include <linux/input/mt.h> 19#include <linux/input/mt.h>
20#include <linux/platform_device.h>
19#include <linux/serio.h> 21#include <linux/serio.h>
20#include <linux/libps2.h> 22#include <linux/libps2.h>
21#include <asm/unaligned.h> 23#include <asm/unaligned.h>
22#include "psmouse.h" 24#include "psmouse.h"
23#include "elantech.h" 25#include "elantech.h"
26#include "elan_i2c.h"
24 27
25#define elantech_debug(fmt, ...) \ 28#define elantech_debug(fmt, ...) \
26 do { \ 29 do { \
@@ -1084,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
1084 1087
1085static int elantech_get_resolution_v4(struct psmouse *psmouse, 1088static int elantech_get_resolution_v4(struct psmouse *psmouse,
1086 unsigned int *x_res, 1089 unsigned int *x_res,
1087 unsigned int *y_res) 1090 unsigned int *y_res,
1091 unsigned int *bus)
1088{ 1092{
1089 unsigned char param[3]; 1093 unsigned char param[3];
1090 1094
@@ -1093,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
1093 1097
1094 *x_res = elantech_convert_res(param[1] & 0x0f); 1098 *x_res = elantech_convert_res(param[1] & 0x0f);
1095 *y_res = elantech_convert_res((param[1] & 0xf0) >> 4); 1099 *y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
1100 *bus = param[2];
1096 1101
1097 return 0; 1102 return 0;
1098} 1103}
@@ -1474,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
1474{ 1479{
1475 struct elantech_data *etd = psmouse->private; 1480 struct elantech_data *etd = psmouse->private;
1476 1481
1482 /*
1483 * We might have left a breadcrumb when trying to
1484 * set up SMbus companion.
1485 */
1486 psmouse_smbus_cleanup(psmouse);
1487
1477 if (etd->tp_dev) 1488 if (etd->tp_dev)
1478 input_unregister_device(etd->tp_dev); 1489 input_unregister_device(etd->tp_dev);
1479 sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, 1490 sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1659,6 +1670,8 @@ static int elantech_query_info(struct psmouse *psmouse,
1659{ 1670{
1660 unsigned char param[3]; 1671 unsigned char param[3];
1661 1672
1673 memset(info, 0, sizeof(*info));
1674
1662 /* 1675 /*
1663 * Do the version query again so we can store the result 1676 * Do the version query again so we can store the result
1664 */ 1677 */
@@ -1717,7 +1730,8 @@ static int elantech_query_info(struct psmouse *psmouse,
1717 if (info->hw_version == 4) { 1730 if (info->hw_version == 4) {
1718 if (elantech_get_resolution_v4(psmouse, 1731 if (elantech_get_resolution_v4(psmouse,
1719 &info->x_res, 1732 &info->x_res,
1720 &info->y_res)) { 1733 &info->y_res,
1734 &info->bus)) {
1721 psmouse_warn(psmouse, 1735 psmouse_warn(psmouse,
1722 "failed to query resolution data.\n"); 1736 "failed to query resolution data.\n");
1723 } 1737 }
@@ -1726,6 +1740,129 @@ static int elantech_query_info(struct psmouse *psmouse,
1726 return 0; 1740 return 0;
1727} 1741}
1728 1742
1743#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
1744
1745/*
1746 * The newest Elantech device can use a secondary bus (over SMBus) which
1747 * provides a better bandwidth and allow a better control of the touchpads.
1748 * This is used to decide if we need to use this bus or not.
1749 */
1750enum {
1751 ELANTECH_SMBUS_NOT_SET = -1,
1752 ELANTECH_SMBUS_OFF,
1753 ELANTECH_SMBUS_ON,
1754};
1755
1756static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
1757 ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
1758module_param_named(elantech_smbus, elantech_smbus, int, 0644);
1759MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
1760
1761static int elantech_create_smbus(struct psmouse *psmouse,
1762 struct elantech_device_info *info,
1763 bool leave_breadcrumbs)
1764{
1765 const struct property_entry i2c_properties[] = {
1766 PROPERTY_ENTRY_BOOL("elan,trackpoint"),
1767 { },
1768 };
1769 struct i2c_board_info smbus_board = {
1770 I2C_BOARD_INFO("elan_i2c", 0x15),
1771 .flags = I2C_CLIENT_HOST_NOTIFY,
1772 };
1773
1774 if (info->has_trackpoint)
1775 smbus_board.properties = i2c_properties;
1776
1777 return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0,
1778 leave_breadcrumbs);
1779}
1780
1781/**
1782 * elantech_setup_smbus - called once the PS/2 devices are enumerated
1783 * and decides to instantiate a SMBus InterTouch device.
1784 */
1785static int elantech_setup_smbus(struct psmouse *psmouse,
1786 struct elantech_device_info *info,
1787 bool leave_breadcrumbs)
1788{
1789 int error;
1790
1791 if (elantech_smbus == ELANTECH_SMBUS_OFF)
1792 return -ENXIO;
1793
1794 if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
1795 /*
1796 * FIXME:
1797 * constraint the I2C capable devices by using FW version,
1798 * board version, or by using DMI matching
1799 */
1800 return -ENXIO;
1801 }
1802
1803 psmouse_info(psmouse, "Trying to set up SMBus access\n");
1804
1805 error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
1806 if (error) {
1807 if (error == -EAGAIN)
1808 psmouse_info(psmouse, "SMbus companion is not ready yet\n");
1809 else
1810 psmouse_err(psmouse, "unable to create intertouch device\n");
1811
1812 return error;
1813 }
1814
1815 return 0;
1816}
1817
1818static bool elantech_use_host_notify(struct psmouse *psmouse,
1819 struct elantech_device_info *info)
1820{
1821 switch (info->bus) {
1822 case ETP_BUS_PS2_ONLY:
1823 /* expected case */
1824 break;
1825 case ETP_BUS_SMB_ALERT_ONLY:
1826 /* fall-through */
1827 case ETP_BUS_PS2_SMB_ALERT:
1828 psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
1829 break;
1830 case ETP_BUS_SMB_HST_NTFY_ONLY:
1831 /* fall-through */
1832 case ETP_BUS_PS2_SMB_HST_NTFY:
1833 return true;
1834 default:
1835 psmouse_dbg(psmouse,
1836 "Ignoring SMBus bus provider %d.\n",
1837 info->bus);
1838 }
1839
1840 return false;
1841}
1842
1843int elantech_init_smbus(struct psmouse *psmouse)
1844{
1845 struct elantech_device_info info;
1846 int error = -EINVAL;
1847
1848 psmouse_reset(psmouse);
1849
1850 error = elantech_query_info(psmouse, &info);
1851 if (error)
1852 goto init_fail;
1853
1854 if (info.hw_version < 4) {
1855 error = -ENXIO;
1856 goto init_fail;
1857 }
1858
1859 return elantech_create_smbus(psmouse, &info, false);
1860 init_fail:
1861 psmouse_reset(psmouse);
1862 return error;
1863}
1864#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
1865
1729/* 1866/*
1730 * Initialize the touchpad and create sysfs entries 1867 * Initialize the touchpad and create sysfs entries
1731 */ 1868 */
@@ -1734,7 +1871,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
1734{ 1871{
1735 struct elantech_data *etd; 1872 struct elantech_data *etd;
1736 int i; 1873 int i;
1737 int error; 1874 int error = -EINVAL;
1738 struct input_dev *tp_dev; 1875 struct input_dev *tp_dev;
1739 1876
1740 psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL); 1877 psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
@@ -1821,7 +1958,7 @@ static int elantech_setup_ps2(struct psmouse *psmouse,
1821 return error; 1958 return error;
1822} 1959}
1823 1960
1824int elantech_init(struct psmouse *psmouse) 1961int elantech_init_ps2(struct psmouse *psmouse)
1825{ 1962{
1826 struct elantech_device_info info; 1963 struct elantech_device_info info;
1827 int error = -EINVAL; 1964 int error = -EINVAL;
@@ -1841,3 +1978,46 @@ int elantech_init(struct psmouse *psmouse)
1841 psmouse_reset(psmouse); 1978 psmouse_reset(psmouse);
1842 return error; 1979 return error;
1843} 1980}
1981
1982int elantech_init(struct psmouse *psmouse)
1983{
1984 struct elantech_device_info info;
1985 int error = -EINVAL;
1986
1987 psmouse_reset(psmouse);
1988
1989 error = elantech_query_info(psmouse, &info);
1990 if (error)
1991 goto init_fail;
1992
1993#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
1994
1995 if (elantech_use_host_notify(psmouse, &info)) {
1996 if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
1997 !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
1998 psmouse_warn(psmouse,
1999 "The touchpad can support a better bus than the too old PS/2 protocol. "
2000 "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
2001 }
2002 error = elantech_setup_smbus(psmouse, &info, true);
2003 if (!error)
2004 return PSMOUSE_ELANTECH_SMBUS;
2005 }
2006
2007#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
2008
2009 error = elantech_setup_ps2(psmouse, &info);
2010 if (error < 0) {
2011 /*
2012 * Not using any flavor of Elantech support, so clean up
2013 * SMbus breadcrumbs, if any.
2014 */
2015 psmouse_smbus_cleanup(psmouse);
2016 goto init_fail;
2017 }
2018
2019 return PSMOUSE_ELANTECH;
2020 init_fail:
2021 psmouse_reset(psmouse);
2022 return error;
2023}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index 851df4ce6232..f9b1c485e8d9 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -107,6 +107,15 @@
107#define ETP_WEIGHT_VALUE 5 107#define ETP_WEIGHT_VALUE 5
108 108
109/* 109/*
110 * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
111 */
112#define ETP_BUS_PS2_ONLY 0
113#define ETP_BUS_SMB_ALERT_ONLY 1
114#define ETP_BUS_SMB_HST_NTFY_ONLY 2
115#define ETP_BUS_PS2_SMB_ALERT 3
116#define ETP_BUS_PS2_SMB_HST_NTFY 4
117
118/*
110 * The base position for one finger, v4 hardware 119 * The base position for one finger, v4 hardware
111 */ 120 */
112struct finger_pos { 121struct finger_pos {
@@ -122,6 +131,7 @@ struct elantech_device_info {
122 unsigned int fw_version; 131 unsigned int fw_version;
123 unsigned int x_res; 132 unsigned int x_res;
124 unsigned int y_res; 133 unsigned int y_res;
134 unsigned int bus;
125 bool paritycheck; 135 bool paritycheck;
126 bool jumpy_cursor; 136 bool jumpy_cursor;
127 bool reports_pressure; 137 bool reports_pressure;
@@ -156,6 +166,7 @@ struct elantech_data {
156 166
157#ifdef CONFIG_MOUSE_PS2_ELANTECH 167#ifdef CONFIG_MOUSE_PS2_ELANTECH
158int elantech_detect(struct psmouse *psmouse, bool set_properties); 168int elantech_detect(struct psmouse *psmouse, bool set_properties);
169int elantech_init_ps2(struct psmouse *psmouse);
159int elantech_init(struct psmouse *psmouse); 170int elantech_init(struct psmouse *psmouse);
160#else 171#else
161static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) 172static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
@@ -166,6 +177,19 @@ static inline int elantech_init(struct psmouse *psmouse)
166{ 177{
167 return -ENOSYS; 178 return -ENOSYS;
168} 179}
180static inline int elantech_init_ps2(struct psmouse *psmouse)
181{
182 return -ENOSYS;
183}
169#endif /* CONFIG_MOUSE_PS2_ELANTECH */ 184#endif /* CONFIG_MOUSE_PS2_ELANTECH */
170 185
186#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
187int elantech_init_smbus(struct psmouse *psmouse);
188#else
189static inline int elantech_init_smbus(struct psmouse *psmouse)
190{
191 return -ENOSYS;
192}
193#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
194
171#endif 195#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 8900c3166ebf..5ff5b1952be0 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
856 .name = "ETPS/2", 856 .name = "ETPS/2",
857 .alias = "elantech", 857 .alias = "elantech",
858 .detect = elantech_detect, 858 .detect = elantech_detect,
859 .init = elantech_init, 859 .init = elantech_init_ps2,
860 },
861#endif
862#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
863 {
864 .type = PSMOUSE_ELANTECH_SMBUS,
865 .name = "ETSMBus",
866 .alias = "elantech-smbus",
867 .detect = elantech_detect,
868 .init = elantech_init_smbus,
869 .smbus_companion = true,
860 }, 870 },
861#endif 871#endif
862#ifdef CONFIG_MOUSE_PS2_SENTELIC 872#ifdef CONFIG_MOUSE_PS2_SENTELIC
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
1158 /* Try Elantech touchpad */ 1168 /* Try Elantech touchpad */
1159 if (max_proto > PSMOUSE_IMEX && 1169 if (max_proto > PSMOUSE_IMEX &&
1160 psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, 1170 psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
1161 &max_proto, set_properties, true)) { 1171 &max_proto, set_properties, false)) {
1162 return PSMOUSE_ELANTECH; 1172 if (!set_properties)
1173 return PSMOUSE_ELANTECH;
1174
1175 ret = elantech_init(psmouse);
1176 if (ret >= 0)
1177 return ret;
1163 } 1178 }
1164 1179
1165 if (max_proto > PSMOUSE_IMEX) { 1180 if (max_proto > PSMOUSE_IMEX) {
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
index c7ac24d119c1..c8a3b1f35ce3 100644
--- a/drivers/input/mouse/psmouse-smbus.c
+++ b/drivers/input/mouse/psmouse-smbus.c
@@ -237,10 +237,13 @@ int psmouse_smbus_init(struct psmouse *psmouse,
237 smbdev->psmouse = psmouse; 237 smbdev->psmouse = psmouse;
238 smbdev->board = *board; 238 smbdev->board = *board;
239 239
240 smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL); 240 if (pdata) {
241 if (!smbdev->board.platform_data) { 241 smbdev->board.platform_data = kmemdup(pdata, pdata_size,
242 kfree(smbdev); 242 GFP_KERNEL);
243 return -ENOMEM; 243 if (!smbdev->board.platform_data) {
244 kfree(smbdev);
245 return -ENOMEM;
246 }
244 } 247 }
245 248
246 psmouse->private = smbdev; 249 psmouse->private = smbdev;
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 71ac50082c8b..dd4ec1f602d7 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -68,6 +68,7 @@ enum psmouse_type {
68 PSMOUSE_VMMOUSE, 68 PSMOUSE_VMMOUSE,
69 PSMOUSE_BYD, 69 PSMOUSE_BYD,
70 PSMOUSE_SYNAPTICS_SMBUS, 70 PSMOUSE_SYNAPTICS_SMBUS,
71 PSMOUSE_ELANTECH_SMBUS,
71 PSMOUSE_AUTO /* This one should always be last */ 72 PSMOUSE_AUTO /* This one should always be last */
72}; 73};
73 74