xref: /linux/drivers/platform/chrome/chromeos_laptop.c (revision 3932b9ca55b0be314a36d3e84faff3e823c081f5)
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/i2c/atmel_mxt_ts.h>
27 #include <linux/input.h>
28 #include <linux/interrupt.h>
29 #include <linux/module.h>
30 #include <linux/platform_device.h>
31 
32 #define ATMEL_TP_I2C_ADDR	0x4b
33 #define ATMEL_TP_I2C_BL_ADDR	0x25
34 #define ATMEL_TS_I2C_ADDR	0x4a
35 #define ATMEL_TS_I2C_BL_ADDR	0x26
36 #define CYAPA_TP_I2C_ADDR	0x67
37 #define ISL_ALS_I2C_ADDR	0x44
38 #define TAOS_ALS_I2C_ADDR	0x29
39 
40 #define MAX_I2C_DEVICE_DEFERRALS	5
41 
42 static struct i2c_client *als;
43 static struct i2c_client *tp;
44 static struct i2c_client *ts;
45 
46 static const char *i2c_adapter_names[] = {
47 	"SMBus I801 adapter",
48 	"i915 gmbus vga",
49 	"i915 gmbus panel",
50 	"i2c-designware-pci",
51 	"i2c-designware-pci",
52 };
53 
54 /* Keep this enum consistent with i2c_adapter_names */
55 enum i2c_adapter_type {
56 	I2C_ADAPTER_SMBUS = 0,
57 	I2C_ADAPTER_VGADDC,
58 	I2C_ADAPTER_PANEL,
59 	I2C_ADAPTER_DESIGNWARE_0,
60 	I2C_ADAPTER_DESIGNWARE_1,
61 };
62 
63 enum i2c_peripheral_state {
64 	UNPROBED = 0,
65 	PROBED,
66 	TIMEDOUT,
67 };
68 
69 struct i2c_peripheral {
70 	int (*add)(enum i2c_adapter_type type);
71 	enum i2c_adapter_type type;
72 	enum i2c_peripheral_state state;
73 	int tries;
74 };
75 
76 #define MAX_I2C_PERIPHERALS 3
77 
78 struct chromeos_laptop {
79 	struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
80 };
81 
82 static struct chromeos_laptop *cros_laptop;
83 
84 static struct i2c_board_info cyapa_device = {
85 	I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
86 	.flags		= I2C_CLIENT_WAKE,
87 };
88 
89 static struct i2c_board_info isl_als_device = {
90 	I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
91 };
92 
93 static struct i2c_board_info tsl2583_als_device = {
94 	I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
95 };
96 
97 static struct i2c_board_info tsl2563_als_device = {
98 	I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
99 };
100 
101 static int mxt_t19_keys[] = {
102 	KEY_RESERVED,
103 	KEY_RESERVED,
104 	KEY_RESERVED,
105 	KEY_RESERVED,
106 	KEY_RESERVED,
107 	BTN_LEFT
108 };
109 
110 static struct mxt_platform_data atmel_224s_tp_platform_data = {
111 	.irqflags		= IRQF_TRIGGER_FALLING,
112 	.t19_num_keys		= ARRAY_SIZE(mxt_t19_keys),
113 	.t19_keymap		= mxt_t19_keys,
114 };
115 
116 static struct i2c_board_info atmel_224s_tp_device = {
117 	I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR),
118 	.platform_data = &atmel_224s_tp_platform_data,
119 	.flags		= I2C_CLIENT_WAKE,
120 };
121 
122 static struct mxt_platform_data atmel_1664s_platform_data = {
123 	.irqflags		= IRQF_TRIGGER_FALLING,
124 };
125 
126 static struct i2c_board_info atmel_1664s_device = {
127 	I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR),
128 	.platform_data = &atmel_1664s_platform_data,
129 	.flags		= I2C_CLIENT_WAKE,
130 };
131 
132 static struct i2c_client *__add_probed_i2c_device(
133 		const char *name,
134 		int bus,
135 		struct i2c_board_info *info,
136 		const unsigned short *addrs)
137 {
138 	const struct dmi_device *dmi_dev;
139 	const struct dmi_dev_onboard *dev_data;
140 	struct i2c_adapter *adapter;
141 	struct i2c_client *client;
142 
143 	if (bus < 0)
144 		return NULL;
145 	/*
146 	 * If a name is specified, look for irq platform information stashed
147 	 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware.
148 	 */
149 	if (name) {
150 		dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL);
151 		if (!dmi_dev) {
152 			pr_err("%s failed to dmi find device %s.\n",
153 			       __func__,
154 			       name);
155 			return NULL;
156 		}
157 		dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data;
158 		if (!dev_data) {
159 			pr_err("%s failed to get data from dmi for %s.\n",
160 			       __func__, name);
161 			return NULL;
162 		}
163 		info->irq = dev_data->instance;
164 	}
165 
166 	adapter = i2c_get_adapter(bus);
167 	if (!adapter) {
168 		pr_err("%s failed to get i2c adapter %d.\n", __func__, bus);
169 		return NULL;
170 	}
171 
172 	/* add the i2c device */
173 	client = i2c_new_probed_device(adapter, info, addrs, NULL);
174 	if (!client)
175 		pr_notice("%s failed to register device %d-%02x\n",
176 			  __func__, bus, info->addr);
177 	else
178 		pr_debug("%s added i2c device %d-%02x\n",
179 			 __func__, bus, info->addr);
180 
181 	i2c_put_adapter(adapter);
182 	return client;
183 }
184 
185 struct i2c_lookup {
186 	const char *name;
187 	int instance;
188 	int n;
189 };
190 
191 static int __find_i2c_adap(struct device *dev, void *data)
192 {
193 	struct i2c_lookup *lookup = data;
194 	static const char *prefix = "i2c-";
195 	struct i2c_adapter *adapter;
196 
197 	if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
198 		return 0;
199 	adapter = to_i2c_adapter(dev);
200 	if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
201 	    lookup->n++ == lookup->instance)
202 		return 1;
203 	return 0;
204 }
205 
206 static int find_i2c_adapter_num(enum i2c_adapter_type type)
207 {
208 	struct device *dev = NULL;
209 	struct i2c_adapter *adapter;
210 	struct i2c_lookup lookup;
211 
212 	memset(&lookup, 0, sizeof(lookup));
213 	lookup.name = i2c_adapter_names[type];
214 	lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0;
215 
216 	/* find the adapter by name */
217 	dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
218 	if (!dev) {
219 		/* Adapters may appear later. Deferred probing will retry */
220 		pr_notice("%s: i2c adapter %s not found on system.\n", __func__,
221 			  lookup.name);
222 		return -ENODEV;
223 	}
224 	adapter = to_i2c_adapter(dev);
225 	return adapter->nr;
226 }
227 
228 /*
229  * Takes a list of addresses in addrs as such :
230  * { addr1, ... , addrn, I2C_CLIENT_END };
231  * add_probed_i2c_device will use i2c_new_probed_device
232  * and probe for devices at all of the addresses listed.
233  * Returns NULL if no devices found.
234  * See Documentation/i2c/instantiating-devices for more information.
235  */
236 static struct i2c_client *add_probed_i2c_device(
237 		const char *name,
238 		enum i2c_adapter_type type,
239 		struct i2c_board_info *info,
240 		const unsigned short *addrs)
241 {
242 	return __add_probed_i2c_device(name,
243 				       find_i2c_adapter_num(type),
244 				       info,
245 				       addrs);
246 }
247 
248 /*
249  * Probes for a device at a single address, the one provided by
250  * info->addr.
251  * Returns NULL if no device found.
252  */
253 static struct i2c_client *add_i2c_device(const char *name,
254 						enum i2c_adapter_type type,
255 						struct i2c_board_info *info)
256 {
257 	const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
258 
259 	return __add_probed_i2c_device(name,
260 				       find_i2c_adapter_num(type),
261 				       info,
262 				       addr_list);
263 }
264 
265 static int setup_cyapa_tp(enum i2c_adapter_type type)
266 {
267 	if (tp)
268 		return 0;
269 
270 	/* add cyapa touchpad */
271 	tp = add_i2c_device("trackpad", type, &cyapa_device);
272 	return (!tp) ? -EAGAIN : 0;
273 }
274 
275 static int setup_atmel_224s_tp(enum i2c_adapter_type type)
276 {
277 	const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR,
278 					     ATMEL_TP_I2C_ADDR,
279 					     I2C_CLIENT_END };
280 	if (tp)
281 		return 0;
282 
283 	/* add atmel mxt touchpad */
284 	tp = add_probed_i2c_device("trackpad", type,
285 				   &atmel_224s_tp_device, addr_list);
286 	return (!tp) ? -EAGAIN : 0;
287 }
288 
289 static int setup_atmel_1664s_ts(enum i2c_adapter_type type)
290 {
291 	const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR,
292 					     ATMEL_TS_I2C_ADDR,
293 					     I2C_CLIENT_END };
294 	if (ts)
295 		return 0;
296 
297 	/* add atmel mxt touch device */
298 	ts = add_probed_i2c_device("touchscreen", type,
299 				   &atmel_1664s_device, addr_list);
300 	return (!ts) ? -EAGAIN : 0;
301 }
302 
303 static int setup_isl29018_als(enum i2c_adapter_type type)
304 {
305 	if (als)
306 		return 0;
307 
308 	/* add isl29018 light sensor */
309 	als = add_i2c_device("lightsensor", type, &isl_als_device);
310 	return (!als) ? -EAGAIN : 0;
311 }
312 
313 static int setup_tsl2583_als(enum i2c_adapter_type type)
314 {
315 	if (als)
316 		return 0;
317 
318 	/* add tsl2583 light sensor */
319 	als = add_i2c_device(NULL, type, &tsl2583_als_device);
320 	return (!als) ? -EAGAIN : 0;
321 }
322 
323 static int setup_tsl2563_als(enum i2c_adapter_type type)
324 {
325 	if (als)
326 		return 0;
327 
328 	/* add tsl2563 light sensor */
329 	als = add_i2c_device(NULL, type, &tsl2563_als_device);
330 	return (!als) ? -EAGAIN : 0;
331 }
332 
333 static int __init chromeos_laptop_dmi_matched(const struct dmi_system_id *id)
334 {
335 	cros_laptop = (void *)id->driver_data;
336 	pr_debug("DMI Matched %s.\n", id->ident);
337 
338 	/* Indicate to dmi_scan that processing is done. */
339 	return 1;
340 }
341 
342 static int chromeos_laptop_probe(struct platform_device *pdev)
343 {
344 	int i;
345 	int ret = 0;
346 
347 	for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
348 		struct i2c_peripheral *i2c_dev;
349 
350 		i2c_dev = &cros_laptop->i2c_peripherals[i];
351 
352 		/* No more peripherals. */
353 		if (i2c_dev->add == NULL)
354 			break;
355 
356 		if (i2c_dev->state == TIMEDOUT || i2c_dev->state == PROBED)
357 			continue;
358 
359 		/*
360 		 * Check that the i2c adapter is present.
361 		 * -EPROBE_DEFER if missing as the adapter may appear much
362 		 * later.
363 		 */
364 		if (find_i2c_adapter_num(i2c_dev->type) == -ENODEV) {
365 			ret = -EPROBE_DEFER;
366 			continue;
367 		}
368 
369 		/* Add the device. */
370 		if (i2c_dev->add(i2c_dev->type) == -EAGAIN) {
371 			/*
372 			 * Set -EPROBE_DEFER a limited num of times
373 			 * if device is not successfully added.
374 			 */
375 			if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) {
376 				ret = -EPROBE_DEFER;
377 			} else {
378 				/* Ran out of tries. */
379 				pr_notice("%s: Ran out of tries for device.\n",
380 					  __func__);
381 				i2c_dev->state = TIMEDOUT;
382 			}
383 		} else {
384 			i2c_dev->state = PROBED;
385 		}
386 	}
387 
388 	return ret;
389 }
390 
391 static struct chromeos_laptop samsung_series_5_550 = {
392 	.i2c_peripherals = {
393 		/* Touchpad. */
394 		{ .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
395 		/* Light Sensor. */
396 		{ .add = setup_isl29018_als, I2C_ADAPTER_SMBUS },
397 	},
398 };
399 
400 static struct chromeos_laptop samsung_series_5 = {
401 	.i2c_peripherals = {
402 		/* Light Sensor. */
403 		{ .add = setup_tsl2583_als, I2C_ADAPTER_SMBUS },
404 	},
405 };
406 
407 static struct chromeos_laptop chromebook_pixel = {
408 	.i2c_peripherals = {
409 		/* Touch Screen. */
410 		{ .add = setup_atmel_1664s_ts, I2C_ADAPTER_PANEL },
411 		/* Touchpad. */
412 		{ .add = setup_atmel_224s_tp, I2C_ADAPTER_VGADDC },
413 		/* Light Sensor. */
414 		{ .add = setup_isl29018_als, I2C_ADAPTER_PANEL },
415 	},
416 };
417 
418 static struct chromeos_laptop hp_chromebook_14 = {
419 	.i2c_peripherals = {
420 		/* Touchpad. */
421 		{ .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
422 	},
423 };
424 
425 static struct chromeos_laptop dell_chromebook_11 = {
426 	.i2c_peripherals = {
427 		/* Touchpad. */
428 		{ .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
429 	},
430 };
431 
432 static struct chromeos_laptop toshiba_cb35 = {
433 	.i2c_peripherals = {
434 		/* Touchpad. */
435 		{ .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
436 	},
437 };
438 
439 static struct chromeos_laptop acer_c7_chromebook = {
440 	.i2c_peripherals = {
441 		/* Touchpad. */
442 		{ .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
443 	},
444 };
445 
446 static struct chromeos_laptop acer_ac700 = {
447 	.i2c_peripherals = {
448 		/* Light Sensor. */
449 		{ .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
450 	},
451 };
452 
453 static struct chromeos_laptop acer_c720 = {
454 	.i2c_peripherals = {
455 		/* Touchscreen. */
456 		{ .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 },
457 		/* Touchpad. */
458 		{ .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 },
459 		/* Light Sensor. */
460 		{ .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 },
461 	},
462 };
463 
464 static struct chromeos_laptop hp_pavilion_14_chromebook = {
465 	.i2c_peripherals = {
466 		/* Touchpad. */
467 		{ .add = setup_cyapa_tp, I2C_ADAPTER_SMBUS },
468 	},
469 };
470 
471 static struct chromeos_laptop cr48 = {
472 	.i2c_peripherals = {
473 		/* Light Sensor. */
474 		{ .add = setup_tsl2563_als, I2C_ADAPTER_SMBUS },
475 	},
476 };
477 
478 #define _CBDD(board_) \
479 	.callback = chromeos_laptop_dmi_matched, \
480 	.driver_data = (void *)&board_
481 
482 static struct dmi_system_id chromeos_laptop_dmi_table[] __initdata = {
483 	{
484 		.ident = "Samsung Series 5 550",
485 		.matches = {
486 			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
487 			DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
488 		},
489 		_CBDD(samsung_series_5_550),
490 	},
491 	{
492 		.ident = "Samsung Series 5",
493 		.matches = {
494 			DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
495 		},
496 		_CBDD(samsung_series_5),
497 	},
498 	{
499 		.ident = "Chromebook Pixel",
500 		.matches = {
501 			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
502 			DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
503 		},
504 		_CBDD(chromebook_pixel),
505 	},
506 	{
507 		.ident = "Wolf",
508 		.matches = {
509 			DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
510 			DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
511 		},
512 		_CBDD(dell_chromebook_11),
513 	},
514 	{
515 		.ident = "HP Chromebook 14",
516 		.matches = {
517 			DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
518 			DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
519 		},
520 		_CBDD(hp_chromebook_14),
521 	},
522 	{
523 		.ident = "Toshiba CB35",
524 		.matches = {
525 			DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
526 			DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
527 		},
528 		_CBDD(toshiba_cb35),
529 	},
530 	{
531 		.ident = "Acer C7 Chromebook",
532 		.matches = {
533 			DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
534 		},
535 		_CBDD(acer_c7_chromebook),
536 	},
537 	{
538 		.ident = "Acer AC700",
539 		.matches = {
540 			DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
541 		},
542 		_CBDD(acer_ac700),
543 	},
544 	{
545 		.ident = "Acer C720",
546 		.matches = {
547 			DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
548 		},
549 		_CBDD(acer_c720),
550 	},
551 	{
552 		.ident = "HP Pavilion 14 Chromebook",
553 		.matches = {
554 			DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
555 		},
556 		_CBDD(hp_pavilion_14_chromebook),
557 	},
558 	{
559 		.ident = "Cr-48",
560 		.matches = {
561 			DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
562 		},
563 		_CBDD(cr48),
564 	},
565 	{ }
566 };
567 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
568 
569 static struct platform_device *cros_platform_device;
570 
571 static struct platform_driver cros_platform_driver = {
572 	.driver = {
573 		.name = "chromeos_laptop",
574 		.owner = THIS_MODULE,
575 	},
576 	.probe = chromeos_laptop_probe,
577 };
578 
579 static int __init chromeos_laptop_init(void)
580 {
581 	int ret;
582 
583 	if (!dmi_check_system(chromeos_laptop_dmi_table)) {
584 		pr_debug("%s unsupported system.\n", __func__);
585 		return -ENODEV;
586 	}
587 
588 	ret = platform_driver_register(&cros_platform_driver);
589 	if (ret)
590 		return ret;
591 
592 	cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
593 	if (!cros_platform_device) {
594 		ret = -ENOMEM;
595 		goto fail_platform_device1;
596 	}
597 
598 	ret = platform_device_add(cros_platform_device);
599 	if (ret)
600 		goto fail_platform_device2;
601 
602 	return 0;
603 
604 fail_platform_device2:
605 	platform_device_put(cros_platform_device);
606 fail_platform_device1:
607 	platform_driver_unregister(&cros_platform_driver);
608 	return ret;
609 }
610 
611 static void __exit chromeos_laptop_exit(void)
612 {
613 	if (als)
614 		i2c_unregister_device(als);
615 	if (tp)
616 		i2c_unregister_device(tp);
617 	if (ts)
618 		i2c_unregister_device(ts);
619 
620 	platform_device_unregister(cros_platform_device);
621 	platform_driver_unregister(&cros_platform_driver);
622 }
623 
624 module_init(chromeos_laptop_init);
625 module_exit(chromeos_laptop_exit);
626 
627 MODULE_DESCRIPTION("Chrome OS Laptop driver");
628 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
629 MODULE_LICENSE("GPL");
630