1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Freescale QUICC Engine USB Host Controller Driver 4 * 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> 12 */ 13 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> 19 #include <linux/io.h> 20 #include <linux/usb.h> 21 #include <linux/usb/hcd.h> 22 #include <linux/gpio/consumer.h> 23 #include <soc/fsl/qe/qe.h> 24 #include "fhci.h" 25 26 /* virtual root hub specific descriptor */ 27 static u8 root_hub_des[] = { 28 0x09, /* blength */ 29 USB_DT_HUB, /* bDescriptorType;hub-descriptor */ 30 0x01, /* bNbrPorts */ 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 */ 37 }; 38 39 static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on) 40 { 41 struct gpio_desc *gpiod = fhci->gpiods[gpio_nr]; 42 43 if (!gpiod) 44 return; 45 46 gpiod_set_value(gpiod, on); 47 mdelay(5); 48 } 49 50 void fhci_config_transceiver(struct fhci_hcd *fhci, 51 enum fhci_port_status status) 52 { 53 fhci_dbg(fhci, "-> %s: %d\n", __func__, status); 54 55 switch (status) { 56 case FHCI_PORT_POWER_OFF: 57 fhci_gpio_set_value(fhci, GPIO_POWER, false); 58 break; 59 case FHCI_PORT_DISABLED: 60 case FHCI_PORT_WAITING: 61 fhci_gpio_set_value(fhci, GPIO_POWER, true); 62 break; 63 case FHCI_PORT_LOW: 64 fhci_gpio_set_value(fhci, GPIO_SPEED, false); 65 break; 66 case FHCI_PORT_FULL: 67 fhci_gpio_set_value(fhci, GPIO_SPEED, true); 68 break; 69 default: 70 WARN_ON(1); 71 break; 72 } 73 74 fhci_dbg(fhci, "<- %s: %d\n", __func__, status); 75 } 76 77 /* disable the USB port by clearing the EN bit in the USBMOD register */ 78 void fhci_port_disable(struct fhci_hcd *fhci) 79 { 80 struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld; 81 enum fhci_port_status port_status; 82 83 fhci_dbg(fhci, "-> %s\n", __func__); 84 85 fhci_stop_sof_timer(fhci); 86 87 fhci_flush_all_transmissions(usb); 88 89 fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld); 90 port_status = usb->port_status; 91 usb->port_status = FHCI_PORT_DISABLED; 92 93 /* Enable IDLE since we want to know if something comes along */ 94 usb->saved_msk |= USB_E_IDLE_MASK; 95 out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk); 96 97 /* check if during the disconnection process attached new device */ 98 if (port_status == FHCI_PORT_WAITING) 99 fhci_device_connected_interrupt(fhci); 100 usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE; 101 usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; 102 fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld); 103 104 fhci_dbg(fhci, "<- %s\n", __func__); 105 } 106 107 /* enable the USB port by setting the EN bit in the USBMOD register */ 108 void fhci_port_enable(void *lld) 109 { 110 struct fhci_usb *usb = (struct fhci_usb *)lld; 111 struct fhci_hcd *fhci = usb->fhci; 112 113 fhci_dbg(fhci, "-> %s\n", __func__); 114 115 fhci_config_transceiver(fhci, usb->port_status); 116 117 if ((usb->port_status != FHCI_PORT_FULL) && 118 (usb->port_status != FHCI_PORT_LOW)) 119 fhci_start_sof_timer(fhci); 120 121 usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE; 122 usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; 123 124 fhci_dbg(fhci, "<- %s\n", __func__); 125 } 126 127 void fhci_io_port_generate_reset(struct fhci_hcd *fhci) 128 { 129 fhci_dbg(fhci, "-> %s\n", __func__); 130 131 gpiod_direction_output(fhci->gpiods[GPIO_USBOE], 0); 132 gpiod_direction_output(fhci->gpiods[GPIO_USBTP], 0); 133 gpiod_direction_output(fhci->gpiods[GPIO_USBTN], 0); 134 135 mdelay(5); 136 137 qe_pin_set_dedicated(fhci->pins[PIN_USBOE]); 138 qe_pin_set_dedicated(fhci->pins[PIN_USBTP]); 139 qe_pin_set_dedicated(fhci->pins[PIN_USBTN]); 140 141 fhci_dbg(fhci, "<- %s\n", __func__); 142 } 143 144 /* generate the RESET condition on the bus */ 145 void fhci_port_reset(void *lld) 146 { 147 struct fhci_usb *usb = (struct fhci_usb *)lld; 148 struct fhci_hcd *fhci = usb->fhci; 149 u8 mode; 150 u16 mask; 151 152 fhci_dbg(fhci, "-> %s\n", __func__); 153 154 fhci_stop_sof_timer(fhci); 155 /* disable the USB controller */ 156 mode = in_8(&fhci->regs->usb_usmod); 157 out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN)); 158 159 /* disable idle interrupts */ 160 mask = in_be16(&fhci->regs->usb_usbmr); 161 out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK)); 162 163 fhci_io_port_generate_reset(fhci); 164 165 /* enable interrupt on this endpoint */ 166 out_be16(&fhci->regs->usb_usbmr, mask); 167 168 /* enable the USB controller */ 169 mode = in_8(&fhci->regs->usb_usmod); 170 out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN); 171 fhci_start_sof_timer(fhci); 172 173 fhci_dbg(fhci, "<- %s\n", __func__); 174 } 175 176 int fhci_hub_status_data(struct usb_hcd *hcd, char *buf) 177 { 178 struct fhci_hcd *fhci = hcd_to_fhci(hcd); 179 int ret = 0; 180 unsigned long flags; 181 182 fhci_dbg(fhci, "-> %s\n", __func__); 183 184 spin_lock_irqsave(&fhci->lock, flags); 185 186 if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION | 187 USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND | 188 USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) { 189 *buf = 1 << 1; 190 ret = 1; 191 fhci_dbg(fhci, "-- %s\n", __func__); 192 } 193 194 spin_unlock_irqrestore(&fhci->lock, flags); 195 196 fhci_dbg(fhci, "<- %s\n", __func__); 197 198 return ret; 199 } 200 201 int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 202 u16 wIndex, char *buf, u16 wLength) 203 { 204 struct fhci_hcd *fhci = hcd_to_fhci(hcd); 205 int retval = 0; 206 struct usb_hub_status *hub_status; 207 struct usb_port_status *port_status; 208 unsigned long flags; 209 210 spin_lock_irqsave(&fhci->lock, flags); 211 212 fhci_dbg(fhci, "-> %s\n", __func__); 213 214 switch (typeReq) { 215 case ClearHubFeature: 216 switch (wValue) { 217 case C_HUB_LOCAL_POWER: 218 case C_HUB_OVER_CURRENT: 219 break; 220 default: 221 goto error; 222 } 223 break; 224 case ClearPortFeature: 225 fhci->vroot_hub->feature &= (1 << wValue); 226 227 switch (wValue) { 228 case USB_PORT_FEAT_ENABLE: 229 fhci->vroot_hub->port.wPortStatus &= 230 ~USB_PORT_STAT_ENABLE; 231 fhci_port_disable(fhci); 232 break; 233 case USB_PORT_FEAT_C_ENABLE: 234 fhci->vroot_hub->port.wPortChange &= 235 ~USB_PORT_STAT_C_ENABLE; 236 break; 237 case USB_PORT_FEAT_SUSPEND: 238 fhci->vroot_hub->port.wPortStatus &= 239 ~USB_PORT_STAT_SUSPEND; 240 fhci_stop_sof_timer(fhci); 241 break; 242 case USB_PORT_FEAT_C_SUSPEND: 243 fhci->vroot_hub->port.wPortChange &= 244 ~USB_PORT_STAT_C_SUSPEND; 245 break; 246 case USB_PORT_FEAT_POWER: 247 fhci->vroot_hub->port.wPortStatus &= 248 ~USB_PORT_STAT_POWER; 249 fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); 250 break; 251 case USB_PORT_FEAT_C_CONNECTION: 252 fhci->vroot_hub->port.wPortChange &= 253 ~USB_PORT_STAT_C_CONNECTION; 254 break; 255 case USB_PORT_FEAT_C_OVER_CURRENT: 256 fhci->vroot_hub->port.wPortChange &= 257 ~USB_PORT_STAT_C_OVERCURRENT; 258 break; 259 case USB_PORT_FEAT_C_RESET: 260 fhci->vroot_hub->port.wPortChange &= 261 ~USB_PORT_STAT_C_RESET; 262 break; 263 default: 264 goto error; 265 } 266 break; 267 case GetHubDescriptor: 268 memcpy(buf, root_hub_des, sizeof(root_hub_des)); 269 break; 270 case GetHubStatus: 271 hub_status = (struct usb_hub_status *)buf; 272 hub_status->wHubStatus = 273 cpu_to_le16(fhci->vroot_hub->hub.wHubStatus); 274 hub_status->wHubChange = 275 cpu_to_le16(fhci->vroot_hub->hub.wHubChange); 276 break; 277 case GetPortStatus: 278 port_status = (struct usb_port_status *)buf; 279 port_status->wPortStatus = 280 cpu_to_le16(fhci->vroot_hub->port.wPortStatus); 281 port_status->wPortChange = 282 cpu_to_le16(fhci->vroot_hub->port.wPortChange); 283 break; 284 case SetHubFeature: 285 switch (wValue) { 286 case C_HUB_OVER_CURRENT: 287 case C_HUB_LOCAL_POWER: 288 break; 289 default: 290 goto error; 291 } 292 break; 293 case SetPortFeature: 294 fhci->vroot_hub->feature |= (1 << wValue); 295 296 switch (wValue) { 297 case USB_PORT_FEAT_ENABLE: 298 fhci->vroot_hub->port.wPortStatus |= 299 USB_PORT_STAT_ENABLE; 300 fhci_port_enable(fhci->usb_lld); 301 break; 302 case USB_PORT_FEAT_SUSPEND: 303 fhci->vroot_hub->port.wPortStatus |= 304 USB_PORT_STAT_SUSPEND; 305 fhci_stop_sof_timer(fhci); 306 break; 307 case USB_PORT_FEAT_RESET: 308 fhci->vroot_hub->port.wPortStatus |= 309 USB_PORT_STAT_RESET; 310 fhci_port_reset(fhci->usb_lld); 311 fhci->vroot_hub->port.wPortStatus |= 312 USB_PORT_STAT_ENABLE; 313 fhci->vroot_hub->port.wPortStatus &= 314 ~USB_PORT_STAT_RESET; 315 break; 316 case USB_PORT_FEAT_POWER: 317 fhci->vroot_hub->port.wPortStatus |= 318 USB_PORT_STAT_POWER; 319 fhci_config_transceiver(fhci, FHCI_PORT_WAITING); 320 break; 321 default: 322 goto error; 323 } 324 break; 325 default: 326 error: 327 retval = -EPIPE; 328 } 329 330 fhci_dbg(fhci, "<- %s\n", __func__); 331 332 spin_unlock_irqrestore(&fhci->lock, flags); 333 334 return retval; 335 } 336