3176 lines · 76.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * USB4 specific functionality * * Copyright (C) 2019, Intel Corporation * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> *	    Rajmohan Mani <rajmohan.mani@intel.com> */ #include <linux/delay.h>#include <linux/ktime.h>#include <linux/string_choices.h>#include <linux/units.h> #include "sb_regs.h"#include "tb.h" #define USB4_DATA_RETRIES		3#define USB4_DATA_DWORDS		16 #define USB4_NVM_READ_OFFSET_MASK	GENMASK(23, 2)#define USB4_NVM_READ_OFFSET_SHIFT	2#define USB4_NVM_READ_LENGTH_MASK	GENMASK(27, 24)#define USB4_NVM_READ_LENGTH_SHIFT	24 #define USB4_NVM_SET_OFFSET_MASK	USB4_NVM_READ_OFFSET_MASK#define USB4_NVM_SET_OFFSET_SHIFT	USB4_NVM_READ_OFFSET_SHIFT #define USB4_DROM_ADDRESS_MASK		GENMASK(14, 2)#define USB4_DROM_ADDRESS_SHIFT		2#define USB4_DROM_SIZE_MASK		GENMASK(19, 15)#define USB4_DROM_SIZE_SHIFT		15 #define USB4_NVM_SECTOR_SIZE_MASK	GENMASK(23, 0) #define USB4_BA_LENGTH_MASK		GENMASK(7, 0)#define USB4_BA_INDEX_MASK		GENMASK(15, 0) enum usb4_ba_index {	USB4_BA_MAX_USB3 = 0x1,	USB4_BA_MIN_DP_AUX = 0x2,	USB4_BA_MIN_DP_MAIN = 0x3,	USB4_BA_MAX_PCIE = 0x4,	USB4_BA_MAX_HI = 0x5,}; #define USB4_BA_VALUE_MASK		GENMASK(31, 16)#define USB4_BA_VALUE_SHIFT		16 /* Delays in us used with usb4_port_wait_for_bit() */#define USB4_PORT_DELAY			50#define USB4_PORT_SB_DELAY		1000 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 (metadata) {		ret = tb_sw_write(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1);		if (ret)			return ret;	}	if (tx_dwords) {		ret = tb_sw_write(sw, tx_data, TB_CFG_SWITCH, ROUTER_CS_9,				  tx_dwords);		if (ret)			return ret;	} 	val = opcode | ROUTER_CS_26_OV;	ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1);	if (ret)		return ret; 	ret = tb_switch_wait_for_bit(sw, ROUTER_CS_26, ROUTER_CS_26_OV, 0, 500);	if (ret)		return ret; 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_26, 1);	if (ret)		return ret; 	if (val & ROUTER_CS_26_ONS)		return -EOPNOTSUPP; 	if (status)		*status = (val & ROUTER_CS_26_STATUS_MASK) >>			ROUTER_CS_26_STATUS_SHIFT; 	if (metadata) {		ret = tb_sw_read(sw, metadata, TB_CFG_SWITCH, ROUTER_CS_25, 1);		if (ret)			return ret;	}	if (rx_dwords) {		ret = tb_sw_read(sw, rx_data, TB_CFG_SWITCH, ROUTER_CS_9,				 rx_dwords);		if (ret)			return ret;	} 	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){	return __usb4_switch_op(sw, opcode, metadata, status, NULL, 0, NULL, 0);} static inline int usb4_switch_op_data(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){	return __usb4_switch_op(sw, opcode, metadata, status, tx_data,				tx_dwords, rx_data, rx_dwords);} /** * usb4_switch_check_wakes() - Check for wakes and notify PM core about them * @sw: Router whose wakes to check * * Checks wakes occurred during suspend and notify the PM core about them. */void usb4_switch_check_wakes(struct tb_switch *sw){	bool wakeup_usb4 = false;	struct usb4_port *usb4;	struct tb_port *port;	bool wakeup = false;	u32 val; 	if (tb_route(sw)) {		if (tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1))			return; 		tb_sw_dbg(sw, "PCIe wake: %s, USB3 wake: %s\n",			  str_yes_no(val & ROUTER_CS_6_WOPS),			  str_yes_no(val & ROUTER_CS_6_WOUS)); 		wakeup = val & (ROUTER_CS_6_WOPS | ROUTER_CS_6_WOUS);	} 	/*	 * Check for any downstream ports for USB4 wake,	 * connection wake and disconnection wake.	 */	tb_switch_for_each_port(sw, port) {		if (!port->cap_usb4)			continue; 		if (tb_port_read(port, &val, TB_CFG_PORT,				 port->cap_usb4 + PORT_CS_18, 1))			break; 		tb_port_dbg(port, "USB4 wake: %s, connection wake: %s, disconnection wake: %s\n",			    str_yes_no(val & PORT_CS_18_WOU4S),			    str_yes_no(val & PORT_CS_18_WOCS),			    str_yes_no(val & PORT_CS_18_WODS)); 		wakeup_usb4 = val & (PORT_CS_18_WOU4S | PORT_CS_18_WOCS |				     PORT_CS_18_WODS); 		usb4 = port->usb4;		if (device_may_wakeup(&usb4->dev) && wakeup_usb4)			pm_wakeup_event(&usb4->dev, 0); 		wakeup |= wakeup_usb4;	} 	if (wakeup)		pm_wakeup_event(&sw->dev, 0);} static bool link_is_usb4(struct tb_port *port){	u32 val; 	if (!port->cap_usb4)		return false; 	if (tb_port_read(port, &val, TB_CFG_PORT,			 port->cap_usb4 + PORT_CS_18, 1))		return false; 	return !(val & PORT_CS_18_TCM);} /** * usb4_switch_setup() - Additional setup for USB4 device * @sw: USB4 router to setup * * USB4 routers need additional settings in order to enable all the * tunneling. This function enables USB and PCIe tunneling if it can be * enabled (e.g the parent switch also supports them). If USB tunneling * is not available for some reason (like that there is Thunderbolt 3 * switch upstream) then the internal xHCI controller is enabled * instead. * * This does not set the configuration valid bit of the router. To do * that call usb4_switch_configuration_valid(). * * Return: %0 on success, negative errno otherwise. */int usb4_switch_setup(struct tb_switch *sw){	struct tb_switch *parent = tb_switch_parent(sw);	struct tb_port *down;	bool tbt3, xhci;	u32 val = 0;	int ret; 	if (!tb_route(sw))		return 0; 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1);	if (ret)		return ret; 	down = tb_switch_downstream_port(sw);	sw->link_usb4 = link_is_usb4(down);	tb_sw_dbg(sw, "link: %s\n", sw->link_usb4 ? "USB4" : "TBT"); 	xhci = val & ROUTER_CS_6_HCI;	tbt3 = !(val & ROUTER_CS_6_TNS); 	tb_sw_dbg(sw, "TBT3 support: %s, xHCI: %s\n",		  str_yes_no(tbt3), str_yes_no(xhci)); 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);	if (ret)		return ret; 	if (tb_acpi_may_tunnel_usb3() && sw->link_usb4 &&	    tb_switch_find_port(parent, TB_TYPE_USB3_DOWN)) {		val |= ROUTER_CS_5_UTO;		xhci = false;	} 	/*	 * Only enable PCIe tunneling if the parent router supports it	 * and it is not disabled.	 */	if (tb_acpi_may_tunnel_pcie() &&	    tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) {		val |= ROUTER_CS_5_PTO;		/*		 * xHCI can be enabled if PCIe tunneling is supported		 * and the parent does not have any USB3 downstream		 * adapters (so we cannot do USB 3.x tunneling).		 */		if (xhci)			val |= ROUTER_CS_5_HCO;	} 	/* TBT3 supported by the CM */	val &= ~ROUTER_CS_5_CNS; 	ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);	if (ret)		return ret; 	return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_RR,				      ROUTER_CS_6_RR, 500);} /** * usb4_switch_configuration_valid() - Set tunneling configuration to be valid * @sw: USB4 router * * Sets configuration valid bit for the router. Must be called before * any tunnels can be set through the router and after * usb4_switch_setup() has been called. Can be called to host and device * routers (does nothing for the former). * * Return: %0 on success, negative errno otherwise. */int usb4_switch_configuration_valid(struct tb_switch *sw){	u32 val;	int ret; 	if (!tb_route(sw))		return 0; 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);	if (ret)		return ret; 	val |= ROUTER_CS_5_CV; 	ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);	if (ret)		return ret; 	return tb_switch_wait_for_bit(sw, ROUTER_CS_6, ROUTER_CS_6_CR,				      ROUTER_CS_6_CR, 500);} /** * usb4_switch_read_uid() - Read UID from USB4 router * @sw: USB4 router * @uid: UID is stored here * * Reads 64-bit UID from USB4 router config space. * * Return: %0 on success, negative errno otherwise. */int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid){	return tb_sw_read(sw, uid, TB_CFG_SWITCH, ROUTER_CS_7, 2);} static int usb4_switch_drom_read_block(void *data,				       unsigned int dwaddress, void *buf,				       size_t dwords){	struct tb_switch *sw = data;	u8 status = 0;	u32 metadata;	int ret; 	metadata = (dwords << USB4_DROM_SIZE_SHIFT) & USB4_DROM_SIZE_MASK;	metadata |= (dwaddress << USB4_DROM_ADDRESS_SHIFT) &		USB4_DROM_ADDRESS_MASK; 	ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_DROM_READ, &metadata,				  &status, NULL, 0, buf, dwords);	if (ret)		return ret; 	return status ? -EIO : 0;} /** * usb4_switch_drom_read() - Read arbitrary bytes from USB4 router DROM * @sw: USB4 router * @address: Byte address inside DROM to start reading * @buf: Buffer where the DROM content is stored * @size: Number of bytes to read from DROM * * Uses USB4 router operations to read router DROM. For devices this * should always work but for hosts it may return %-EOPNOTSUPP in which * case the host router does not have DROM. * * Return: %0 on success, negative errno otherwise. */int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,			  size_t size){	return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,				usb4_switch_drom_read_block, sw);} /** * usb4_switch_lane_bonding_possible() - Are conditions met for lane bonding * @sw: USB4 router * * Checks whether conditions are met so that lane bonding can be * established with the upstream router. Call only for device routers. * * Return: %true if lane bonding is possible, %false otherwise. */bool usb4_switch_lane_bonding_possible(struct tb_switch *sw){	struct tb_port *up;	int ret;	u32 val; 	up = tb_upstream_port(sw);	ret = tb_port_read(up, &val, TB_CFG_PORT, up->cap_usb4 + PORT_CS_18, 1);	if (ret)		return false; 	return !!(val & PORT_CS_18_BE);} /** * usb4_switch_set_wake() - Enabled/disable wake * @sw: USB4 router * @flags: Wakeup flags (%0 to disable) * @runtime: Wake is being programmed during system runtime * * Enables/disables router to wake up from sleep. * * Return: %0 on success, negative errno otherwise. */int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags, bool runtime){	struct tb_port *port;	u64 route = tb_route(sw);	u32 val;	int ret; 	/*	 * Enable wakes coming from all USB4 downstream ports (from	 * child routers). For device routers do this also for the	 * upstream USB4 port.	 */	tb_switch_for_each_port(sw, port) {		if (!tb_port_is_null(port))			continue;		if (!route && tb_is_upstream_port(port))			continue;		if (!port->cap_usb4)			continue; 		ret = tb_port_read(port, &val, TB_CFG_PORT,				   port->cap_usb4 + PORT_CS_19, 1);		if (ret)			return ret; 		val &= ~(PORT_CS_19_WOC | PORT_CS_19_WOD | PORT_CS_19_WOU4); 		if (tb_is_upstream_port(port)) {			val |= PORT_CS_19_WOU4;		} else {			bool configured = val & PORT_CS_19_PC;			bool wakeup = runtime || device_may_wakeup(&port->usb4->dev); 			if ((flags & TB_WAKE_ON_CONNECT) && wakeup && !configured)				val |= PORT_CS_19_WOC;			if ((flags & TB_WAKE_ON_DISCONNECT) && wakeup && configured)				val |= PORT_CS_19_WOD;			if ((flags & TB_WAKE_ON_USB4) && configured)				val |= PORT_CS_19_WOU4;		} 		ret = tb_port_write(port, &val, TB_CFG_PORT,				    port->cap_usb4 + PORT_CS_19, 1);		if (ret)			return ret;	} 	/*	 * Enable wakes from PCIe, USB 3.x and DP on this router. Only	 * needed for device routers.	 */	if (route) {		ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);		if (ret)			return ret; 		val &= ~(ROUTER_CS_5_WOP | ROUTER_CS_5_WOU | ROUTER_CS_5_WOD);		if (flags & TB_WAKE_ON_USB3)			val |= ROUTER_CS_5_WOU;		if (flags & TB_WAKE_ON_PCIE)			val |= ROUTER_CS_5_WOP;		if (flags & TB_WAKE_ON_DP)			val |= ROUTER_CS_5_WOD; 		ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1);		if (ret)			return ret;	} 	return 0;} /** * usb4_switch_set_sleep() - Prepare the router to enter sleep * @sw: USB4 router

Lines 1–500 of 3176.