| .. | .. |
|---|
| 19 | 19 | #include <linux/module.h> |
|---|
| 20 | 20 | #include <linux/platform_device.h> |
|---|
| 21 | 21 | #include <linux/pm_runtime.h> |
|---|
| 22 | +#include <linux/property.h> |
|---|
| 22 | 23 | #include <linux/usb/role.h> |
|---|
| 23 | 24 | |
|---|
| 24 | 25 | /* register definition */ |
|---|
| .. | .. |
|---|
| 26 | 27 | #define SW_VBUS_VALID BIT(24) |
|---|
| 27 | 28 | #define SW_IDPIN_EN BIT(21) |
|---|
| 28 | 29 | #define SW_IDPIN BIT(20) |
|---|
| 30 | +#define SW_SWITCH_EN BIT(16) |
|---|
| 31 | + |
|---|
| 32 | +#define DRD_CONFIG_DYNAMIC 0 |
|---|
| 33 | +#define DRD_CONFIG_STATIC_HOST 1 |
|---|
| 34 | +#define DRD_CONFIG_STATIC_DEVICE 2 |
|---|
| 35 | +#define DRD_CONFIG_MASK 3 |
|---|
| 29 | 36 | |
|---|
| 30 | 37 | #define DUAL_ROLE_CFG1 0x6c |
|---|
| 31 | 38 | #define HOST_MODE BIT(29) |
|---|
| .. | .. |
|---|
| 35 | 42 | #define DRV_NAME "intel_xhci_usb_sw" |
|---|
| 36 | 43 | |
|---|
| 37 | 44 | struct intel_xhci_usb_data { |
|---|
| 45 | + struct device *dev; |
|---|
| 38 | 46 | struct usb_role_switch *role_sw; |
|---|
| 39 | 47 | void __iomem *base; |
|---|
| 48 | + bool enable_sw_switch; |
|---|
| 40 | 49 | }; |
|---|
| 41 | 50 | |
|---|
| 42 | | -static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) |
|---|
| 51 | +static const struct software_node intel_xhci_usb_node = { |
|---|
| 52 | + "intel-xhci-usb-sw", |
|---|
| 53 | +}; |
|---|
| 54 | + |
|---|
| 55 | +static int intel_xhci_usb_set_role(struct usb_role_switch *sw, |
|---|
| 56 | + enum usb_role role) |
|---|
| 43 | 57 | { |
|---|
| 44 | | - struct intel_xhci_usb_data *data = dev_get_drvdata(dev); |
|---|
| 58 | + struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw); |
|---|
| 45 | 59 | unsigned long timeout; |
|---|
| 46 | 60 | acpi_status status; |
|---|
| 47 | 61 | u32 glk, val; |
|---|
| 62 | + u32 drd_config = DRD_CONFIG_DYNAMIC; |
|---|
| 48 | 63 | |
|---|
| 49 | 64 | /* |
|---|
| 50 | 65 | * On many CHT devices ACPI event (_AEI) handlers read / modify / |
|---|
| .. | .. |
|---|
| 53 | 68 | */ |
|---|
| 54 | 69 | status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); |
|---|
| 55 | 70 | if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { |
|---|
| 56 | | - dev_err(dev, "Error could not acquire lock\n"); |
|---|
| 71 | + dev_err(data->dev, "Error could not acquire lock\n"); |
|---|
| 57 | 72 | return -EIO; |
|---|
| 58 | 73 | } |
|---|
| 59 | 74 | |
|---|
| 60 | | - pm_runtime_get_sync(dev); |
|---|
| 75 | + pm_runtime_get_sync(data->dev); |
|---|
| 61 | 76 | |
|---|
| 62 | | - /* Set idpin value as requested */ |
|---|
| 77 | + /* |
|---|
| 78 | + * Set idpin value as requested. |
|---|
| 79 | + * Since some devices rely on firmware setting DRD_CONFIG and |
|---|
| 80 | + * SW_SWITCH_EN bits to be zero for role switch, |
|---|
| 81 | + * do not set these bits for those devices. |
|---|
| 82 | + */ |
|---|
| 63 | 83 | val = readl(data->base + DUAL_ROLE_CFG0); |
|---|
| 64 | 84 | switch (role) { |
|---|
| 65 | 85 | case USB_ROLE_NONE: |
|---|
| 66 | 86 | val |= SW_IDPIN; |
|---|
| 67 | 87 | val &= ~SW_VBUS_VALID; |
|---|
| 88 | + drd_config = DRD_CONFIG_DYNAMIC; |
|---|
| 68 | 89 | break; |
|---|
| 69 | 90 | case USB_ROLE_HOST: |
|---|
| 70 | 91 | val &= ~SW_IDPIN; |
|---|
| 71 | 92 | val &= ~SW_VBUS_VALID; |
|---|
| 93 | + drd_config = DRD_CONFIG_STATIC_HOST; |
|---|
| 72 | 94 | break; |
|---|
| 73 | 95 | case USB_ROLE_DEVICE: |
|---|
| 74 | 96 | val |= SW_IDPIN; |
|---|
| 75 | 97 | val |= SW_VBUS_VALID; |
|---|
| 98 | + drd_config = DRD_CONFIG_STATIC_DEVICE; |
|---|
| 76 | 99 | break; |
|---|
| 77 | 100 | } |
|---|
| 78 | 101 | val |= SW_IDPIN_EN; |
|---|
| 79 | | - |
|---|
| 102 | + if (data->enable_sw_switch) { |
|---|
| 103 | + val &= ~DRD_CONFIG_MASK; |
|---|
| 104 | + val |= SW_SWITCH_EN | drd_config; |
|---|
| 105 | + } |
|---|
| 80 | 106 | writel(val, data->base + DUAL_ROLE_CFG0); |
|---|
| 81 | 107 | |
|---|
| 82 | 108 | acpi_release_global_lock(glk); |
|---|
| .. | .. |
|---|
| 88 | 114 | do { |
|---|
| 89 | 115 | val = readl(data->base + DUAL_ROLE_CFG1); |
|---|
| 90 | 116 | if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) { |
|---|
| 91 | | - pm_runtime_put(dev); |
|---|
| 117 | + pm_runtime_put(data->dev); |
|---|
| 92 | 118 | return 0; |
|---|
| 93 | 119 | } |
|---|
| 94 | 120 | |
|---|
| .. | .. |
|---|
| 96 | 122 | usleep_range(5000, 10000); |
|---|
| 97 | 123 | } while (time_before(jiffies, timeout)); |
|---|
| 98 | 124 | |
|---|
| 99 | | - pm_runtime_put(dev); |
|---|
| 125 | + pm_runtime_put(data->dev); |
|---|
| 100 | 126 | |
|---|
| 101 | | - dev_warn(dev, "Timeout waiting for role-switch\n"); |
|---|
| 127 | + dev_warn(data->dev, "Timeout waiting for role-switch\n"); |
|---|
| 102 | 128 | return -ETIMEDOUT; |
|---|
| 103 | 129 | } |
|---|
| 104 | 130 | |
|---|
| 105 | | -static enum usb_role intel_xhci_usb_get_role(struct device *dev) |
|---|
| 131 | +static enum usb_role intel_xhci_usb_get_role(struct usb_role_switch *sw) |
|---|
| 106 | 132 | { |
|---|
| 107 | | - struct intel_xhci_usb_data *data = dev_get_drvdata(dev); |
|---|
| 133 | + struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw); |
|---|
| 108 | 134 | enum usb_role role; |
|---|
| 109 | 135 | u32 val; |
|---|
| 110 | 136 | |
|---|
| 111 | | - pm_runtime_get_sync(dev); |
|---|
| 137 | + pm_runtime_get_sync(data->dev); |
|---|
| 112 | 138 | val = readl(data->base + DUAL_ROLE_CFG0); |
|---|
| 113 | | - pm_runtime_put(dev); |
|---|
| 139 | + pm_runtime_put(data->dev); |
|---|
| 114 | 140 | |
|---|
| 115 | 141 | if (!(val & SW_IDPIN)) |
|---|
| 116 | 142 | role = USB_ROLE_HOST; |
|---|
| .. | .. |
|---|
| 122 | 148 | return role; |
|---|
| 123 | 149 | } |
|---|
| 124 | 150 | |
|---|
| 125 | | -static const struct usb_role_switch_desc sw_desc = { |
|---|
| 126 | | - .set = intel_xhci_usb_set_role, |
|---|
| 127 | | - .get = intel_xhci_usb_get_role, |
|---|
| 128 | | - .allow_userspace_control = true, |
|---|
| 129 | | -}; |
|---|
| 130 | | - |
|---|
| 131 | 151 | static int intel_xhci_usb_probe(struct platform_device *pdev) |
|---|
| 132 | 152 | { |
|---|
| 153 | + struct usb_role_switch_desc sw_desc = { }; |
|---|
| 133 | 154 | struct device *dev = &pdev->dev; |
|---|
| 134 | 155 | struct intel_xhci_usb_data *data; |
|---|
| 135 | 156 | struct resource *res; |
|---|
| 157 | + int ret; |
|---|
| 136 | 158 | |
|---|
| 137 | 159 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); |
|---|
| 138 | 160 | if (!data) |
|---|
| .. | .. |
|---|
| 141 | 163 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 142 | 164 | if (!res) |
|---|
| 143 | 165 | return -EINVAL; |
|---|
| 144 | | - data->base = devm_ioremap_nocache(dev, res->start, resource_size(res)); |
|---|
| 166 | + data->base = devm_ioremap(dev, res->start, resource_size(res)); |
|---|
| 145 | 167 | if (!data->base) |
|---|
| 146 | 168 | return -ENOMEM; |
|---|
| 147 | 169 | |
|---|
| 148 | 170 | platform_set_drvdata(pdev, data); |
|---|
| 149 | 171 | |
|---|
| 172 | + ret = software_node_register(&intel_xhci_usb_node); |
|---|
| 173 | + if (ret) |
|---|
| 174 | + return ret; |
|---|
| 175 | + |
|---|
| 176 | + sw_desc.set = intel_xhci_usb_set_role, |
|---|
| 177 | + sw_desc.get = intel_xhci_usb_get_role, |
|---|
| 178 | + sw_desc.allow_userspace_control = true, |
|---|
| 179 | + sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node); |
|---|
| 180 | + sw_desc.driver_data = data; |
|---|
| 181 | + |
|---|
| 182 | + data->dev = dev; |
|---|
| 183 | + data->enable_sw_switch = !device_property_read_bool(dev, |
|---|
| 184 | + "sw_switch_disable"); |
|---|
| 185 | + |
|---|
| 150 | 186 | data->role_sw = usb_role_switch_register(dev, &sw_desc); |
|---|
| 151 | | - if (IS_ERR(data->role_sw)) |
|---|
| 187 | + if (IS_ERR(data->role_sw)) { |
|---|
| 188 | + fwnode_handle_put(sw_desc.fwnode); |
|---|
| 152 | 189 | return PTR_ERR(data->role_sw); |
|---|
| 190 | + } |
|---|
| 153 | 191 | |
|---|
| 154 | 192 | pm_runtime_set_active(dev); |
|---|
| 155 | 193 | pm_runtime_enable(dev); |
|---|
| .. | .. |
|---|
| 164 | 202 | pm_runtime_disable(&pdev->dev); |
|---|
| 165 | 203 | |
|---|
| 166 | 204 | usb_role_switch_unregister(data->role_sw); |
|---|
| 205 | + fwnode_handle_put(software_node_fwnode(&intel_xhci_usb_node)); |
|---|
| 206 | + |
|---|
| 167 | 207 | return 0; |
|---|
| 168 | 208 | } |
|---|
| 169 | 209 | |
|---|