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: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
parent
463e48fa54
commit
661b19473b
@ -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:
|
||||
return -EOPNOTSUPP;
|
||||
default:
|
||||
return -EIO;
|
||||
/* 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;
|
||||
|
||||
*status = (val & ROUTER_CS_26_STATUS_MASK) >>
|
||||
ROUTER_CS_26_STATUS_SHIFT;
|
||||
} else {
|
||||
*status = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user