diff options
-rw-r--r-- | drivers/thunderbolt/tb.h | 13 | ||||
-rw-r--r-- | drivers/thunderbolt/usb4.c | 50 |
2 files changed, 57 insertions, 6 deletions
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 3885f2515aae..d19dbc8e9457 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -367,6 +367,14 @@ struct tb_path { * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update * @approve_xdomain_paths: Approve (establish) XDomain DMA paths * @disconnect_xdomain_paths: Disconnect XDomain DMA paths + * @usb4_switch_op: Optional proxy for USB4 router operations. If set + * this will be called whenever USB4 router operation is + * performed. If this returns %-EOPNOTSUPP then the + * native USB4 router operation is called. + * @usb4_switch_nvm_authenticate_status: Optional callback that the CM + * implementation can be used to + * return status of USB4 NVM_AUTH + * router operation. */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -393,6 +401,11 @@ struct tb_cm_ops { int (*disconnect_pcie_paths)(struct tb *tb); int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); + int (*usb4_switch_op)(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_data_len, + void *rx_data, size_t rx_data_len); + int (*usb4_switch_nvm_authenticate_status)(struct tb_switch *sw, + u32 *status); }; static inline void *tb_priv(struct tb *tb) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index c1bb5ec6e1db..cbf1c0536360 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -143,16 +143,14 @@ static int usb4_do_write_data(unsigned int address, const void *buf, size_t size return 0; } -static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, - u8 *status, const void *tx_data, size_t tx_dwords, - void *rx_data, size_t rx_dwords) +static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode, + u32 *metadata, u8 *status, + const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) { u32 val; int ret; - if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS) - return -EINVAL; - if (metadata) { ret = tb_sw_write(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1); if (ret) @@ -200,6 +198,39 @@ static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, return 0; } +static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, + u8 *status, const void *tx_data, size_t tx_dwords, + void *rx_data, size_t rx_dwords) +{ + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + + if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS) + return -EINVAL; + + /* + * If the connection manager implementation provides USB4 router + * operation proxy callback, call it here instead of running the + * operation natively. + */ + if (cm_ops->usb4_switch_op) { + int ret; + + ret = cm_ops->usb4_switch_op(sw, opcode, metadata, status, + tx_data, tx_dwords, rx_data, + rx_dwords); + if (ret != -EOPNOTSUPP) + return ret; + + /* + * If the proxy was not supported then run the native + * router operation instead. + */ + } + + return usb4_native_switch_op(sw, opcode, metadata, status, tx_data, + tx_dwords, rx_data, rx_dwords); +} + static inline int usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, u8 *status) { @@ -674,10 +705,17 @@ int usb4_switch_nvm_authenticate(struct tb_switch *sw) */ int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status) { + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; u16 opcode; u32 val; int ret; + if (cm_ops->usb4_switch_nvm_authenticate_status) { + ret = cm_ops->usb4_switch_nvm_authenticate_status(sw, status); + if (ret != -EOPNOTSUPP) + return ret; + } + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1); if (ret) return ret; |