From: Mika Westerberg mika.westerberg@linux.intel.com
commit f022ff7bf377ca94367be05de61277934d42ea74 upstream.
Kai-Heng reported that sometimes DROM parsing of ASUS PA27AC Thunderbolt 3 monitor fails. This makes the driver to fail to add the device so only DisplayPort tunneling is functional.
It is not clear what exactly happens but waiting for 100 ms and retrying the read seems to work this around so we do that here.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=206493 Reported-by: Kai-Heng Feng kai.heng.feng@canonical.com Tested-by: Kai-Heng Feng kai.heng.feng@canonical.com Cc: stable@vger.kernel.org Signed-off-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
--- drivers/thunderbolt/eeprom.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
--- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -7,6 +7,7 @@ */
#include <linux/crc32.h> +#include <linux/delay.h> #include <linux/property.h> #include <linux/slab.h> #include "tb.h" @@ -389,8 +390,8 @@ static int tb_drom_parse_entries(struct struct tb_drom_entry_header *entry = (void *) (sw->drom + pos); if (pos + 1 == drom_size || pos + entry->len > drom_size || !entry->len) { - tb_sw_warn(sw, "drom buffer overrun, aborting\n"); - return -EIO; + tb_sw_warn(sw, "DROM buffer overrun\n"); + return -EILSEQ; }
switch (entry->type) { @@ -526,7 +527,8 @@ int tb_drom_read(struct tb_switch *sw) u16 size; u32 crc; struct tb_drom_header *header; - int res; + int res, retries = 1; + if (sw->drom) return 0;
@@ -611,7 +613,17 @@ parse: tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", header->device_rom_revision);
- return tb_drom_parse_entries(sw); + res = tb_drom_parse_entries(sw); + /* If the DROM parsing fails, wait a moment and retry once */ + if (res == -EILSEQ && retries--) { + tb_sw_warn(sw, "parsing DROM failed, retrying\n"); + msleep(100); + res = tb_drom_read_n(sw, 0, sw->drom, size); + if (!res) + goto parse; + } + + return res; err: kfree(sw->drom); sw->drom = NULL;