From: Mario Limonciello <mario.limonciello@amd.com>
To: <mika.westerberg@linux.intel.com>,
Andreas Noever <andreas.noever@gmail.com>,
Michael Jamet <michael.jamet@intel.com>,
"Yehezkel Bernat" <YehezkelShB@gmail.com>
Cc: <Sanju.Mehta@amd.com>,
Mario Limonciello <mario.limonciello@amd.com>,
<linux-usb@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: [PATCH v4 3/3] thunderbolt: Refactor DROM reading
Date: Thu, 23 Feb 2023 15:07:43 -0600 [thread overview]
Message-ID: <20230223210743.9819-4-mario.limonciello@amd.com> (raw)
In-Reply-To: <20230223210743.9819-1-mario.limonciello@amd.com>
The NVM reading code has a series of gotos that potentially introduce
unexpected behaviors with retries if something unexpected has failed
to parse.
Additionally the retry logic introduced in commit f022ff7bf377
("thunderbolt: Retry DROM read once if parsing fails") was added from
failures in bit banging, which aren't expected to be present when the
DROM is fetched directly from the NVM.
Refactor the code to remove the gotos and drop the retry logic.
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
---
v3->v4:
* style fixups
* rebase on earlier patch
* split out root switch case
---
drivers/thunderbolt/eeprom.c | 219 +++++++++++++++++++----------------
1 file changed, 120 insertions(+), 99 deletions(-)
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 873fa4300507..1f7f2d8c453d 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -416,7 +416,7 @@ static int tb_drom_parse_entries(struct tb_switch *sw, size_t header_size)
if (pos + 1 == drom_size || pos + entry->len > drom_size
|| !entry->len) {
tb_sw_warn(sw, "DROM buffer overrun\n");
- return -EILSEQ;
+ return -EIO;
}
switch (entry->type) {
@@ -512,7 +512,7 @@ static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
return ret;
}
-static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size)
+static int usb4_copy_drom(struct tb_switch *sw, u16 *size)
{
int ret;
@@ -535,15 +535,40 @@ static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size)
return ret;
}
-static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
- size_t count)
+static int tb_drom_bit_bang(struct tb_switch *sw, u16 *size)
{
- if (tb_switch_is_usb4(sw))
- return usb4_switch_drom_read(sw, offset, val, count);
- return tb_eeprom_read_n(sw, offset, val, count);
+ int ret;
+
+ ret = tb_eeprom_read_n(sw, 14, (u8 *) size, 2);
+ if (ret)
+ return ret;
+
+ *size &= 0x3ff;
+ *size += TB_DROM_DATA_START;
+
+ tb_sw_dbg(sw, "reading DROM (length: %#x)\n", *size);
+ if (*size < sizeof(struct tb_drom_header)) {
+ tb_sw_warn(sw, "DROM too small, aborting\n");
+ return -EIO;
+ }
+
+ sw->drom = kzalloc(*size, GFP_KERNEL);
+ if (!sw->drom)
+ return -ENOMEM;
+
+ ret = tb_eeprom_read_n(sw, 0, sw->drom, *size);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ kfree(sw->drom);
+ sw->drom = NULL;
+ return ret;
}
-static int tb_drom_parse(struct tb_switch *sw)
+static int tb_drom_parse_v1(struct tb_switch *sw)
{
const struct tb_drom_header *header =
(const struct tb_drom_header *)sw->drom;
@@ -554,7 +579,7 @@ static int tb_drom_parse(struct tb_switch *sw)
tb_sw_warn(sw,
"DROM UID CRC8 mismatch (expected: %#x, got: %#x)\n",
header->uid_crc8, crc);
- return -EILSEQ;
+ return -EIO;
}
if (!sw->uid)
sw->uid = header->uid;
@@ -588,90 +613,14 @@ static int usb4_drom_parse(struct tb_switch *sw)
return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE);
}
-/**
- * tb_drom_read() - Copy DROM to sw->drom and parse it
- * @sw: Router whose DROM to read and parse
- *
- * This function reads router DROM and if successful parses the entries and
- * populates the fields in @sw accordingly. Can be called for any router
- * generation.
- *
- * Returns %0 in case of success and negative errno otherwise.
- */
-int tb_drom_read(struct tb_switch *sw)
+static int tb_drom_parse(struct tb_switch *sw, u16 *size)
{
- u16 size;
- struct tb_drom_header *header;
- int res, retries = 1;
-
- if (sw->drom)
- return 0;
-
- if (tb_route(sw) == 0) {
- /*
- * Apple's NHI EFI driver supplies a DROM for the root switch
- * in a device property. Use it if available.
- */
- if (tb_drom_copy_efi(sw, &size) == 0)
- goto parse;
-
- /* Non-Apple hardware has the DROM as part of NVM */
- if (tb_drom_copy_nvm(sw, &size) == 0)
- goto parse;
-
- /*
- * USB4 hosts may support reading DROM through router
- * operations.
- */
- if (tb_switch_is_usb4(sw)) {
- usb4_switch_read_uid(sw, &sw->uid);
- if (!usb4_copy_host_drom(sw, &size))
- goto parse;
- } else {
- /*
- * The root switch contains only a dummy drom
- * (header only, no entries). Hardcode the
- * configuration here.
- */
- tb_drom_read_uid_only(sw, &sw->uid);
- }
-
- return 0;
- }
-
- /* We can use LC to get UUID later */
- if (sw->cap_lc && !tb_switch_is_usb4(sw) &&
- tb_drom_copy_nvm(sw, &size) == 0)
- goto parse;
-
- res = tb_drom_read_n(sw, 14, (u8 *) &size, 2);
- if (res)
- return res;
- size &= 0x3ff;
- size += TB_DROM_DATA_START;
- tb_sw_dbg(sw, "reading drom (length: %#x)\n", size);
- if (size < sizeof(*header)) {
- tb_sw_warn(sw, "drom too small, aborting\n");
- return -EIO;
- }
-
- sw->drom = kzalloc(size, GFP_KERNEL);
- if (!sw->drom)
- return -ENOMEM;
-read:
- res = tb_drom_read_n(sw, 0, sw->drom, size);
- if (res)
- goto err;
-
-parse:
- header = (void *) sw->drom;
+ struct tb_drom_header *header = (void *) sw->drom;
+ int ret;
- if (header->data_len + TB_DROM_DATA_START != size) {
+ if (header->data_len + TB_DROM_DATA_START != *size) {
tb_sw_warn(sw, "drom size mismatch\n");
- if (retries--) {
- msleep(100);
- goto read;
- }
+ ret = -EIO;
goto err;
}
@@ -679,29 +628,101 @@ int tb_drom_read(struct tb_switch *sw)
switch (header->device_rom_revision) {
case 3:
- res = usb4_drom_parse(sw);
+ ret = usb4_drom_parse(sw);
break;
default:
tb_sw_warn(sw, "DROM device_rom_revision %#x unknown\n",
header->device_rom_revision);
fallthrough;
case 1:
- res = tb_drom_parse(sw);
+ ret = tb_drom_parse_v1(sw);
break;
}
- /* If the DROM parsing fails, wait a moment and retry once */
- if (res == -EILSEQ && retries--) {
+ if (ret) {
tb_sw_warn(sw, "parsing DROM failed\n");
- msleep(100);
- goto read;
+ goto err;
}
- if (!res)
- return 0;
+ return 0;
err:
kfree(sw->drom);
sw->drom = NULL;
- return -EIO;
+
+ return ret;
+}
+
+/**
+ * tb_drom_handle_root() - Handle reading DROM from a root switch
+ * @sw: Router whose DROM to read and parse
+ *
+ * First determine if the switch is USB4. If so, then use USB4
+ * router operations.
+ *
+ * If the switch is not USB4, first try to read from EFI, as Apple's
+ * NHI supplies a DROM for the root switch in a device property.
+ *
+ * If this doesn't work, then try to get it from the NVM directly
+ * without using any bit banging operations.
+ *
+ * If none of those pass, then read just the UID as the root
+ * switch contains a dummy DROM with a header but no entries.
+ *
+ * Returns %0 in case of success and negative errno otherwise.
+ */
+static int tb_drom_handle_root(struct tb_switch *sw)
+{
+ u16 size;
+
+ if (tb_switch_is_usb4(sw)) {
+ usb4_switch_read_uid(sw, &sw->uid);
+ if (usb4_copy_drom(sw, &size) == 0)
+ return tb_drom_parse(sw, &size);
+ } else {
+ if (tb_drom_copy_efi(sw, &size) == 0)
+ return tb_drom_parse(sw, &size);
+
+ if (tb_drom_copy_nvm(sw, &size) == 0)
+ return tb_drom_parse(sw, &size);
+
+ tb_drom_read_uid_only(sw, &sw->uid);
+ }
+
+ return 0;
+}
+
+/**
+ * tb_drom_read() - Copy DROM to sw->drom and parse it
+ * @sw: Router whose DROM to read and parse
+ *
+ * This function reads router DROM and if successful parses the entries and
+ * populates the fields in @sw accordingly. Can be called for any router
+ * generation.
+ *
+ * Returns %0 in case of success and negative errno otherwise.
+ */
+int tb_drom_read(struct tb_switch *sw)
+{
+ u16 size;
+ int ret;
+
+ if (sw->drom)
+ return 0;
+
+ if (tb_route(sw) == 0)
+ return tb_drom_handle_root(sw);
+
+ if (tb_switch_is_usb4(sw)) {
+ usb4_switch_read_uid(sw, &sw->uid);
+ ret = usb4_copy_drom(sw, &size);
+ } else if (sw->cap_lc) {
+ /* We can use LC to get UUID later */
+ ret = tb_drom_copy_nvm(sw, &size);
+ } else
+ ret = tb_drom_bit_bang(sw, &size);
+ if (ret)
+ return ret;
+
+ return tb_drom_parse(sw, &size);
}
--
2.34.1
next prev parent reply other threads:[~2023-02-23 21:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-23 21:07 [PATCH v4 0/3] Fix problems fetching TBT3 DROM from AMD USB4 routers Mario Limonciello
2023-02-23 21:07 ` [PATCH v4 1/3] thunderbolt: Adjust how NVM reading works Mario Limonciello
2023-02-23 21:07 ` [PATCH v4 2/3] thunderbolt: use `tb_eeprom_get_drom_offset` to discover DROM offset Mario Limonciello
2023-02-23 21:07 ` Mario Limonciello [this message]
2023-03-06 9:57 ` [PATCH v4 0/3] Fix problems fetching TBT3 DROM from AMD USB4 routers Mika Westerberg
2023-03-06 15:14 ` Limonciello, Mario
2023-03-07 8:18 ` Mika Westerberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230223210743.9819-4-mario.limonciello@amd.com \
--to=mario.limonciello@amd.com \
--cc=Sanju.Mehta@amd.com \
--cc=YehezkelShB@gmail.com \
--cc=andreas.noever@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=michael.jamet@intel.com \
--cc=mika.westerberg@linux.intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®