diff options
author | Karthikeyan Ramasubramanian <kramasub@chromium.org> | 2019-06-06 15:35:11 -0600 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2019-07-19 17:14:35 +0000 |
commit | ef0c2265d73004860a7b18ae5e0f9cb1accfb869 (patch) | |
tree | 30a46e55b35a4531adfc43e6b91f9064a1a2ec33 /src/soc/intel/common | |
parent | 0f718312f1b57ec300b7486c95e53562be5a2325 (diff) |
soc/intel/common/block/xhci: Add API to disable USB devices
Add API to disable USB devices that are not present but are configured
in the device tree either after probing the concerned port status or as
explicitly configured by the variants.
BUG=None
BRANCH=octopus
TEST=Boot to ChromeOS.
Change-Id: Ied12faabee1b8c096f2b27de89ab42ee8be5d94d
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/33377
Reviewed-by: Furquan Shaikh <furquan@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/soc/intel/common')
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/xhci.h | 17 | ||||
-rw-r--r-- | src/soc/intel/common/block/xhci/xhci.c | 85 |
2 files changed, 102 insertions, 0 deletions
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 <arch/acpi_device.h> +#include <console/console.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> +#include <drivers/usb/acpi/chip.h> #include <intelblocks/acpi.h> #include <intelblocks/xhci.h> +#include <soc/pci_devs.h> + +#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 */ } |