linux/drivers/usb/host/fhci-hub.c
<<
>>
Prefs
   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 */
  27static 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
  39static 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
  51void 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 */
  79void 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 */
 109void 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
 128void 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 */
 146void 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
 177int 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
 202int 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:
 327error:
 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}
 337