root/drivers/usb/host/fhci-hub.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fhci_gpio_set_value
  2. fhci_config_transceiver
  3. fhci_port_disable
  4. fhci_port_enable
  5. fhci_io_port_generate_reset
  6. fhci_port_reset
  7. fhci_hub_status_data
  8. fhci_hub_control

   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.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         int gpio = fhci->gpios[gpio_nr];
  42         bool alow = fhci->alow_gpios[gpio_nr];
  43 
  44         if (!gpio_is_valid(gpio))
  45                 return;
  46 
  47         gpio_set_value(gpio, on ^ alow);
  48         mdelay(5);
  49 }
  50 
  51 void fhci_config_transceiver(struct fhci_hcd *fhci,
  52                              enum fhci_port_status status)
  53 {
  54         fhci_dbg(fhci, "-> %s: %d\n", __func__, status);
  55 
  56         switch (status) {
  57         case FHCI_PORT_POWER_OFF:
  58                 fhci_gpio_set_value(fhci, GPIO_POWER, false);
  59                 break;
  60         case FHCI_PORT_DISABLED:
  61         case FHCI_PORT_WAITING:
  62                 fhci_gpio_set_value(fhci, GPIO_POWER, true);
  63                 break;
  64         case FHCI_PORT_LOW:
  65                 fhci_gpio_set_value(fhci, GPIO_SPEED, false);
  66                 break;
  67         case FHCI_PORT_FULL:
  68                 fhci_gpio_set_value(fhci, GPIO_SPEED, true);
  69                 break;
  70         default:
  71                 WARN_ON(1);
  72                 break;
  73         }
  74 
  75         fhci_dbg(fhci, "<- %s: %d\n", __func__, status);
  76 }
  77 
  78 /* disable the USB port by clearing the EN bit in the USBMOD register */
  79 void fhci_port_disable(struct fhci_hcd *fhci)
  80 {
  81         struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld;
  82         enum fhci_port_status port_status;
  83 
  84         fhci_dbg(fhci, "-> %s\n", __func__);
  85 
  86         fhci_stop_sof_timer(fhci);
  87 
  88         fhci_flush_all_transmissions(usb);
  89 
  90         fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld);
  91         port_status = usb->port_status;
  92         usb->port_status = FHCI_PORT_DISABLED;
  93 
  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);
  97 
  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);
 104 
 105         fhci_dbg(fhci, "<- %s\n", __func__);
 106 }
 107 
 108 /* enable the USB port by setting the EN bit in the USBMOD register */
 109 void fhci_port_enable(void *lld)
 110 {
 111         struct fhci_usb *usb = (struct fhci_usb *)lld;
 112         struct fhci_hcd *fhci = usb->fhci;
 113 
 114         fhci_dbg(fhci, "-> %s\n", __func__);
 115 
 116         fhci_config_transceiver(fhci, usb->port_status);
 117 
 118         if ((usb->port_status != FHCI_PORT_FULL) &&
 119                         (usb->port_status != FHCI_PORT_LOW))
 120                 fhci_start_sof_timer(fhci);
 121 
 122         usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE;
 123         usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE;
 124 
 125         fhci_dbg(fhci, "<- %s\n", __func__);
 126 }
 127 
 128 void fhci_io_port_generate_reset(struct fhci_hcd *fhci)
 129 {
 130         fhci_dbg(fhci, "-> %s\n", __func__);
 131 
 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);
 135 
 136         mdelay(5);
 137 
 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]);
 141 
 142         fhci_dbg(fhci, "<- %s\n", __func__);
 143 }
 144 
 145 /* generate the RESET condition on the bus */
 146 void fhci_port_reset(void *lld)
 147 {
 148         struct fhci_usb *usb = (struct fhci_usb *)lld;
 149         struct fhci_hcd *fhci = usb->fhci;
 150         u8 mode;
 151         u16 mask;
 152 
 153         fhci_dbg(fhci, "-> %s\n", __func__);
 154 
 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));
 159 
 160         /* disable idle interrupts */
 161         mask = in_be16(&fhci->regs->usb_usbmr);
 162         out_be16(&fhci->regs->usb_usbmr, mask & (~USB_E_IDLE_MASK));
 163 
 164         fhci_io_port_generate_reset(fhci);
 165 
 166         /* enable interrupt on this endpoint */
 167         out_be16(&fhci->regs->usb_usbmr, mask);
 168 
 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);
 173 
 174         fhci_dbg(fhci, "<- %s\n", __func__);
 175 }
 176 
 177 int fhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 178 {
 179         struct fhci_hcd *fhci = hcd_to_fhci(hcd);
 180         int ret = 0;
 181         unsigned long flags;
 182 
 183         fhci_dbg(fhci, "-> %s\n", __func__);
 184 
 185         spin_lock_irqsave(&fhci->lock, flags);
 186 
 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)) {
 190                 *buf = 1 << 1;
 191                 ret = 1;
 192                 fhci_dbg(fhci, "-- %s\n", __func__);
 193         }
 194 
 195         spin_unlock_irqrestore(&fhci->lock, flags);
 196 
 197         fhci_dbg(fhci, "<- %s\n", __func__);
 198 
 199         return ret;
 200 }
 201 
 202 int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 203                             u16 wIndex, char *buf, u16 wLength)
 204 {
 205         struct fhci_hcd *fhci = hcd_to_fhci(hcd);
 206         int retval = 0;
 207         struct usb_hub_status *hub_status;
 208         struct usb_port_status *port_status;
 209         unsigned long flags;
 210 
 211         spin_lock_irqsave(&fhci->lock, flags);
 212 
 213         fhci_dbg(fhci, "-> %s\n", __func__);
 214 
 215         switch (typeReq) {
 216         case ClearHubFeature:
 217                 switch (wValue) {
 218                 case C_HUB_LOCAL_POWER:
 219                 case C_HUB_OVER_CURRENT:
 220                         break;
 221                 default:
 222                         goto error;
 223                 }
 224                 break;
 225         case ClearPortFeature:
 226                 fhci->vroot_hub->feature &= (1 << wValue);
 227 
 228                 switch (wValue) {
 229                 case USB_PORT_FEAT_ENABLE:
 230                         fhci->vroot_hub->port.wPortStatus &=
 231                             ~USB_PORT_STAT_ENABLE;
 232                         fhci_port_disable(fhci);
 233                         break;
 234                 case USB_PORT_FEAT_C_ENABLE:
 235                         fhci->vroot_hub->port.wPortChange &=
 236                             ~USB_PORT_STAT_C_ENABLE;
 237                         break;
 238                 case USB_PORT_FEAT_SUSPEND:
 239                         fhci->vroot_hub->port.wPortStatus &=
 240                             ~USB_PORT_STAT_SUSPEND;
 241                         fhci_stop_sof_timer(fhci);
 242                         break;
 243                 case USB_PORT_FEAT_C_SUSPEND:
 244                         fhci->vroot_hub->port.wPortChange &=
 245                             ~USB_PORT_STAT_C_SUSPEND;
 246                         break;
 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);
 251                         break;
 252                 case USB_PORT_FEAT_C_CONNECTION:
 253                         fhci->vroot_hub->port.wPortChange &=
 254                             ~USB_PORT_STAT_C_CONNECTION;
 255                         break;
 256                 case USB_PORT_FEAT_C_OVER_CURRENT:
 257                         fhci->vroot_hub->port.wPortChange &=
 258                             ~USB_PORT_STAT_C_OVERCURRENT;
 259                         break;
 260                 case USB_PORT_FEAT_C_RESET:
 261                         fhci->vroot_hub->port.wPortChange &=
 262                             ~USB_PORT_STAT_C_RESET;
 263                         break;
 264                 default:
 265                         goto error;
 266                 }
 267                 break;
 268         case GetHubDescriptor:
 269                 memcpy(buf, root_hub_des, sizeof(root_hub_des));
 270                 break;
 271         case GetHubStatus:
 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);
 277                 break;
 278         case GetPortStatus:
 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);
 284                 break;
 285         case SetHubFeature:
 286                 switch (wValue) {
 287                 case C_HUB_OVER_CURRENT:
 288                 case C_HUB_LOCAL_POWER:
 289                         break;
 290                 default:
 291                         goto error;
 292                 }
 293                 break;
 294         case SetPortFeature:
 295                 fhci->vroot_hub->feature |= (1 << wValue);
 296 
 297                 switch (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);
 302                         break;
 303                 case USB_PORT_FEAT_SUSPEND:
 304                         fhci->vroot_hub->port.wPortStatus |=
 305                             USB_PORT_STAT_SUSPEND;
 306                         fhci_stop_sof_timer(fhci);
 307                         break;
 308                 case USB_PORT_FEAT_RESET:
 309                         fhci->vroot_hub->port.wPortStatus |=
 310                             USB_PORT_STAT_RESET;
 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;
 316                         break;
 317                 case USB_PORT_FEAT_POWER:
 318                         fhci->vroot_hub->port.wPortStatus |=
 319                             USB_PORT_STAT_POWER;
 320                         fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
 321                         break;
 322                 default:
 323                         goto error;
 324                 }
 325                 break;
 326         default:
 327 error:
 328                 retval = -EPIPE;
 329         }
 330 
 331         fhci_dbg(fhci, "<- %s\n", __func__);
 332 
 333         spin_unlock_irqrestore(&fhci->lock, flags);
 334 
 335         return retval;
 336 }

/* [<][>][^][v][top][bottom][index][help] */