5c47f451e43b198d55ee5a6a3d83052d4869cded
[muen/linux.git] / drivers / platform / chrome / chromeos_laptop.c
1 // 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>
6
7 #define pr_fmt(fmt)             KBUILD_MODNAME ": " fmt
8
9 #include <linux/dmi.h>
10 #include <linux/i2c.h>
11 #include <linux/input.h>
12 #include <linux/interrupt.h>
13 #include <linux/ioport.h>
14 #include <linux/module.h>
15 #include <linux/pci.h>
16 #include <linux/platform_device.h>
17 #include <linux/property.h>
18
19 #define ATMEL_TP_I2C_ADDR       0x4b
20 #define ATMEL_TP_I2C_BL_ADDR    0x25
21 #define ATMEL_TS_I2C_ADDR       0x4a
22 #define ATMEL_TS_I2C_BL_ADDR    0x26
23 #define CYAPA_TP_I2C_ADDR       0x67
24 #define ELAN_TP_I2C_ADDR        0x15
25 #define ISL_ALS_I2C_ADDR        0x44
26 #define TAOS_ALS_I2C_ADDR       0x29
27
28 static const char *i2c_adapter_names[] = {
29         "SMBus I801 adapter",
30         "i915 gmbus vga",
31         "i915 gmbus panel",
32         "Synopsys DesignWare I2C adapter",
33 };
34
35 /* Keep this enum consistent with i2c_adapter_names */
36 enum i2c_adapter_type {
37         I2C_ADAPTER_SMBUS = 0,
38         I2C_ADAPTER_VGADDC,
39         I2C_ADAPTER_PANEL,
40         I2C_ADAPTER_DESIGNWARE,
41 };
42
43 struct i2c_peripheral {
44         struct i2c_board_info board_info;
45         unsigned short alt_addr;
46
47         const char *dmi_name;
48         unsigned long irqflags;
49         struct resource irq_resource;
50
51         enum i2c_adapter_type type;
52         u32 pci_devid;
53
54         struct i2c_client *client;
55 };
56
57 struct chromeos_laptop {
58         /*
59          * Note that we can't mark this pointer as const because
60          * i2c_new_probed_device() changes passed in I2C board info, so.
61          */
62         struct i2c_peripheral *i2c_peripherals;
63         unsigned int num_i2c_peripherals;
64 };
65
66 static const struct chromeos_laptop *cros_laptop;
67
68 static struct i2c_client *
69 chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
70                                       struct i2c_board_info *info,
71                                       unsigned short alt_addr)
72 {
73         const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
74         struct i2c_client *client;
75
76         /*
77          * Add the i2c device. If we can't detect it at the primary
78          * address we scan secondary addresses. In any case the client
79          * structure gets assigned primary address.
80          */
81         client = i2c_new_probed_device(adapter, info, addr_list, NULL);
82         if (!client && alt_addr) {
83                 struct i2c_board_info dummy_info = {
84                         I2C_BOARD_INFO("dummy", info->addr),
85                 };
86                 const unsigned short alt_addr_list[] = {
87                         alt_addr, I2C_CLIENT_END
88                 };
89                 struct i2c_client *dummy;
90
91                 dummy = i2c_new_probed_device(adapter, &dummy_info,
92                                               alt_addr_list, NULL);
93                 if (dummy) {
94                         pr_debug("%d-%02x is probed at %02x\n",
95                                  adapter->nr, info->addr, dummy->addr);
96                         i2c_unregister_device(dummy);
97                         client = i2c_new_device(adapter, info);
98                 }
99         }
100
101         if (!client)
102                 pr_debug("failed to register device %d-%02x\n",
103                          adapter->nr, info->addr);
104         else
105                 pr_debug("added i2c device %d-%02x\n",
106                          adapter->nr, info->addr);
107
108         return client;
109 }
110
111 static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
112 {
113         struct pci_dev *pdev;
114
115         if (!dev_is_pci(dev))
116                 return false;
117
118         pdev = to_pci_dev(dev);
119         return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
120 }
121
122 static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
123 {
124         struct i2c_peripheral *i2c_dev;
125         int i;
126
127         for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
128                 i2c_dev = &cros_laptop->i2c_peripherals[i];
129
130                 /* Skip devices already created */
131                 if (i2c_dev->client)
132                         continue;
133
134                 if (strncmp(adapter->name, i2c_adapter_names[i2c_dev->type],
135                             strlen(i2c_adapter_names[i2c_dev->type])))
136                         continue;
137
138                 if (i2c_dev->pci_devid &&
139                     !chromeos_laptop_match_adapter_devid(adapter->dev.parent,
140                                                          i2c_dev->pci_devid)) {
141                         continue;
142                 }
143
144                 i2c_dev->client =
145                         chromes_laptop_instantiate_i2c_device(adapter,
146                                                         &i2c_dev->board_info,
147                                                         i2c_dev->alt_addr);
148         }
149 }
150
151 static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
152 {
153         struct i2c_peripheral *i2c_dev;
154         int i;
155
156         for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
157                 i2c_dev = &cros_laptop->i2c_peripherals[i];
158
159                 if (i2c_dev->client == client)
160                         i2c_dev->client = NULL;
161         }
162 }
163
164 static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
165                                              unsigned long action, void *data)
166 {
167         struct device *dev = data;
168
169         switch (action) {
170         case BUS_NOTIFY_ADD_DEVICE:
171                 if (dev->type == &i2c_adapter_type)
172                         chromeos_laptop_check_adapter(to_i2c_adapter(dev));
173                 break;
174
175         case BUS_NOTIFY_REMOVED_DEVICE:
176                 if (dev->type == &i2c_client_type)
177                         chromeos_laptop_detach_i2c_client(to_i2c_client(dev));
178                 break;
179         }
180
181         return 0;
182 }
183
184 static struct notifier_block chromeos_laptop_i2c_notifier = {
185         .notifier_call = chromeos_laptop_i2c_notifier_call,
186 };
187
188 #define DECLARE_CROS_LAPTOP(_name)                                      \
189 static const struct chromeos_laptop _name __initconst = {               \
190         .i2c_peripherals        = _name##_peripherals,                  \
191         .num_i2c_peripherals    = ARRAY_SIZE(_name##_peripherals),      \
192 }
193
194 static struct i2c_peripheral samsung_series_5_550_peripherals[] __initdata = {
195         /* Touchpad. */
196         {
197                 .board_info     = {
198                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
199                         .flags          = I2C_CLIENT_WAKE,
200                 },
201                 .dmi_name       = "trackpad",
202                 .type           = I2C_ADAPTER_SMBUS,
203         },
204         /* Light Sensor. */
205         {
206                 .board_info     = {
207                         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
208                 },
209                 .dmi_name       = "lightsensor",
210                 .type           = I2C_ADAPTER_SMBUS,
211         },
212 };
213 DECLARE_CROS_LAPTOP(samsung_series_5_550);
214
215 static struct i2c_peripheral samsung_series_5_peripherals[] __initdata = {
216         /* Light Sensor. */
217         {
218                 .board_info     = {
219                         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
220                 },
221                 .type           = I2C_ADAPTER_SMBUS,
222         },
223 };
224 DECLARE_CROS_LAPTOP(samsung_series_5);
225
226 static const int chromebook_pixel_tp_keys[] __initconst = {
227         KEY_RESERVED,
228         KEY_RESERVED,
229         KEY_RESERVED,
230         KEY_RESERVED,
231         KEY_RESERVED,
232         BTN_LEFT
233 };
234
235 static const struct property_entry
236 chromebook_pixel_trackpad_props[] __initconst = {
237         PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_pixel_tp_keys),
238         { }
239 };
240
241 static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {
242         /* Touch Screen. */
243         {
244                 .board_info     = {
245                         I2C_BOARD_INFO("atmel_mxt_ts",
246                                         ATMEL_TS_I2C_ADDR),
247                         .flags          = I2C_CLIENT_WAKE,
248                 },
249                 .dmi_name       = "touchscreen",
250                 .irqflags       = IRQF_TRIGGER_FALLING,
251                 .type           = I2C_ADAPTER_PANEL,
252                 .alt_addr       = ATMEL_TS_I2C_BL_ADDR,
253         },
254         /* Touchpad. */
255         {
256                 .board_info     = {
257                         I2C_BOARD_INFO("atmel_mxt_tp",
258                                         ATMEL_TP_I2C_ADDR),
259                         .properties     =
260                                 chromebook_pixel_trackpad_props,
261                         .flags          = I2C_CLIENT_WAKE,
262                 },
263                 .dmi_name       = "trackpad",
264                 .irqflags       = IRQF_TRIGGER_FALLING,
265                 .type           = I2C_ADAPTER_VGADDC,
266                 .alt_addr       = ATMEL_TP_I2C_BL_ADDR,
267         },
268         /* Light Sensor. */
269         {
270                 .board_info     = {
271                         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
272                 },
273                 .dmi_name       = "lightsensor",
274                 .type           = I2C_ADAPTER_PANEL,
275         },
276 };
277 DECLARE_CROS_LAPTOP(chromebook_pixel);
278
279 static struct i2c_peripheral hp_chromebook_14_peripherals[] __initdata = {
280         /* Touchpad. */
281         {
282                 .board_info     = {
283                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
284                         .flags          = I2C_CLIENT_WAKE,
285                 },
286                 .dmi_name       = "trackpad",
287                 .type           = I2C_ADAPTER_DESIGNWARE,
288         },
289 };
290 DECLARE_CROS_LAPTOP(hp_chromebook_14);
291
292 static struct i2c_peripheral dell_chromebook_11_peripherals[] __initdata = {
293         /* Touchpad. */
294         {
295                 .board_info     = {
296                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
297                         .flags          = I2C_CLIENT_WAKE,
298                 },
299                 .dmi_name       = "trackpad",
300                 .type           = I2C_ADAPTER_DESIGNWARE,
301         },
302         /* Elan Touchpad option. */
303         {
304                 .board_info     = {
305                         I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
306                         .flags          = I2C_CLIENT_WAKE,
307                 },
308                 .dmi_name       = "trackpad",
309                 .type           = I2C_ADAPTER_DESIGNWARE,
310         },
311 };
312 DECLARE_CROS_LAPTOP(dell_chromebook_11);
313
314 static struct i2c_peripheral toshiba_cb35_peripherals[] __initdata = {
315         /* Touchpad. */
316         {
317                 .board_info     = {
318                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
319                         .flags          = I2C_CLIENT_WAKE,
320                 },
321                 .dmi_name       = "trackpad",
322                 .type           = I2C_ADAPTER_DESIGNWARE,
323         },
324 };
325 DECLARE_CROS_LAPTOP(toshiba_cb35);
326
327 static struct i2c_peripheral acer_c7_chromebook_peripherals[] __initdata = {
328         /* Touchpad. */
329         {
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         },
337 };
338 DECLARE_CROS_LAPTOP(acer_c7_chromebook);
339
340 static struct i2c_peripheral acer_ac700_peripherals[] __initdata = {
341         /* Light Sensor. */
342         {
343                 .board_info     = {
344                         I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR),
345                 },
346                 .type           = I2C_ADAPTER_SMBUS,
347         },
348 };
349 DECLARE_CROS_LAPTOP(acer_ac700);
350
351 static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
352         /* Touchscreen. */
353         {
354                 .board_info     = {
355                         I2C_BOARD_INFO("atmel_mxt_ts",
356                                         ATMEL_TS_I2C_ADDR),
357                         .flags          = I2C_CLIENT_WAKE,
358                 },
359                 .dmi_name       = "touchscreen",
360                 .irqflags       = IRQF_TRIGGER_FALLING,
361                 .type           = I2C_ADAPTER_DESIGNWARE,
362                 .pci_devid      = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
363                 .alt_addr       = ATMEL_TS_I2C_BL_ADDR,
364         },
365         /* Touchpad. */
366         {
367                 .board_info     = {
368                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
369                         .flags          = I2C_CLIENT_WAKE,
370                 },
371                 .dmi_name       = "trackpad",
372                 .type           = I2C_ADAPTER_DESIGNWARE,
373                 .pci_devid      = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
374         },
375         /* Elan Touchpad option. */
376         {
377                 .board_info     = {
378                         I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR),
379                         .flags          = I2C_CLIENT_WAKE,
380                 },
381                 .dmi_name       = "trackpad",
382                 .type           = I2C_ADAPTER_DESIGNWARE,
383                 .pci_devid      = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
384         },
385         /* Light Sensor. */
386         {
387                 .board_info     = {
388                         I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
389                 },
390                 .dmi_name       = "lightsensor",
391                 .type           = I2C_ADAPTER_DESIGNWARE,
392                 .pci_devid      = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
393         },
394 };
395 DECLARE_CROS_LAPTOP(acer_c720);
396
397 static struct i2c_peripheral
398 hp_pavilion_14_chromebook_peripherals[] __initdata = {
399         /* Touchpad. */
400         {
401                 .board_info     = {
402                         I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR),
403                         .flags          = I2C_CLIENT_WAKE,
404                 },
405                 .dmi_name       = "trackpad",
406                 .type           = I2C_ADAPTER_SMBUS,
407         },
408 };
409 DECLARE_CROS_LAPTOP(hp_pavilion_14_chromebook);
410
411 static struct i2c_peripheral cr48_peripherals[] __initdata = {
412         /* Light Sensor. */
413         {
414                 .board_info     = {
415                         I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
416                 },
417                 .type           = I2C_ADAPTER_SMBUS,
418         },
419 };
420 DECLARE_CROS_LAPTOP(cr48);
421
422 static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
423         {
424                 .ident = "Samsung Series 5 550",
425                 .matches = {
426                         DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"),
427                         DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"),
428                 },
429                 .driver_data = (void *)&samsung_series_5_550,
430         },
431         {
432                 .ident = "Samsung Series 5",
433                 .matches = {
434                         DMI_MATCH(DMI_PRODUCT_NAME, "Alex"),
435                 },
436                 .driver_data = (void *)&samsung_series_5,
437         },
438         {
439                 .ident = "Chromebook Pixel",
440                 .matches = {
441                         DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
442                         DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
443                 },
444                 .driver_data = (void *)&chromebook_pixel,
445         },
446         {
447                 .ident = "Wolf",
448                 .matches = {
449                         DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
450                         DMI_MATCH(DMI_PRODUCT_NAME, "Wolf"),
451                 },
452                 .driver_data = (void *)&dell_chromebook_11,
453         },
454         {
455                 .ident = "HP Chromebook 14",
456                 .matches = {
457                         DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
458                         DMI_MATCH(DMI_PRODUCT_NAME, "Falco"),
459                 },
460                 .driver_data = (void *)&hp_chromebook_14,
461         },
462         {
463                 .ident = "Toshiba CB35",
464                 .matches = {
465                         DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
466                         DMI_MATCH(DMI_PRODUCT_NAME, "Leon"),
467                 },
468                 .driver_data = (void *)&toshiba_cb35,
469         },
470         {
471                 .ident = "Acer C7 Chromebook",
472                 .matches = {
473                         DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"),
474                 },
475                 .driver_data = (void *)&acer_c7_chromebook,
476         },
477         {
478                 .ident = "Acer AC700",
479                 .matches = {
480                         DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"),
481                 },
482                 .driver_data = (void *)&acer_ac700,
483         },
484         {
485                 .ident = "Acer C720",
486                 .matches = {
487                         DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
488                 },
489                 .driver_data = (void *)&acer_c720,
490         },
491         {
492                 .ident = "HP Pavilion 14 Chromebook",
493                 .matches = {
494                         DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"),
495                 },
496                 .driver_data = (void *)&hp_pavilion_14_chromebook,
497         },
498         {
499                 .ident = "Cr-48",
500                 .matches = {
501                         DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
502                 },
503                 .driver_data = (void *)&cr48,
504         },
505         { }
506 };
507 MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
508
509 static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
510 {
511         struct i2c_adapter *adapter;
512
513         adapter = i2c_verify_adapter(dev);
514         if (adapter)
515                 chromeos_laptop_check_adapter(adapter);
516
517         return 0;
518 }
519
520 static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
521 {
522         const struct dmi_device *dmi_dev;
523         const struct dmi_dev_onboard *dev_data;
524
525         dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, dmi_name, NULL);
526         if (!dmi_dev) {
527                 pr_err("failed to find DMI device '%s'\n", dmi_name);
528                 return -ENOENT;
529         }
530
531         dev_data = dmi_dev->device_data;
532         if (!dev_data) {
533                 pr_err("failed to get data from DMI for '%s'\n", dmi_name);
534                 return -EINVAL;
535         }
536
537         return dev_data->instance;
538 }
539
540 static int __init chromeos_laptop_setup_irq(struct i2c_peripheral *i2c_dev)
541 {
542         int irq;
543
544         if (i2c_dev->dmi_name) {
545                 irq = chromeos_laptop_get_irq_from_dmi(i2c_dev->dmi_name);
546                 if (irq < 0)
547                         return irq;
548
549                 i2c_dev->irq_resource  = (struct resource)
550                         DEFINE_RES_NAMED(irq, 1, NULL,
551                                          IORESOURCE_IRQ | i2c_dev->irqflags);
552                 i2c_dev->board_info.resources = &i2c_dev->irq_resource;
553                 i2c_dev->board_info.num_resources = 1;
554         }
555
556         return 0;
557 }
558
559 static struct chromeos_laptop * __init
560 chromeos_laptop_prepare(const struct chromeos_laptop *src)
561 {
562         struct chromeos_laptop *cros_laptop;
563         struct i2c_peripheral *i2c_dev;
564         struct i2c_board_info *info;
565         int error;
566         int i;
567
568         cros_laptop = kzalloc(sizeof(*cros_laptop), GFP_KERNEL);
569         if (!cros_laptop)
570                 return ERR_PTR(-ENOMEM);
571
572         cros_laptop->i2c_peripherals = kmemdup(src->i2c_peripherals,
573                                                src->num_i2c_peripherals *
574                                                 sizeof(*src->i2c_peripherals),
575                                                GFP_KERNEL);
576         if (!cros_laptop->i2c_peripherals) {
577                 error = -ENOMEM;
578                 goto err_free_cros_laptop;
579         }
580
581         cros_laptop->num_i2c_peripherals = src->num_i2c_peripherals;
582
583         for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
584                 i2c_dev = &cros_laptop->i2c_peripherals[i];
585                 info = &i2c_dev->board_info;
586
587                 error = chromeos_laptop_setup_irq(i2c_dev);
588                 if (error)
589                         goto err_destroy_cros_peripherals;
590
591                 /* We need to deep-copy properties */
592                 if (info->properties) {
593                         info->properties =
594                                 property_entries_dup(info->properties);
595                         if (IS_ERR(info->properties)) {
596                                 error = PTR_ERR(info->properties);
597                                 goto err_destroy_cros_peripherals;
598                         }
599                 }
600         }
601
602         return cros_laptop;
603
604 err_destroy_cros_peripherals:
605         while (--i >= 0) {
606                 i2c_dev = &cros_laptop->i2c_peripherals[i];
607                 info = &i2c_dev->board_info;
608                 if (info->properties)
609                         property_entries_free(info->properties);
610         }
611         kfree(cros_laptop->i2c_peripherals);
612 err_free_cros_laptop:
613         kfree(cros_laptop);
614         return ERR_PTR(error);
615 }
616
617 static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
618 {
619         struct i2c_peripheral *i2c_dev;
620         struct i2c_board_info *info;
621         int i;
622
623         for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
624                 i2c_dev = &cros_laptop->i2c_peripherals[i];
625                 info = &i2c_dev->board_info;
626
627                 if (i2c_dev->client)
628                         i2c_unregister_device(i2c_dev->client);
629
630                 if (info->properties)
631                         property_entries_free(info->properties);
632         }
633
634         kfree(cros_laptop->i2c_peripherals);
635         kfree(cros_laptop);
636 }
637
638 static int __init chromeos_laptop_init(void)
639 {
640         const struct dmi_system_id *dmi_id;
641         int error;
642
643         dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
644         if (!dmi_id) {
645                 pr_debug("unsupported system\n");
646                 return -ENODEV;
647         }
648
649         pr_debug("DMI Matched %s\n", dmi_id->ident);
650
651         cros_laptop = chromeos_laptop_prepare((void *)dmi_id->driver_data);
652         if (IS_ERR(cros_laptop))
653                 return PTR_ERR(cros_laptop);
654
655         error = bus_register_notifier(&i2c_bus_type,
656                                       &chromeos_laptop_i2c_notifier);
657         if (error) {
658                 pr_err("failed to register i2c bus notifier: %d\n", error);
659                 chromeos_laptop_destroy(cros_laptop);
660                 return error;
661         }
662
663         /*
664          * Scan adapters that have been registered before we installed
665          * the notifier to make sure we do not miss any devices.
666          */
667         i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
668
669         return 0;
670 }
671
672 static void __exit chromeos_laptop_exit(void)
673 {
674         bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
675         chromeos_laptop_destroy(cros_laptop);
676 }
677
678 module_init(chromeos_laptop_init);
679 module_exit(chromeos_laptop_exit);
680
681 MODULE_DESCRIPTION("Chrome OS Laptop driver");
682 MODULE_AUTHOR("Benson Leung <bleung@chromium.org>");
683 MODULE_LICENSE("GPL");