diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 65 |
1 files changed, 50 insertions, 15 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index eac5b53aa9e7..208b805b80eb 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -129,6 +129,50 @@ static u32 xhci_port_state_to_neutral(u32 state) | |||
129 | return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); | 129 | return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); |
130 | } | 130 | } |
131 | 131 | ||
132 | static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex, | ||
133 | u32 __iomem *addr, u32 port_status) | ||
134 | { | ||
135 | /* Write 1 to disable the port */ | ||
136 | xhci_writel(xhci, port_status | PORT_PE, addr); | ||
137 | port_status = xhci_readl(xhci, addr); | ||
138 | xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n", | ||
139 | wIndex, port_status); | ||
140 | } | ||
141 | |||
142 | static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, | ||
143 | u16 wIndex, u32 __iomem *addr, u32 port_status) | ||
144 | { | ||
145 | char *port_change_bit; | ||
146 | u32 status; | ||
147 | |||
148 | switch (wValue) { | ||
149 | case USB_PORT_FEAT_C_RESET: | ||
150 | status = PORT_RC; | ||
151 | port_change_bit = "reset"; | ||
152 | break; | ||
153 | case USB_PORT_FEAT_C_CONNECTION: | ||
154 | status = PORT_CSC; | ||
155 | port_change_bit = "connect"; | ||
156 | break; | ||
157 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
158 | status = PORT_OCC; | ||
159 | port_change_bit = "over-current"; | ||
160 | break; | ||
161 | case USB_PORT_FEAT_C_ENABLE: | ||
162 | status = PORT_PEC; | ||
163 | port_change_bit = "enable/disable"; | ||
164 | break; | ||
165 | default: | ||
166 | /* Should never happen */ | ||
167 | return; | ||
168 | } | ||
169 | /* Change bits are all write 1 to clear */ | ||
170 | xhci_writel(xhci, port_status | status, addr); | ||
171 | port_status = xhci_readl(xhci, addr); | ||
172 | xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", | ||
173 | port_change_bit, wIndex, port_status); | ||
174 | } | ||
175 | |||
132 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | 176 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, |
133 | u16 wIndex, char *buf, u16 wLength) | 177 | u16 wIndex, char *buf, u16 wLength) |
134 | { | 178 | { |
@@ -138,7 +182,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
138 | u32 temp, status; | 182 | u32 temp, status; |
139 | int retval = 0; | 183 | int retval = 0; |
140 | u32 __iomem *addr; | 184 | u32 __iomem *addr; |
141 | char *port_change_bit; | ||
142 | 185 | ||
143 | ports = HCS_MAX_PORTS(xhci->hcs_params1); | 186 | ports = HCS_MAX_PORTS(xhci->hcs_params1); |
144 | 187 | ||
@@ -229,26 +272,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
229 | temp = xhci_port_state_to_neutral(temp); | 272 | temp = xhci_port_state_to_neutral(temp); |
230 | switch (wValue) { | 273 | switch (wValue) { |
231 | case USB_PORT_FEAT_C_RESET: | 274 | case USB_PORT_FEAT_C_RESET: |
232 | status = PORT_RC; | ||
233 | port_change_bit = "reset"; | ||
234 | break; | ||
235 | case USB_PORT_FEAT_C_CONNECTION: | 275 | case USB_PORT_FEAT_C_CONNECTION: |
236 | status = PORT_CSC; | ||
237 | port_change_bit = "connect"; | ||
238 | break; | ||
239 | case USB_PORT_FEAT_C_OVER_CURRENT: | 276 | case USB_PORT_FEAT_C_OVER_CURRENT: |
240 | status = PORT_OCC; | 277 | case USB_PORT_FEAT_C_ENABLE: |
241 | port_change_bit = "over-current"; | 278 | xhci_clear_port_change_bit(xhci, wValue, wIndex, |
279 | addr, temp); | ||
280 | break; | ||
281 | case USB_PORT_FEAT_ENABLE: | ||
282 | xhci_disable_port(xhci, wIndex, addr, temp); | ||
242 | break; | 283 | break; |
243 | default: | 284 | default: |
244 | goto error; | 285 | goto error; |
245 | } | 286 | } |
246 | /* Change bits are all write 1 to clear */ | ||
247 | xhci_writel(xhci, temp | status, addr); | ||
248 | temp = xhci_readl(xhci, addr); | ||
249 | xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", | ||
250 | port_change_bit, wIndex, temp); | ||
251 | temp = xhci_readl(xhci, addr); /* unblock any posted writes */ | ||
252 | break; | 287 | break; |
253 | default: | 288 | default: |
254 | error: | 289 | error: |