aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2018-03-20 18:31:34 -0400
committerBenson Leung <bleung@chromium.org>2018-04-10 01:46:09 -0400
commit8d88cb03c22e4cbde4c3020883f534e8ba7f5c79 (patch)
tree6925d7f404931e6ea3e8c805d16d9699c995a972
parent65582920d72d2562e44c07aa530586a3583477b9 (diff)
platform/chrome: chromeos_laptop - use I2C notifier to create devices
Instead of using platform device and deferrals to handle the case when i2C adapters appear late in the game, and not handling device unbinding all that well, let's switch to using I2C bus notifier to get told when a new I2C adapter appears in the system, and attempt to add appropriate devices at that time. In case when we have 2 Designware adapters in the system (Acer C720), instead of counting and hoping they get enumerate din the right order, let's switch to using their PCI devids (slot/function) that should be stable. Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Benson Leung <bleung@chromium.org>
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c247
1 files changed, 102 insertions, 145 deletions
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index d6d2bc6f3aaf..e5015dfaa81e 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -12,6 +12,7 @@
12#include <linux/input.h> 12#include <linux/input.h>
13#include <linux/interrupt.h> 13#include <linux/interrupt.h>
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/pci.h>
15#include <linux/platform_device.h> 16#include <linux/platform_device.h>
16 17
17#define ATMEL_TP_I2C_ADDR 0x4b 18#define ATMEL_TP_I2C_ADDR 0x4b
@@ -23,14 +24,11 @@
23#define ISL_ALS_I2C_ADDR 0x44 24#define ISL_ALS_I2C_ADDR 0x44
24#define TAOS_ALS_I2C_ADDR 0x29 25#define TAOS_ALS_I2C_ADDR 0x29
25 26
26#define MAX_I2C_DEVICE_DEFERRALS 5
27
28static const char *i2c_adapter_names[] = { 27static const char *i2c_adapter_names[] = {
29 "SMBus I801 adapter", 28 "SMBus I801 adapter",
30 "i915 gmbus vga", 29 "i915 gmbus vga",
31 "i915 gmbus panel", 30 "i915 gmbus panel",
32 "Synopsys DesignWare I2C adapter", 31 "Synopsys DesignWare I2C adapter",
33 "Synopsys DesignWare I2C adapter",
34}; 32};
35 33
36/* Keep this enum consistent with i2c_adapter_names */ 34/* Keep this enum consistent with i2c_adapter_names */
@@ -38,15 +36,7 @@ enum i2c_adapter_type {
38 I2C_ADAPTER_SMBUS = 0, 36 I2C_ADAPTER_SMBUS = 0,
39 I2C_ADAPTER_VGADDC, 37 I2C_ADAPTER_VGADDC,
40 I2C_ADAPTER_PANEL, 38 I2C_ADAPTER_PANEL,
41 I2C_ADAPTER_DESIGNWARE_0, 39 I2C_ADAPTER_DESIGNWARE,
42 I2C_ADAPTER_DESIGNWARE_1,
43};
44
45enum i2c_peripheral_state {
46 UNPROBED = 0,
47 PROBED,
48 TIMEDOUT,
49 FAILED,
50}; 40};
51 41
52struct i2c_peripheral { 42struct i2c_peripheral {
@@ -54,10 +44,9 @@ struct i2c_peripheral {
54 unsigned short alt_addr; 44 unsigned short alt_addr;
55 const char *dmi_name; 45 const char *dmi_name;
56 enum i2c_adapter_type type; 46 enum i2c_adapter_type type;
47 u32 pci_devid;
57 48
58 enum i2c_peripheral_state state;
59 struct i2c_client *client; 49 struct i2c_client *client;
60 int tries;
61}; 50};
62 51
63#define MAX_I2C_PERIPHERALS 4 52#define MAX_I2C_PERIPHERALS 4
@@ -69,19 +58,12 @@ struct chromeos_laptop {
69static struct chromeos_laptop *cros_laptop; 58static struct chromeos_laptop *cros_laptop;
70 59
71static struct i2c_client * 60static struct i2c_client *
72chromes_laptop_instantiate_i2c_device(int bus, 61chromes_laptop_instantiate_i2c_device(struct i2c_adapter *adapter,
73 struct i2c_board_info *info, 62 struct i2c_board_info *info,
74 unsigned short alt_addr) 63 unsigned short alt_addr)
75{ 64{
76 struct i2c_adapter *adapter;
77 struct i2c_client *client = NULL;
78 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; 65 const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END };
79 66 struct i2c_client *client;
80 adapter = i2c_get_adapter(bus);
81 if (!adapter) {
82 pr_err("failed to get i2c adapter %d\n", bus);
83 return NULL;
84 }
85 67
86 /* 68 /*
87 * Add the i2c device. If we can't detect it at the primary 69 * Add the i2c device. If we can't detect it at the primary
@@ -102,126 +84,103 @@ chromes_laptop_instantiate_i2c_device(int bus,
102 alt_addr_list, NULL); 84 alt_addr_list, NULL);
103 if (dummy) { 85 if (dummy) {
104 pr_debug("%d-%02x is probed at %02x\n", 86 pr_debug("%d-%02x is probed at %02x\n",
105 bus, info->addr, dummy->addr); 87 adapter->nr, info->addr, dummy->addr);
106 i2c_unregister_device(dummy); 88 i2c_unregister_device(dummy);
107 client = i2c_new_device(adapter, info); 89 client = i2c_new_device(adapter, info);
108 } 90 }
109 } 91 }
110 92
111 if (!client) 93 if (!client)
112 pr_notice("failed to register device %d-%02x\n", 94 pr_debug("failed to register device %d-%02x\n",
113 bus, info->addr); 95 adapter->nr, info->addr);
114 else 96 else
115 pr_debug("added i2c device %d-%02x\n", bus, info->addr); 97 pr_debug("added i2c device %d-%02x\n",
98 adapter->nr, info->addr);
116 99
117 i2c_put_adapter(adapter);
118 return client; 100 return client;
119} 101}
120 102
121struct i2c_lookup { 103static bool chromeos_laptop_match_adapter_devid(struct device *dev, u32 devid)
122 const char *name;
123 int instance;
124 int n;
125};
126
127static int __find_i2c_adap(struct device *dev, void *data)
128{ 104{
129 struct i2c_lookup *lookup = data; 105 struct pci_dev *pdev;
130 static const char *prefix = "i2c-";
131 struct i2c_adapter *adapter;
132 106
133 if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) 107 if (!dev_is_pci(dev))
134 return 0; 108 return false;
135 adapter = to_i2c_adapter(dev);
136 if (strncmp(adapter->name, lookup->name, strlen(lookup->name)) == 0 &&
137 lookup->n++ == lookup->instance)
138 return 1;
139 return 0;
140}
141 109
142static int find_i2c_adapter_num(enum i2c_adapter_type type) 110 pdev = to_pci_dev(dev);
143{ 111 return devid == PCI_DEVID(pdev->bus->number, pdev->devfn);
144 struct device *dev = NULL;
145 struct i2c_adapter *adapter;
146 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
152 /* find the adapter by name */
153 dev = bus_find_device(&i2c_bus_type, NULL, &lookup, __find_i2c_adap);
154 if (!dev) {
155 /* Adapters may appear later. Deferred probing will retry */
156 pr_notice("i2c adapter %s not found on system.\n",
157 lookup.name);
158 return -ENODEV;
159 }
160 adapter = to_i2c_adapter(dev);
161 return adapter->nr;
162} 112}
163 113
164static int chromeos_laptop_add_peripheral(struct i2c_peripheral *i2c_dev) 114static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
165{ 115{
166 struct i2c_client *client; 116 struct i2c_peripheral *i2c_dev;
167 int bus; 117 int i;
168 118
169 /* 119 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
170 * Check that the i2c adapter is present. 120 i2c_dev = &cros_laptop->i2c_peripherals[i];
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;
177
178 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 }
195 121
196 i2c_dev->client = client; 122 /* No more peripherals */
197 i2c_dev->state = PROBED; 123 if (!i2c_dev->board_info.addr)
124 break;
198 125
199 return 0; 126 /* Skip devices already created */
127 if (i2c_dev->client)
128 continue;
129
130 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 }
200} 145}
201 146
202static int chromeos_laptop_probe(struct platform_device *pdev) 147static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
203{ 148{
204 struct i2c_peripheral *i2c_dev; 149 struct i2c_peripheral *i2c_dev;
205 int i; 150 int i;
206 int ret = 0;
207 151
208 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { 152 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
209 i2c_dev = &cros_laptop->i2c_peripherals[i]; 153 i2c_dev = &cros_laptop->i2c_peripherals[i];
210 154
211 /* No more peripherals. */ 155 if (i2c_dev->client == client)
212 if (!i2c_dev->board_info.addr) 156 i2c_dev->client = NULL;
213 break; 157 }
214 158}
215 if (i2c_dev->state != UNPROBED)
216 continue;
217 159
218 if (chromeos_laptop_add_peripheral(i2c_dev) == -EPROBE_DEFER) 160static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
219 ret = -EPROBE_DEFER; 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;
220 } 175 }
221 176
222 return ret; 177 return 0;
223} 178}
224 179
180static struct notifier_block chromeos_laptop_i2c_notifier = {
181 .notifier_call = chromeos_laptop_i2c_notifier_call,
182};
183
225static struct chromeos_laptop samsung_series_5_550 = { 184static struct chromeos_laptop samsung_series_5_550 = {
226 .i2c_peripherals = { 185 .i2c_peripherals = {
227 /* Touchpad. */ 186 /* Touchpad. */
@@ -322,7 +281,7 @@ static struct chromeos_laptop hp_chromebook_14 = {
322 .flags = I2C_CLIENT_WAKE, 281 .flags = I2C_CLIENT_WAKE,
323 }, 282 },
324 .dmi_name = "trackpad", 283 .dmi_name = "trackpad",
325 .type = I2C_ADAPTER_DESIGNWARE_0, 284 .type = I2C_ADAPTER_DESIGNWARE,
326 }, 285 },
327 }, 286 },
328}; 287};
@@ -336,7 +295,7 @@ static struct chromeos_laptop dell_chromebook_11 = {
336 .flags = I2C_CLIENT_WAKE, 295 .flags = I2C_CLIENT_WAKE,
337 }, 296 },
338 .dmi_name = "trackpad", 297 .dmi_name = "trackpad",
339 .type = I2C_ADAPTER_DESIGNWARE_0, 298 .type = I2C_ADAPTER_DESIGNWARE,
340 }, 299 },
341 /* Elan Touchpad option. */ 300 /* Elan Touchpad option. */
342 { 301 {
@@ -345,7 +304,7 @@ static struct chromeos_laptop dell_chromebook_11 = {
345 .flags = I2C_CLIENT_WAKE, 304 .flags = I2C_CLIENT_WAKE,
346 }, 305 },
347 .dmi_name = "trackpad", 306 .dmi_name = "trackpad",
348 .type = I2C_ADAPTER_DESIGNWARE_0, 307 .type = I2C_ADAPTER_DESIGNWARE,
349 }, 308 },
350 }, 309 },
351}; 310};
@@ -359,7 +318,7 @@ static struct chromeos_laptop toshiba_cb35 = {
359 .flags = I2C_CLIENT_WAKE, 318 .flags = I2C_CLIENT_WAKE,
360 }, 319 },
361 .dmi_name = "trackpad", 320 .dmi_name = "trackpad",
362 .type = I2C_ADAPTER_DESIGNWARE_0, 321 .type = I2C_ADAPTER_DESIGNWARE,
363 }, 322 },
364 }, 323 },
365}; 324};
@@ -401,7 +360,8 @@ static struct chromeos_laptop acer_c720 = {
401 .flags = I2C_CLIENT_WAKE, 360 .flags = I2C_CLIENT_WAKE,
402 }, 361 },
403 .dmi_name = "touchscreen", 362 .dmi_name = "touchscreen",
404 .type = I2C_ADAPTER_DESIGNWARE_1, 363 .type = I2C_ADAPTER_DESIGNWARE,
364 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
405 .alt_addr = ATMEL_TS_I2C_BL_ADDR, 365 .alt_addr = ATMEL_TS_I2C_BL_ADDR,
406 }, 366 },
407 /* Touchpad. */ 367 /* Touchpad. */
@@ -411,7 +371,8 @@ static struct chromeos_laptop acer_c720 = {
411 .flags = I2C_CLIENT_WAKE, 371 .flags = I2C_CLIENT_WAKE,
412 }, 372 },
413 .dmi_name = "trackpad", 373 .dmi_name = "trackpad",
414 .type = I2C_ADAPTER_DESIGNWARE_0, 374 .type = I2C_ADAPTER_DESIGNWARE,
375 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
415 }, 376 },
416 /* Elan Touchpad option. */ 377 /* Elan Touchpad option. */
417 { 378 {
@@ -420,7 +381,8 @@ static struct chromeos_laptop acer_c720 = {
420 .flags = I2C_CLIENT_WAKE, 381 .flags = I2C_CLIENT_WAKE,
421 }, 382 },
422 .dmi_name = "trackpad", 383 .dmi_name = "trackpad",
423 .type = I2C_ADAPTER_DESIGNWARE_0, 384 .type = I2C_ADAPTER_DESIGNWARE,
385 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x1)),
424 }, 386 },
425 /* Light Sensor. */ 387 /* Light Sensor. */
426 { 388 {
@@ -428,7 +390,8 @@ static struct chromeos_laptop acer_c720 = {
428 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), 390 I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR),
429 }, 391 },
430 .dmi_name = "lightsensor", 392 .dmi_name = "lightsensor",
431 .type = I2C_ADAPTER_DESIGNWARE_1, 393 .type = I2C_ADAPTER_DESIGNWARE,
394 .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
432 }, 395 },
433 }, 396 },
434}; 397};
@@ -546,14 +509,16 @@ static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
546}; 509};
547MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); 510MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table);
548 511
549static struct platform_device *cros_platform_device; 512static int __init chromeos_laptop_scan_adapter(struct device *dev, void *data)
513{
514 struct i2c_adapter *adapter;
550 515
551static struct platform_driver cros_platform_driver = { 516 adapter = i2c_verify_adapter(dev);
552 .driver = { 517 if (adapter)
553 .name = "chromeos_laptop", 518 chromeos_laptop_check_adapter(adapter);
554 }, 519
555 .probe = chromeos_laptop_probe, 520 return 0;
556}; 521}
557 522
558static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name) 523static int __init chromeos_laptop_get_irq_from_dmi(const char *dmi_name)
559{ 524{
@@ -602,7 +567,7 @@ chromeos_laptop_prepare(const struct dmi_system_id *id)
602static int __init chromeos_laptop_init(void) 567static int __init chromeos_laptop_init(void)
603{ 568{
604 const struct dmi_system_id *dmi_id; 569 const struct dmi_system_id *dmi_id;
605 int ret; 570 int error;
606 571
607 dmi_id = dmi_first_match(chromeos_laptop_dmi_table); 572 dmi_id = dmi_first_match(chromeos_laptop_dmi_table);
608 if (!dmi_id) { 573 if (!dmi_id) {
@@ -616,27 +581,20 @@ static int __init chromeos_laptop_init(void)
616 if (IS_ERR(cros_laptop)) 581 if (IS_ERR(cros_laptop))
617 return PTR_ERR(cros_laptop); 582 return PTR_ERR(cros_laptop);
618 583
619 ret = platform_driver_register(&cros_platform_driver); 584 error = bus_register_notifier(&i2c_bus_type,
620 if (ret) 585 &chromeos_laptop_i2c_notifier);
621 return ret; 586 if (error) {
622 587 pr_err("failed to register i2c bus notifier: %d\n", error);
623 cros_platform_device = platform_device_alloc("chromeos_laptop", -1); 588 return error;
624 if (!cros_platform_device) {
625 ret = -ENOMEM;
626 goto fail_platform_device1;
627 } 589 }
628 590
629 ret = platform_device_add(cros_platform_device); 591 /*
630 if (ret) 592 * Scan adapters that have been registered before we installed
631 goto fail_platform_device2; 593 * the notifier to make sure we do not miss any devices.
594 */
595 i2c_for_each_dev(NULL, chromeos_laptop_scan_adapter);
632 596
633 return 0; 597 return 0;
634
635fail_platform_device2:
636 platform_device_put(cros_platform_device);
637fail_platform_device1:
638 platform_driver_unregister(&cros_platform_driver);
639 return ret;
640} 598}
641 599
642static void __exit chromeos_laptop_exit(void) 600static void __exit chromeos_laptop_exit(void)
@@ -644,8 +602,7 @@ static void __exit chromeos_laptop_exit(void)
644 struct i2c_peripheral *i2c_dev; 602 struct i2c_peripheral *i2c_dev;
645 int i; 603 int i;
646 604
647 platform_device_unregister(cros_platform_device); 605 bus_unregister_notifier(&i2c_bus_type, &chromeos_laptop_i2c_notifier);
648 platform_driver_unregister(&cros_platform_driver);
649 606
650 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) { 607 for (i = 0; i < MAX_I2C_PERIPHERALS; i++) {
651 i2c_dev = &cros_laptop->i2c_peripherals[i]; 608 i2c_dev = &cros_laptop->i2c_peripherals[i];
@@ -654,7 +611,7 @@ static void __exit chromeos_laptop_exit(void)
654 if (!i2c_dev->board_info.type) 611 if (!i2c_dev->board_info.type)
655 break; 612 break;
656 613
657 if (i2c_dev->state == PROBED) 614 if (i2c_dev->client)
658 i2c_unregister_device(i2c_dev->client); 615 i2c_unregister_device(i2c_dev->client);
659 } 616 }
660} 617}