162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MUSB OTG driver virtual root hub support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2005 Mentor Graphics Corporation 662306a36Sopenharmony_ci * Copyright (C) 2005-2006 by Texas Instruments 762306a36Sopenharmony_ci * Copyright (C) 2006-2007 Nokia Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/time.h> 1562306a36Sopenharmony_ci#include <linux/timer.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/unaligned.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "musb_core.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_civoid musb_host_finish_resume(struct work_struct *work) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct musb *musb; 2462306a36Sopenharmony_ci unsigned long flags; 2562306a36Sopenharmony_ci u8 power; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci musb = container_of(work, struct musb, finish_resume_work.work); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci power = musb_readb(musb->mregs, MUSB_POWER); 3262306a36Sopenharmony_ci power &= ~MUSB_POWER_RESUME; 3362306a36Sopenharmony_ci musb_dbg(musb, "root port resume stopped, power %02x", power); 3462306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_POWER, power); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* 3762306a36Sopenharmony_ci * ISSUE: DaVinci (RTL 1.300) disconnects after 3862306a36Sopenharmony_ci * resume of high speed peripherals (but not full 3962306a36Sopenharmony_ci * speed ones). 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci musb->is_active = 1; 4262306a36Sopenharmony_ci musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME); 4362306a36Sopenharmony_ci musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; 4462306a36Sopenharmony_ci usb_hcd_poll_rh_status(musb->hcd); 4562306a36Sopenharmony_ci /* NOTE: it might really be A_WAIT_BCON ... */ 4662306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_A_HOST); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint musb_port_suspend(struct musb *musb, bool do_suspend) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci u8 power; 5462306a36Sopenharmony_ci void __iomem *mbase = musb->mregs; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!is_host_active(musb)) 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* NOTE: this doesn't necessarily put PHY into low power mode, 6062306a36Sopenharmony_ci * turning off its clock; that's a function of PHY integration and 6162306a36Sopenharmony_ci * MUSB_POWER_ENSUSPEND. PHY may need a clock (sigh) to detect 6262306a36Sopenharmony_ci * SE0 changing to connect (J) or wakeup (K) states. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci power = musb_readb(mbase, MUSB_POWER); 6562306a36Sopenharmony_ci if (do_suspend) { 6662306a36Sopenharmony_ci int retries = 10000; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (power & MUSB_POWER_RESUME) 6962306a36Sopenharmony_ci return -EBUSY; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!(power & MUSB_POWER_SUSPENDM)) { 7262306a36Sopenharmony_ci power |= MUSB_POWER_SUSPENDM; 7362306a36Sopenharmony_ci musb_writeb(mbase, MUSB_POWER, power); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Needed for OPT A tests */ 7662306a36Sopenharmony_ci power = musb_readb(mbase, MUSB_POWER); 7762306a36Sopenharmony_ci while (power & MUSB_POWER_SUSPENDM) { 7862306a36Sopenharmony_ci power = musb_readb(mbase, MUSB_POWER); 7962306a36Sopenharmony_ci if (retries-- < 1) 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci musb_dbg(musb, "Root port suspended, power %02x", power); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci musb->port1_status |= USB_PORT_STAT_SUSPEND; 8762306a36Sopenharmony_ci switch (musb_get_state(musb)) { 8862306a36Sopenharmony_ci case OTG_STATE_A_HOST: 8962306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_A_SUSPEND); 9062306a36Sopenharmony_ci musb->is_active = musb->xceiv && 9162306a36Sopenharmony_ci musb->xceiv->otg->host->b_hnp_enable; 9262306a36Sopenharmony_ci if (musb->is_active) 9362306a36Sopenharmony_ci mod_timer(&musb->otg_timer, jiffies 9462306a36Sopenharmony_ci + msecs_to_jiffies( 9562306a36Sopenharmony_ci OTG_TIME_A_AIDL_BDIS)); 9662306a36Sopenharmony_ci musb_platform_try_idle(musb, 0); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case OTG_STATE_B_HOST: 9962306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_B_WAIT_ACON); 10062306a36Sopenharmony_ci musb->is_active = musb->xceiv && 10162306a36Sopenharmony_ci musb->xceiv->otg->host->b_hnp_enable; 10262306a36Sopenharmony_ci musb_platform_try_idle(musb, 0); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci musb_dbg(musb, "bogus rh suspend? %s", 10662306a36Sopenharmony_ci musb_otg_state_string(musb)); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } else if (power & MUSB_POWER_SUSPENDM) { 10962306a36Sopenharmony_ci power &= ~MUSB_POWER_SUSPENDM; 11062306a36Sopenharmony_ci power |= MUSB_POWER_RESUME; 11162306a36Sopenharmony_ci musb_writeb(mbase, MUSB_POWER, power); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci musb_dbg(musb, "Root port resuming, power %02x", power); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci musb->port1_status |= MUSB_PORT_STAT_RESUME; 11662306a36Sopenharmony_ci schedule_delayed_work(&musb->finish_resume_work, 11762306a36Sopenharmony_ci msecs_to_jiffies(USB_RESUME_TIMEOUT)); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid musb_port_reset(struct musb *musb, bool do_reset) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci u8 power; 12562306a36Sopenharmony_ci void __iomem *mbase = musb->mregs; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (musb_get_state(musb) == OTG_STATE_B_IDLE) { 12862306a36Sopenharmony_ci musb_dbg(musb, "HNP: Returning from HNP; no hub reset from b_idle"); 12962306a36Sopenharmony_ci musb->port1_status &= ~USB_PORT_STAT_RESET; 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (!is_host_active(musb)) 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* NOTE: caller guarantees it will turn off the reset when 13762306a36Sopenharmony_ci * the appropriate amount of time has passed 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci power = musb_readb(mbase, MUSB_POWER); 14062306a36Sopenharmony_ci if (do_reset) { 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * If RESUME is set, we must make sure it stays minimum 20 ms. 14362306a36Sopenharmony_ci * Then we must clear RESUME and wait a bit to let musb start 14462306a36Sopenharmony_ci * generating SOFs. If we don't do this, OPT HS A 6.8 tests 14562306a36Sopenharmony_ci * fail with "Error! Did not receive an SOF before suspend 14662306a36Sopenharmony_ci * detected". 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (power & MUSB_POWER_RESUME) { 14962306a36Sopenharmony_ci long remain = (unsigned long) musb->rh_timer - jiffies; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (musb->rh_timer > 0 && remain > 0) { 15262306a36Sopenharmony_ci /* take into account the minimum delay after resume */ 15362306a36Sopenharmony_ci schedule_delayed_work( 15462306a36Sopenharmony_ci &musb->deassert_reset_work, remain); 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci musb_writeb(mbase, MUSB_POWER, 15962306a36Sopenharmony_ci power & ~MUSB_POWER_RESUME); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Give the core 1 ms to clear MUSB_POWER_RESUME */ 16262306a36Sopenharmony_ci schedule_delayed_work(&musb->deassert_reset_work, 16362306a36Sopenharmony_ci msecs_to_jiffies(1)); 16462306a36Sopenharmony_ci return; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci power &= 0xf0; 16862306a36Sopenharmony_ci musb_writeb(mbase, MUSB_POWER, 16962306a36Sopenharmony_ci power | MUSB_POWER_RESET); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci musb->port1_status |= USB_PORT_STAT_RESET; 17262306a36Sopenharmony_ci musb->port1_status &= ~USB_PORT_STAT_ENABLE; 17362306a36Sopenharmony_ci schedule_delayed_work(&musb->deassert_reset_work, 17462306a36Sopenharmony_ci msecs_to_jiffies(50)); 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci musb_dbg(musb, "root port reset stopped"); 17762306a36Sopenharmony_ci musb_platform_pre_root_reset_end(musb); 17862306a36Sopenharmony_ci musb_writeb(mbase, MUSB_POWER, 17962306a36Sopenharmony_ci power & ~MUSB_POWER_RESET); 18062306a36Sopenharmony_ci musb_platform_post_root_reset_end(musb); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci power = musb_readb(mbase, MUSB_POWER); 18362306a36Sopenharmony_ci if (power & MUSB_POWER_HSMODE) { 18462306a36Sopenharmony_ci musb_dbg(musb, "high-speed device connected"); 18562306a36Sopenharmony_ci musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci musb->port1_status &= ~USB_PORT_STAT_RESET; 18962306a36Sopenharmony_ci musb->port1_status |= USB_PORT_STAT_ENABLE 19062306a36Sopenharmony_ci | (USB_PORT_STAT_C_RESET << 16) 19162306a36Sopenharmony_ci | (USB_PORT_STAT_C_ENABLE << 16); 19262306a36Sopenharmony_ci usb_hcd_poll_rh_status(musb->hcd); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci musb->vbuserr_retry = VBUSERR_RETRY_COUNT; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid musb_root_disconnect(struct musb *musb) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci musb->port1_status = USB_PORT_STAT_POWER 20162306a36Sopenharmony_ci | (USB_PORT_STAT_C_CONNECTION << 16); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci usb_hcd_poll_rh_status(musb->hcd); 20462306a36Sopenharmony_ci musb->is_active = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci switch (musb_get_state(musb)) { 20762306a36Sopenharmony_ci case OTG_STATE_A_SUSPEND: 20862306a36Sopenharmony_ci if (musb->xceiv && musb->xceiv->otg->host->b_hnp_enable) { 20962306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_A_PERIPHERAL); 21062306a36Sopenharmony_ci musb->g.is_a_peripheral = 1; 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci fallthrough; 21462306a36Sopenharmony_ci case OTG_STATE_A_HOST: 21562306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_A_WAIT_BCON); 21662306a36Sopenharmony_ci musb->is_active = 0; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case OTG_STATE_A_WAIT_VFALL: 21962306a36Sopenharmony_ci musb_set_state(musb, OTG_STATE_B_IDLE); 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci default: 22262306a36Sopenharmony_ci musb_dbg(musb, "host disconnect (%s)", 22362306a36Sopenharmony_ci musb_otg_state_string(musb)); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(musb_root_disconnect); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/*---------------------------------------------------------------------*/ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* Caller may or may not hold musb->lock */ 23262306a36Sopenharmony_ciint musb_hub_status_data(struct usb_hcd *hcd, char *buf) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct musb *musb = hcd_to_musb(hcd); 23562306a36Sopenharmony_ci int retval = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* called in_irq() via usb_hcd_poll_rh_status() */ 23862306a36Sopenharmony_ci if (musb->port1_status & 0xffff0000) { 23962306a36Sopenharmony_ci *buf = 0x02; 24062306a36Sopenharmony_ci retval = 1; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci return retval; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int musb_has_gadget(struct musb *musb) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * In host-only mode we start a connection right away. In OTG mode 24962306a36Sopenharmony_ci * we have to wait until we loaded a gadget. We don't really need a 25062306a36Sopenharmony_ci * gadget if we operate as a host but we should not start a session 25162306a36Sopenharmony_ci * as a device without a gadget or else we explode. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci#ifdef CONFIG_USB_MUSB_HOST 25462306a36Sopenharmony_ci return 1; 25562306a36Sopenharmony_ci#else 25662306a36Sopenharmony_ci return musb->port_mode == MUSB_HOST; 25762306a36Sopenharmony_ci#endif 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint musb_hub_control( 26162306a36Sopenharmony_ci struct usb_hcd *hcd, 26262306a36Sopenharmony_ci u16 typeReq, 26362306a36Sopenharmony_ci u16 wValue, 26462306a36Sopenharmony_ci u16 wIndex, 26562306a36Sopenharmony_ci char *buf, 26662306a36Sopenharmony_ci u16 wLength) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct musb *musb = hcd_to_musb(hcd); 26962306a36Sopenharmony_ci u32 temp; 27062306a36Sopenharmony_ci int retval = 0; 27162306a36Sopenharmony_ci unsigned long flags; 27262306a36Sopenharmony_ci bool start_musb = false; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { 27762306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 27862306a36Sopenharmony_ci return -ESHUTDOWN; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* hub features: always zero, setting is a NOP 28262306a36Sopenharmony_ci * port features: reported, sometimes updated when host is active 28362306a36Sopenharmony_ci * no indicators 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci switch (typeReq) { 28662306a36Sopenharmony_ci case ClearHubFeature: 28762306a36Sopenharmony_ci case SetHubFeature: 28862306a36Sopenharmony_ci switch (wValue) { 28962306a36Sopenharmony_ci case C_HUB_OVER_CURRENT: 29062306a36Sopenharmony_ci case C_HUB_LOCAL_POWER: 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci goto error; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case ClearPortFeature: 29762306a36Sopenharmony_ci if ((wIndex & 0xff) != 1) 29862306a36Sopenharmony_ci goto error; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci switch (wValue) { 30162306a36Sopenharmony_ci case USB_PORT_FEAT_ENABLE: 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 30462306a36Sopenharmony_ci musb_port_suspend(musb, false); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 30762306a36Sopenharmony_ci if (!hcd->self.is_b_host) 30862306a36Sopenharmony_ci musb_platform_set_vbus(musb, 0); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case USB_PORT_FEAT_C_CONNECTION: 31162306a36Sopenharmony_ci case USB_PORT_FEAT_C_ENABLE: 31262306a36Sopenharmony_ci case USB_PORT_FEAT_C_OVER_CURRENT: 31362306a36Sopenharmony_ci case USB_PORT_FEAT_C_RESET: 31462306a36Sopenharmony_ci case USB_PORT_FEAT_C_SUSPEND: 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci default: 31762306a36Sopenharmony_ci goto error; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci musb_dbg(musb, "clear feature %d", wValue); 32062306a36Sopenharmony_ci musb->port1_status &= ~(1 << wValue); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case GetHubDescriptor: 32362306a36Sopenharmony_ci { 32462306a36Sopenharmony_ci struct usb_hub_descriptor *desc = (void *)buf; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci desc->bDescLength = 9; 32762306a36Sopenharmony_ci desc->bDescriptorType = USB_DT_HUB; 32862306a36Sopenharmony_ci desc->bNbrPorts = 1; 32962306a36Sopenharmony_ci desc->wHubCharacteristics = cpu_to_le16( 33062306a36Sopenharmony_ci HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */ 33162306a36Sopenharmony_ci | HUB_CHAR_NO_OCPM /* no overcurrent reporting */ 33262306a36Sopenharmony_ci ); 33362306a36Sopenharmony_ci desc->bPwrOn2PwrGood = 5; /* msec/2 */ 33462306a36Sopenharmony_ci desc->bHubContrCurrent = 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* workaround bogus struct definition */ 33762306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */ 33862306a36Sopenharmony_ci desc->u.hs.DeviceRemovable[1] = 0xff; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case GetHubStatus: 34262306a36Sopenharmony_ci temp = 0; 34362306a36Sopenharmony_ci *(__le32 *) buf = cpu_to_le32(temp); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case GetPortStatus: 34662306a36Sopenharmony_ci if (wIndex != 1) 34762306a36Sopenharmony_ci goto error; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci put_unaligned(cpu_to_le32(musb->port1_status 35062306a36Sopenharmony_ci & ~MUSB_PORT_STAT_RESUME), 35162306a36Sopenharmony_ci (__le32 *) buf); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* port change status is more interesting */ 35462306a36Sopenharmony_ci musb_dbg(musb, "port status %08x", musb->port1_status); 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case SetPortFeature: 35762306a36Sopenharmony_ci if ((wIndex & 0xff) != 1) 35862306a36Sopenharmony_ci goto error; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci switch (wValue) { 36162306a36Sopenharmony_ci case USB_PORT_FEAT_POWER: 36262306a36Sopenharmony_ci /* NOTE: this controller has a strange state machine 36362306a36Sopenharmony_ci * that involves "requesting sessions" according to 36462306a36Sopenharmony_ci * magic side effects from incompletely-described 36562306a36Sopenharmony_ci * rules about startup... 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * This call is what really starts the host mode; be 36862306a36Sopenharmony_ci * very careful about side effects if you reorder any 36962306a36Sopenharmony_ci * initialization logic, e.g. for OTG, or change any 37062306a36Sopenharmony_ci * logic relating to VBUS power-up. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (!hcd->self.is_b_host && musb_has_gadget(musb)) 37362306a36Sopenharmony_ci start_musb = true; 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case USB_PORT_FEAT_RESET: 37662306a36Sopenharmony_ci musb_port_reset(musb, true); 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci case USB_PORT_FEAT_SUSPEND: 37962306a36Sopenharmony_ci musb_port_suspend(musb, true); 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case USB_PORT_FEAT_TEST: 38262306a36Sopenharmony_ci if (unlikely(is_host_active(musb))) 38362306a36Sopenharmony_ci goto error; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci wIndex >>= 8; 38662306a36Sopenharmony_ci switch (wIndex) { 38762306a36Sopenharmony_ci case USB_TEST_J: 38862306a36Sopenharmony_ci pr_debug("USB_TEST_J\n"); 38962306a36Sopenharmony_ci temp = MUSB_TEST_J; 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case USB_TEST_K: 39262306a36Sopenharmony_ci pr_debug("USB_TEST_K\n"); 39362306a36Sopenharmony_ci temp = MUSB_TEST_K; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case USB_TEST_SE0_NAK: 39662306a36Sopenharmony_ci pr_debug("USB_TEST_SE0_NAK\n"); 39762306a36Sopenharmony_ci temp = MUSB_TEST_SE0_NAK; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case USB_TEST_PACKET: 40062306a36Sopenharmony_ci pr_debug("USB_TEST_PACKET\n"); 40162306a36Sopenharmony_ci temp = MUSB_TEST_PACKET; 40262306a36Sopenharmony_ci musb_load_testpacket(musb); 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case USB_TEST_FORCE_ENABLE: 40562306a36Sopenharmony_ci pr_debug("USB_TEST_FORCE_ENABLE\n"); 40662306a36Sopenharmony_ci temp = MUSB_TEST_FORCE_HOST 40762306a36Sopenharmony_ci | MUSB_TEST_FORCE_HS; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_DEVCTL, 41062306a36Sopenharmony_ci MUSB_DEVCTL_SESSION); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci case 6: 41362306a36Sopenharmony_ci pr_debug("TEST_FIFO_ACCESS\n"); 41462306a36Sopenharmony_ci temp = MUSB_TEST_FIFO_ACCESS; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci goto error; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci musb_writeb(musb->mregs, MUSB_TESTMODE, temp); 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci default: 42262306a36Sopenharmony_ci goto error; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci musb_dbg(musb, "set feature %d", wValue); 42562306a36Sopenharmony_ci musb->port1_status |= 1 << wValue; 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_cierror: 43062306a36Sopenharmony_ci /* "protocol stall" on error */ 43162306a36Sopenharmony_ci retval = -EPIPE; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (start_musb) 43662306a36Sopenharmony_ci musb_start(musb); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return retval; 43962306a36Sopenharmony_ci} 440