blob: e5015dfaa81ecf51b00c11b69b0c2c806acb2797 [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>
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070015#include <linux/pci.h>
Benson Leung9ad36922013-10-20 20:58:25 -070016#include <linux/platform_device.h>
Benson Leungd1381f42012-10-25 14:21:21 -070017
Benson Leung8e1ad4c2013-02-21 12:14:59 -080018#define ATMEL_TP_I2C_ADDR 0x4b
19#define ATMEL_TP_I2C_BL_ADDR 0x25
Yufeng Shen33a84f82013-02-21 12:15:00 -080020#define ATMEL_TS_I2C_ADDR 0x4a
21#define ATMEL_TS_I2C_BL_ADDR 0x26
Benson Leungd1381f42012-10-25 14:21:21 -070022#define CYAPA_TP_I2C_ADDR 0x67
Benson Leung9bd9a902016-05-02 08:57:16 +080023#define ELAN_TP_I2C_ADDR 0x15
Benson Leungd1381f42012-10-25 14:21:21 -070024#define ISL_ALS_I2C_ADDR 0x44
Benson Leungaabf3f42013-02-01 14:34:45 -080025#define TAOS_ALS_I2C_ADDR 0x29
Benson Leungd1381f42012-10-25 14:21:21 -070026
Olof Johansson6d3c1af2013-11-25 13:10:25 -080027static const char *i2c_adapter_names[] = {
Benson Leungd1381f42012-10-25 14:21:21 -070028 "SMBus I801 adapter",
Benson Leung741bf0c2013-02-21 12:14:55 -080029 "i915 gmbus vga",
30 "i915 gmbus panel",
Jarkko Nikulaebaf31c2015-11-03 13:09:00 +020031 "Synopsys DesignWare I2C adapter",
Benson Leungd1381f42012-10-25 14:21:21 -070032};
33
34/* Keep this enum consistent with i2c_adapter_names */
35enum i2c_adapter_type {
36 I2C_ADAPTER_SMBUS = 0,
Benson Leung741bf0c2013-02-21 12:14:55 -080037 I2C_ADAPTER_VGADDC,
38 I2C_ADAPTER_PANEL,
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070039 I2C_ADAPTER_DESIGNWARE,
Benson Leungd1381f42012-10-25 14:21:21 -070040};
41
Aaron Durbinec199dd2013-10-20 20:58:24 -070042struct i2c_peripheral {
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070043 struct i2c_board_info board_info;
44 unsigned short alt_addr;
45 const char *dmi_name;
Aaron Durbinec199dd2013-10-20 20:58:24 -070046 enum i2c_adapter_type type;
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070047 u32 pci_devid;
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070048
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070049 struct i2c_client *client;
Aaron Durbinec199dd2013-10-20 20:58:24 -070050};
51
Benson Leung9bd9a902016-05-02 08:57:16 +080052#define MAX_I2C_PERIPHERALS 4
Aaron Durbinec199dd2013-10-20 20:58:24 -070053
54struct chromeos_laptop {
55 struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
56};
57
Benson Leung9ad36922013-10-20 20:58:25 -070058static struct chromeos_laptop *cros_laptop;
59
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070060static struct i2c_client *
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070061chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070062 struct i2c_board_info *info,
63 unsigned short alt_addr)
Benson Leungd1381f42012-10-25 14:21:21 -070064{
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070065 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070066 struct i2c_client *client;
Benson Leungd1381f42012-10-25 14:21:21 -070067
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070068 /*
69 * Add the i2c device. If we can't detect it at the primary
70 * address we scan secondary addresses. In any case the client
71 * structure gets assigned primary address.
72 */
73 client = i2c_new_probed_device(adapter, info, addr_list, NULL);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070074 if (!client && alt_addr) {
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070075 struct i2c_board_info dummy_info = {
76 I2C_BOARD_INFO("dummy", info->addr),
77 };
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070078 const unsigned short alt_addr_list[] = {
79 alt_addr, I2C_CLIENT_END
80 };
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070081 struct i2c_client *dummy;
82
83 dummy = i2c_new_probed_device(adapter, &dummy_info,
84 alt_addr_list, NULL);
85 if (dummy) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -070086 pr_debug("%d-%02x is probed at %02x\n",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070087 adapter->nr, info->addr, dummy->addr);
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070088 i2c_unregister_device(dummy);
89 client = i2c_new_device(adapter, info);
90 }
91 }
92
Benson Leungd1381f42012-10-25 14:21:21 -070093 if (!client)
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070094 pr_debug("failed to register device %d-%02x\n",
95 adapter->nr, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -070096 else
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070097 pr_debug("added i2c device %d-%02x\n",
98 adapter->nr, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -070099
Benson Leungd1381f42012-10-25 14:21:21 -0700100 return client;
101}
102
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700103static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
Benson Leungd1381f42012-10-25 14:21:21 -0700104{
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700105 struct pci_dev *pdev;
Robin Schroer49c68a22014-05-29 20:45:07 +0200106
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700107 if (!dev_is_pci(dev))
108 return false;
109
110 pdev = to_pci_dev(dev);
111 return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
Benson Leungd1381f42012-10-25 14:21:21 -0700112}
113
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700114static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
Benson Leungaabf3f42013-02-01 14:34:45 -0800115{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700116 struct i2c_peripheral *i2c_dev;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700117 int i;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700118
119 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700120 i2c_dev = &cros_laptop->i2c_peripherals[i];
121
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700122 /* No more peripherals */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700123 if (!i2c_dev->board_info.addr)
Aaron Durbinec199dd2013-10-20 20:58:24 -0700124 break;
125
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700126 /* Skip devices already created */
127 if (i2c_dev->client)
Benson Leung55024862014-07-15 17:43:11 -0700128 continue;
129
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700130 if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],
131 strlen(i2c_adapter_names[i2c_dev->type])))
132 continue;
133
134 if (i2c_dev->pci_devid &&
135 !chromeos_laptop_match_adapter_devid(adapter->dev.parent,
136 i2c_dev->pci_devid)) {
137 continue;
138 }
139
140 i2c_dev->client =
141 chromes_laptop_instantiate_i2c_device(adapter,
142 &i2c_dev->board_info,
143 i2c_dev->alt_addr);
144 }
145}
146
147static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
148{
149 struct i2c_peripheral *i2c_dev;
150 int i;
151
152 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
153 i2c_dev = &cros_laptop->i2c_peripherals[i];
154
155 if (i2c_dev->client == client)
156 i2c_dev->client = NULL;
157 }
158}
159
160static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
161 unsigned long action, void *data)
162{
163 struct device *dev = data;
164
165 switch (action) {
166 case BUS_NOTIFY_ADD_DEVICE:
167 if (dev->type == &i2c_adapter_type)
168 chromeos_laptop_check_adapter(to_i2c_adapter(dev));
169 break;
170
171 case BUS_NOTIFY_REMOVED_DEVICE:
172 if (dev->type == &i2c_client_type)
173 chromeos_laptop_detach_i2c_client(to_i2c_client(dev));
174 break;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700175 }
176
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700177 return 0;
Benson Leungaabf3f42013-02-01 14:34:45 -0800178}
179
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700180static struct notifier_block chromeos_laptop_i2c_notifier = {
181 .notifier_call = chromeos_laptop_i2c_notifier_call,
182};
183
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800184static struct chromeos_laptop samsung_series_5_550 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700185 .i2c_peripherals = {
186 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700187 {
188 .board_info = {
189 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
190 .flags = I2C_CLIENT_WAKE,
191 },
192 .dmi_name = "trackpad",
193 .type = I2C_ADAPTER_SMBUS,
194 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700195 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700196 {
197 .board_info = {
198 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
199 },
200 .dmi_name = "lightsensor",
201 .type = I2C_ADAPTER_SMBUS,
202 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700203 },
204};
205
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800206static struct chromeos_laptop samsung_series_5 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700207 .i2c_peripherals = {
208 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700209 {
210 .board_info = {
211 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
212 },
213 .type = I2C_ADAPTER_SMBUS,
214 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700215 },
216};
217
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700218static struct mxt_platform_data atmel_1664s_platform_data = {
219 .irqflags = IRQF_TRIGGER_FALLING,
220};
221
222static int chromebook_pixel_tp_keys[] = {
223 KEY_RESERVED,
224 KEY_RESERVED,
225 KEY_RESERVED,
226 KEY_RESERVED,
227 KEY_RESERVED,
228 BTN_LEFT
229};
230
231static struct mxt_platform_data chromebook_pixel_tp_platform_data = {
232 .irqflags = IRQF_TRIGGER_FALLING,
233 .t19_num_keys = ARRAY_SIZE(chromebook_pixel_tp_keys),
234 .t19_keymap = chromebook_pixel_tp_keys,
235};
236
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800237static struct chromeos_laptop chromebook_pixel = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700238 .i2c_peripherals = {
239 /* Touch Screen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700240 {
241 .board_info = {
242 I2C_BOARD_INFO("atmel_mxt_ts",
243 ATMEL_TS_I2C_ADDR),
244 .platform_data = &atmel_1664s_platform_data,
245 .flags = I2C_CLIENT_WAKE,
246 },
247 .dmi_name = "touchscreen",
248 .type = I2C_ADAPTER_PANEL,
249 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
250 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700251 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700252 {
253 .board_info = {
254 I2C_BOARD_INFO("atmel_mxt_tp",
255 ATMEL_TP_I2C_ADDR),
256 .platform_data =
257 &chromebook_pixel_tp_platform_data,
258 .flags = I2C_CLIENT_WAKE,
259 },
260 .dmi_name = "trackpad",
261 .type = I2C_ADAPTER_VGADDC,
262 .alt_addr = ATMEL_TP_I2C_BL_ADDR,
263 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700264 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700265 {
266 .board_info = {
267 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
268 },
269 .dmi_name = "lightsensor",
270 .type = I2C_ADAPTER_PANEL,
271 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700272 },
273};
274
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800275static struct chromeos_laptop hp_chromebook_14 = {
Benson Leung5ea95672014-06-17 14:02:01 -0700276 .i2c_peripherals = {
277 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700278 {
279 .board_info = {
280 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
281 .flags = I2C_CLIENT_WAKE,
282 },
283 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700284 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700285 },
Benson Leung5ea95672014-06-17 14:02:01 -0700286 },
287};
288
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800289static struct chromeos_laptop dell_chromebook_11 = {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700290 .i2c_peripherals = {
291 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700292 {
293 .board_info = {
294 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
295 .flags = I2C_CLIENT_WAKE,
296 },
297 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700298 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700299 },
Charlie Mooney9e96aa72016-05-02 08:57:17 +0800300 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700301 {
302 .board_info = {
303 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
304 .flags = I2C_CLIENT_WAKE,
305 },
306 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700307 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700308 },
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700309 },
310};
311
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800312static struct chromeos_laptop toshiba_cb35 = {
Gene Chen963cb6f2014-06-17 14:02:03 -0700313 .i2c_peripherals = {
314 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700315 {
316 .board_info = {
317 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
318 .flags = I2C_CLIENT_WAKE,
319 },
320 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700321 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700322 },
Gene Chen963cb6f2014-06-17 14:02:03 -0700323 },
324};
325
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800326static struct chromeos_laptop acer_c7_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700327 .i2c_peripherals = {
328 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700329 {
330 .board_info = {
331 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
332 .flags = I2C_CLIENT_WAKE,
333 },
334 .dmi_name = "trackpad",
335 .type = I2C_ADAPTER_SMBUS,
336 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700337 },
338};
339
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800340static struct chromeos_laptop acer_ac700 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700341 .i2c_peripherals = {
342 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700343 {
344 .board_info = {
345 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
346 },
347 .type = I2C_ADAPTER_SMBUS,
348 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700349 },
350};
351
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800352static struct chromeos_laptop acer_c720 = {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700353 .i2c_peripherals = {
Michael Mullinb90b3c42014-07-15 20:00:54 -0400354 /* Touchscreen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700355 {
356 .board_info = {
357 I2C_BOARD_INFO("atmel_mxt_ts",
358 ATMEL_TS_I2C_ADDR),
359 .platform_data = &atmel_1664s_platform_data,
360 .flags = I2C_CLIENT_WAKE,
361 },
362 .dmi_name = "touchscreen",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700363 .type = I2C_ADAPTER_DESIGNWARE,
364 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700365 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
366 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700367 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700368 {
369 .board_info = {
370 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
371 .flags = I2C_CLIENT_WAKE,
372 },
373 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700374 .type = I2C_ADAPTER_DESIGNWARE,
375 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700376 },
Benson Leung9bd9a902016-05-02 08:57:16 +0800377 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700378 {
379 .board_info = {
380 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
381 .flags = I2C_CLIENT_WAKE,
382 },
383 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700384 .type = I2C_ADAPTER_DESIGNWARE,
385 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700386 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700387 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700388 {
389 .board_info = {
390 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
391 },
392 .dmi_name = "lightsensor",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700393 .type = I2C_ADAPTER_DESIGNWARE,
394 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700395 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700396 },
397};
398
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800399static struct chromeos_laptop hp_pavilion_14_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700400 .i2c_peripherals = {
401 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700402 {
403 .board_info = {
404 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
405 .flags = I2C_CLIENT_WAKE,
406 },
407 .dmi_name = "trackpad",
408 .type = I2C_ADAPTER_SMBUS,
409 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700410 },
411};
412
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800413static struct chromeos_laptop cr48 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700414 .i2c_peripherals = {
415 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700416 {
417 .board_info = {
418 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
419 },
420 .type = I2C_ADAPTER_SMBUS,
421 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700422 },
423};
424
Christoph Hellwig6faadbb2017-09-14 11:59:30 +0200425static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
Benson Leungd1381f42012-10-25 14:21:21 -0700426 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700427 .ident = "Samsung Series 5 550",
Benson Leungd1381f42012-10-25 14:21:21 -0700428 .matches = {
429 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
430 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
431 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700432 .driver_data = (void *)&samsung_series_5_550,
Benson Leungd1381f42012-10-25 14:21:21 -0700433 },
434 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700435 .ident = "Samsung Series 5",
Benson Leung8016bcb2013-02-01 14:34:46 -0800436 .matches = {
437 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
438 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700439 .driver_data = (void *)&samsung_series_5,
Benson Leung8016bcb2013-02-01 14:34:46 -0800440 },
441 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700442 .ident = "Chromebook Pixel",
Benson Leungaabf3f42013-02-01 14:34:45 -0800443 .matches = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700444 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
445 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
Benson Leungaabf3f42013-02-01 14:34:45 -0800446 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700447 .driver_data = (void *)&chromebook_pixel,
Benson Leungaabf3f42013-02-01 14:34:45 -0800448 },
449 {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700450 .ident = "Wolf",
451 .matches = {
452 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
453 DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
454 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700455 .driver_data = (void *)&dell_chromebook_11,
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700456 },
457 {
Benson Leung5ea95672014-06-17 14:02:01 -0700458 .ident = "HP Chromebook 14",
459 .matches = {
460 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
461 DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
462 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700463 .driver_data = (void *)&hp_chromebook_14,
Benson Leung5ea95672014-06-17 14:02:01 -0700464 },
465 {
Gene Chen963cb6f2014-06-17 14:02:03 -0700466 .ident = "Toshiba CB35",
467 .matches = {
468 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
469 DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
470 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700471 .driver_data = (void *)&toshiba_cb35,
Gene Chen963cb6f2014-06-17 14:02:03 -0700472 },
473 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700474 .ident = "Acer C7 Chromebook",
475 .matches = {
476 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
477 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700478 .driver_data = (void *)&acer_c7_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700479 },
480 {
481 .ident = "Acer AC700",
Benson Leungaabf3f42013-02-01 14:34:45 -0800482 .matches = {
483 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
484 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700485 .driver_data = (void *)&acer_ac700,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700486 },
487 {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700488 .ident = "Acer C720",
489 .matches = {
490 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
491 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700492 .driver_data = (void *)&acer_c720,
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700493 },
494 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700495 .ident = "HP Pavilion 14 Chromebook",
496 .matches = {
497 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
498 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700499 .driver_data = (void *)&hp_pavilion_14_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700500 },
501 {
502 .ident = "Cr-48",
503 .matches = {
504 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
505 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700506 .driver_data = (void *)&cr48,
Benson Leungaabf3f42013-02-01 14:34:45 -0800507 },
Benson Leungd1381f42012-10-25 14:21:21 -0700508 { }
509};
510MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
511
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700512static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
513{
514 struct i2c_adapter *adapter;
Benson Leung9ad36922013-10-20 20:58:25 -0700515
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700516 adapter = i2c_verify_adapter(dev);
517 if (adapter)
518 chromeos_laptop_check_adapter(adapter);
519
520 return 0;
521}
Benson Leung9ad36922013-10-20 20:58:25 -0700522
Dmitry Torokhov65582922018-03-20 15:31:33 -0700523static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
524{
525 const struct dmi_device *dmi_dev;
526 const struct dmi_dev_onboard *dev_data;
527
528 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
529 if (!dmi_dev) {
530 pr_err("failed to find DMI device '%s'\n", dmi_name);
531 return -ENOENT;
532 }
533
534 dev_data = dmi_dev->device_data;
535 if (!dev_data) {
536 pr_err("failed to get data from DMI for '%s'\n", dmi_name);
537 return -EINVAL;
538 }
539
540 return dev_data->instance;
541}
542
543static struct chromeos_laptop * __init
544chromeos_laptop_prepare(const struct dmi_system_id *id)
545{
546 struct i2c_peripheral *i2c_dev;
547 int irq;
548 int i;
549
550 cros_laptop = (void *)id->driver_data;
551
552 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
553 i2c_dev = &cros_laptop->i2c_peripherals[i];
554
555 if (!i2c_dev->dmi_name)
556 continue;
557
558 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
559 if (irq < 0)
560 return ERR_PTR(irq);
561 }
562
563 return cros_laptop;
564}
565
566
Benson Leungd1381f42012-10-25 14:21:21 -0700567static int __init chromeos_laptop_init(void)
568{
Dmitry Torokhov65582922018-03-20 15:31:33 -0700569 const struct dmi_system_id *dmi_id;
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700570 int error;
Robin Schroer49c68a22014-05-29 20:45:07 +0200571
Dmitry Torokhov65582922018-03-20 15:31:33 -0700572 dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
573 if (!dmi_id) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700574 pr_debug("unsupported system\n");
Benson Leungd1381f42012-10-25 14:21:21 -0700575 return -ENODEV;
576 }
Benson Leung9ad36922013-10-20 20:58:25 -0700577
Dmitry Torokhov65582922018-03-20 15:31:33 -0700578 pr_debug("DMI Matched %s\n", dmi_id->ident);
579
580 cros_laptop = chromeos_laptop_prepare(dmi_id->driver_data);
581 if (IS_ERR(cros_laptop))
582 return PTR_ERR(cros_laptop);
583
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700584 error = bus_register_notifier(&i2c_bus_type,
585 &chromeos_laptop_i2c_notifier);
586 if (error) {
587 pr_err("failed to register i2c bus notifier: %d\n", error);
588 return error;
Benson Leung9ad36922013-10-20 20:58:25 -0700589 }
590
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700591 /*
592 * Scan adapters that have been registered before we installed
593 * the notifier to make sure we do not miss any devices.
594 */
595 i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
Benson Leung9ad36922013-10-20 20:58:25 -0700596
Benson Leungd1381f42012-10-25 14:21:21 -0700597 return 0;
598}
599
600static void __exit chromeos_laptop_exit(void)
601{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700602 struct i2c_peripheral *i2c_dev;
603 int i;
Wei Yongjun2b8454a2013-11-27 11:34:58 +0800604
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700605 bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700606
607 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
608 i2c_dev = &cros_laptop->i2c_peripherals[i];
609
610 /* No more peripherals */
611 if (!i2c_dev->board_info.type)
612 break;
613
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700614 if (i2c_dev->client)
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700615 i2c_unregister_device(i2c_dev->client);
616 }
Benson Leungd1381f42012-10-25 14:21:21 -0700617}
618
619module_init(chromeos_laptop_init);
620module_exit(chromeos_laptop_exit);
621
622MODULE_DESCRIPTION("Chrome OS Laptop driver");
623MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
624MODULE_LICENSE("GPL");