linux/drivers/usb/musb/musb_virthub.c
<<
>>
Prefs
   1/*
   2 * MUSB OTG driver virtual root hub support
   3 *
   4 * Copyright 2005 Mentor Graphics Corporation
   5 * Copyright (C) 2005-2006 by Texas Instruments
   6 * Copyright (C) 2006-2007 Nokia Corporation
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * version 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA
  21 *
  22 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
  23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
  25 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  29 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32 *
  33 */
  34
  35#include <linux/module.h>
  36#include <linux/kernel.h>
  37#include <linux/sched.h>
  38#include <linux/errno.h>
  39#include <linux/init.h>
  40#include <linux/time.h>
  41#include <linux/timer.h>
  42
  43#include <asm/unaligned.h>
  44
  45#include "musb_core.h"
  46
  47static void musb_port_suspend(struct musb *musb, bool do_suspend)
  48{
  49        struct usb_otg  *otg = musb->xceiv->otg;
  50        u8              power;
  51        void __iomem    *mbase = musb->mregs;
  52
  53        if (!is_host_active(musb))
  54                return;
  55
  56        /* NOTE:  this doesn't necessarily put PHY into low power mode,
  57         * turning off its clock; that's a function of PHY integration and
  58         * MUSB_POWER_ENSUSPEND.  PHY may need a clock (sigh) to detect
  59         * SE0 changing to connect (J) or wakeup (K) states.
  60         */
  61        power = musb_readb(mbase, MUSB_POWER);
  62        if (do_suspend) {
  63                int retries = 10000;
  64
  65                power &= ~MUSB_POWER_RESUME;
  66                power |= MUSB_POWER_SUSPENDM;
  67                musb_writeb(mbase, MUSB_POWER, power);
  68
  69                /* Needed for OPT A tests */
  70                power = musb_readb(mbase, MUSB_POWER);
  71                while (power & MUSB_POWER_SUSPENDM) {
  72                        power = musb_readb(mbase, MUSB_POWER);
  73                        if (retries-- < 1)
  74                                break;
  75                }
  76
  77                dev_dbg(musb->controller, "Root port suspended, power %02x\n", power);
  78
  79                musb->port1_status |= USB_PORT_STAT_SUSPEND;
  80                switch (musb->xceiv->state) {
  81                case OTG_STATE_A_HOST:
  82                        musb->xceiv->state = OTG_STATE_A_SUSPEND;
  83                        musb->is_active = otg->host->b_hnp_enable;
  84                        if (musb->is_active)
  85                                mod_timer(&musb->otg_timer, jiffies
  86                                        + msecs_to_jiffies(
  87                                                OTG_TIME_A_AIDL_BDIS));
  88                        musb_platform_try_idle(musb, 0);
  89                        break;
  90                case OTG_STATE_B_HOST:
  91                        musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
  92                        musb->is_active = otg->host->b_hnp_enable;
  93                        musb_platform_try_idle(musb, 0);
  94                        break;
  95                default:
  96                        dev_dbg(musb->controller, "bogus rh suspend? %s\n",
  97                                usb_otg_state_string(musb->xceiv->state));
  98                }
  99        } else if (power & MUSB_POWER_SUSPENDM) {
 100                power &= ~MUSB_POWER_SUSPENDM;
 101                power |= MUSB_POWER_RESUME;
 102                musb_writeb(mbase, MUSB_POWER, power);
 103
 104                dev_dbg(musb->controller, "Root port resuming, power %02x\n", power);
 105
 106                /* later, GetPortStatus will stop RESUME signaling */
 107                musb->port1_status |= MUSB_PORT_STAT_RESUME;
 108                musb->rh_timer = jiffies + msecs_to_jiffies(20);
 109        }
 110}
 111
 112static void musb_port_reset(struct musb *musb, bool do_reset)
 113{
 114        u8              power;
 115        void __iomem    *mbase = musb->mregs;
 116
 117        if (musb->xceiv->state == OTG_STATE_B_IDLE) {
 118                dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n");
 119                musb->port1_status &= ~USB_PORT_STAT_RESET;
 120                return;
 121        }
 122
 123        if (!is_host_active(musb))
 124                return;
 125
 126        /* NOTE:  caller guarantees it will turn off the reset when
 127         * the appropriate amount of time has passed
 128         */
 129        power = musb_readb(mbase, MUSB_POWER);
 130        if (do_reset) {
 131
 132                /*
 133                 * If RESUME is set, we must make sure it stays minimum 20 ms.
 134                 * Then we must clear RESUME and wait a bit to let musb start
 135                 * generating SOFs. If we don't do this, OPT HS A 6.8 tests
 136                 * fail with "Error! Did not receive an SOF before suspend
 137                 * detected".
 138                 */
 139                if (power &  MUSB_POWER_RESUME) {
 140                        while (time_before(jiffies, musb->rh_timer))
 141                                msleep(1);
 142                        musb_writeb(mbase, MUSB_POWER,
 143                                power & ~MUSB_POWER_RESUME);
 144                        msleep(1);
 145                }
 146
 147                power &= 0xf0;
 148                musb_writeb(mbase, MUSB_POWER,
 149                                power | MUSB_POWER_RESET);
 150
 151                musb->port1_status |= USB_PORT_STAT_RESET;
 152                musb->port1_status &= ~USB_PORT_STAT_ENABLE;
 153                musb->rh_timer = jiffies + msecs_to_jiffies(50);
 154        } else {
 155                dev_dbg(musb->controller, "root port reset stopped\n");
 156                musb_writeb(mbase, MUSB_POWER,
 157                                power & ~MUSB_POWER_RESET);
 158
 159                power = musb_readb(mbase, MUSB_POWER);
 160                if (power & MUSB_POWER_HSMODE) {
 161                        dev_dbg(musb->controller, "high-speed device connected\n");
 162                        musb->port1_status |= USB_PORT_STAT_HIGH_SPEED;
 163                }
 164
 165                musb->port1_status &= ~USB_PORT_STAT_RESET;
 166                musb->port1_status |= USB_PORT_STAT_ENABLE
 167                                        | (USB_PORT_STAT_C_RESET << 16)
 168                                        | (USB_PORT_STAT_C_ENABLE << 16);
 169                usb_hcd_poll_rh_status(musb->hcd);
 170
 171                musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
 172        }
 173}
 174
 175void musb_root_disconnect(struct musb *musb)
 176{
 177        struct usb_otg  *otg = musb->xceiv->otg;
 178
 179        musb->port1_status = USB_PORT_STAT_POWER
 180                        | (USB_PORT_STAT_C_CONNECTION << 16);
 181
 182        usb_hcd_poll_rh_status(musb->hcd);
 183        musb->is_active = 0;
 184
 185        switch (musb->xceiv->state) {
 186        case OTG_STATE_A_SUSPEND:
 187                if (otg->host->b_hnp_enable) {
 188                        musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
 189                        musb->g.is_a_peripheral = 1;
 190                        break;
 191                }
 192                /* FALLTHROUGH */
 193        case OTG_STATE_A_HOST:
 194                musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
 195                musb->is_active = 0;
 196                break;
 197        case OTG_STATE_A_WAIT_VFALL:
 198                musb->xceiv->state = OTG_STATE_B_IDLE;
 199                break;
 200        default:
 201                dev_dbg(musb->controller, "host disconnect (%s)\n",
 202                        usb_otg_state_string(musb->xceiv->state));
 203        }
 204}
 205
 206
 207/*---------------------------------------------------------------------*/
 208
 209/* Caller may or may not hold musb->lock */
 210int musb_hub_status_data(struct usb_hcd *hcd, char *buf)
 211{
 212        struct musb     *musb = hcd_to_musb(hcd);
 213        int             retval = 0;
 214
 215        /* called in_irq() via usb_hcd_poll_rh_status() */
 216        if (musb->port1_status & 0xffff0000) {
 217                *buf = 0x02;
 218                retval = 1;
 219        }
 220        return retval;
 221}
 222
 223int musb_hub_control(
 224        struct usb_hcd  *hcd,
 225        u16             typeReq,
 226        u16             wValue,
 227        u16             wIndex,
 228        char            *buf,
 229        u16             wLength)
 230{
 231        struct musb     *musb = hcd_to_musb(hcd);
 232        u32             temp;
 233        int             retval = 0;
 234        unsigned long   flags;
 235
 236        spin_lock_irqsave(&musb->lock, flags);
 237
 238        if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) {
 239                spin_unlock_irqrestore(&musb->lock, flags);
 240                return -ESHUTDOWN;
 241        }
 242
 243        /* hub features:  always zero, setting is a NOP
 244         * port features: reported, sometimes updated when host is active
 245         * no indicators
 246         */
 247        switch (typeReq) {
 248        case ClearHubFeature:
 249        case SetHubFeature:
 250                switch (wValue) {
 251                case C_HUB_OVER_CURRENT:
 252                case C_HUB_LOCAL_POWER:
 253                        break;
 254                default:
 255                        goto error;
 256                }
 257                break;
 258        case ClearPortFeature:
 259                if ((wIndex & 0xff) != 1)
 260                        goto error;
 261
 262                switch (wValue) {
 263                case USB_PORT_FEAT_ENABLE:
 264                        break;
 265                case USB_PORT_FEAT_SUSPEND:
 266                        musb_port_suspend(musb, false);
 267                        break;
 268                case USB_PORT_FEAT_POWER:
 269                        if (!hcd->self.is_b_host)
 270                                musb_platform_set_vbus(musb, 0);
 271                        break;
 272                case USB_PORT_FEAT_C_CONNECTION:
 273                case USB_PORT_FEAT_C_ENABLE:
 274                case USB_PORT_FEAT_C_OVER_CURRENT:
 275                case USB_PORT_FEAT_C_RESET:
 276                case USB_PORT_FEAT_C_SUSPEND:
 277                        break;
 278                default:
 279                        goto error;
 280                }
 281                dev_dbg(musb->controller, "clear feature %d\n", wValue);
 282                musb->port1_status &= ~(1 << wValue);
 283                break;
 284        case GetHubDescriptor:
 285                {
 286                struct usb_hub_descriptor *desc = (void *)buf;
 287
 288                desc->bDescLength = 9;
 289                desc->bDescriptorType = 0x29;
 290                desc->bNbrPorts = 1;
 291                desc->wHubCharacteristics = cpu_to_le16(
 292                                  0x0001        /* per-port power switching */
 293                                | 0x0010        /* no overcurrent reporting */
 294                                );
 295                desc->bPwrOn2PwrGood = 5;       /* msec/2 */
 296                desc->bHubContrCurrent = 0;
 297
 298                /* workaround bogus struct definition */
 299                desc->u.hs.DeviceRemovable[0] = 0x02;   /* port 1 */
 300                desc->u.hs.DeviceRemovable[1] = 0xff;
 301                }
 302                break;
 303        case GetHubStatus:
 304                temp = 0;
 305                *(__le32 *) buf = cpu_to_le32(temp);
 306                break;
 307        case GetPortStatus:
 308                if (wIndex != 1)
 309                        goto error;
 310
 311                /* finish RESET signaling? */
 312                if ((musb->port1_status & USB_PORT_STAT_RESET)
 313                                && time_after_eq(jiffies, musb->rh_timer))
 314                        musb_port_reset(musb, false);
 315
 316                /* finish RESUME signaling? */
 317                if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
 318                                && time_after_eq(jiffies, musb->rh_timer)) {
 319                        u8              power;
 320
 321                        power = musb_readb(musb->mregs, MUSB_POWER);
 322                        power &= ~MUSB_POWER_RESUME;
 323                        dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
 324                                        power);
 325                        musb_writeb(musb->mregs, MUSB_POWER, power);
 326
 327                        /* ISSUE:  DaVinci (RTL 1.300) disconnects after
 328                         * resume of high speed peripherals (but not full
 329                         * speed ones).
 330                         */
 331
 332                        musb->is_active = 1;
 333                        musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
 334                                        | MUSB_PORT_STAT_RESUME);
 335                        musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
 336                        usb_hcd_poll_rh_status(musb->hcd);
 337                        /* NOTE: it might really be A_WAIT_BCON ... */
 338                        musb->xceiv->state = OTG_STATE_A_HOST;
 339                }
 340
 341                put_unaligned(cpu_to_le32(musb->port1_status
 342                                        & ~MUSB_PORT_STAT_RESUME),
 343                                (__le32 *) buf);
 344
 345                /* port change status is more interesting */
 346                dev_dbg(musb->controller, "port status %08x\n",
 347                                musb->port1_status);
 348                break;
 349        case SetPortFeature:
 350                if ((wIndex & 0xff) != 1)
 351                        goto error;
 352
 353                switch (wValue) {
 354                case USB_PORT_FEAT_POWER:
 355                        /* NOTE: this controller has a strange state machine
 356                         * that involves "requesting sessions" according to
 357                         * magic side effects from incompletely-described
 358                         * rules about startup...
 359                         *
 360                         * This call is what really starts the host mode; be
 361                         * very careful about side effects if you reorder any
 362                         * initialization logic, e.g. for OTG, or change any
 363                         * logic relating to VBUS power-up.
 364                         */
 365                        if (!hcd->self.is_b_host)
 366                                musb_start(musb);
 367                        break;
 368                case USB_PORT_FEAT_RESET:
 369                        musb_port_reset(musb, true);
 370                        break;
 371                case USB_PORT_FEAT_SUSPEND:
 372                        musb_port_suspend(musb, true);
 373                        break;
 374                case USB_PORT_FEAT_TEST:
 375                        if (unlikely(is_host_active(musb)))
 376                                goto error;
 377
 378                        wIndex >>= 8;
 379                        switch (wIndex) {
 380                        case 1:
 381                                pr_debug("TEST_J\n");
 382                                temp = MUSB_TEST_J;
 383                                break;
 384                        case 2:
 385                                pr_debug("TEST_K\n");
 386                                temp = MUSB_TEST_K;
 387                                break;
 388                        case 3:
 389                                pr_debug("TEST_SE0_NAK\n");
 390                                temp = MUSB_TEST_SE0_NAK;
 391                                break;
 392                        case 4:
 393                                pr_debug("TEST_PACKET\n");
 394                                temp = MUSB_TEST_PACKET;
 395                                musb_load_testpacket(musb);
 396                                break;
 397                        case 5:
 398                                pr_debug("TEST_FORCE_ENABLE\n");
 399                                temp = MUSB_TEST_FORCE_HOST
 400                                        | MUSB_TEST_FORCE_HS;
 401
 402                                musb_writeb(musb->mregs, MUSB_DEVCTL,
 403                                                MUSB_DEVCTL_SESSION);
 404                                break;
 405                        case 6:
 406                                pr_debug("TEST_FIFO_ACCESS\n");
 407                                temp = MUSB_TEST_FIFO_ACCESS;
 408                                break;
 409                        default:
 410                                goto error;
 411                        }
 412                        musb_writeb(musb->mregs, MUSB_TESTMODE, temp);
 413                        break;
 414                default:
 415                        goto error;
 416                }
 417                dev_dbg(musb->controller, "set feature %d\n", wValue);
 418                musb->port1_status |= 1 << wValue;
 419                break;
 420
 421        default:
 422error:
 423                /* "protocol stall" on error */
 424                retval = -EPIPE;
 425        }
 426        spin_unlock_irqrestore(&musb->lock, flags);
 427        return retval;
 428}
 429