1 // SPDX-License-Identifier: GPL-2.0+
3 * Freescale QUICC Engine USB Host Controller Driver
5 * Copyright (c) Freescale Semicondutor, Inc. 2006.
6 * Shlomi Gridish <gridish@freescale.com>
7 * Jerry Huang <Chang-Ming.Huang@freescale.com>
8 * Copyright (c) Logic Product Development, Inc. 2007
9 * Peter Barada <peterb@logicpd.com>
10 * Copyright (c) MontaVista Software, Inc. 2008.
11 * Anton Vorontsov <avorontsov@ru.mvista.com>
14 #include <linux/kernel.h>
15 #include <linux/types.h>
16 #include <linux/spinlock.h>
17 #include <linux/delay.h>
18 #include <linux/errno.h>
20 #include <linux/usb.h>
21 #include <linux/usb/hcd.h>
22 #include <linux/gpio.h>
23 #include <soc/fsl/qe/qe.h>
26 /* virtual root hub specific descriptor */
27 static u8 root_hub_des
[] = {
29 USB_DT_HUB
, /* bDescriptorType;hub-descriptor */
31 HUB_CHAR_INDV_PORT_LPSM
| HUB_CHAR_NO_OCPM
, /* wHubCharacteristics */
32 0x00, /* per-port power, no overcurrent */
33 0x01, /* bPwrOn2pwrGood;2ms */
34 0x00, /* bHubContrCurrent;0mA */
35 0x00, /* DeviceRemoveable */
36 0xff, /* PortPwrCtrlMask */
39 static void fhci_gpio_set_value(struct fhci_hcd
*fhci
, int gpio_nr
, bool on
)
41 int gpio
= fhci
->gpios
[gpio_nr
];
42 bool alow
= fhci
->alow_gpios
[gpio_nr
];
44 if (!gpio_is_valid(gpio
))
47 gpio_set_value(gpio
, on
^ alow
);
51 void fhci_config_transceiver(struct fhci_hcd
*fhci
,
52 enum fhci_port_status status
)
54 fhci_dbg(fhci
, "-> %s: %d\n", __func__
, status
);
57 case FHCI_PORT_POWER_OFF
:
58 fhci_gpio_set_value(fhci
, GPIO_POWER
, false);
60 case FHCI_PORT_DISABLED
:
61 case FHCI_PORT_WAITING
:
62 fhci_gpio_set_value(fhci
, GPIO_POWER
, true);
65 fhci_gpio_set_value(fhci
, GPIO_SPEED
, false);
68 fhci_gpio_set_value(fhci
, GPIO_SPEED
, true);
75 fhci_dbg(fhci
, "<- %s: %d\n", __func__
, status
);
78 /* disable the USB port by clearing the EN bit in the USBMOD register */
79 void fhci_port_disable(struct fhci_hcd
*fhci
)
81 struct fhci_usb
*usb
= (struct fhci_usb
*)fhci
->usb_lld
;
82 enum fhci_port_status port_status
;
84 fhci_dbg(fhci
, "-> %s\n", __func__
);
86 fhci_stop_sof_timer(fhci
);
88 fhci_flush_all_transmissions(usb
);
90 fhci_usb_disable_interrupt((struct fhci_usb
*)fhci
->usb_lld
);
91 port_status
= usb
->port_status
;
92 usb
->port_status
= FHCI_PORT_DISABLED
;
94 /* Enable IDLE since we want to know if something comes along */
95 usb
->saved_msk
|= USB_E_IDLE_MASK
;
96 out_be16(&usb
->fhci
->regs
->usb_usbmr
, usb
->saved_msk
);
98 /* check if during the disconnection process attached new device */
99 if (port_status
== FHCI_PORT_WAITING
)
100 fhci_device_connected_interrupt(fhci
);
101 usb
->vroot_hub
->port
.wPortStatus
&= ~USB_PORT_STAT_ENABLE
;
102 usb
->vroot_hub
->port
.wPortChange
|= USB_PORT_STAT_C_ENABLE
;
103 fhci_usb_enable_interrupt((struct fhci_usb
*)fhci
->usb_lld
);
105 fhci_dbg(fhci
, "<- %s\n", __func__
);
108 /* enable the USB port by setting the EN bit in the USBMOD register */
109 void fhci_port_enable(void *lld
)
111 struct fhci_usb
*usb
= (struct fhci_usb
*)lld
;
112 struct fhci_hcd
*fhci
= usb
->fhci
;
114 fhci_dbg(fhci
, "-> %s\n", __func__
);
116 fhci_config_transceiver(fhci
, usb
->port_status
);
118 if ((usb
->port_status
!= FHCI_PORT_FULL
) &&
119 (usb
->port_status
!= FHCI_PORT_LOW
))
120 fhci_start_sof_timer(fhci
);
122 usb
->vroot_hub
->port
.wPortStatus
|= USB_PORT_STAT_ENABLE
;
123 usb
->vroot_hub
->port
.wPortChange
|= USB_PORT_STAT_C_ENABLE
;
125 fhci_dbg(fhci
, "<- %s\n", __func__
);
128 void fhci_io_port_generate_reset(struct fhci_hcd
*fhci
)
130 fhci_dbg(fhci
, "-> %s\n", __func__
);
132 gpio_direction_output(fhci
->gpios
[GPIO_USBOE
], 0);
133 gpio_direction_output(fhci
->gpios
[GPIO_USBTP
], 0);
134 gpio_direction_output(fhci
->gpios
[GPIO_USBTN
], 0);
138 qe_pin_set_dedicated(fhci
->pins
[PIN_USBOE
]);
139 qe_pin_set_dedicated(fhci
->pins
[PIN_USBTP
]);
140 qe_pin_set_dedicated(fhci
->pins
[PIN_USBTN
]);
142 fhci_dbg(fhci
, "<- %s\n", __func__
);
145 /* generate the RESET condition on the bus */
146 void fhci_port_reset(void *lld
)
148 struct fhci_usb
*usb
= (struct fhci_usb
*)lld
;
149 struct fhci_hcd
*fhci
= usb
->fhci
;
153 fhci_dbg(fhci
, "-> %s\n", __func__
);
155 fhci_stop_sof_timer(fhci
);
156 /* disable the USB controller */
157 mode
= in_8(&fhci
->regs
->usb_usmod
);
158 out_8(&fhci
->regs
->usb_usmod
, mode
& (~USB_MODE_EN
));
160 /* disable idle interrupts */
161 mask
= in_be16(&fhci
->regs
->usb_usbmr
);
162 out_be16(&fhci
->regs
->usb_usbmr
, mask
& (~USB_E_IDLE_MASK
));
164 fhci_io_port_generate_reset(fhci
);
166 /* enable interrupt on this endpoint */
167 out_be16(&fhci
->regs
->usb_usbmr
, mask
);
169 /* enable the USB controller */
170 mode
= in_8(&fhci
->regs
->usb_usmod
);
171 out_8(&fhci
->regs
->usb_usmod
, mode
| USB_MODE_EN
);
172 fhci_start_sof_timer(fhci
);
174 fhci_dbg(fhci
, "<- %s\n", __func__
);
177 int fhci_hub_status_data(struct usb_hcd
*hcd
, char *buf
)
179 struct fhci_hcd
*fhci
= hcd_to_fhci(hcd
);
183 fhci_dbg(fhci
, "-> %s\n", __func__
);
185 spin_lock_irqsave(&fhci
->lock
, flags
);
187 if (fhci
->vroot_hub
->port
.wPortChange
& (USB_PORT_STAT_C_CONNECTION
|
188 USB_PORT_STAT_C_ENABLE
| USB_PORT_STAT_C_SUSPEND
|
189 USB_PORT_STAT_C_RESET
| USB_PORT_STAT_C_OVERCURRENT
)) {
192 fhci_dbg(fhci
, "-- %s\n", __func__
);
195 spin_unlock_irqrestore(&fhci
->lock
, flags
);
197 fhci_dbg(fhci
, "<- %s\n", __func__
);
202 int fhci_hub_control(struct usb_hcd
*hcd
, u16 typeReq
, u16 wValue
,
203 u16 wIndex
, char *buf
, u16 wLength
)
205 struct fhci_hcd
*fhci
= hcd_to_fhci(hcd
);
207 struct usb_hub_status
*hub_status
;
208 struct usb_port_status
*port_status
;
211 spin_lock_irqsave(&fhci
->lock
, flags
);
213 fhci_dbg(fhci
, "-> %s\n", __func__
);
216 case ClearHubFeature
:
218 case C_HUB_LOCAL_POWER
:
219 case C_HUB_OVER_CURRENT
:
225 case ClearPortFeature
:
226 fhci
->vroot_hub
->feature
&= (1 << wValue
);
229 case USB_PORT_FEAT_ENABLE
:
230 fhci
->vroot_hub
->port
.wPortStatus
&=
231 ~USB_PORT_STAT_ENABLE
;
232 fhci_port_disable(fhci
);
234 case USB_PORT_FEAT_C_ENABLE
:
235 fhci
->vroot_hub
->port
.wPortChange
&=
236 ~USB_PORT_STAT_C_ENABLE
;
238 case USB_PORT_FEAT_SUSPEND
:
239 fhci
->vroot_hub
->port
.wPortStatus
&=
240 ~USB_PORT_STAT_SUSPEND
;
241 fhci_stop_sof_timer(fhci
);
243 case USB_PORT_FEAT_C_SUSPEND
:
244 fhci
->vroot_hub
->port
.wPortChange
&=
245 ~USB_PORT_STAT_C_SUSPEND
;
247 case USB_PORT_FEAT_POWER
:
248 fhci
->vroot_hub
->port
.wPortStatus
&=
249 ~USB_PORT_STAT_POWER
;
250 fhci_config_transceiver(fhci
, FHCI_PORT_POWER_OFF
);
252 case USB_PORT_FEAT_C_CONNECTION
:
253 fhci
->vroot_hub
->port
.wPortChange
&=
254 ~USB_PORT_STAT_C_CONNECTION
;
256 case USB_PORT_FEAT_C_OVER_CURRENT
:
257 fhci
->vroot_hub
->port
.wPortChange
&=
258 ~USB_PORT_STAT_C_OVERCURRENT
;
260 case USB_PORT_FEAT_C_RESET
:
261 fhci
->vroot_hub
->port
.wPortChange
&=
262 ~USB_PORT_STAT_C_RESET
;
268 case GetHubDescriptor
:
269 memcpy(buf
, root_hub_des
, sizeof(root_hub_des
));
272 hub_status
= (struct usb_hub_status
*)buf
;
273 hub_status
->wHubStatus
=
274 cpu_to_le16(fhci
->vroot_hub
->hub
.wHubStatus
);
275 hub_status
->wHubChange
=
276 cpu_to_le16(fhci
->vroot_hub
->hub
.wHubChange
);
279 port_status
= (struct usb_port_status
*)buf
;
280 port_status
->wPortStatus
=
281 cpu_to_le16(fhci
->vroot_hub
->port
.wPortStatus
);
282 port_status
->wPortChange
=
283 cpu_to_le16(fhci
->vroot_hub
->port
.wPortChange
);
287 case C_HUB_OVER_CURRENT
:
288 case C_HUB_LOCAL_POWER
:
295 fhci
->vroot_hub
->feature
|= (1 << wValue
);
298 case USB_PORT_FEAT_ENABLE
:
299 fhci
->vroot_hub
->port
.wPortStatus
|=
300 USB_PORT_STAT_ENABLE
;
301 fhci_port_enable(fhci
->usb_lld
);
303 case USB_PORT_FEAT_SUSPEND
:
304 fhci
->vroot_hub
->port
.wPortStatus
|=
305 USB_PORT_STAT_SUSPEND
;
306 fhci_stop_sof_timer(fhci
);
308 case USB_PORT_FEAT_RESET
:
309 fhci
->vroot_hub
->port
.wPortStatus
|=
311 fhci_port_reset(fhci
->usb_lld
);
312 fhci
->vroot_hub
->port
.wPortStatus
|=
313 USB_PORT_STAT_ENABLE
;
314 fhci
->vroot_hub
->port
.wPortStatus
&=
315 ~USB_PORT_STAT_RESET
;
317 case USB_PORT_FEAT_POWER
:
318 fhci
->vroot_hub
->port
.wPortStatus
|=
320 fhci_config_transceiver(fhci
, FHCI_PORT_WAITING
);
331 fhci_dbg(fhci
, "<- %s\n", __func__
);
333 spin_unlock_irqrestore(&fhci
->lock
, flags
);