blob: d6d2bc6f3aaf0cab49b76ea054c3dded6152eb88 [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 Torokhov28cd38f2018-03-20 15:31:32 -070071static struct i2c_client *
72chromes_laptop_instantiate_i2c_device(int bus,
73 struct i2c_board_info *info,
74 unsigned short alt_addr)
Benson Leungd1381f42012-10-25 14:21:21 -070075{
Benson Leungd1381f42012-10-25 14:21:21 -070076 struct i2c_adapter *adapter;
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070077 struct i2c_client *client = NULL;
78 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
Benson Leungd1381f42012-10-25 14:21:21 -070079
Benson Leungd1381f42012-10-25 14:21:21 -070080 adapter = i2c_get_adapter(bus);
81 if (!adapter) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -070082 pr_err("failed to get i2c adapter %d\n", bus);
Benson Leungd1381f42012-10-25 14:21:21 -070083 return NULL;
84 }
85
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070086 /*
87 * Add the i2c device. If we can't detect it at the primary
88 * address we scan secondary addresses. In any case the client
89 * structure gets assigned primary address.
90 */
91 client = i2c_new_probed_device(adapter, info, addr_list, NULL);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070092 if (!client && alt_addr) {
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070093 struct i2c_board_info dummy_info = {
94 I2C_BOARD_INFO("dummy", info->addr),
95 };
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070096 const unsigned short alt_addr_list[] = {
97 alt_addr, I2C_CLIENT_END
98 };
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070099 struct i2c_client *dummy;
100
101 dummy = i2c_new_probed_device(adapter, &dummy_info,
102 alt_addr_list, NULL);
103 if (dummy) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700104 pr_debug("%d-%02x is probed at %02x\n",
105 bus, info->addr, dummy->addr);
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -0700106 i2c_unregister_device(dummy);
107 client = i2c_new_device(adapter, info);
108 }
109 }
110
Benson Leungd1381f42012-10-25 14:21:21 -0700111 if (!client)
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700112 pr_notice("failed to register device %d-%02x\n",
113 bus, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700114 else
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700115 pr_debug("added i2c device %d-%02x\n", bus, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700116
117 i2c_put_adapter(adapter);
118 return client;
119}
120
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700121struct i2c_lookup {
122 const char *name;
123 int instance;
124 int n;
125};
126
Benson Leung9ad36922013-10-20 20:58:25 -0700127static int __find_i2c_adap(struct device *dev, void *data)
Benson Leungd1381f42012-10-25 14:21:21 -0700128{
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700129 struct i2c_lookup *lookup = data;
Benson Leungd1381f42012-10-25 14:21:21 -0700130 static const char *prefix = "i2c-";
131 struct i2c_adapter *adapter;
Robin Schroer49c68a22014-05-29 20:45:07 +0200132
Benson Leungd1381f42012-10-25 14:21:21 -0700133 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0)
134 return 0;
135 adapter = to_i2c_adapter(dev);
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700136 if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
137 lookup->n++ == lookup->instance)
138 return 1;
139 return 0;
Benson Leungd1381f42012-10-25 14:21:21 -0700140}
141
Benson Leung9ad36922013-10-20 20:58:25 -0700142static int find_i2c_adapter_num(enum i2c_adapter_type type)
Benson Leungd1381f42012-10-25 14:21:21 -0700143{
144 struct device *dev = NULL;
145 struct i2c_adapter *adapter;
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700146 struct i2c_lookup lookup;
147
148 memset(&lookup, 0, sizeof(lookup));
149 lookup.name = i2c_adapter_names[type];
150 lookup.instance = (type == I2C_ADAPTER_DESIGNWARE_1) ? 1 : 0;
151
Benson Leungd1381f42012-10-25 14:21:21 -0700152 /* find the adapter by name */
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700153 dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
Benson Leungd1381f42012-10-25 14:21:21 -0700154 if (!dev) {
Benson Leung9ad36922013-10-20 20:58:25 -0700155 /* Adapters may appear later. Deferred probing will retry */
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700156 pr_notice("i2c adapter %s not found on system.\n",
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700157 lookup.name);
Benson Leungd1381f42012-10-25 14:21:21 -0700158 return -ENODEV;
159 }
160 adapter = to_i2c_adapter(dev);
161 return adapter->nr;
162}
163
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700164static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev)
Benson Leungbcaf0892013-02-21 12:14:58 -0800165{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700166 struct i2c_client *client;
167 int bus;
Benson Leungbcaf0892013-02-21 12:14:58 -0800168
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700169 /*
170 * Check that the i2c adapter is present.
171 * -EPROBE_DEFER if missing as the adapter may appear much
172 * later.
173 */
174 bus = find_i2c_adapter_num(i2c_dev->type);
175 if (bus < 0)
176 return bus == -ENODEV ? -EPROBE_DEFER : bus;
Benson Leungd1381f42012-10-25 14:21:21 -0700177
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700178 client = chromes_laptop_instantiate_i2c_device(bus,
179 &i2c_dev->board_info,
180 i2c_dev->alt_addr);
181 if (!client) {
182 /*
183 * Set -EPROBE_DEFER a limited num of times
184 * if device is not successfully added.
185 */
186 if (++i2c_dev->tries < MAX_I2C_DEVICE_DEFERRALS) {
187 return -EPROBE_DEFER;
188 } else {
189 /* Ran out of tries. */
190 pr_notice("ran out of tries for device.\n");
191 i2c_dev->state = TIMEDOUT;
192 return -EIO;
193 }
194 }
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800195
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700196 i2c_dev->client = client;
197 i2c_dev->state = PROBED;
Benson Leung8e1ad4c2013-02-21 12:14:59 -0800198
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700199 return 0;
Benson Leung8016bcb2013-02-01 14:34:46 -0800200}
201
Benson Leung9ad36922013-10-20 20:58:25 -0700202static int chromeos_laptop_probe(struct platform_device *pdev)
Benson Leungaabf3f42013-02-01 14:34:45 -0800203{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700204 struct i2c_peripheral *i2c_dev;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700205 int i;
Benson Leung9ad36922013-10-20 20:58:25 -0700206 int ret = 0;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700207
208 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700209 i2c_dev = &cros_laptop->i2c_peripherals[i];
210
211 /* No more peripherals. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700212 if (!i2c_dev->board_info.addr)
Aaron Durbinec199dd2013-10-20 20:58:24 -0700213 break;
214
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700215 if (i2c_dev->state != UNPROBED)
Benson Leung55024862014-07-15 17:43:11 -0700216 continue;
217
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700218 if (chromeos_laptop_add_peripheral(i2c_dev) == -EPROBE_DEFER)
Benson Leung9ad36922013-10-20 20:58:25 -0700219 ret = -EPROBE_DEFER;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700220 }
221
Benson Leung9ad36922013-10-20 20:58:25 -0700222 return ret;
Benson Leungaabf3f42013-02-01 14:34:45 -0800223}
224
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800225static struct chromeos_laptop samsung_series_5_550 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700226 .i2c_peripherals = {
227 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700228 {
229 .board_info = {
230 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
231 .flags = I2C_CLIENT_WAKE,
232 },
233 .dmi_name = "trackpad",
234 .type = I2C_ADAPTER_SMBUS,
235 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700236 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700237 {
238 .board_info = {
239 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
240 },
241 .dmi_name = "lightsensor",
242 .type = I2C_ADAPTER_SMBUS,
243 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700244 },
245};
246
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800247static struct chromeos_laptop samsung_series_5 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700248 .i2c_peripherals = {
249 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700250 {
251 .board_info = {
252 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
253 },
254 .type = I2C_ADAPTER_SMBUS,
255 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700256 },
257};
258
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700259static struct mxt_platform_data atmel_1664s_platform_data = {
260 .irqflags = IRQF_TRIGGER_FALLING,
261};
262
263static int chromebook_pixel_tp_keys[] = {
264 KEY_RESERVED,
265 KEY_RESERVED,
266 KEY_RESERVED,
267 KEY_RESERVED,
268 KEY_RESERVED,
269 BTN_LEFT
270};
271
272static struct mxt_platform_data chromebook_pixel_tp_platform_data = {
273 .irqflags = IRQF_TRIGGER_FALLING,
274 .t19_num_keys = ARRAY_SIZE(chromebook_pixel_tp_keys),
275 .t19_keymap = chromebook_pixel_tp_keys,
276};
277
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800278static struct chromeos_laptop chromebook_pixel = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700279 .i2c_peripherals = {
280 /* Touch Screen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700281 {
282 .board_info = {
283 I2C_BOARD_INFO("atmel_mxt_ts",
284 ATMEL_TS_I2C_ADDR),
285 .platform_data = &atmel_1664s_platform_data,
286 .flags = I2C_CLIENT_WAKE,
287 },
288 .dmi_name = "touchscreen",
289 .type = I2C_ADAPTER_PANEL,
290 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
291 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700292 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700293 {
294 .board_info = {
295 I2C_BOARD_INFO("atmel_mxt_tp",
296 ATMEL_TP_I2C_ADDR),
297 .platform_data =
298 &chromebook_pixel_tp_platform_data,
299 .flags = I2C_CLIENT_WAKE,
300 },
301 .dmi_name = "trackpad",
302 .type = I2C_ADAPTER_VGADDC,
303 .alt_addr = ATMEL_TP_I2C_BL_ADDR,
304 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700305 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700306 {
307 .board_info = {
308 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
309 },
310 .dmi_name = "lightsensor",
311 .type = I2C_ADAPTER_PANEL,
312 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700313 },
314};
315
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800316static struct chromeos_laptop hp_chromebook_14 = {
Benson Leung5ea95672014-06-17 14:02:01 -0700317 .i2c_peripherals = {
318 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700319 {
320 .board_info = {
321 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
322 .flags = I2C_CLIENT_WAKE,
323 },
324 .dmi_name = "trackpad",
325 .type = I2C_ADAPTER_DESIGNWARE_0,
326 },
Benson Leung5ea95672014-06-17 14:02:01 -0700327 },
328};
329
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800330static struct chromeos_laptop dell_chromebook_11 = {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700331 .i2c_peripherals = {
332 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700333 {
334 .board_info = {
335 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
336 .flags = I2C_CLIENT_WAKE,
337 },
338 .dmi_name = "trackpad",
339 .type = I2C_ADAPTER_DESIGNWARE_0,
340 },
Charlie Mooney9e96aa72016-05-02 08:57:17 +0800341 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700342 {
343 .board_info = {
344 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
345 .flags = I2C_CLIENT_WAKE,
346 },
347 .dmi_name = "trackpad",
348 .type = I2C_ADAPTER_DESIGNWARE_0,
349 },
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700350 },
351};
352
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800353static struct chromeos_laptop toshiba_cb35 = {
Gene Chen963cb6f2014-06-17 14:02:03 -0700354 .i2c_peripherals = {
355 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700356 {
357 .board_info = {
358 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
359 .flags = I2C_CLIENT_WAKE,
360 },
361 .dmi_name = "trackpad",
362 .type = I2C_ADAPTER_DESIGNWARE_0,
363 },
Gene Chen963cb6f2014-06-17 14:02:03 -0700364 },
365};
366
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800367static struct chromeos_laptop acer_c7_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700368 .i2c_peripherals = {
369 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700370 {
371 .board_info = {
372 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
373 .flags = I2C_CLIENT_WAKE,
374 },
375 .dmi_name = "trackpad",
376 .type = I2C_ADAPTER_SMBUS,
377 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700378 },
379};
380
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800381static struct chromeos_laptop acer_ac700 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700382 .i2c_peripherals = {
383 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700384 {
385 .board_info = {
386 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
387 },
388 .type = I2C_ADAPTER_SMBUS,
389 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700390 },
391};
392
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800393static struct chromeos_laptop acer_c720 = {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700394 .i2c_peripherals = {
Michael Mullinb90b3c42014-07-15 20:00:54 -0400395 /* Touchscreen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700396 {
397 .board_info = {
398 I2C_BOARD_INFO("atmel_mxt_ts",
399 ATMEL_TS_I2C_ADDR),
400 .platform_data = &atmel_1664s_platform_data,
401 .flags = I2C_CLIENT_WAKE,
402 },
403 .dmi_name = "touchscreen",
404 .type = I2C_ADAPTER_DESIGNWARE_1,
405 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
406 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700407 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700408 {
409 .board_info = {
410 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
411 .flags = I2C_CLIENT_WAKE,
412 },
413 .dmi_name = "trackpad",
414 .type = I2C_ADAPTER_DESIGNWARE_0,
415 },
Benson Leung9bd9a902016-05-02 08:57:16 +0800416 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700417 {
418 .board_info = {
419 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
420 .flags = I2C_CLIENT_WAKE,
421 },
422 .dmi_name = "trackpad",
423 .type = I2C_ADAPTER_DESIGNWARE_0,
424 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700425 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700426 {
427 .board_info = {
428 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
429 },
430 .dmi_name = "lightsensor",
431 .type = I2C_ADAPTER_DESIGNWARE_1,
432 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700433 },
434};
435
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800436static struct chromeos_laptop hp_pavilion_14_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700437 .i2c_peripherals = {
438 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700439 {
440 .board_info = {
441 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
442 .flags = I2C_CLIENT_WAKE,
443 },
444 .dmi_name = "trackpad",
445 .type = I2C_ADAPTER_SMBUS,
446 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700447 },
448};
449
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800450static struct chromeos_laptop cr48 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700451 .i2c_peripherals = {
452 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700453 {
454 .board_info = {
455 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
456 },
457 .type = I2C_ADAPTER_SMBUS,
458 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700459 },
460};
461
Christoph Hellwig6faadbb2017-09-14 11:59:30 +0200462static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
Benson Leungd1381f42012-10-25 14:21:21 -0700463 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700464 .ident = "Samsung Series 5 550",
Benson Leungd1381f42012-10-25 14:21:21 -0700465 .matches = {
466 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
467 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
468 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700469 .driver_data = (void *)&samsung_series_5_550,
Benson Leungd1381f42012-10-25 14:21:21 -0700470 },
471 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700472 .ident = "Samsung Series 5",
Benson Leung8016bcb2013-02-01 14:34:46 -0800473 .matches = {
474 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
475 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700476 .driver_data = (void *)&samsung_series_5,
Benson Leung8016bcb2013-02-01 14:34:46 -0800477 },
478 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700479 .ident = "Chromebook Pixel",
Benson Leungaabf3f42013-02-01 14:34:45 -0800480 .matches = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700481 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
482 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
Benson Leungaabf3f42013-02-01 14:34:45 -0800483 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700484 .driver_data = (void *)&chromebook_pixel,
Benson Leungaabf3f42013-02-01 14:34:45 -0800485 },
486 {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700487 .ident = "Wolf",
488 .matches = {
489 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
490 DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
491 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700492 .driver_data = (void *)&dell_chromebook_11,
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700493 },
494 {
Benson Leung5ea95672014-06-17 14:02:01 -0700495 .ident = "HP Chromebook 14",
496 .matches = {
497 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
498 DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
499 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700500 .driver_data = (void *)&hp_chromebook_14,
Benson Leung5ea95672014-06-17 14:02:01 -0700501 },
502 {
Gene Chen963cb6f2014-06-17 14:02:03 -0700503 .ident = "Toshiba CB35",
504 .matches = {
505 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
506 DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
507 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700508 .driver_data = (void *)&toshiba_cb35,
Gene Chen963cb6f2014-06-17 14:02:03 -0700509 },
510 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700511 .ident = "Acer C7 Chromebook",
512 .matches = {
513 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
514 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700515 .driver_data = (void *)&acer_c7_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700516 },
517 {
518 .ident = "Acer AC700",
Benson Leungaabf3f42013-02-01 14:34:45 -0800519 .matches = {
520 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
521 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700522 .driver_data = (void *)&acer_ac700,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700523 },
524 {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700525 .ident = "Acer C720",
526 .matches = {
527 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
528 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700529 .driver_data = (void *)&acer_c720,
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700530 },
531 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700532 .ident = "HP Pavilion 14 Chromebook",
533 .matches = {
534 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
535 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700536 .driver_data = (void *)&hp_pavilion_14_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700537 },
538 {
539 .ident = "Cr-48",
540 .matches = {
541 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
542 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700543 .driver_data = (void *)&cr48,
Benson Leungaabf3f42013-02-01 14:34:45 -0800544 },
Benson Leungd1381f42012-10-25 14:21:21 -0700545 { }
546};
547MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
548
Benson Leung9ad36922013-10-20 20:58:25 -0700549static struct platform_device *cros_platform_device;
550
551static struct platform_driver cros_platform_driver = {
552 .driver = {
553 .name = "chromeos_laptop",
Benson Leung9ad36922013-10-20 20:58:25 -0700554 },
555 .probe = chromeos_laptop_probe,
556};
557
Dmitry Torokhov65582922018-03-20 15:31:33 -0700558static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
559{
560 const struct dmi_device *dmi_dev;
561 const struct dmi_dev_onboard *dev_data;
562
563 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
564 if (!dmi_dev) {
565 pr_err("failed to find DMI device '%s'\n", dmi_name);
566 return -ENOENT;
567 }
568
569 dev_data = dmi_dev->device_data;
570 if (!dev_data) {
571 pr_err("failed to get data from DMI for '%s'\n", dmi_name);
572 return -EINVAL;
573 }
574
575 return dev_data->instance;
576}
577
578static struct chromeos_laptop * __init
579chromeos_laptop_prepare(const struct dmi_system_id *id)
580{
581 struct i2c_peripheral *i2c_dev;
582 int irq;
583 int i;
584
585 cros_laptop = (void *)id->driver_data;
586
587 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
588 i2c_dev = &cros_laptop->i2c_peripherals[i];
589
590 if (!i2c_dev->dmi_name)
591 continue;
592
593 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
594 if (irq < 0)
595 return ERR_PTR(irq);
596 }
597
598 return cros_laptop;
599}
600
601
Benson Leungd1381f42012-10-25 14:21:21 -0700602static int __init chromeos_laptop_init(void)
603{
Dmitry Torokhov65582922018-03-20 15:31:33 -0700604 const struct dmi_system_id *dmi_id;
Benson Leung9ad36922013-10-20 20:58:25 -0700605 int ret;
Robin Schroer49c68a22014-05-29 20:45:07 +0200606
Dmitry Torokhov65582922018-03-20 15:31:33 -0700607 dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
608 if (!dmi_id) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700609 pr_debug("unsupported system\n");
Benson Leungd1381f42012-10-25 14:21:21 -0700610 return -ENODEV;
611 }
Benson Leung9ad36922013-10-20 20:58:25 -0700612
Dmitry Torokhov65582922018-03-20 15:31:33 -0700613 pr_debug("DMI Matched %s\n", dmi_id->ident);
614
615 cros_laptop = chromeos_laptop_prepare(dmi_id->driver_data);
616 if (IS_ERR(cros_laptop))
617 return PTR_ERR(cros_laptop);
618
Benson Leung9ad36922013-10-20 20:58:25 -0700619 ret = platform_driver_register(&cros_platform_driver);
620 if (ret)
621 return ret;
622
623 cros_platform_device = platform_device_alloc("chromeos_laptop", -1);
624 if (!cros_platform_device) {
625 ret = -ENOMEM;
626 goto fail_platform_device1;
627 }
628
629 ret = platform_device_add(cros_platform_device);
630 if (ret)
631 goto fail_platform_device2;
632
Benson Leungd1381f42012-10-25 14:21:21 -0700633 return 0;
Benson Leung9ad36922013-10-20 20:58:25 -0700634
635fail_platform_device2:
636 platform_device_put(cros_platform_device);
637fail_platform_device1:
638 platform_driver_unregister(&cros_platform_driver);
639 return ret;
Benson Leungd1381f42012-10-25 14:21:21 -0700640}
641
642static void __exit chromeos_laptop_exit(void)
643{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700644 struct i2c_peripheral *i2c_dev;
645 int i;
Wei Yongjun2b8454a2013-11-27 11:34:58 +0800646
647 platform_device_unregister(cros_platform_device);
648 platform_driver_unregister(&cros_platform_driver);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700649
650 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
651 i2c_dev = &cros_laptop->i2c_peripherals[i];
652
653 /* No more peripherals */
654 if (!i2c_dev->board_info.type)
655 break;
656
657 if (i2c_dev->state == PROBED)
658 i2c_unregister_device(i2c_dev->client);
659 }
Benson Leungd1381f42012-10-25 14:21:21 -0700660}
661
662module_init(chromeos_laptop_init);
663module_exit(chromeos_laptop_exit);
664
665MODULE_DESCRIPTION("Chrome OS Laptop driver");
666MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
667MODULE_LICENSE("GPL");