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 <asm/qe.h>
  28#include "fhci.h"
  29
  30/* virtual root hub specific descriptor */
  31static u8 root_hub_des[] = {
  32        0x09, /* blength */
  33        0x29, /* bDescriptorType;hub-descriptor */
  34        0x01, /* bNbrPorts */
  35        0x00, /* wHubCharacteristics */
  36        0x00,
  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        int len = 0;
 212        struct usb_hub_status *hub_status;
 213        struct usb_port_status *port_status;
 214        unsigned long flags;
 215
 216        spin_lock_irqsave(&fhci->lock, flags);
 217
 218        fhci_dbg(fhci, "-> %s\n", __func__);
 219
 220        switch (typeReq) {
 221        case ClearHubFeature:
 222                switch (wValue) {
 223                case C_HUB_LOCAL_POWER:
 224                case C_HUB_OVER_CURRENT:
 225                        break;
 226                default:
 227                        goto error;
 228                }
 229                break;
 230        case ClearPortFeature:
 231                fhci->vroot_hub->feature &= (1 << wValue);
 232
 233                switch (wValue) {
 234                case USB_PORT_FEAT_ENABLE:
 235                        fhci->vroot_hub->port.wPortStatus &=
 236                            ~USB_PORT_STAT_ENABLE;
 237                        fhci_port_disable(fhci);
 238                        break;
 239                case USB_PORT_FEAT_C_ENABLE:
 240                        fhci->vroot_hub->port.wPortChange &=
 241                            ~USB_PORT_STAT_C_ENABLE;
 242                        break;
 243                case USB_PORT_FEAT_SUSPEND:
 244                        fhci->vroot_hub->port.wPortStatus &=
 245                            ~USB_PORT_STAT_SUSPEND;
 246                        fhci_stop_sof_timer(fhci);
 247                        break;
 248                case USB_PORT_FEAT_C_SUSPEND:
 249                        fhci->vroot_hub->port.wPortChange &=
 250                            ~USB_PORT_STAT_C_SUSPEND;
 251                        break;
 252                case USB_PORT_FEAT_POWER:
 253                        fhci->vroot_hub->port.wPortStatus &=
 254                            ~USB_PORT_STAT_POWER;
 255                        fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF);
 256                        break;
 257                case USB_PORT_FEAT_C_CONNECTION:
 258                        fhci->vroot_hub->port.wPortChange &=
 259                            ~USB_PORT_STAT_C_CONNECTION;
 260                        break;
 261                case USB_PORT_FEAT_C_OVER_CURRENT:
 262                        fhci->vroot_hub->port.wPortChange &=
 263                            ~USB_PORT_STAT_C_OVERCURRENT;
 264                        break;
 265                case USB_PORT_FEAT_C_RESET:
 266                        fhci->vroot_hub->port.wPortChange &=
 267                            ~USB_PORT_STAT_C_RESET;
 268                        break;
 269                default:
 270                        goto error;
 271                }
 272                break;
 273        case GetHubDescriptor:
 274                memcpy(buf, root_hub_des, sizeof(root_hub_des));
 275                buf[3] = 0x11; /* per-port power, no ovrcrnt */
 276                len = (buf[0] < wLength) ? buf[0] : wLength;
 277                break;
 278        case GetHubStatus:
 279                hub_status = (struct usb_hub_status *)buf;
 280                hub_status->wHubStatus =
 281                    cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
 282                hub_status->wHubChange =
 283                    cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
 284                len = 4;
 285                break;
 286        case GetPortStatus:
 287                port_status = (struct usb_port_status *)buf;
 288                port_status->wPortStatus =
 289                    cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
 290                port_status->wPortChange =
 291                    cpu_to_le16(fhci->vroot_hub->port.wPortChange);
 292                len = 4;
 293                break;
 294        case SetHubFeature:
 295                switch (wValue) {
 296                case C_HUB_OVER_CURRENT:
 297                case C_HUB_LOCAL_POWER:
 298                        break;
 299                default:
 300                        goto error;
 301                }
 302                break;
 303        case SetPortFeature:
 304                fhci->vroot_hub->feature |= (1 << wValue);
 305
 306                switch (wValue) {
 307                case USB_PORT_FEAT_ENABLE:
 308                        fhci->vroot_hub->port.wPortStatus |=
 309                            USB_PORT_STAT_ENABLE;
 310                        fhci_port_enable(fhci->usb_lld);
 311                        break;
 312                case USB_PORT_FEAT_SUSPEND:
 313                        fhci->vroot_hub->port.wPortStatus |=
 314                            USB_PORT_STAT_SUSPEND;
 315                        fhci_stop_sof_timer(fhci);
 316                        break;
 317                case USB_PORT_FEAT_RESET:
 318                        fhci->vroot_hub->port.wPortStatus |=
 319                            USB_PORT_STAT_RESET;
 320                        fhci_port_reset(fhci->usb_lld);
 321                        fhci->vroot_hub->port.wPortStatus |=
 322                            USB_PORT_STAT_ENABLE;
 323                        fhci->vroot_hub->port.wPortStatus &=
 324                            ~USB_PORT_STAT_RESET;
 325                        break;
 326                case USB_PORT_FEAT_POWER:
 327                        fhci->vroot_hub->port.wPortStatus |=
 328                            USB_PORT_STAT_POWER;
 329                        fhci_config_transceiver(fhci, FHCI_PORT_WAITING);
 330                        break;
 331                default:
 332                        goto error;
 333                }
 334                break;
 335        default:
 336error:
 337                retval = -EPIPE;
 338        }
 339
 340        fhci_dbg(fhci, "<- %s\n", __func__);
 341
 342        spin_unlock_irqrestore(&fhci->lock, flags);
 343
 344        return retval;
 345}
 346