.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | /* |
---|
3 | | - * Thunderbolt Cactus Ridge driver - eeprom access |
---|
| 3 | + * Thunderbolt driver - eeprom access |
---|
4 | 4 | * |
---|
5 | 5 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> |
---|
| 6 | + * Copyright (C) 2018, Intel Corporation |
---|
6 | 7 | */ |
---|
7 | 8 | |
---|
8 | 9 | #include <linux/crc32.h> |
---|
| 10 | +#include <linux/delay.h> |
---|
9 | 11 | #include <linux/property.h> |
---|
10 | 12 | #include <linux/slab.h> |
---|
11 | 13 | #include "tb.h" |
---|
.. | .. |
---|
130 | 132 | } |
---|
131 | 133 | |
---|
132 | 134 | /** |
---|
| 135 | + * tb_eeprom_get_drom_offset - get drom offset within eeprom |
---|
| 136 | + */ |
---|
| 137 | +static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) |
---|
| 138 | +{ |
---|
| 139 | + struct tb_cap_plug_events cap; |
---|
| 140 | + int res; |
---|
| 141 | + |
---|
| 142 | + if (!sw->cap_plug_events) { |
---|
| 143 | + tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); |
---|
| 144 | + return -ENODEV; |
---|
| 145 | + } |
---|
| 146 | + res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, |
---|
| 147 | + sizeof(cap) / 4); |
---|
| 148 | + if (res) |
---|
| 149 | + return res; |
---|
| 150 | + |
---|
| 151 | + if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { |
---|
| 152 | + tb_sw_warn(sw, "no NVM\n"); |
---|
| 153 | + return -ENODEV; |
---|
| 154 | + } |
---|
| 155 | + |
---|
| 156 | + if (cap.drom_offset > 0xffff) { |
---|
| 157 | + tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", |
---|
| 158 | + cap.drom_offset); |
---|
| 159 | + return -ENXIO; |
---|
| 160 | + } |
---|
| 161 | + *offset = cap.drom_offset; |
---|
| 162 | + return 0; |
---|
| 163 | +} |
---|
| 164 | + |
---|
| 165 | +/** |
---|
133 | 166 | * tb_eeprom_read_n - read count bytes from offset into val |
---|
134 | 167 | */ |
---|
135 | 168 | static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val, |
---|
136 | 169 | size_t count) |
---|
137 | 170 | { |
---|
| 171 | + u16 drom_offset; |
---|
138 | 172 | int i, res; |
---|
| 173 | + |
---|
| 174 | + res = tb_eeprom_get_drom_offset(sw, &drom_offset); |
---|
| 175 | + if (res) |
---|
| 176 | + return res; |
---|
| 177 | + |
---|
| 178 | + offset += drom_offset; |
---|
| 179 | + |
---|
139 | 180 | res = tb_eeprom_active(sw, true); |
---|
140 | 181 | if (res) |
---|
141 | 182 | return res; |
---|
.. | .. |
---|
207 | 248 | |
---|
208 | 249 | struct tb_drom_entry_generic { |
---|
209 | 250 | struct tb_drom_entry_header header; |
---|
210 | | - u8 data[0]; |
---|
| 251 | + u8 data[]; |
---|
211 | 252 | } __packed; |
---|
212 | 253 | |
---|
213 | 254 | struct tb_drom_entry_port { |
---|
.. | .. |
---|
238 | 279 | |
---|
239 | 280 | |
---|
240 | 281 | /** |
---|
241 | | - * tb_eeprom_get_drom_offset - get drom offset within eeprom |
---|
242 | | - */ |
---|
243 | | -static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) |
---|
244 | | -{ |
---|
245 | | - struct tb_cap_plug_events cap; |
---|
246 | | - int res; |
---|
247 | | - if (!sw->cap_plug_events) { |
---|
248 | | - tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); |
---|
249 | | - return -ENOSYS; |
---|
250 | | - } |
---|
251 | | - res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, |
---|
252 | | - sizeof(cap) / 4); |
---|
253 | | - if (res) |
---|
254 | | - return res; |
---|
255 | | - |
---|
256 | | - if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { |
---|
257 | | - tb_sw_warn(sw, "no NVM\n"); |
---|
258 | | - return -ENOSYS; |
---|
259 | | - } |
---|
260 | | - |
---|
261 | | - if (cap.drom_offset > 0xffff) { |
---|
262 | | - tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", |
---|
263 | | - cap.drom_offset); |
---|
264 | | - return -ENXIO; |
---|
265 | | - } |
---|
266 | | - *offset = cap.drom_offset; |
---|
267 | | - return 0; |
---|
268 | | -} |
---|
269 | | - |
---|
270 | | -/** |
---|
271 | 282 | * tb_drom_read_uid_only - read uid directly from drom |
---|
272 | 283 | * |
---|
273 | 284 | * Does not use the cached copy in sw->drom. Used during resume to check switch |
---|
.. | .. |
---|
276 | 287 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid) |
---|
277 | 288 | { |
---|
278 | 289 | u8 data[9]; |
---|
279 | | - u16 drom_offset; |
---|
280 | 290 | u8 crc; |
---|
281 | | - int res = tb_eeprom_get_drom_offset(sw, &drom_offset); |
---|
282 | | - if (res) |
---|
283 | | - return res; |
---|
284 | | - |
---|
285 | | - if (drom_offset == 0) |
---|
286 | | - return -ENODEV; |
---|
| 291 | + int res; |
---|
287 | 292 | |
---|
288 | 293 | /* read uid */ |
---|
289 | | - res = tb_eeprom_read_n(sw, drom_offset, data, 9); |
---|
| 294 | + res = tb_eeprom_read_n(sw, 0, data, 9); |
---|
290 | 295 | if (res) |
---|
291 | 296 | return res; |
---|
292 | 297 | |
---|
.. | .. |
---|
385 | 390 | struct tb_drom_entry_header *entry = (void *) (sw->drom + pos); |
---|
386 | 391 | if (pos + 1 == drom_size || pos + entry->len > drom_size |
---|
387 | 392 | || !entry->len) { |
---|
388 | | - tb_sw_warn(sw, "drom buffer overrun, aborting\n"); |
---|
389 | | - return -EIO; |
---|
| 393 | + tb_sw_warn(sw, "DROM buffer overrun\n"); |
---|
| 394 | + return -EILSEQ; |
---|
390 | 395 | } |
---|
391 | 396 | |
---|
392 | 397 | switch (entry->type) { |
---|
.. | .. |
---|
413 | 418 | struct device *dev = &sw->tb->nhi->pdev->dev; |
---|
414 | 419 | int len, res; |
---|
415 | 420 | |
---|
416 | | - len = device_property_read_u8_array(dev, "ThunderboltDROM", NULL, 0); |
---|
| 421 | + len = device_property_count_u8(dev, "ThunderboltDROM"); |
---|
417 | 422 | if (len < 0 || len < sizeof(struct tb_drom_header)) |
---|
418 | 423 | return -EINVAL; |
---|
419 | 424 | |
---|
.. | .. |
---|
483 | 488 | return ret; |
---|
484 | 489 | } |
---|
485 | 490 | |
---|
| 491 | +static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size) |
---|
| 492 | +{ |
---|
| 493 | + int ret; |
---|
| 494 | + |
---|
| 495 | + ret = usb4_switch_drom_read(sw, 14, size, sizeof(*size)); |
---|
| 496 | + if (ret) |
---|
| 497 | + return ret; |
---|
| 498 | + |
---|
| 499 | + /* Size includes CRC8 + UID + CRC32 */ |
---|
| 500 | + *size += 1 + 8 + 4; |
---|
| 501 | + sw->drom = kzalloc(*size, GFP_KERNEL); |
---|
| 502 | + if (!sw->drom) |
---|
| 503 | + return -ENOMEM; |
---|
| 504 | + |
---|
| 505 | + ret = usb4_switch_drom_read(sw, 0, sw->drom, *size); |
---|
| 506 | + if (ret) { |
---|
| 507 | + kfree(sw->drom); |
---|
| 508 | + sw->drom = NULL; |
---|
| 509 | + } |
---|
| 510 | + |
---|
| 511 | + return ret; |
---|
| 512 | +} |
---|
| 513 | + |
---|
| 514 | +static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, |
---|
| 515 | + size_t count) |
---|
| 516 | +{ |
---|
| 517 | + if (tb_switch_is_usb4(sw)) |
---|
| 518 | + return usb4_switch_drom_read(sw, offset, val, count); |
---|
| 519 | + return tb_eeprom_read_n(sw, offset, val, count); |
---|
| 520 | +} |
---|
| 521 | + |
---|
486 | 522 | /** |
---|
487 | 523 | * tb_drom_read - copy drom to sw->drom and parse it |
---|
488 | 524 | */ |
---|
489 | 525 | int tb_drom_read(struct tb_switch *sw) |
---|
490 | 526 | { |
---|
491 | | - u16 drom_offset; |
---|
492 | 527 | u16 size; |
---|
493 | 528 | u32 crc; |
---|
494 | 529 | struct tb_drom_header *header; |
---|
495 | | - int res; |
---|
| 530 | + int res, retries = 1; |
---|
| 531 | + |
---|
496 | 532 | if (sw->drom) |
---|
497 | 533 | return 0; |
---|
498 | 534 | |
---|
.. | .. |
---|
509 | 545 | goto parse; |
---|
510 | 546 | |
---|
511 | 547 | /* |
---|
512 | | - * The root switch contains only a dummy drom (header only, |
---|
513 | | - * no entries). Hardcode the configuration here. |
---|
| 548 | + * USB4 hosts may support reading DROM through router |
---|
| 549 | + * operations. |
---|
514 | 550 | */ |
---|
515 | | - tb_drom_read_uid_only(sw, &sw->uid); |
---|
516 | | - |
---|
517 | | - sw->ports[1].link_nr = 0; |
---|
518 | | - sw->ports[2].link_nr = 1; |
---|
519 | | - sw->ports[1].dual_link_port = &sw->ports[2]; |
---|
520 | | - sw->ports[2].dual_link_port = &sw->ports[1]; |
---|
521 | | - |
---|
522 | | - sw->ports[3].link_nr = 0; |
---|
523 | | - sw->ports[4].link_nr = 1; |
---|
524 | | - sw->ports[3].dual_link_port = &sw->ports[4]; |
---|
525 | | - sw->ports[4].dual_link_port = &sw->ports[3]; |
---|
526 | | - |
---|
527 | | - /* Port 5 is inaccessible on this gen 1 controller */ |
---|
528 | | - if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE) |
---|
529 | | - sw->ports[5].disabled = true; |
---|
| 551 | + if (tb_switch_is_usb4(sw)) { |
---|
| 552 | + usb4_switch_read_uid(sw, &sw->uid); |
---|
| 553 | + if (!usb4_copy_host_drom(sw, &size)) |
---|
| 554 | + goto parse; |
---|
| 555 | + } else { |
---|
| 556 | + /* |
---|
| 557 | + * The root switch contains only a dummy drom |
---|
| 558 | + * (header only, no entries). Hardcode the |
---|
| 559 | + * configuration here. |
---|
| 560 | + */ |
---|
| 561 | + tb_drom_read_uid_only(sw, &sw->uid); |
---|
| 562 | + } |
---|
530 | 563 | |
---|
531 | 564 | return 0; |
---|
532 | 565 | } |
---|
533 | 566 | |
---|
534 | | - res = tb_eeprom_get_drom_offset(sw, &drom_offset); |
---|
535 | | - if (res) |
---|
536 | | - return res; |
---|
537 | | - |
---|
538 | | - res = tb_eeprom_read_n(sw, drom_offset + 14, (u8 *) &size, 2); |
---|
| 567 | + res = tb_drom_read_n(sw, 14, (u8 *) &size, 2); |
---|
539 | 568 | if (res) |
---|
540 | 569 | return res; |
---|
541 | 570 | size &= 0x3ff; |
---|
542 | 571 | size += TB_DROM_DATA_START; |
---|
543 | | - tb_sw_info(sw, "reading drom (length: %#x)\n", size); |
---|
| 572 | + tb_sw_dbg(sw, "reading drom (length: %#x)\n", size); |
---|
544 | 573 | if (size < sizeof(*header)) { |
---|
545 | 574 | tb_sw_warn(sw, "drom too small, aborting\n"); |
---|
546 | 575 | return -EIO; |
---|
.. | .. |
---|
549 | 578 | sw->drom = kzalloc(size, GFP_KERNEL); |
---|
550 | 579 | if (!sw->drom) |
---|
551 | 580 | return -ENOMEM; |
---|
552 | | - res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size); |
---|
| 581 | + res = tb_drom_read_n(sw, 0, sw->drom, size); |
---|
553 | 582 | if (res) |
---|
554 | 583 | goto err; |
---|
555 | 584 | |
---|
.. | .. |
---|
572 | 601 | sw->uid = header->uid; |
---|
573 | 602 | sw->vendor = header->vendor_id; |
---|
574 | 603 | sw->device = header->model_id; |
---|
| 604 | + tb_check_quirks(sw); |
---|
575 | 605 | |
---|
576 | 606 | crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); |
---|
577 | 607 | if (crc != header->data_crc32) { |
---|
.. | .. |
---|
584 | 614 | tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", |
---|
585 | 615 | header->device_rom_revision); |
---|
586 | 616 | |
---|
587 | | - return tb_drom_parse_entries(sw); |
---|
| 617 | + res = tb_drom_parse_entries(sw); |
---|
| 618 | + /* If the DROM parsing fails, wait a moment and retry once */ |
---|
| 619 | + if (res == -EILSEQ && retries--) { |
---|
| 620 | + tb_sw_warn(sw, "parsing DROM failed, retrying\n"); |
---|
| 621 | + msleep(100); |
---|
| 622 | + res = tb_drom_read_n(sw, 0, sw->drom, size); |
---|
| 623 | + if (!res) |
---|
| 624 | + goto parse; |
---|
| 625 | + } |
---|
| 626 | + |
---|
| 627 | + return res; |
---|
588 | 628 | err: |
---|
589 | 629 | kfree(sw->drom); |
---|
590 | 630 | sw->drom = NULL; |
---|