Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Freescale QUICC Engine USB Host Controller Driver
0004  *
0005  * Copyright (c) Freescale Semicondutor, Inc. 2006.
0006  *               Shlomi Gridish <gridish@freescale.com>
0007  *               Jerry Huang <Chang-Ming.Huang@freescale.com>
0008  * Copyright (c) Logic Product Development, Inc. 2007
0009  *               Peter Barada <peterb@logicpd.com>
0010  * Copyright (c) MontaVista Software, Inc. 2008.
0011  *               Anton Vorontsov <avorontsov@ru.mvista.com>
0012  */
0013 
0014 #include <linux/kernel.h>
0015 #include <linux/types.h>
0016 #include <linux/spinlock.h>
0017 #include <linux/delay.h>
0018 #include <linux/errno.h>
0019 #include <linux/io.h>
0020 #include <linux/usb.h>
0021 #include <linux/usb/hcd.h>
0022 #include <linux/gpio.h>
0023 #include <soc/fsl/qe/qe.h>
0024 #include "fhci.h"
0025 
0026 /* virtual root hub specific descriptor */
0027 static u8 root_hub_des[] = {
0028     0x09, /* blength */
0029     USB_DT_HUB, /* bDescriptorType;hub-descriptor */
0030     0x01, /* bNbrPorts */
0031     HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
0032     0x00, /* per-port power, no overcurrent */
0033     0x01, /* bPwrOn2pwrGood;2ms */
0034     0x00, /* bHubContrCurrent;0mA */
0035     0x00, /* DeviceRemoveable */
0036     0xff, /* PortPwrCtrlMask */
0037 };
0038 
0039 static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on)
0040 {
0041     int gpio = fhci->gpios[gpio_nr];
0042     bool alow = fhci->alow_gpios[gpio_nr];
0043 
0044     if (!gpio_is_valid(gpio))
0045         return;
0046 
0047     gpio_set_value(gpio, on ^ alow);
0048     mdelay(5);
0049 }
0050 
0051 void fhci_config_transceiver(struct fhci_hcd *fhci,
0052                  enum fhci_port_status status)
0053 {
0054     fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
0055 
0056     switch (status) {
0057     case FHCI_PORT_POWER_OFF:
0058         fhci_gpio_set_value(fhci, GPIO_POWER, false);
0059         break;
0060     case FHCI_PORT_DISABLED:
0061     case FHCI_PORT_WAITING:
0062         fhci_gpio_set_value(fhci, GPIO_POWER, true);
0063         break;
0064     case FHCI_PORT_LOW:
0065         fhci_gpio_set_value(fhci, GPIO_SPEED, false);
0066         break;
0067     case FHCI_PORT_FULL:
0068         fhci_gpio_set_value(fhci, GPIO_SPEED, true);
0069         break;
0070     default:
0071         WARN_ON(1);
0072         break;
0073     }
0074 
0075     fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
0076 }
0077 
0078 /* disable the USB port by clearing the EN bit in the USBMOD register */
0079 void fhci_port_disable(struct fhci_hcd *fhci)
0080 {
0081     struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
0082     enum fhci_port_status port_status;
0083 
0084     fhci_dbg(fhci, "-> %s\n", __func__);
0085 
0086     fhci_stop_sof_timer(fhci);
0087 
0088     fhci_flush_all_transmissions(usb);
0089 
0090     fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
0091     port_status = usb->port_status;
0092     usb->port_status = FHCI_PORT_DISABLED;
0093 
0094     /* Enable IDLE since we want to know if something comes along */
0095     usb->saved_msk |= USB_E_IDLE_MASK;
0096     out_be16(&usb->fhci->regs->usb_usbmr, usb->saved_msk);
0097 
0098     /* check if during the disconnection process attached new device */
0099     if (port_status == FHCI_PORT_WAITING)
0100         fhci_device_connected_interrupt(fhci);
0101     usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE;
0102     usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
0103     fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld);
0104 
0105     fhci_dbg(fhci, "<- %s\n", __func__);
0106 }
0107 
0108 /* enable the USB port by setting the EN bit in the USBMOD register */
0109 void fhci_port_enable(void *lld)
0110 {
0111     struct fhci_usb *usb = (struct fhci_usb *)lld;
0112     struct fhci_hcd *fhci = usb->fhci;
0113 
0114     fhci_dbg(fhci, "-> %s\n", __func__);
0115 
0116     fhci_config_transceiver(fhci, usb->port_status);
0117 
0118     if ((usb->port_status != FHCI_PORT_FULL) &&
0119             (usb->port_status != FHCI_PORT_LOW))
0120         fhci_start_sof_timer(fhci);
0121 
0122     usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
0123     usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
0124 
0125     fhci_dbg(fhci, "<- %s\n", __func__);
0126 }
0127 
0128 void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
0129 {
0130     fhci_dbg(fhci, "-> %s\n", __func__);
0131 
0132     gpio_direction_output(fhci->gpios[GPIO_USBOE], 0);
0133     gpio_direction_output(fhci->gpios[GPIO_USBTP], 0);
0134     gpio_direction_output(fhci->gpios[GPIO_USBTN], 0);
0135 
0136     mdelay(5);
0137 
0138     qe_pin_set_dedicated(fhci->pins[PIN_USBOE]);
0139     qe_pin_set_dedicated(fhci->pins[PIN_USBTP]);
0140     qe_pin_set_dedicated(fhci->pins[PIN_USBTN]);
0141 
0142     fhci_dbg(fhci, "<- %s\n", __func__);
0143 }
0144 
0145 /* generate the RESET condition on the bus */
0146 void fhci_port_reset(void *lld)
0147 {
0148     struct fhci_usb *usb = (struct fhci_usb *)lld;
0149     struct fhci_hcd *fhci = usb->fhci;
0150     u8 mode;
0151     u16 mask;
0152 
0153     fhci_dbg(fhci, "-> %s\n", __func__);
0154 
0155     fhci_stop_sof_timer(fhci);
0156     /* disable the USB controller */
0157     mode = in_8(&fhci->regs->usb_usmod);
0158     out_8(&fhci->regs->usb_usmod, mode & (~USB_MODE_EN));
0159 
0160     /* disable idle interrupts */
0161     mask = in_be16(&fhci->regs->usb_usbmr);
0162     out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
0163 
0164     fhci_io_port_generate_reset(fhci);
0165 
0166     /* enable interrupt on this endpoint */
0167     out_be16(&fhci->regs->usb_usbmr, mask);
0168 
0169     /* enable the USB controller */
0170     mode = in_8(&fhci->regs->usb_usmod);
0171     out_8(&fhci->regs->usb_usmod, mode | USB_MODE_EN);
0172     fhci_start_sof_timer(fhci);
0173 
0174     fhci_dbg(fhci, "<- %s\n", __func__);
0175 }
0176 
0177 int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
0178 {
0179     struct fhci_hcd *fhci = hcd_to_fhci(hcd);
0180     int ret = 0;
0181     unsigned long flags;
0182 
0183     fhci_dbg(fhci, "-> %s\n", __func__);
0184 
0185     spin_lock_irqsave(&fhci->lock, flags);
0186 
0187     if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION |
0188             USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND |
0189             USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) {
0190         *buf = 1 << 1;
0191         ret = 1;
0192         fhci_dbg(fhci, "-- %s\n", __func__);
0193     }
0194 
0195     spin_unlock_irqrestore(&fhci->lock, flags);
0196 
0197     fhci_dbg(fhci, "<- %s\n", __func__);
0198 
0199     return ret;
0200 }
0201 
0202 int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
0203                 u16 wIndex, char *buf, u16 wLength)
0204 {
0205     struct fhci_hcd *fhci = hcd_to_fhci(hcd);
0206     int retval = 0;
0207     struct usb_hub_status *hub_status;
0208     struct usb_port_status *port_status;
0209     unsigned long flags;
0210 
0211     spin_lock_irqsave(&fhci->lock, flags);
0212 
0213     fhci_dbg(fhci, "-> %s\n", __func__);
0214 
0215     switch (typeReq) {
0216     case ClearHubFeature:
0217         switch (wValue) {
0218         case C_HUB_LOCAL_POWER:
0219         case C_HUB_OVER_CURRENT:
0220             break;
0221         default:
0222             goto error;
0223         }
0224         break;
0225     case ClearPortFeature:
0226         fhci->vroot_hub->feature &= (1 << wValue);
0227 
0228         switch (wValue) {
0229         case USB_PORT_FEAT_ENABLE:
0230             fhci->vroot_hub->port.wPortStatus &=
0231                 ~USB_PORT_STAT_ENABLE;
0232             fhci_port_disable(fhci);
0233             break;
0234         case USB_PORT_FEAT_C_ENABLE:
0235             fhci->vroot_hub->port.wPortChange &=
0236                 ~USB_PORT_STAT_C_ENABLE;
0237             break;
0238         case USB_PORT_FEAT_SUSPEND:
0239             fhci->vroot_hub->port.wPortStatus &=
0240                 ~USB_PORT_STAT_SUSPEND;
0241             fhci_stop_sof_timer(fhci);
0242             break;
0243         case USB_PORT_FEAT_C_SUSPEND:
0244             fhci->vroot_hub->port.wPortChange &=
0245                 ~USB_PORT_STAT_C_SUSPEND;
0246             break;
0247         case USB_PORT_FEAT_POWER:
0248             fhci->vroot_hub->port.wPortStatus &=
0249                 ~USB_PORT_STAT_POWER;
0250             fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
0251             break;
0252         case USB_PORT_FEAT_C_CONNECTION:
0253             fhci->vroot_hub->port.wPortChange &=
0254                 ~USB_PORT_STAT_C_CONNECTION;
0255             break;
0256         case USB_PORT_FEAT_C_OVER_CURRENT:
0257             fhci->vroot_hub->port.wPortChange &=
0258                 ~USB_PORT_STAT_C_OVERCURRENT;
0259             break;
0260         case USB_PORT_FEAT_C_RESET:
0261             fhci->vroot_hub->port.wPortChange &=
0262                 ~USB_PORT_STAT_C_RESET;
0263             break;
0264         default:
0265             goto error;
0266         }
0267         break;
0268     case GetHubDescriptor:
0269         memcpy(buf, root_hub_des, sizeof(root_hub_des));
0270         break;
0271     case GetHubStatus:
0272         hub_status = (struct usb_hub_status *)buf;
0273         hub_status->wHubStatus =
0274             cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
0275         hub_status->wHubChange =
0276             cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
0277         break;
0278     case GetPortStatus:
0279         port_status = (struct usb_port_status *)buf;
0280         port_status->wPortStatus =
0281             cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
0282         port_status->wPortChange =
0283             cpu_to_le16(fhci->vroot_hub->port.wPortChange);
0284         break;
0285     case SetHubFeature:
0286         switch (wValue) {
0287         case C_HUB_OVER_CURRENT:
0288         case C_HUB_LOCAL_POWER:
0289             break;
0290         default:
0291             goto error;
0292         }
0293         break;
0294     case SetPortFeature:
0295         fhci->vroot_hub->feature |= (1 << wValue);
0296 
0297         switch (wValue) {
0298         case USB_PORT_FEAT_ENABLE:
0299             fhci->vroot_hub->port.wPortStatus |=
0300                 USB_PORT_STAT_ENABLE;
0301             fhci_port_enable(fhci->usb_lld);
0302             break;
0303         case USB_PORT_FEAT_SUSPEND:
0304             fhci->vroot_hub->port.wPortStatus |=
0305                 USB_PORT_STAT_SUSPEND;
0306             fhci_stop_sof_timer(fhci);
0307             break;
0308         case USB_PORT_FEAT_RESET:
0309             fhci->vroot_hub->port.wPortStatus |=
0310                 USB_PORT_STAT_RESET;
0311             fhci_port_reset(fhci->usb_lld);
0312             fhci->vroot_hub->port.wPortStatus |=
0313                 USB_PORT_STAT_ENABLE;
0314             fhci->vroot_hub->port.wPortStatus &=
0315                 ~USB_PORT_STAT_RESET;
0316             break;
0317         case USB_PORT_FEAT_POWER:
0318             fhci->vroot_hub->port.wPortStatus |=
0319                 USB_PORT_STAT_POWER;
0320             fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
0321             break;
0322         default:
0323             goto error;
0324         }
0325         break;
0326     default:
0327 error:
0328         retval = -EPIPE;
0329     }
0330 
0331     fhci_dbg(fhci, "<- %s\n", __func__);
0332 
0333     spin_unlock_irqrestore(&fhci->lock, flags);
0334 
0335     return retval;
0336 }