diff options
author | Benson Leung <bleung@chromium.org> | 2012-10-25 17:21:21 -0400 |
---|---|---|
committer | Matthew Garrett <matthew.garrett@nebula.com> | 2013-02-24 17:49:57 -0500 |
commit | d1381f45ad54e0bb40ef19f99e87187ff3458bdb (patch) | |
tree | db199e11fc1fb1f41144797aac59f803aea4ba65 | |
parent | 16a3d9f5aa1c9e0ecddd48bdfcf44445c9dbb601 (diff) |
Platform: x86: Add Chrome OS Laptop driver
This adds the chromeos_laptop driver. It supports
the Cypress APA SMBUS touchpad as well as the isl29018 i2c ambient
light sensor on the Samsung Series 5 550 Chromebook.
Signed-off-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
-rw-r--r-- | drivers/platform/x86/Kconfig | 11 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/chromeos_laptop.c | 205 |
3 files changed, 217 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7ab0b2fba503..585db6fa64b8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -79,6 +79,17 @@ config ASUS_LAPTOP | |||
79 | 79 | ||
80 | If you have an ACPI-compatible ASUS laptop, say Y or M here. | 80 | If you have an ACPI-compatible ASUS laptop, say Y or M here. |
81 | 81 | ||
82 | config CHROMEOS_LAPTOP | ||
83 | tristate "Chrome OS Laptop" | ||
84 | depends on I2C | ||
85 | depends on DMI | ||
86 | ---help--- | ||
87 | This driver instantiates i2c and smbus devices such as | ||
88 | light sensors and touchpads. | ||
89 | |||
90 | If you have a supported Chromebook, choose Y or M here. | ||
91 | The module will be called chromeos_laptop. | ||
92 | |||
82 | config DELL_LAPTOP | 93 | config DELL_LAPTOP |
83 | tristate "Dell Laptop Extras" | 94 | tristate "Dell Laptop Extras" |
84 | depends on X86 | 95 | depends on X86 |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index bf7e4f935b17..ace2b38942fe 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -50,3 +50,4 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o | |||
50 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o | 50 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o |
51 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o | 51 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o |
52 | obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o | 52 | obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o |
53 | obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o | ||
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c new file mode 100644 index 000000000000..a8329c3a9d0d --- /dev/null +++ b/drivers/platform/x86/chromeos_laptop.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices. | ||
3 | * | ||
4 | * Author : Benson Leung <bleung@chromium.org> | ||
5 | * | ||
6 | * Copyright (C) 2012 Google, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/dmi.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/module.h> | ||
27 | |||
28 | #define CYAPA_TP_I2C_ADDR 0x67 | ||
29 | #define ISL_ALS_I2C_ADDR 0x44 | ||
30 | |||
31 | static struct i2c_client *als; | ||
32 | static struct i2c_client *tp; | ||
33 | |||
34 | const char *i2c_adapter_names[] = { | ||
35 | "SMBus I801 adapter", | ||
36 | }; | ||
37 | |||
38 | /* Keep this enum consistent with i2c_adapter_names */ | ||
39 | enum i2c_adapter_type { | ||
40 | I2C_ADAPTER_SMBUS = 0, | ||
41 | }; | ||
42 | |||
43 | static struct i2c_board_info __initdata cyapa_device = { | ||
44 | I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), | ||
45 | .flags = I2C_CLIENT_WAKE, | ||
46 | }; | ||
47 | |||
48 | static struct i2c_board_info __initdata isl_als_device = { | ||
49 | I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), | ||
50 | }; | ||
51 | |||
52 | static struct i2c_client __init *__add_probed_i2c_device( | ||
53 | const char *name, | ||
54 | int bus, | ||
55 | struct i2c_board_info *info, | ||
56 | const unsigned short *addrs) | ||
57 | { | ||
58 | const struct dmi_device *dmi_dev; | ||
59 | const struct dmi_dev_onboard *dev_data; | ||
60 | struct i2c_adapter *adapter; | ||
61 | struct i2c_client *client; | ||
62 | |||
63 | if (bus < 0) | ||
64 | return NULL; | ||
65 | /* | ||
66 | * If a name is specified, look for irq platform information stashed | ||
67 | * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. | ||
68 | */ | ||
69 | if (name) { | ||
70 | dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); | ||
71 | if (!dmi_dev) { | ||
72 | pr_err("%s failed to dmi find device %s.\n", | ||
73 | __func__, | ||
74 | name); | ||
75 | return NULL; | ||
76 | } | ||
77 | dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; | ||
78 | if (!dev_data) { | ||
79 | pr_err("%s failed to get data from dmi for %s.\n", | ||
80 | __func__, name); | ||
81 | return NULL; | ||
82 | } | ||
83 | info->irq = dev_data->instance; | ||
84 | } | ||
85 | |||
86 | adapter = i2c_get_adapter(bus); | ||
87 | if (!adapter) { | ||
88 | pr_err("%s failed to get i2c adapter %d.\n", __func__, bus); | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | /* add the i2c device */ | ||
93 | client = i2c_new_probed_device(adapter, info, addrs, NULL); | ||
94 | if (!client) | ||
95 | pr_err("%s failed to register device %d-%02x\n", | ||
96 | __func__, bus, info->addr); | ||
97 | else | ||
98 | pr_debug("%s added i2c device %d-%02x\n", | ||
99 | __func__, bus, info->addr); | ||
100 | |||
101 | i2c_put_adapter(adapter); | ||
102 | return client; | ||
103 | } | ||
104 | |||
105 | static int __init __find_i2c_adap(struct device *dev, void *data) | ||
106 | { | ||
107 | const char *name = data; | ||
108 | static const char *prefix = "i2c-"; | ||
109 | struct i2c_adapter *adapter; | ||
110 | if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) | ||
111 | return 0; | ||
112 | adapter = to_i2c_adapter(dev); | ||
113 | return (strncmp(adapter->name, name, strlen(name)) == 0); | ||
114 | } | ||
115 | |||
116 | static int __init find_i2c_adapter_num(enum i2c_adapter_type type) | ||
117 | { | ||
118 | struct device *dev = NULL; | ||
119 | struct i2c_adapter *adapter; | ||
120 | const char *name = i2c_adapter_names[type]; | ||
121 | /* find the adapter by name */ | ||
122 | dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, | ||
123 | __find_i2c_adap); | ||
124 | if (!dev) { | ||
125 | pr_err("%s: i2c adapter %s not found on system.\n", __func__, | ||
126 | name); | ||
127 | return -ENODEV; | ||
128 | } | ||
129 | adapter = to_i2c_adapter(dev); | ||
130 | return adapter->nr; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Probes for a device at a single address, the one provided by | ||
135 | * info->addr. | ||
136 | * Returns NULL if no device found. | ||
137 | */ | ||
138 | static struct i2c_client __init *add_smbus_device(const char *name, | ||
139 | struct i2c_board_info *info) | ||
140 | { | ||
141 | const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; | ||
142 | return __add_probed_i2c_device(name, | ||
143 | find_i2c_adapter_num(I2C_ADAPTER_SMBUS), | ||
144 | info, | ||
145 | addr_list); | ||
146 | } | ||
147 | |||
148 | static int __init setup_lumpy_tp(const struct dmi_system_id *id) | ||
149 | { | ||
150 | /* add cyapa touchpad on smbus */ | ||
151 | tp = add_smbus_device("trackpad", &cyapa_device); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int __init setup_isl29018_als(const struct dmi_system_id *id) | ||
156 | { | ||
157 | /* add isl29018 light sensor */ | ||
158 | als = add_smbus_device("lightsensor", &isl_als_device); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = { | ||
163 | { | ||
164 | .ident = "Samsung Series 5 550 - Touchpad", | ||
165 | .matches = { | ||
166 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), | ||
167 | DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), | ||
168 | }, | ||
169 | .callback = setup_lumpy_tp, | ||
170 | }, | ||
171 | { | ||
172 | .ident = "Samsung Series 5 550 - Light Sensor", | ||
173 | .matches = { | ||
174 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), | ||
175 | DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), | ||
176 | }, | ||
177 | .callback = setup_isl29018_als, | ||
178 | }, | ||
179 | { } | ||
180 | }; | ||
181 | MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); | ||
182 | |||
183 | static int __init chromeos_laptop_init(void) | ||
184 | { | ||
185 | if (!dmi_check_system(chromeos_laptop_dmi_table)) { | ||
186 | pr_debug("%s unsupported system.\n", __func__); | ||
187 | return -ENODEV; | ||
188 | } | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static void __exit chromeos_laptop_exit(void) | ||
193 | { | ||
194 | if (als) | ||
195 | i2c_unregister_device(als); | ||
196 | if (tp) | ||
197 | i2c_unregister_device(tp); | ||
198 | } | ||
199 | |||
200 | module_init(chromeos_laptop_init); | ||
201 | module_exit(chromeos_laptop_exit); | ||
202 | |||
203 | MODULE_DESCRIPTION("Chrome OS Laptop driver"); | ||
204 | MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); | ||
205 | MODULE_LICENSE("GPL"); | ||