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