summaryrefslogtreecommitdiffstats
path: root/src/soc/intel
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel')
-rw-r--r--src/soc/intel/common/block/include/intelblocks/xhci.h17
-rw-r--r--src/soc/intel/common/block/xhci/xhci.c85
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 492c32a002b8..dd95bfb024be 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 c429e7dd5898..0bdf1d97ba60 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 */ }