summaryrefslogtreecommitdiffstats
path: root/drivers/usb/roles/intel-xhci-usb-role-switch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/roles/intel-xhci-usb-role-switch.c')
-rw-r--r--drivers/usb/roles/intel-xhci-usb-role-switch.c27
1 files changed, 25 insertions, 2 deletions
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 7325a84dd1c8..409851306e99 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/usb/role.h>
/* register definition */
@@ -26,6 +27,12 @@
#define SW_VBUS_VALID BIT(24)
#define SW_IDPIN_EN BIT(21)
#define SW_IDPIN BIT(20)
+#define SW_SWITCH_EN BIT(16)
+
+#define DRD_CONFIG_DYNAMIC 0
+#define DRD_CONFIG_STATIC_HOST 1
+#define DRD_CONFIG_STATIC_DEVICE 2
+#define DRD_CONFIG_MASK 3
#define DUAL_ROLE_CFG1 0x6c
#define HOST_MODE BIT(29)
@@ -37,6 +44,7 @@
struct intel_xhci_usb_data {
struct usb_role_switch *role_sw;
void __iomem *base;
+ bool enable_sw_switch;
};
static const struct software_node intel_xhci_usb_node = {
@@ -49,6 +57,7 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
unsigned long timeout;
acpi_status status;
u32 glk, val;
+ u32 drd_config = DRD_CONFIG_DYNAMIC;
/*
* On many CHT devices ACPI event (_AEI) handlers read / modify /
@@ -63,24 +72,35 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
pm_runtime_get_sync(dev);
- /* Set idpin value as requested */
+ /*
+ * Set idpin value as requested.
+ * Since some devices rely on firmware setting DRD_CONFIG and
+ * SW_SWITCH_EN bits to be zero for role switch,
+ * do not set these bits for those devices.
+ */
val = readl(data->base + DUAL_ROLE_CFG0);
switch (role) {
case USB_ROLE_NONE:
val |= SW_IDPIN;
val &= ~SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_DYNAMIC;
break;
case USB_ROLE_HOST:
val &= ~SW_IDPIN;
val &= ~SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_STATIC_HOST;
break;
case USB_ROLE_DEVICE:
val |= SW_IDPIN;
val |= SW_VBUS_VALID;
+ drd_config = DRD_CONFIG_STATIC_DEVICE;
break;
}
val |= SW_IDPIN_EN;
-
+ if (data->enable_sw_switch) {
+ val &= ~DRD_CONFIG_MASK;
+ val |= SW_SWITCH_EN | drd_config;
+ }
writel(val, data->base + DUAL_ROLE_CFG0);
acpi_release_global_lock(glk);
@@ -156,6 +176,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev)
sw_desc.allow_userspace_control = true,
sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node);
+ data->enable_sw_switch = !device_property_read_bool(dev,
+ "sw_switch_disable");
+
data->role_sw = usb_role_switch_register(dev, &sw_desc);
if (IS_ERR(data->role_sw)) {
fwnode_handle_put(sw_desc.fwnode);