blob: 188b7dac77806c8e3b9f37a7a5bea1347ea13f38 [file] [log] [blame]
Benson Leungd1381f42012-10-25 14:21:21 -07001/*
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
Benson Leungaabf3f42013-02-01 14:34:45 -080030#define TAOS_ALS_I2C_ADDR 0x29
Benson Leungd1381f42012-10-25 14:21:21 -070031
32static struct i2c_client *als;
33static struct i2c_client *tp;
34
35const char *i2c_adapter_names[] = {
36 "SMBus I801 adapter",
37};
38
39/* Keep this enum consistent with i2c_adapter_names */
40enum i2c_adapter_type {
41 I2C_ADAPTER_SMBUS = 0,
42};
43
44static struct i2c_board_info __initdata cyapa_device = {
45 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
46 .flags = I2C_CLIENT_WAKE,
47};
48
49static struct i2c_board_info __initdata isl_als_device = {
50 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
51};
52
Benson Leungaabf3f42013-02-01 14:34:45 -080053static struct i2c_board_info __initdata tsl2563_als_device = {
54 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
55};
56
Benson Leungd1381f42012-10-25 14:21:21 -070057static struct i2c_client __init *__add_probed_i2c_device(
58 const char *name,
59 int bus,
60 struct i2c_board_info *info,
61 const unsigned short *addrs)
62{
63 const struct dmi_device *dmi_dev;
64 const struct dmi_dev_onboard *dev_data;
65 struct i2c_adapter *adapter;
66 struct i2c_client *client;
67
68 if (bus < 0)
69 return NULL;
70 /*
71 * If a name is specified, look for irq platform information stashed
72 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
73 */
74 if (name) {
75 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
76 if (!dmi_dev) {
77 pr_err("%s failed to dmi find device %s.\n",
78 __func__,
79 name);
80 return NULL;
81 }
82 dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
83 if (!dev_data) {
84 pr_err("%s failed to get data from dmi for %s.\n",
85 __func__, name);
86 return NULL;
87 }
88 info->irq = dev_data->instance;
89 }
90
91 adapter = i2c_get_adapter(bus);
92 if (!adapter) {
93 pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
94 return NULL;
95 }
96
97 /* add the i2c device */
98 client = i2c_new_probed_device(adapter, info, addrs, NULL);
99 if (!client)
100 pr_err("%s failed to register device %d-%02x\n",
101 __func__, bus, info->addr);
102 else
103 pr_debug("%s added i2c device %d-%02x\n",
104 __func__, bus, info->addr);
105
106 i2c_put_adapter(adapter);
107 return client;
108}
109
110static int __init __find_i2c_adap(struct device *dev, void *data)
111{
112 const char *name = data;
113 static const char *prefix = "i2c-";
114 struct i2c_adapter *adapter;
115 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
116 return 0;
117 adapter = to_i2c_adapter(dev);
118 return (strncmp(adapter->name, name, strlen(name)) == 0);
119}
120
121static int __init find_i2c_adapter_num(enum i2c_adapter_type type)
122{
123 struct device *dev = NULL;
124 struct i2c_adapter *adapter;
125 const char *name = i2c_adapter_names[type];
126 /* find the adapter by name */
127 dev = bus_find_device(&i2c_bus_type, NULL, (void *)name,
128 __find_i2c_adap);
129 if (!dev) {
130 pr_err("%s: i2c adapter %s not found on system.\n", __func__,
131 name);
132 return -ENODEV;
133 }
134 adapter = to_i2c_adapter(dev);
135 return adapter->nr;
136}
137
138/*
139 * Probes for a device at a single address, the one provided by
140 * info->addr.
141 * Returns NULL if no device found.
142 */
143static struct i2c_client __init *add_smbus_device(const char *name,
144 struct i2c_board_info *info)
145{
146 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
147 return __add_probed_i2c_device(name,
148 find_i2c_adapter_num(I2C_ADAPTER_SMBUS),
149 info,
150 addr_list);
151}
152
Benson Leung6e710942013-02-01 14:34:43 -0800153static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id)
Benson Leungd1381f42012-10-25 14:21:21 -0700154{
155 /* add cyapa touchpad on smbus */
156 tp = add_smbus_device("trackpad", &cyapa_device);
157 return 0;
158}
159
160static int __init setup_isl29018_als(const struct dmi_system_id *id)
161{
162 /* add isl29018 light sensor */
163 als = add_smbus_device("lightsensor", &isl_als_device);
164 return 0;
165}
166
Benson Leungaabf3f42013-02-01 14:34:45 -0800167static int __init setup_tsl2563_als(const struct dmi_system_id *id)
168{
169 /* add tsl2563 light sensor on smbus */
170 als = add_smbus_device(NULL, &tsl2563_als_device);
171 return 0;
172}
173
Benson Leungd1381f42012-10-25 14:21:21 -0700174static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = {
175 {
176 .ident = "Samsung Series 5 550 - Touchpad",
177 .matches = {
178 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
179 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
180 },
Benson Leung6e710942013-02-01 14:34:43 -0800181 .callback = setup_cyapa_smbus_tp,
Benson Leungd1381f42012-10-25 14:21:21 -0700182 },
183 {
184 .ident = "Samsung Series 5 550 - Light Sensor",
185 .matches = {
186 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
187 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
188 },
189 .callback = setup_isl29018_als,
190 },
Benson Leung261f1712013-02-01 14:34:44 -0800191 {
192 .ident = "Acer C7 Chromebook - Touchpad",
193 .matches = {
194 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
195 },
196 .callback = setup_cyapa_smbus_tp,
197 },
Benson Leungaabf3f42013-02-01 14:34:45 -0800198 {
199 .ident = "Cr-48 - Light Sensor",
200 .matches = {
201 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
202 },
203 .callback = setup_tsl2563_als,
204 },
205 {
206 .ident = "Acer AC700 - Light Sensor",
207 .matches = {
208 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
209 },
210 .callback = setup_tsl2563_als,
211 },
Benson Leungd1381f42012-10-25 14:21:21 -0700212 { }
213};
214MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
215
216static int __init chromeos_laptop_init(void)
217{
218 if (!dmi_check_system(chromeos_laptop_dmi_table)) {
219 pr_debug("%s unsupported system.\n", __func__);
220 return -ENODEV;
221 }
222 return 0;
223}
224
225static void __exit chromeos_laptop_exit(void)
226{
227 if (als)
228 i2c_unregister_device(als);
229 if (tp)
230 i2c_unregister_device(tp);
231}
232
233module_init(chromeos_laptop_init);
234module_exit(chromeos_laptop_exit);
235
236MODULE_DESCRIPTION("Chrome OS Laptop driver");
237MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
238MODULE_LICENSE("GPL");