blob: 2a81ae4c15c935ce7c28c830742e7b43ac415923 [file] [log] [blame]
Dmitry Torokhov203e0ba2018-03-20 15:31:28 -07001// SPDX-License-Identifier: GPL-2.0+
2// Driver to instantiate Chromebook i2c/smbus devices.
3//
4// Copyright (C) 2012 Google, Inc.
5// Author: Benson Leung <bleung@chromium.org>
Benson Leungd1381f42012-10-25 14:21:21 -07006
Dmitry Torokhov4f27f672018-03-20 15:31:30 -07007#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
Benson Leungd1381f42012-10-25 14:21:21 -07009#include <linux/dmi.h>
10#include <linux/i2c.h>
Nick Dyer7f3884f72015-08-04 16:36:29 -070011#include <linux/platform_data/atmel_mxt_ts.h>
Benson Leung2ef39202013-03-07 19:43:34 -080012#include <linux/input.h>
13#include <linux/interrupt.h>
Benson Leungd1381f42012-10-25 14:21:21 -070014#include <linux/module.h>
Benson Leung9ad36922013-10-20 20:58:25 -070015#include <linux/platform_device.h>
Benson Leungd1381f42012-10-25 14:21:21 -070016
Benson Leung8e1ad4c2013-02-21 12:14:59 -080017#define ATMEL_TP_I2C_ADDR 0x4b
18#define ATMEL_TP_I2C_BL_ADDR 0x25
Yufeng Shen33a84f82013-02-21 12:15:00 -080019#define ATMEL_TS_I2C_ADDR 0x4a
20#define ATMEL_TS_I2C_BL_ADDR 0x26
Benson Leungd1381f42012-10-25 14:21:21 -070021#define CYAPA_TP_I2C_ADDR 0x67
Benson Leung9bd9a902016-05-02 08:57:16 +080022#define ELAN_TP_I2C_ADDR 0x15
Benson Leungd1381f42012-10-25 14:21:21 -070023#define ISL_ALS_I2C_ADDR 0x44
Benson Leungaabf3f42013-02-01 14:34:45 -080024#define TAOS_ALS_I2C_ADDR 0x29
Benson Leungd1381f42012-10-25 14:21:21 -070025
Benson Leung55024862014-07-15 17:43:11 -070026#define MAX_I2C_DEVICE_DEFERRALS 5
27
Olof Johansson6d3c1af2013-11-25 13:10:25 -080028static const char *i2c_adapter_names[] = {
Benson Leungd1381f42012-10-25 14:21:21 -070029 "SMBus I801 adapter",
Benson Leung741bf0c2013-02-21 12:14:55 -080030 "i915 gmbus vga",
31 "i915 gmbus panel",
Jarkko Nikulaebaf31c2015-11-03 13:09:00 +020032 "Synopsys DesignWare I2C adapter",
33 "Synopsys DesignWare I2C adapter",
Benson Leungd1381f42012-10-25 14:21:21 -070034};
35
36/* Keep this enum consistent with i2c_adapter_names */
37enum i2c_adapter_type {
38 I2C_ADAPTER_SMBUS = 0,
Benson Leung741bf0c2013-02-21 12:14:55 -080039 I2C_ADAPTER_VGADDC,
40 I2C_ADAPTER_PANEL,
Mika Westerbergda3b0ab2014-06-17 14:02:00 -070041 I2C_ADAPTER_DESIGNWARE_0,
42 I2C_ADAPTER_DESIGNWARE_1,
Benson Leungd1381f42012-10-25 14:21:21 -070043};
44
Benson Leung55024862014-07-15 17:43:11 -070045enum i2c_peripheral_state {
46 UNPROBED = 0,
47 PROBED,
48 TIMEDOUT,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070049 FAILED,
Benson Leungd1381f42012-10-25 14:21:21 -070050};
51
Aaron Durbinec199dd2013-10-20 20:58:24 -070052struct i2c_peripheral {
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070053 struct i2c_board_info board_info;
54 unsigned short alt_addr;
55 const char *dmi_name;
Aaron Durbinec199dd2013-10-20 20:58:24 -070056 enum i2c_adapter_type type;
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070057
Benson Leung55024862014-07-15 17:43:11 -070058 enum i2c_peripheral_state state;
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070059 struct i2c_client *client;
Benson Leung55024862014-07-15 17:43:11 -070060 int tries;
Aaron Durbinec199dd2013-10-20 20:58:24 -070061};
62
Benson Leung9bd9a902016-05-02 08:57:16 +080063#define MAX_I2C_PERIPHERALS 4
Aaron Durbinec199dd2013-10-20 20:58:24 -070064
65struct chromeos_laptop {
66 struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
67};
68
Benson Leung9ad36922013-10-20 20:58:25 -070069static struct chromeos_laptop *cros_laptop;
70
Dmitry Torokhovab6c5602018-03-20 15:31:31 -070071static int chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
72{
73 const struct dmi_device *dmi_dev;
74 const struct dmi_dev_onboard *dev_data;
75
76 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
77 if (!dmi_dev) {
78 pr_err("failed to find DMI device '%s'\n", dmi_name);
79 return -ENOENT;
80 }
81
82 dev_data = dmi_dev->device_data;
83 if (!dev_data) {
84 pr_err("failed to get data from DMI for '%s'\n", dmi_name);
85 return -EINVAL;
86 }
87
88 return dev_data->instance;
89}
90
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070091static struct i2c_client *
92chromes_laptop_instantiate_i2c_device(int bus,
93 struct i2c_board_info *info,
94 unsigned short alt_addr)
Benson Leungd1381f42012-10-25 14:21:21 -070095{
Benson Leungd1381f42012-10-25 14:21:21 -070096 struct i2c_adapter *adapter;
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070097 struct i2c_client *client = NULL;
98 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
Benson Leungd1381f42012-10-25 14:21:21 -070099
Benson Leungd1381f42012-10-25 14:21:21 -0700100 adapter = i2c_get_adapter(bus);
101 if (!adapter) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700102 pr_err("failed to get i2c adapter %d\n", bus);
Benson Leungd1381f42012-10-25 14:21:21 -0700103 return NULL;
104 }
105
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -0700106 /*
107 * Add the i2c device. If we can't detect it at the primary
108 * address we scan secondary addresses. In any case the client
109 * structure gets assigned primary address.
110 */
111 client = i2c_new_probed_device(adapter, info, addr_list, NULL);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700112 if (!client && alt_addr) {
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -0700113 struct i2c_board_info dummy_info = {
114 I2C_BOARD_INFO("dummy", info->addr),
115 };
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700116 const unsigned short alt_addr_list[] = {
117 alt_addr, I2C_CLIENT_END
118 };
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -0700119 struct i2c_client *dummy;
120
121 dummy = i2c_new_probed_device(adapter, &dummy_info,
122 alt_addr_list, NULL);
123 if (dummy) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700124 pr_debug("%d-%02x is probed at %02x\n",
125 bus, info->addr, dummy->addr);
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -0700126 i2c_unregister_device(dummy);
127 client = i2c_new_device(adapter, info);
128 }
129 }
130
Benson Leungd1381f42012-10-25 14:21:21 -0700131 if (!client)
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700132 pr_notice("failed to register device %d-%02x\n",
133 bus, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700134 else
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700135 pr_debug("added i2c device %d-%02x\n", bus, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700136
137 i2c_put_adapter(adapter);
138 return client;
139}
140
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700141struct i2c_lookup {
142 const char *name;
143 int instance;
144 int n;
145};
146
Benson Leung9ad36922013-10-20 20:58:25 -0700147static int __find_i2c_adap(struct device *dev, void *data)
Benson Leungd1381f42012-10-25 14:21:21 -0700148{
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700149 struct i2c_lookup *lookup = data;
Benson Leungd1381f42012-10-25 14:21:21 -0700150 static const char *prefix = "i2c-";
151 struct i2c_adapter *adapter;
Robin Schroer49c68a22014-05-29 20:45:07 +0200152
Benson Leungd1381f42012-10-25 14:21:21 -0700153 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
154 return 0;
155 adapter = to_i2c_adapter(dev);
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700156 if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
157 lookup->n++ == lookup->instance)
158 return 1;
159 return 0;
Benson Leungd1381f42012-10-25 14:21:21 -0700160}
161
Benson Leung9ad36922013-10-20 20:58:25 -0700162static int find_i2c_adapter_num(enum i2c_adapter_type type)
Benson Leungd1381f42012-10-25 14:21:21 -0700163{
164 struct device *dev = NULL;
165 struct i2c_adapter *adapter;
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700166 struct i2c_lookup lookup;
167
168 memset(&lookup, 0, sizeof(lookup));
169 lookup.name = i2c_adapter_names[type];
170 lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0;
171
Benson Leungd1381f42012-10-25 14:21:21 -0700172 /* find the adapter by name */
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700173 dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
Benson Leungd1381f42012-10-25 14:21:21 -0700174 if (!dev) {
Benson Leung9ad36922013-10-20 20:58:25 -0700175 /* Adapters may appear later. Deferred probing will retry */
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700176 pr_notice("i2c adapter %s not found on system.\n",
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700177 lookup.name);
Benson Leungd1381f42012-10-25 14:21:21 -0700178 return -ENODEV;
179 }
180 adapter = to_i2c_adapter(dev);
181 return adapter->nr;
182}
183
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700184static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev)
Benson Leungbcaf0892013-02-21 12:14:58 -0800185{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700186 struct i2c_client *client;
187 int bus;
188 int irq;
Benson Leungbcaf0892013-02-21 12:14:58 -0800189
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700190 /*
191 * Check that the i2c adapter is present.
192 * -EPROBE_DEFER if missing as the adapter may appear much
193 * later.
194 */
195 bus = find_i2c_adapter_num(i2c_dev->type);
196 if (bus < 0)
197 return bus == -ENODEV ? -EPROBE_DEFER : bus;
Benson Leungd1381f42012-10-25 14:21:21 -0700198
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700199 if (i2c_dev->dmi_name) {
200 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
201 if (irq < 0) {
202 i2c_dev->state = FAILED;
203 return irq;
204 }
Benson Leung9ad36922013-10-20 20:58:25 -0700205
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700206 i2c_dev->board_info.irq = irq;
207 }
Benson Leungd1381f42012-10-25 14:21:21 -0700208
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700209 client = chromes_laptop_instantiate_i2c_device(bus,
210 &i2c_dev->board_info,
211 i2c_dev->alt_addr);
212 if (!client) {
213 /*
214 * Set -EPROBE_DEFER a limited num of times
215 * if device is not successfully added.
216 */
217 if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) {
218 return -EPROBE_DEFER;
219 } else {
220 /* Ran out of tries. */
221 pr_notice("ran out of tries for device.\n");
222 i2c_dev->state = TIMEDOUT;
223 return -EIO;
224 }
225 }
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800226
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700227 i2c_dev->client = client;
228 i2c_dev->state = PROBED;
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800229
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700230 return 0;
Benson Leung8016bcb2013-02-01 14:34:46 -0800231}
232
Benson Leung9ad36922013-10-20 20:58:25 -0700233static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id)
234{
235 cros_laptop = (void *)id->driver_data;
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700236 pr_debug("DMI Matched %s\n", id->ident);
Benson Leung9ad36922013-10-20 20:58:25 -0700237
238 /* Indicate to dmi_scan that processing is done. */
239 return 1;
240}
241
242static int chromeos_laptop_probe(struct platform_device *pdev)
Benson Leungaabf3f42013-02-01 14:34:45 -0800243{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700244 struct i2c_peripheral *i2c_dev;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700245 int i;
Benson Leung9ad36922013-10-20 20:58:25 -0700246 int ret = 0;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700247
248 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700249 i2c_dev = &cros_laptop->i2c_peripherals[i];
250
251 /* No more peripherals. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700252 if (!i2c_dev->board_info.addr)
Aaron Durbinec199dd2013-10-20 20:58:24 -0700253 break;
254
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700255 if (i2c_dev->state != UNPROBED)
Benson Leung55024862014-07-15 17:43:11 -0700256 continue;
257
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700258 if (chromeos_laptop_add_peripheral(i2c_dev) == -EPROBE_DEFER)
Benson Leung9ad36922013-10-20 20:58:25 -0700259 ret = -EPROBE_DEFER;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700260 }
261
Benson Leung9ad36922013-10-20 20:58:25 -0700262 return ret;
Benson Leungaabf3f42013-02-01 14:34:45 -0800263}
264
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800265static struct chromeos_laptop samsung_series_5_550 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700266 .i2c_peripherals = {
267 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700268 {
269 .board_info = {
270 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
271 .flags = I2C_CLIENT_WAKE,
272 },
273 .dmi_name = "trackpad",
274 .type = I2C_ADAPTER_SMBUS,
275 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700276 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700277 {
278 .board_info = {
279 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
280 },
281 .dmi_name = "lightsensor",
282 .type = I2C_ADAPTER_SMBUS,
283 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700284 },
285};
286
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800287static struct chromeos_laptop samsung_series_5 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700288 .i2c_peripherals = {
289 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700290 {
291 .board_info = {
292 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
293 },
294 .type = I2C_ADAPTER_SMBUS,
295 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700296 },
297};
298
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700299static struct mxt_platform_data atmel_1664s_platform_data = {
300 .irqflags = IRQF_TRIGGER_FALLING,
301};
302
303static int chromebook_pixel_tp_keys[] = {
304 KEY_RESERVED,
305 KEY_RESERVED,
306 KEY_RESERVED,
307 KEY_RESERVED,
308 KEY_RESERVED,
309 BTN_LEFT
310};
311
312static struct mxt_platform_data chromebook_pixel_tp_platform_data = {
313 .irqflags = IRQF_TRIGGER_FALLING,
314 .t19_num_keys = ARRAY_SIZE(chromebook_pixel_tp_keys),
315 .t19_keymap = chromebook_pixel_tp_keys,
316};
317
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800318static struct chromeos_laptop chromebook_pixel = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700319 .i2c_peripherals = {
320 /* Touch Screen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700321 {
322 .board_info = {
323 I2C_BOARD_INFO("atmel_mxt_ts",
324 ATMEL_TS_I2C_ADDR),
325 .platform_data = &atmel_1664s_platform_data,
326 .flags = I2C_CLIENT_WAKE,
327 },
328 .dmi_name = "touchscreen",
329 .type = I2C_ADAPTER_PANEL,
330 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
331 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700332 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700333 {
334 .board_info = {
335 I2C_BOARD_INFO("atmel_mxt_tp",
336 ATMEL_TP_I2C_ADDR),
337 .platform_data =
338 &chromebook_pixel_tp_platform_data,
339 .flags = I2C_CLIENT_WAKE,
340 },
341 .dmi_name = "trackpad",
342 .type = I2C_ADAPTER_VGADDC,
343 .alt_addr = ATMEL_TP_I2C_BL_ADDR,
344 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700345 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700346 {
347 .board_info = {
348 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
349 },
350 .dmi_name = "lightsensor",
351 .type = I2C_ADAPTER_PANEL,
352 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700353 },
354};
355
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800356static struct chromeos_laptop hp_chromebook_14 = {
Benson Leung5ea95672014-06-17 14:02:01 -0700357 .i2c_peripherals = {
358 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700359 {
360 .board_info = {
361 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
362 .flags = I2C_CLIENT_WAKE,
363 },
364 .dmi_name = "trackpad",
365 .type = I2C_ADAPTER_DESIGNWARE_0,
366 },
Benson Leung5ea95672014-06-17 14:02:01 -0700367 },
368};
369
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800370static struct chromeos_laptop dell_chromebook_11 = {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700371 .i2c_peripherals = {
372 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700373 {
374 .board_info = {
375 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
376 .flags = I2C_CLIENT_WAKE,
377 },
378 .dmi_name = "trackpad",
379 .type = I2C_ADAPTER_DESIGNWARE_0,
380 },
Charlie Mooney9e96aa72016-05-02 08:57:17 +0800381 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700382 {
383 .board_info = {
384 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
385 .flags = I2C_CLIENT_WAKE,
386 },
387 .dmi_name = "trackpad",
388 .type = I2C_ADAPTER_DESIGNWARE_0,
389 },
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700390 },
391};
392
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800393static struct chromeos_laptop toshiba_cb35 = {
Gene Chen963cb6f2014-06-17 14:02:03 -0700394 .i2c_peripherals = {
395 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700396 {
397 .board_info = {
398 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
399 .flags = I2C_CLIENT_WAKE,
400 },
401 .dmi_name = "trackpad",
402 .type = I2C_ADAPTER_DESIGNWARE_0,
403 },
Gene Chen963cb6f2014-06-17 14:02:03 -0700404 },
405};
406
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800407static struct chromeos_laptop acer_c7_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700408 .i2c_peripherals = {
409 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700410 {
411 .board_info = {
412 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
413 .flags = I2C_CLIENT_WAKE,
414 },
415 .dmi_name = "trackpad",
416 .type = I2C_ADAPTER_SMBUS,
417 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700418 },
419};
420
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800421static struct chromeos_laptop acer_ac700 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700422 .i2c_peripherals = {
423 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700424 {
425 .board_info = {
426 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
427 },
428 .type = I2C_ADAPTER_SMBUS,
429 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700430 },
431};
432
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800433static struct chromeos_laptop acer_c720 = {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700434 .i2c_peripherals = {
Michael Mullinb90b3c42014-07-15 20:00:54 -0400435 /* Touchscreen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700436 {
437 .board_info = {
438 I2C_BOARD_INFO("atmel_mxt_ts",
439 ATMEL_TS_I2C_ADDR),
440 .platform_data = &atmel_1664s_platform_data,
441 .flags = I2C_CLIENT_WAKE,
442 },
443 .dmi_name = "touchscreen",
444 .type = I2C_ADAPTER_DESIGNWARE_1,
445 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
446 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700447 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700448 {
449 .board_info = {
450 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
451 .flags = I2C_CLIENT_WAKE,
452 },
453 .dmi_name = "trackpad",
454 .type = I2C_ADAPTER_DESIGNWARE_0,
455 },
Benson Leung9bd9a902016-05-02 08:57:16 +0800456 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700457 {
458 .board_info = {
459 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
460 .flags = I2C_CLIENT_WAKE,
461 },
462 .dmi_name = "trackpad",
463 .type = I2C_ADAPTER_DESIGNWARE_0,
464 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700465 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700466 {
467 .board_info = {
468 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
469 },
470 .dmi_name = "lightsensor",
471 .type = I2C_ADAPTER_DESIGNWARE_1,
472 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700473 },
474};
475
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800476static struct chromeos_laptop hp_pavilion_14_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700477 .i2c_peripherals = {
478 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700479 {
480 .board_info = {
481 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
482 .flags = I2C_CLIENT_WAKE,
483 },
484 .dmi_name = "trackpad",
485 .type = I2C_ADAPTER_SMBUS,
486 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700487 },
488};
489
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800490static struct chromeos_laptop cr48 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700491 .i2c_peripherals = {
492 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700493 {
494 .board_info = {
495 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
496 },
497 .type = I2C_ADAPTER_SMBUS,
498 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700499 },
500};
501
502#define _CBDD(board_) \
Benson Leung9ad36922013-10-20 20:58:25 -0700503 .callback = chromeos_laptop_dmi_matched, \
Aaron Durbinec199dd2013-10-20 20:58:24 -0700504 .driver_data = (void *)&board_
505
Christoph Hellwig6faadbb2017-09-14 11:59:30 +0200506static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
Benson Leungd1381f42012-10-25 14:21:21 -0700507 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700508 .ident = "Samsung Series 5 550",
Benson Leungd1381f42012-10-25 14:21:21 -0700509 .matches = {
510 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
511 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
512 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700513 _CBDD(samsung_series_5_550),
Benson Leungd1381f42012-10-25 14:21:21 -0700514 },
515 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700516 .ident = "Samsung Series 5",
Benson Leung8016bcb2013-02-01 14:34:46 -0800517 .matches = {
518 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
519 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700520 _CBDD(samsung_series_5),
Benson Leung8016bcb2013-02-01 14:34:46 -0800521 },
522 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700523 .ident = "Chromebook Pixel",
Benson Leungaabf3f42013-02-01 14:34:45 -0800524 .matches = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700525 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
526 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
Benson Leungaabf3f42013-02-01 14:34:45 -0800527 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700528 _CBDD(chromebook_pixel),
Benson Leungaabf3f42013-02-01 14:34:45 -0800529 },
530 {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700531 .ident = "Wolf",
532 .matches = {
533 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
534 DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
535 },
536 _CBDD(dell_chromebook_11),
537 },
538 {
Benson Leung5ea95672014-06-17 14:02:01 -0700539 .ident = "HP Chromebook 14",
540 .matches = {
541 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
542 DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
543 },
544 _CBDD(hp_chromebook_14),
545 },
546 {
Gene Chen963cb6f2014-06-17 14:02:03 -0700547 .ident = "Toshiba CB35",
548 .matches = {
549 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
550 DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
551 },
552 _CBDD(toshiba_cb35),
553 },
554 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700555 .ident = "Acer C7 Chromebook",
556 .matches = {
557 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
558 },
559 _CBDD(acer_c7_chromebook),
560 },
561 {
562 .ident = "Acer AC700",
Benson Leungaabf3f42013-02-01 14:34:45 -0800563 .matches = {
564 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
565 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700566 _CBDD(acer_ac700),
567 },
568 {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700569 .ident = "Acer C720",
570 .matches = {
571 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
572 },
573 _CBDD(acer_c720),
574 },
575 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700576 .ident = "HP Pavilion 14 Chromebook",
577 .matches = {
578 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
579 },
580 _CBDD(hp_pavilion_14_chromebook),
581 },
582 {
583 .ident = "Cr-48",
584 .matches = {
585 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
586 },
587 _CBDD(cr48),
Benson Leungaabf3f42013-02-01 14:34:45 -0800588 },
Benson Leungd1381f42012-10-25 14:21:21 -0700589 { }
590};
591MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
592
Benson Leung9ad36922013-10-20 20:58:25 -0700593static struct platform_device *cros_platform_device;
594
595static struct platform_driver cros_platform_driver = {
596 .driver = {
597 .name = "chromeos_laptop",
Benson Leung9ad36922013-10-20 20:58:25 -0700598 },
599 .probe = chromeos_laptop_probe,
600};
601
Benson Leungd1381f42012-10-25 14:21:21 -0700602static int __init chromeos_laptop_init(void)
603{
Benson Leung9ad36922013-10-20 20:58:25 -0700604 int ret;
Robin Schroer49c68a22014-05-29 20:45:07 +0200605
Benson Leungd1381f42012-10-25 14:21:21 -0700606 if (!dmi_check_system(chromeos_laptop_dmi_table)) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700607 pr_debug("unsupported system\n");
Benson Leungd1381f42012-10-25 14:21:21 -0700608 return -ENODEV;
609 }
Benson Leung9ad36922013-10-20 20:58:25 -0700610
611 ret = platform_driver_register(&cros_platform_driver);
612 if (ret)
613 return ret;
614
615 cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
616 if (!cros_platform_device) {
617 ret = -ENOMEM;
618 goto fail_platform_device1;
619 }
620
621 ret = platform_device_add(cros_platform_device);
622 if (ret)
623 goto fail_platform_device2;
624
Benson Leungd1381f42012-10-25 14:21:21 -0700625 return 0;
Benson Leung9ad36922013-10-20 20:58:25 -0700626
627fail_platform_device2:
628 platform_device_put(cros_platform_device);
629fail_platform_device1:
630 platform_driver_unregister(&cros_platform_driver);
631 return ret;
Benson Leungd1381f42012-10-25 14:21:21 -0700632}
633
634static void __exit chromeos_laptop_exit(void)
635{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700636 struct i2c_peripheral *i2c_dev;
637 int i;
Wei Yongjun2b8454a2013-11-27 11:34:58 +0800638
639 platform_device_unregister(cros_platform_device);
640 platform_driver_unregister(&cros_platform_driver);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700641
642 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
643 i2c_dev = &cros_laptop->i2c_peripherals[i];
644
645 /* No more peripherals */
646 if (!i2c_dev->board_info.type)
647 break;
648
649 if (i2c_dev->state == PROBED)
650 i2c_unregister_device(i2c_dev->client);
651 }
Benson Leungd1381f42012-10-25 14:21:21 -0700652}
653
654module_init(chromeos_laptop_init);
655module_exit(chromeos_laptop_exit);
656
657MODULE_DESCRIPTION("Chrome OS Laptop driver");
658MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
659MODULE_LICENSE("GPL");