blob: 1191c1a3a0cd18c5a892694a35c4f7461ba8fcc4 [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>
Dmitry Torokhove6215eea2018-03-20 15:31:35 -070014#include <linux/ioport.h>
Benson Leungd1381f42012-10-25 14:21:21 -070015#include <linux/module.h>
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070016#include <linux/pci.h>
Benson Leung9ad36922013-10-20 20:58:25 -070017#include <linux/platform_device.h>
Benson Leungd1381f42012-10-25 14:21:21 -070018
Benson Leung8e1ad4c2013-02-21 12:14:59 -080019#define ATMEL_TP_I2C_ADDR 0x4b
20#define ATMEL_TP_I2C_BL_ADDR 0x25
Yufeng Shen33a84f82013-02-21 12:15:00 -080021#define ATMEL_TS_I2C_ADDR 0x4a
22#define ATMEL_TS_I2C_BL_ADDR 0x26
Benson Leungd1381f42012-10-25 14:21:21 -070023#define CYAPA_TP_I2C_ADDR 0x67
Benson Leung9bd9a902016-05-02 08:57:16 +080024#define ELAN_TP_I2C_ADDR 0x15
Benson Leungd1381f42012-10-25 14:21:21 -070025#define ISL_ALS_I2C_ADDR 0x44
Benson Leungaabf3f42013-02-01 14:34:45 -080026#define TAOS_ALS_I2C_ADDR 0x29
Benson Leungd1381f42012-10-25 14:21:21 -070027
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",
Benson Leungd1381f42012-10-25 14:21:21 -070033};
34
35/* Keep this enum consistent with i2c_adapter_names */
36enum i2c_adapter_type {
37 I2C_ADAPTER_SMBUS = 0,
Benson Leung741bf0c2013-02-21 12:14:55 -080038 I2C_ADAPTER_VGADDC,
39 I2C_ADAPTER_PANEL,
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070040 I2C_ADAPTER_DESIGNWARE,
Benson Leungd1381f42012-10-25 14:21:21 -070041};
42
Aaron Durbinec199dd2013-10-20 20:58:24 -070043struct i2c_peripheral {
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070044 struct i2c_board_info board_info;
45 unsigned short alt_addr;
Dmitry Torokhove6215eea2018-03-20 15:31:35 -070046
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070047 const char *dmi_name;
Dmitry Torokhove6215eea2018-03-20 15:31:35 -070048 unsigned long irqflags;
49 struct resource irq_resource;
50
Aaron Durbinec199dd2013-10-20 20:58:24 -070051 enum i2c_adapter_type type;
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070052 u32 pci_devid;
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070053
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070054 struct i2c_client *client;
Aaron Durbinec199dd2013-10-20 20:58:24 -070055};
56
Benson Leung9bd9a902016-05-02 08:57:16 +080057#define MAX_I2C_PERIPHERALS 4
Aaron Durbinec199dd2013-10-20 20:58:24 -070058
59struct chromeos_laptop {
60 struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS];
61};
62
Benson Leung9ad36922013-10-20 20:58:25 -070063static struct chromeos_laptop *cros_laptop;
64
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070065static struct i2c_client *
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070066chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070067 struct i2c_board_info *info,
68 unsigned short alt_addr)
Benson Leungd1381f42012-10-25 14:21:21 -070069{
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070070 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070071 struct i2c_client *client;
Benson Leungd1381f42012-10-25 14:21:21 -070072
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070073 /*
74 * Add the i2c device. If we can't detect it at the primary
75 * address we scan secondary addresses. In any case the client
76 * structure gets assigned primary address.
77 */
78 client = i2c_new_probed_device(adapter, info, addr_list, NULL);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070079 if (!client && alt_addr) {
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070080 struct i2c_board_info dummy_info = {
81 I2C_BOARD_INFO("dummy", info->addr),
82 };
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -070083 const unsigned short alt_addr_list[] = {
84 alt_addr, I2C_CLIENT_END
85 };
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070086 struct i2c_client *dummy;
87
88 dummy = i2c_new_probed_device(adapter, &dummy_info,
89 alt_addr_list, NULL);
90 if (dummy) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -070091 pr_debug("%d-%02x is probed at %02x\n",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070092 adapter->nr, info->addr, dummy->addr);
Dmitry Torokhov96cba9b2015-04-14 13:50:09 -070093 i2c_unregister_device(dummy);
94 client = i2c_new_device(adapter, info);
95 }
96 }
97
Benson Leungd1381f42012-10-25 14:21:21 -070098 if (!client)
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -070099 pr_debug("failed to register device %d-%02x\n",
100 adapter->nr, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700101 else
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700102 pr_debug("added i2c device %d-%02x\n",
103 adapter->nr, info->addr);
Benson Leungd1381f42012-10-25 14:21:21 -0700104
Benson Leungd1381f42012-10-25 14:21:21 -0700105 return client;
106}
107
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700108static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
Benson Leungd1381f42012-10-25 14:21:21 -0700109{
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700110 struct pci_dev *pdev;
Robin Schroer49c68a22014-05-29 20:45:07 +0200111
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700112 if (!dev_is_pci(dev))
113 return false;
114
115 pdev = to_pci_dev(dev);
116 return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
Benson Leungd1381f42012-10-25 14:21:21 -0700117}
118
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700119static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
Benson Leungaabf3f42013-02-01 14:34:45 -0800120{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700121 struct i2c_peripheral *i2c_dev;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700122 int i;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700123
124 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700125 i2c_dev = &cros_laptop->i2c_peripherals[i];
126
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700127 /* No more peripherals */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700128 if (!i2c_dev->board_info.addr)
Aaron Durbinec199dd2013-10-20 20:58:24 -0700129 break;
130
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700131 /* Skip devices already created */
132 if (i2c_dev->client)
Benson Leung55024862014-07-15 17:43:11 -0700133 continue;
134
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700135 if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],
136 strlen(i2c_adapter_names[i2c_dev->type])))
137 continue;
138
139 if (i2c_dev->pci_devid &&
140 !chromeos_laptop_match_adapter_devid(adapter->dev.parent,
141 i2c_dev->pci_devid)) {
142 continue;
143 }
144
145 i2c_dev->client =
146 chromes_laptop_instantiate_i2c_device(adapter,
147 &i2c_dev->board_info,
148 i2c_dev->alt_addr);
149 }
150}
151
152static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
153{
154 struct i2c_peripheral *i2c_dev;
155 int i;
156
157 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
158 i2c_dev = &cros_laptop->i2c_peripherals[i];
159
160 if (i2c_dev->client == client)
161 i2c_dev->client = NULL;
162 }
163}
164
165static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
166 unsigned long action, void *data)
167{
168 struct device *dev = data;
169
170 switch (action) {
171 case BUS_NOTIFY_ADD_DEVICE:
172 if (dev->type == &i2c_adapter_type)
173 chromeos_laptop_check_adapter(to_i2c_adapter(dev));
174 break;
175
176 case BUS_NOTIFY_REMOVED_DEVICE:
177 if (dev->type == &i2c_client_type)
178 chromeos_laptop_detach_i2c_client(to_i2c_client(dev));
179 break;
Aaron Durbinec199dd2013-10-20 20:58:24 -0700180 }
181
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700182 return 0;
Benson Leungaabf3f42013-02-01 14:34:45 -0800183}
184
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700185static struct notifier_block chromeos_laptop_i2c_notifier = {
186 .notifier_call = chromeos_laptop_i2c_notifier_call,
187};
188
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800189static struct chromeos_laptop samsung_series_5_550 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700190 .i2c_peripherals = {
191 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700192 {
193 .board_info = {
194 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
195 .flags = I2C_CLIENT_WAKE,
196 },
197 .dmi_name = "trackpad",
198 .type = I2C_ADAPTER_SMBUS,
199 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700200 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700201 {
202 .board_info = {
203 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
204 },
205 .dmi_name = "lightsensor",
206 .type = I2C_ADAPTER_SMBUS,
207 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700208 },
209};
210
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800211static struct chromeos_laptop samsung_series_5 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700212 .i2c_peripherals = {
213 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700214 {
215 .board_info = {
216 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
217 },
218 .type = I2C_ADAPTER_SMBUS,
219 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700220 },
221};
222
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700223static int chromebook_pixel_tp_keys[] = {
224 KEY_RESERVED,
225 KEY_RESERVED,
226 KEY_RESERVED,
227 KEY_RESERVED,
228 KEY_RESERVED,
229 BTN_LEFT
230};
231
232static struct mxt_platform_data chromebook_pixel_tp_platform_data = {
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700233 .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),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700244 .flags = I2C_CLIENT_WAKE,
245 },
246 .dmi_name = "touchscreen",
Dmitry Torokhove6215eea2018-03-20 15:31:35 -0700247 .irqflags = IRQF_TRIGGER_FALLING,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700248 .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",
Dmitry Torokhove6215eea2018-03-20 15:31:35 -0700261 .irqflags = IRQF_TRIGGER_FALLING,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700262 .type = I2C_ADAPTER_VGADDC,
263 .alt_addr = ATMEL_TP_I2C_BL_ADDR,
264 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700265 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700266 {
267 .board_info = {
268 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
269 },
270 .dmi_name = "lightsensor",
271 .type = I2C_ADAPTER_PANEL,
272 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700273 },
274};
275
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800276static struct chromeos_laptop hp_chromebook_14 = {
Benson Leung5ea95672014-06-17 14:02:01 -0700277 .i2c_peripherals = {
278 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700279 {
280 .board_info = {
281 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
282 .flags = I2C_CLIENT_WAKE,
283 },
284 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700285 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700286 },
Benson Leung5ea95672014-06-17 14:02:01 -0700287 },
288};
289
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800290static struct chromeos_laptop dell_chromebook_11 = {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700291 .i2c_peripherals = {
292 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700293 {
294 .board_info = {
295 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
296 .flags = I2C_CLIENT_WAKE,
297 },
298 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700299 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700300 },
Charlie Mooney9e96aa72016-05-02 08:57:17 +0800301 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700302 {
303 .board_info = {
304 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
305 .flags = I2C_CLIENT_WAKE,
306 },
307 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700308 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700309 },
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700310 },
311};
312
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800313static struct chromeos_laptop toshiba_cb35 = {
Gene Chen963cb6f2014-06-17 14:02:03 -0700314 .i2c_peripherals = {
315 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700316 {
317 .board_info = {
318 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
319 .flags = I2C_CLIENT_WAKE,
320 },
321 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700322 .type = I2C_ADAPTER_DESIGNWARE,
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700323 },
Gene Chen963cb6f2014-06-17 14:02:03 -0700324 },
325};
326
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800327static struct chromeos_laptop acer_c7_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700328 .i2c_peripherals = {
329 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700330 {
331 .board_info = {
332 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
333 .flags = I2C_CLIENT_WAKE,
334 },
335 .dmi_name = "trackpad",
336 .type = I2C_ADAPTER_SMBUS,
337 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700338 },
339};
340
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800341static struct chromeos_laptop acer_ac700 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700342 .i2c_peripherals = {
343 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700344 {
345 .board_info = {
346 I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
347 },
348 .type = I2C_ADAPTER_SMBUS,
349 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700350 },
351};
352
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800353static struct chromeos_laptop acer_c720 = {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700354 .i2c_peripherals = {
Michael Mullinb90b3c42014-07-15 20:00:54 -0400355 /* Touchscreen. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700356 {
357 .board_info = {
358 I2C_BOARD_INFO("atmel_mxt_ts",
359 ATMEL_TS_I2C_ADDR),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700360 .flags = I2C_CLIENT_WAKE,
361 },
362 .dmi_name = "touchscreen",
Dmitry Torokhove6215eea2018-03-20 15:31:35 -0700363 .irqflags = IRQF_TRIGGER_FALLING,
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700364 .type = I2C_ADAPTER_DESIGNWARE,
365 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700366 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
367 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700368 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700369 {
370 .board_info = {
371 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
372 .flags = I2C_CLIENT_WAKE,
373 },
374 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700375 .type = I2C_ADAPTER_DESIGNWARE,
376 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700377 },
Benson Leung9bd9a902016-05-02 08:57:16 +0800378 /* Elan Touchpad option. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700379 {
380 .board_info = {
381 I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
382 .flags = I2C_CLIENT_WAKE,
383 },
384 .dmi_name = "trackpad",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700385 .type = I2C_ADAPTER_DESIGNWARE,
386 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700387 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700388 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700389 {
390 .board_info = {
391 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
392 },
393 .dmi_name = "lightsensor",
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700394 .type = I2C_ADAPTER_DESIGNWARE,
395 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700396 },
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700397 },
398};
399
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800400static struct chromeos_laptop hp_pavilion_14_chromebook = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700401 .i2c_peripherals = {
402 /* Touchpad. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700403 {
404 .board_info = {
405 I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
406 .flags = I2C_CLIENT_WAKE,
407 },
408 .dmi_name = "trackpad",
409 .type = I2C_ADAPTER_SMBUS,
410 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700411 },
412};
413
Dmitry Torokhovfc88bbd2018-03-06 10:59:15 -0800414static struct chromeos_laptop cr48 = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700415 .i2c_peripherals = {
416 /* Light Sensor. */
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700417 {
418 .board_info = {
419 I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
420 },
421 .type = I2C_ADAPTER_SMBUS,
422 },
Aaron Durbinec199dd2013-10-20 20:58:24 -0700423 },
424};
425
Christoph Hellwig6faadbb2017-09-14 11:59:30 +0200426static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
Benson Leungd1381f42012-10-25 14:21:21 -0700427 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700428 .ident = "Samsung Series 5 550",
Benson Leungd1381f42012-10-25 14:21:21 -0700429 .matches = {
430 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
431 DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
432 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700433 .driver_data = (void *)&samsung_series_5_550,
Benson Leungd1381f42012-10-25 14:21:21 -0700434 },
435 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700436 .ident = "Samsung Series 5",
Benson Leung8016bcb2013-02-01 14:34:46 -0800437 .matches = {
438 DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
439 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700440 .driver_data = (void *)&samsung_series_5,
Benson Leung8016bcb2013-02-01 14:34:46 -0800441 },
442 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700443 .ident = "Chromebook Pixel",
Benson Leungaabf3f42013-02-01 14:34:45 -0800444 .matches = {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700445 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
446 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
Benson Leungaabf3f42013-02-01 14:34:45 -0800447 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700448 .driver_data = (void *)&chromebook_pixel,
Benson Leungaabf3f42013-02-01 14:34:45 -0800449 },
450 {
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700451 .ident = "Wolf",
452 .matches = {
453 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
454 DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
455 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700456 .driver_data = (void *)&dell_chromebook_11,
Mohammed Habibulla0e1e5e52014-06-17 14:02:02 -0700457 },
458 {
Benson Leung5ea95672014-06-17 14:02:01 -0700459 .ident = "HP Chromebook 14",
460 .matches = {
461 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
462 DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
463 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700464 .driver_data = (void *)&hp_chromebook_14,
Benson Leung5ea95672014-06-17 14:02:01 -0700465 },
466 {
Gene Chen963cb6f2014-06-17 14:02:03 -0700467 .ident = "Toshiba CB35",
468 .matches = {
469 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
470 DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
471 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700472 .driver_data = (void *)&toshiba_cb35,
Gene Chen963cb6f2014-06-17 14:02:03 -0700473 },
474 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700475 .ident = "Acer C7 Chromebook",
476 .matches = {
477 DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
478 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700479 .driver_data = (void *)&acer_c7_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700480 },
481 {
482 .ident = "Acer AC700",
Benson Leungaabf3f42013-02-01 14:34:45 -0800483 .matches = {
484 DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
485 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700486 .driver_data = (void *)&acer_ac700,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700487 },
488 {
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700489 .ident = "Acer C720",
490 .matches = {
491 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
492 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700493 .driver_data = (void *)&acer_c720,
Mika Westerbergda3b0ab2014-06-17 14:02:00 -0700494 },
495 {
Aaron Durbinec199dd2013-10-20 20:58:24 -0700496 .ident = "HP Pavilion 14 Chromebook",
497 .matches = {
498 DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
499 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700500 .driver_data = (void *)&hp_pavilion_14_chromebook,
Aaron Durbinec199dd2013-10-20 20:58:24 -0700501 },
502 {
503 .ident = "Cr-48",
504 .matches = {
505 DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
506 },
Dmitry Torokhov65582922018-03-20 15:31:33 -0700507 .driver_data = (void *)&cr48,
Benson Leungaabf3f42013-02-01 14:34:45 -0800508 },
Benson Leungd1381f42012-10-25 14:21:21 -0700509 { }
510};
511MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
512
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700513static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
514{
515 struct i2c_adapter *adapter;
Benson Leung9ad36922013-10-20 20:58:25 -0700516
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700517 adapter = i2c_verify_adapter(dev);
518 if (adapter)
519 chromeos_laptop_check_adapter(adapter);
520
521 return 0;
522}
Benson Leung9ad36922013-10-20 20:58:25 -0700523
Dmitry Torokhov65582922018-03-20 15:31:33 -0700524static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
525{
526 const struct dmi_device *dmi_dev;
527 const struct dmi_dev_onboard *dev_data;
528
529 dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
530 if (!dmi_dev) {
531 pr_err("failed to find DMI device '%s'\n", dmi_name);
532 return -ENOENT;
533 }
534
535 dev_data = dmi_dev->device_data;
536 if (!dev_data) {
537 pr_err("failed to get data from DMI for '%s'\n", dmi_name);
538 return -EINVAL;
539 }
540
541 return dev_data->instance;
542}
543
544static struct chromeos_laptop * __init
545chromeos_laptop_prepare(const struct dmi_system_id *id)
546{
547 struct i2c_peripheral *i2c_dev;
548 int irq;
549 int i;
550
551 cros_laptop = (void *)id->driver_data;
552
553 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
554 i2c_dev = &cros_laptop->i2c_peripherals[i];
555
556 if (!i2c_dev->dmi_name)
557 continue;
558
559 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
560 if (irq < 0)
561 return ERR_PTR(irq);
Dmitry Torokhove6215eea2018-03-20 15:31:35 -0700562
563 i2c_dev->irq_resource = (struct resource)
564 DEFINE_RES_NAMED(irq, 1, NULL,
565 IORESOURCE_IRQ | i2c_dev->irqflags);
566 i2c_dev->board_info.resources = &i2c_dev->irq_resource;
567 i2c_dev->board_info.num_resources = 1;
Dmitry Torokhov65582922018-03-20 15:31:33 -0700568 }
569
570 return cros_laptop;
571}
572
573
Benson Leungd1381f42012-10-25 14:21:21 -0700574static int __init chromeos_laptop_init(void)
575{
Dmitry Torokhov65582922018-03-20 15:31:33 -0700576 const struct dmi_system_id *dmi_id;
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700577 int error;
Robin Schroer49c68a22014-05-29 20:45:07 +0200578
Dmitry Torokhov65582922018-03-20 15:31:33 -0700579 dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
580 if (!dmi_id) {
Dmitry Torokhov4f27f672018-03-20 15:31:30 -0700581 pr_debug("unsupported system\n");
Benson Leungd1381f42012-10-25 14:21:21 -0700582 return -ENODEV;
583 }
Benson Leung9ad36922013-10-20 20:58:25 -0700584
Dmitry Torokhov65582922018-03-20 15:31:33 -0700585 pr_debug("DMI Matched %s\n", dmi_id->ident);
586
587 cros_laptop = chromeos_laptop_prepare(dmi_id->driver_data);
588 if (IS_ERR(cros_laptop))
589 return PTR_ERR(cros_laptop);
590
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700591 error = bus_register_notifier(&i2c_bus_type,
592 &chromeos_laptop_i2c_notifier);
593 if (error) {
594 pr_err("failed to register i2c bus notifier: %d\n", error);
595 return error;
Benson Leung9ad36922013-10-20 20:58:25 -0700596 }
597
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700598 /*
599 * Scan adapters that have been registered before we installed
600 * the notifier to make sure we do not miss any devices.
601 */
602 i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
Benson Leung9ad36922013-10-20 20:58:25 -0700603
Benson Leungd1381f42012-10-25 14:21:21 -0700604 return 0;
605}
606
607static void __exit chromeos_laptop_exit(void)
608{
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700609 struct i2c_peripheral *i2c_dev;
610 int i;
Wei Yongjun2b8454a2013-11-27 11:34:58 +0800611
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700612 bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700613
614 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
615 i2c_dev = &cros_laptop->i2c_peripherals[i];
616
617 /* No more peripherals */
618 if (!i2c_dev->board_info.type)
619 break;
620
Dmitry Torokhov8d88cb02018-03-20 15:31:34 -0700621 if (i2c_dev->client)
Dmitry Torokhov28cd38f2018-03-20 15:31:32 -0700622 i2c_unregister_device(i2c_dev->client);
623 }
Benson Leungd1381f42012-10-25 14:21:21 -0700624}
625
626module_init(chromeos_laptop_init);
627module_exit(chromeos_laptop_exit);
628
629MODULE_DESCRIPTION("Chrome OS Laptop driver");
630MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
631MODULE_LICENSE("GPL");