diff options
| author | Ingo Molnar <[email protected]> | 2020-02-24 11:36:09 +0100 |
|---|---|---|
| committer | Ingo Molnar <[email protected]> | 2020-02-24 11:36:09 +0100 |
| commit | 546121b65f47384e11ec1fa2e55449fc9f4846b2 (patch) | |
| tree | 8f18470ec7c0c77b0f48eb1b2338e591b0b0aaff /drivers/thunderbolt/eeprom.c | |
| parent | 000619680c3714020ce9db17eef6a4a7ce2dc28b (diff) | |
| parent | f8788d86ab28f61f7b46eb6be375f8a726783636 (diff) | |
Merge tag 'v5.6-rc3' into sched/core, to pick up fixes and dependent patches
Signed-off-by: Ingo Molnar <[email protected]>
Diffstat (limited to 'drivers/thunderbolt/eeprom.c')
| -rw-r--r-- | drivers/thunderbolt/eeprom.c | 137 |
1 files changed, 89 insertions, 48 deletions
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 8dd7de0cc826..921d164b3f35 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -131,12 +131,51 @@ static int tb_eeprom_in(struct tb_switch *sw, u8 *val) } /** + * tb_eeprom_get_drom_offset - get drom offset within eeprom + */ +static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) +{ + struct tb_cap_plug_events cap; + int res; + + if (!sw->cap_plug_events) { + tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); + return -ENODEV; + } + res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, + sizeof(cap) / 4); + if (res) + return res; + + if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { + tb_sw_warn(sw, "no NVM\n"); + return -ENODEV; + } + + if (cap.drom_offset > 0xffff) { + tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", + cap.drom_offset); + return -ENXIO; + } + *offset = cap.drom_offset; + return 0; +} + +/** * tb_eeprom_read_n - read count bytes from offset into val */ static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val, size_t count) { + u16 drom_offset; int i, res; + + res = tb_eeprom_get_drom_offset(sw, &drom_offset); + if (res) + return res; + + offset += drom_offset; + res = tb_eeprom_active(sw, true); if (res) return res; @@ -239,36 +278,6 @@ struct tb_drom_entry_port { /** - * tb_eeprom_get_drom_offset - get drom offset within eeprom - */ -static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) -{ - struct tb_cap_plug_events cap; - int res; - if (!sw->cap_plug_events) { - tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); - return -ENOSYS; - } - res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, - sizeof(cap) / 4); - if (res) - return res; - - if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { - tb_sw_warn(sw, "no NVM\n"); - return -ENOSYS; - } - - if (cap.drom_offset > 0xffff) { - tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", - cap.drom_offset); - return -ENXIO; - } - *offset = cap.drom_offset; - return 0; -} - -/** * tb_drom_read_uid_only - read uid directly from drom * * Does not use the cached copy in sw->drom. Used during resume to check switch @@ -277,17 +286,11 @@ static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid) { u8 data[9]; - u16 drom_offset; u8 crc; - int res = tb_eeprom_get_drom_offset(sw, &drom_offset); - if (res) - return res; - - if (drom_offset == 0) - return -ENODEV; + int res; /* read uid */ - res = tb_eeprom_read_n(sw, drom_offset, data, 9); + res = tb_eeprom_read_n(sw, 0, data, 9); if (res) return res; @@ -484,12 +487,42 @@ err_free: return ret; } +static int usb4_copy_host_drom(struct tb_switch *sw, u16 *size) +{ + int ret; + + ret = usb4_switch_drom_read(sw, 14, size, sizeof(*size)); + if (ret) + return ret; + + /* Size includes CRC8 + UID + CRC32 */ + *size += 1 + 8 + 4; + sw->drom = kzalloc(*size, GFP_KERNEL); + if (!sw->drom) + return -ENOMEM; + + ret = usb4_switch_drom_read(sw, 0, sw->drom, *size); + if (ret) { + kfree(sw->drom); + sw->drom = NULL; + } + + return ret; +} + +static int tb_drom_read_n(struct tb_switch *sw, u16 offset, u8 *val, + size_t count) +{ + if (tb_switch_is_usb4(sw)) + return usb4_switch_drom_read(sw, offset, val, count); + return tb_eeprom_read_n(sw, offset, val, count); +} + /** * tb_drom_read - copy drom to sw->drom and parse it */ int tb_drom_read(struct tb_switch *sw) { - u16 drom_offset; u16 size; u32 crc; struct tb_drom_header *header; @@ -510,18 +543,26 @@ int tb_drom_read(struct tb_switch *sw) goto parse; /* - * The root switch contains only a dummy drom (header only, - * no entries). Hardcode the configuration here. + * USB4 hosts may support reading DROM through router + * operations. */ - tb_drom_read_uid_only(sw, &sw->uid); + 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; } - res = tb_eeprom_get_drom_offset(sw, &drom_offset); - if (res) - return res; - - res = tb_eeprom_read_n(sw, drom_offset + 14, (u8 *) &size, 2); + res = tb_drom_read_n(sw, 14, (u8 *) &size, 2); if (res) return res; size &= 0x3ff; @@ -535,7 +576,7 @@ int tb_drom_read(struct tb_switch *sw) sw->drom = kzalloc(size, GFP_KERNEL); if (!sw->drom) return -ENOMEM; - res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size); + res = tb_drom_read_n(sw, 0, sw->drom, size); if (res) goto err; |