diff --git a/src/soc/intel/common/block/include/intelblocks/xhci.h b/src/soc/intel/common/block/include/intelblocks/xhci.h index 492c32a002..dd95bfb024 100644 --- a/src/soc/intel/common/block/include/intelblocks/xhci.h +++ b/src/soc/intel/common/block/include/intelblocks/xhci.h @@ -56,4 +56,21 @@ void soc_xhci_init(struct device *dev); */ const struct xhci_usb_info *soc_get_xhci_usb_info(void); +/** + * usb_xhci_disable_unused() - Disable unused USB devices + * @ext_usb_xhci_en_cb: Callback function to be invoked, supplied by mainboard, + * to identify the status of externally visible USB ports. + * (Return true if port is present, false if port is absent) + * + * This function is used to disable unused USB devices/ports that are configured + * in the device tree. For the internal USB ports, the connect status of the port + * is probed from the XHCI controller block and the port is disabled if it is not + * connected. For the external USB ports, the mainboard provides the connect status + * of the concerned port depending on the variants and their SKUs. If the mainboard + * supplied callback function is NULL, then all the externally visible USB devices + * in the device tree are enabled. + */ +void usb_xhci_disable_unused(bool (*ext_usb_xhci_en_cb)(unsigned int port_type, + unsigned int port_id)); + #endif /* SOC_INTEL_COMMON_BLOCK_XHCI_H */ diff --git a/src/soc/intel/common/block/xhci/xhci.c b/src/soc/intel/common/block/xhci/xhci.c index c429e7dd58..0bdf1d97ba 100644 --- a/src/soc/intel/common/block/xhci/xhci.c +++ b/src/soc/intel/common/block/xhci/xhci.c @@ -14,11 +14,96 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include +#include #include #include +#include + +#define XHCI_USB2 2 +#define XHCI_USB3 3 + +/* Current Connect Status */ +#define XHCI_STATUS_CCS (1 << 0) + +static bool is_usb_port_connected(const struct xhci_usb_info *info, + unsigned int port_type, unsigned int port_id) +{ + uintptr_t port_sts_reg; + uint32_t port_status; + const struct resource *res; + + /* Support only USB2 or USB3 ports */ + if (!(port_type == XHCI_USB2 || port_type == XHCI_USB3)) + return false; + + /* Mark out of bound port id as not connected */ + if ((port_type == XHCI_USB2 && port_id >= info->num_usb2_ports) || + (port_type == XHCI_USB3 && port_id >= info->num_usb3_ports)) + return false; + + /* Calculate port status register address and read the status */ + res = find_resource(PCH_DEV_XHCI, PCI_BASE_ADDRESS_0); + /* If the memory BAR is not allocated for XHCI, leave the devices enabled */ + if (!res) + return true; + + if (port_type == XHCI_USB2) + port_sts_reg = (uintptr_t)res->base + + info->usb2_port_status_reg + port_id * 0x10; + else + port_sts_reg = (uintptr_t)res->base + + info->usb3_port_status_reg + port_id * 0x10; + port_status = read32((void *)port_sts_reg); + + /* Ensure that the status is not all 1s */ + if (port_status == 0xffffffff) + return false; + + return !!(port_status & XHCI_STATUS_CCS); +} + +void usb_xhci_disable_unused(bool (*ext_usb_xhci_en_cb)(unsigned int port_type, + unsigned int port_id)) +{ + struct device *xhci, *hub = NULL, *port = NULL; + const struct xhci_usb_info *info = soc_get_xhci_usb_info(); + struct drivers_usb_acpi_config *config; + bool enable; + + xhci = pcidev_path_on_root(PCH_DEVFN_XHCI); + if (!xhci) { + printk(BIOS_ERR, "%s: Could not locate XHCI device in DT\n", __func__); + return; + } + + while ((hub = dev_bus_each_child(xhci->link_list, hub)) != NULL) { + while ((port = dev_bus_each_child(hub->link_list, port)) != NULL) { + enable = true; + config = config_of(port); + if (config->type == UPC_TYPE_INTERNAL) { + /* Probe the connect status of internal ports */ + enable = is_usb_port_connected(info, port->path.usb.port_type, + port->path.usb.port_id); + } else if (ext_usb_xhci_en_cb) { + /* Check the mainboard for the status of external ports */ + enable = ext_usb_xhci_en_cb(port->path.usb.port_type, + port->path.usb.port_id); + } + + if (!enable) { + printk(BIOS_INFO, "%s: Disabling USB Type%d Id%d\n", + __func__, port->path.usb.port_type, + port->path.usb.port_id); + port->enabled = 0; + } + } + } +} __weak void soc_xhci_init(struct device *dev) { /* no-op */ }