Commit 661b1947 authored by Mika Westerberg's avatar Mika Westerberg

thunderbolt: Perform USB4 router NVM upgrade in two phases

The currect code expects that the router returns back the status of the
NVM authentication immediately. When tested against a real USB4 device
what happens is that the router is reset and only after that the result
is updated in the ROUTER_CS_26 register status field. This also seems to
align better what the spec suggests.

For this reason do the same what we already do with the Thunderbolt 3
devices and perform the NVM upgrade in two phases. First start the
NVM_AUTH router operation and once the router is added back after the
reset read the status in ROUTER_CS_26 and expose it to the userspace
accordingly.
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 463e48fa
......@@ -2160,6 +2160,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
fallthrough;
case 3:
case 4:
ret = tb_switch_set_uuid(sw);
if (ret)
return ret;
......@@ -2175,6 +2176,22 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
break;
}
if (sw->no_nvm_upgrade)
return 0;
if (tb_switch_is_usb4(sw)) {
ret = usb4_switch_nvm_authenticate_status(sw, &status);
if (ret)
return ret;
if (status) {
tb_sw_info(sw, "switch flash authentication failed\n");
nvm_set_auth_status(sw, status);
}
return 0;
}
/* Root switch DMA port requires running firmware */
if (!tb_route(sw) && !tb_switch_is_icm(sw))
return 0;
......@@ -2183,9 +2200,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
if (!sw->dma_port)
return 0;
if (sw->no_nvm_upgrade)
return 0;
/*
* If there is status already set then authentication failed
* when the dma_port_flash_update_auth() returned. Power cycling
......
......@@ -972,6 +972,7 @@ int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
const void *buf, size_t size);
int usb4_switch_nvm_authenticate(struct tb_switch *sw);
int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status);
bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
......
......@@ -211,6 +211,7 @@ struct tb_regs_switch_header {
#define ROUTER_CS_9 0x09
#define ROUTER_CS_25 0x19
#define ROUTER_CS_26 0x1a
#define ROUTER_CS_26_OPCODE_MASK GENMASK(15, 0)
#define ROUTER_CS_26_STATUS_MASK GENMASK(29, 24)
#define ROUTER_CS_26_STATUS_SHIFT 24
#define ROUTER_CS_26_ONS BIT(30)
......
......@@ -192,7 +192,9 @@ static int usb4_switch_op(struct tb_switch *sw, u16 opcode, u8 *status)
if (val & ROUTER_CS_26_ONS)
return -EOPNOTSUPP;
*status = (val & ROUTER_CS_26_STATUS_MASK) >> ROUTER_CS_26_STATUS_SHIFT;
if (status)
*status = (val & ROUTER_CS_26_STATUS_MASK) >>
ROUTER_CS_26_STATUS_SHIFT;
return 0;
}
......@@ -634,32 +636,71 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
* @sw: USB4 router
*
* After the new NVM has been written via usb4_switch_nvm_write(), this
* function triggers NVM authentication process. If the authentication
* is successful the router is power cycled and the new NVM starts
* function triggers NVM authentication process. The router gets power
* cycled and if the authentication is successful the new NVM starts
* running. In case of failure returns negative errno.
*
* The caller should call usb4_switch_nvm_authenticate_status() to read
* the status of the authentication after power cycle. It should be the
* first router operation to avoid the status being lost.
*/
int usb4_switch_nvm_authenticate(struct tb_switch *sw)
{
u8 status = 0;
int ret;
ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, &status);
ret = usb4_switch_op(sw, USB4_SWITCH_OP_NVM_AUTH, NULL);
switch (ret) {
/*
* The router is power cycled once NVM_AUTH is started so it is
* expected to get any of the following errors back.
*/
case -EACCES:
case -ENOTCONN:
case -ETIMEDOUT:
return 0;
default:
return ret;
}
}
/**
* usb4_switch_nvm_authenticate_status() - Read status of last NVM authenticate
* @sw: USB4 router
* @status: Status code of the operation
*
* The function checks if there is status available from the last NVM
* authenticate router operation. If there is status then %0 is returned
* and the status code is placed in @status. Returns negative errno in case
* of failure.
*
* Must be called before any other router operation.
*/
int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status)
{
u16 opcode;
u32 val;
int ret;
ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1);
if (ret)
return ret;
switch (status) {
case 0x0:
tb_sw_dbg(sw, "NVM authentication successful\n");
return 0;
case 0x1:
return -EINVAL;
case 0x2:
return -EAGAIN;
case 0x3:
/* Check that the opcode is correct */
opcode = val & ROUTER_CS_26_OPCODE_MASK;
if (opcode == USB4_SWITCH_OP_NVM_AUTH) {
if (val & ROUTER_CS_26_OV)
return -EBUSY;
if (val & ROUTER_CS_26_ONS)
return -EOPNOTSUPP;
default:
return -EIO;
*status = (val & ROUTER_CS_26_STATUS_MASK) >>
ROUTER_CS_26_STATUS_SHIFT;
} else {
*status = 0;
}
return 0;
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment