int usb4_switch_set_wake()

in usb4.c [367:436]


int usb4_switch_set_wake(struct tb_switch *sw, unsigned int flags)
{
	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;

			if ((flags & TB_WAKE_ON_CONNECT) && !configured)
				val |= PORT_CS_19_WOC;
			if ((flags & TB_WAKE_ON_DISCONNECT) && 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;
}