int musb_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct musb *musb = hcd_to_musb(hcd); u32 temp; int retval = 0; unsigned long flags; spin_lock_irqsave(&musb->lock, flags); if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } /* hub features: always zero, setting is a NOP * port features: reported, sometimes updated when host is active * no indicators */ switch (typeReq) { case ClearHubFeature: case SetHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: break; default: goto error; } break; case ClearPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_ENABLE: break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, false); break; case USB_PORT_FEAT_POWER: if (!hcd->self.is_b_host) musb_platform_set_vbus(musb, 0); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_SUSPEND: break; default: goto error; } dev_dbg(musb->controller, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: { struct usb_hub_descriptor *desc = (void *)buf; desc->bDescLength = 9; desc->bDescriptorType = 0x29; desc->bNbrPorts = 1; desc->wHubCharacteristics = cpu_to_le16( 0x0001 /* per-port power switching */ | 0x0010 /* no overcurrent reporting */ ); desc->bPwrOn2PwrGood = 5; /* msec/2 */ desc->bHubContrCurrent = 0; /* workaround bogus struct definition */ desc->u.hs.DeviceRemovable[0] = 0x02; /* port 1 */ desc->u.hs.DeviceRemovable[1] = 0xff; } break; case GetHubStatus: temp = 0; *(__le32 *) buf = cpu_to_le32(temp); break; case GetPortStatus: if (wIndex != 1) goto error; put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME), (__le32 *) buf); /* port change status is more interesting */ dev_dbg(musb->controller, "port status %08x\n", musb->port1_status); break; case SetPortFeature: if ((wIndex & 0xff) != 1) goto error; switch (wValue) { case USB_PORT_FEAT_POWER: /* NOTE: this controller has a strange state machine * that involves "requesting sessions" according to * magic side effects from incompletely-described * rules about startup... * * This call is what really starts the host mode; be * very careful about side effects if you reorder any * initialization logic, e.g. for OTG, or change any * logic relating to VBUS power-up. */ if (!hcd->self.is_b_host && musb_has_gadget(musb)) musb_start(musb); break; case USB_PORT_FEAT_RESET: musb_port_reset(musb, true); break; case USB_PORT_FEAT_SUSPEND: musb_port_suspend(musb, true); break; case USB_PORT_FEAT_TEST: if (unlikely(is_host_active(musb))) goto error; wIndex >>= 8; switch (wIndex) { case 1: pr_debug("TEST_J\n"); temp = MUSB_TEST_J; break; case 2: pr_debug("TEST_K\n"); temp = MUSB_TEST_K; break; case 3: pr_debug("TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; break; case 4: pr_debug("TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_load_testpacket(musb); break; case 5: pr_debug("TEST_FORCE_ENABLE\n"); temp = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_HS; musb_writeb(musb->mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); break; case 6: pr_debug("TEST_FIFO_ACCESS\n"); temp = MUSB_TEST_FIFO_ACCESS; break; default: goto error; } musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; default: goto error; } dev_dbg(musb->controller, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; default: error: /* "protocol stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore(&musb->lock, flags); return retval; }
int musb_otg_exec_cmd(unsigned int cmd){ unsigned char devctl; unsigned char intrusb; unsigned short intrtx; unsigned char power; unsigned short csr0; unsigned int usb_l1intp; unsigned int usb_l1ints; unsigned int ret; unsigned long timeout; bool timeout_flag = false; if(!mtk_musb){ DBG(0,"mtk_musb is NULL,error!\n"); } switch(cmd){ case HOST_CMD_ENV_INIT: musb_otg_env_init(); return 0; case HOST_CMD_ENV_EXIT: musb_otg_env_exit (); return 0; } //init musb_writeb(mtk_musb->mregs, MUSB_POWER, 0x21); musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0); msleep(300); #ifdef DX_DBG devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); power = musb_readb (mtk_musb->mregs,MUSB_POWER); intrusb = musb_readb(mtk_musb->mregs,MUSB_INTRUSB); DBG(0,"1:cmd=%d,devctl=0x%x,power=0x%x,intrusb=0x%x\n",cmd,devctl,power,intrusb); #endif musb_writew(mtk_musb->mregs,MUSB_INTRRX,0xffff); musb_writew(mtk_musb->mregs,MUSB_INTRTX,0xffff); musb_writeb(mtk_musb->mregs,MUSB_INTRUSB,0xff); msleep(10); #ifdef DX_DBG devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); power = musb_readb (mtk_musb->mregs,MUSB_POWER); intrusb = musb_readb(mtk_musb->mregs,MUSB_INTRUSB); DBG(0,"2:cmd=%d,devctl=0x%x,power=0x%x,intrusb=0x%x\n",cmd,devctl,power,intrusb); #endif high_speed = false; g_exec = 1; DBG(0,"before exec:cmd=%d\n",cmd); switch(cmd){ //electrical case OTG_CMD_E_ENABLE_VBUS: DBG(0,"musb::enable VBUS!\n"); musb_otg_set_session (true); musb_platform_set_vbus(mtk_musb, 1); while(g_exec) msleep(100); musb_otg_set_session (false); musb_platform_set_vbus(mtk_musb, 0); break; case OTG_CMD_E_ENABLE_SRP: //need to clear session? DBG(0,"musb::enable srp!\n"); musb_otg_reset_usb(); USBPHY_WRITE8 (0x6c, 0x1); USBPHY_WRITE8 (0x6d, 0x1); musb_writeb(mtk_musb->mregs,0x7B,1); musb_otg_set_session (true); while(g_exec){ msleep(100); } musb_otg_set_session (false); break; case OTG_CMD_E_START_DET_SRP: //need as a A-device musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0); devctl = musb_readb (mtk_musb->mregs, MUSB_DEVCTL); while(g_exec&&(devctl & 0x18)){//VBUS[1:0] should be 0, it indicate below SessionEnd DBG(0,"musb::not below session end!\n"); msleep(100); devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); } while(g_exec&&(!(devctl & 0x10))){ DBG(0,"musb::not above session end!\n"); msleep(100); devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); } devctl |= MUSB_DEVCTL_SESSION; musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, devctl); while(g_exec) msleep(100); musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0); break; case OTG_CMD_E_START_DET_VBUS: usb_l1intp = musb_readl(mtk_musb->mregs,USB_L1INTP); usb_l1intp &= ~(1<<10); musb_writel(mtk_musb->mregs,USB_L1INTP,usb_l1intp); usb_l1ints = musb_readl(mtk_musb->mregs,USB_L1INTS); while((usb_l1ints&(1<<8))==0){ DBG(0,"musb::vbus is 0!\n"); msleep(100); usb_l1ints = musb_readl(mtk_musb->mregs,USB_L1INTS); } DBG(0,"musb::vbus is detected!\n"); power = musb_readb (mtk_musb->mregs,MUSB_POWER); power |= MUSB_POWER_SOFTCONN; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); while(g_exec) msleep(100); musb_writeb(mtk_musb->mregs, MUSB_POWER, 0x21); break; case OTG_CMD_P_B_UUT_TD59: is_td_59 = true; if(is_td_59) DBG(0, "TD5.9 will be tested!\n"); break; //protocal case OTG_CMD_P_A_UUT: DBG(0,"A-UUT starts...\n"); //polling the session req from B-OPT and start a new session device_enumed = false; TD_4_6: musb_otg_reset_usb(); DBG(0,"A-UUT reset success\n"); timeout = jiffies + 5*HZ; musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0); devctl = musb_readb (mtk_musb->mregs, MUSB_DEVCTL); while(g_exec&&(devctl & 0x18)){//VBUS[1:0] should be 0, it indicate below SessionEnd DBG(0,"musb::not below session end!\n"); msleep(100); if(time_after(jiffies,timeout)){ timeout_flag = true; break; } devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); } if(timeout_flag){ timeout_flag = false; musb_otg_reset_usb(); DBG(0,"timeout for below session end, after reset usb, devctl=0x%x\n",musb_readb(mtk_musb->mregs,MUSB_DEVCTL)); } DBG(0,"polling session request,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_SESSREQ); DBG(0,"polling session request,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; musb_otg_set_session(true);//session is set and VBUS will be out. #if 1 power = musb_readb(mtk_musb->mregs,MUSB_POWER); power &= ~MUSB_POWER_SOFTCONN; musb_writeb(mtk_musb->mregs,MUSB_POWER,power); #endif //polling the connect interrupt from B-OPT DBG(0,"polling connect interrupt,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_CONNECT); DBG(0,"polling connect interrupt,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; if(DEV_NOT_CONNECT == ret){ DBG(0,"device is not connected in 15s\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_RESPONSE; break; } DBG(0,"musb::connect interrupt is detected!\n"); msleep(100);//the test is fail beacuse the reset starts less than100 ms from the B-OPT connect. the IF test needs //reset the bus,check whether it is a hs device musb_h_reset();//should last for more than 50ms, TD.4.2 musb_h_enumerate(); //suspend the bus csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0,"after enum B-OPT,csr0=0x%x\n",csr0); musb_h_suspend(); //polling the disconnect interrupt from B-OPT, and remote wakeup(TD.4.8) DBG(0,"polling disconnect or remote wakeup,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_DISCONNECT|MUSB_INTR_RESUME); DBG(0,"polling disconnect or remote wakeup,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; if(MUSB_INTR_RESUME == ret){ //for TD4.8 musb_h_remote_wakeup(); //maybe need to access the B-OPT, get device descriptor if(g_exec) wait_for_completion (&stop_event); break; } //polling the reset interrupt from B-OPT if(!(ret & MUSB_INTR_RESET)){ DBG(0,"polling reset for B-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_RESET); DBG(0,"polling reset for B-OPT,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; if(DEV_NOT_RESET == ret){ if(g_exec) wait_for_completion (&stop_event); break; } } DBG(0,"after receive reset,devctl=0x%x,csr0=0x%x\n",musb_readb(mtk_musb->mregs, MUSB_DEVCTL),musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0)); //enumerate and polling the suspend interrupt form B-OPT do{ intrtx = musb_readw(mtk_musb->mregs, MUSB_INTRTX); mb(); musb_writew(mtk_musb->mregs, MUSB_INTRTX, intrtx); intrusb = musb_readb(mtk_musb->mregs, MUSB_INTRUSB); mb(); musb_writeb(mtk_musb->mregs, MUSB_INTRUSB,intrusb); if(intrtx || (intrusb&MUSB_INTR_SUSPEND)){ if(intrtx){ if(intrtx&0x1) musb_d_enumerated(); } if(intrusb){ if(intrusb&MUSB_INTR_SUSPEND){//maybe receive disconnect interrupt when the session is end if(device_enumed){ break;//return form the while loop } else{//TD.4.6 musb_d_soft_connect (false); goto TD_4_6; } } } } else wait_for_completion_timeout(&stop_event,1); } while(g_exec);//the enum will be repeated for 5 times if(!g_exec){ break;//return form the switch-case } DBG(0,"polling connect form B-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_CONNECT);//B-OPT will connect again 100ms after A disconnect DBG(0,"polling connect form B-OPT,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; musb_h_reset();//should reset bus again, TD.4.7 wait_for_completion (&stop_event); DBG(0,"the test as A-UUT is done\n"); break; case OTG_CMD_P_B_UUT: musb_otg_reset_usb(); //The B-UUT issues an SRP to start a session with the A-OPT musb_otg_set_session (true); //100ms after VBUS begins to decay the A-OPT powers VBUS timeout = jiffies + 5 * HZ; devctl = musb_readb (mtk_musb->mregs, MUSB_DEVCTL); while(((devctl & MUSB_DEVCTL_VBUS)>>MUSB_DEVCTL_VBUS_SHIFT)<0x3){ if(time_after(jiffies, timeout)){ timeout_flag = true; break; } msleep(100); devctl = musb_readb (mtk_musb->mregs,MUSB_DEVCTL); } if(timeout_flag){ DBG(0,"B-UUT set vbus timeout\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_RESPONSE; timeout_flag = false; break; } //After detecting the VBUS, B-UUT should connect to the A_OPT power = musb_readb(mtk_musb->mregs, MUSB_POWER); power |= MUSB_POWER_HSENAB; musb_writeb(mtk_musb->mregs, MUSB_POWER,power); //TD5_5: musb_d_soft_connect(true); device_enumed = false; //polling the reset single form the A-OPT DBG(0,"polling reset form A-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_RESET); DBG(0,"polling reset form A-OPT,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; power = musb_readb(mtk_musb->mregs,MUSB_POWER); if(power & MUSB_POWER_HSMODE){ high_speed = true; } else high_speed = false; //The A-OPT enumerates the B-UUT TD6_13: do{ intrtx = musb_readw(mtk_musb->mregs, MUSB_INTRTX); mb(); musb_writew(mtk_musb->mregs, MUSB_INTRTX,intrtx); intrusb = musb_readb(mtk_musb->mregs, MUSB_INTRUSB); mb(); musb_writeb(mtk_musb->mregs, MUSB_INTRUSB,intrusb); if(intrtx || (intrusb & 0xf7)){ if(intrtx){ //DBG(0,"B-enum,intrtx=0x%x\n",intrtx); if(intrtx&0x1) DBG(0,"ep0 interrupt\n"); musb_d_enumerated(); } if(intrusb){ if(intrusb & 0xf7) DBG(0,"B-enum,intrusb=0x%x,power=0x%x\n",intrusb,musb_readb(mtk_musb->mregs,MUSB_POWER)); if((device_enumed)&&(intrusb & MUSB_INTR_SUSPEND)){ DBG(0,"suspend interrupt is received,power=0x%x,devctl=0x%x\n",musb_readb(mtk_musb->mregs,MUSB_POWER),musb_readb(mtk_musb->mregs,MUSB_DEVCTL)); break; } } } else{ DBG(0,"power=0x%x,devctl=0x%x,intrtx=0x%x,intrusb=0x%x\n",musb_readb(mtk_musb->mregs,MUSB_POWER),musb_readb(mtk_musb->mregs,MUSB_DEVCTL),musb_readw(mtk_musb->mregs,MUSB_INTRTX),musb_readb(mtk_musb->mregs,MUSB_INTRUSB)); wait_for_completion_timeout (&stop_event,1); } } while(g_exec); if(!g_exec) break; DBG(0,"hnp start\n"); if(intrusb & MUSB_INTR_RESUME) goto TD6_13; if(!(intrusb & MUSB_INTR_CONNECT)){ //polling the connect from A-OPT, the UUT acts as host DBG(0,"polling connect or resume form A-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_CONNECT|MUSB_INTR_RESUME); DBG(0,"polling connect or resume form A-OPT,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; if(MUSB_INTR_RESUME == ret){ goto TD6_13; } if(DEV_HNP_TIMEOUT == ret){ DBG(0,"B-UUT HNP timeout\n"); devctl = musb_readb(mtk_musb->mregs,MUSB_DEVCTL); //DBG(0,"hnp timeout,power=0x%x,devctl=0x%x\n",musb_readb(mtk_musb->mregs,MUSB_POWER),devctl); devctl &= ~MUSB_DEVCTL_HR; musb_writeb(mtk_musb->mregs,MUSB_DEVCTL,devctl); if(is_td_59) g_otg_message.msg = OTG_MSG_DEV_NOT_RESPONSE; break; } } //reset the bus and check whether it is a hs device musb_h_reset(); musb_h_enumerate(); //suspend the bus musb_h_suspend(); //polling the disconnect interrupt from A-OPT DBG(0,"polling disconnect form A-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_DISCONNECT); DBG(0,"polling disconnect form A-OPT,done,ret=0x%x\n",ret); //DBG(0,"power=0x%x,devctl=0x%x,intrusb=0x%x\n",musb_readb(mtk_musb->mregs,MUSB_POWER),musb_readb(mtk_musb->mregs,MUSB_DEVCTL),musb_readb(mtk_musb->mregs,MUSB_INTRUSB)); if(TEST_IS_STOP == ret) break; DBG(0,"A-OPT is disconnected, UUT will be back to device\n"); if(!(ret & MUSB_INTR_RESET)){ musb_d_soft_connect(true); //polling the reset single form the A-OPT DBG(0,"polling reset form A-OPT,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_RESET); //musb_d_reset (); DBG(0,"polling reset form A-OPT,done,ret=0x%x\n",ret); if(TEST_IS_STOP == ret) break; } device_enumed = false; if(g_exec) goto TD6_13;//TD5_5 wait_for_completion(&stop_event); DBG(0,"test as B_UUT is done\n"); break; case HOST_CMD_TEST_SE0_NAK: case HOST_CMD_TEST_J: case HOST_CMD_TEST_K: case HOST_CMD_TEST_PACKET: case HOST_CMD_SUSPEND_RESUME: case HOST_CMD_GET_DESCRIPTOR: case HOST_CMD_SET_FEATURE: musb_host_test_mode(cmd); while(g_exec) msleep(100); break; } DBG(0,"musb_otg_exec_cmd--\n"); return 0; }
static void musb_id_pin_work(struct work_struct *data) { u8 devctl = 0; unsigned long flags; spin_lock_irqsave(&mtk_musb->lock, flags); musb_generic_disable(mtk_musb); spin_unlock_irqrestore(&mtk_musb->lock, flags); down(&mtk_musb->musb_lock); DBG(0, "work start, is_host=%d\n", mtk_musb->is_host); if(mtk_musb->in_ipo_off) { DBG(0, "do nothing due to in_ipo_off\n"); goto out; } mtk_musb ->is_host = musb_is_host(); DBG(0,"musb is as %s\n",mtk_musb->is_host?"host":"device"); switch_set_state((struct switch_dev *)&otg_state, mtk_musb->is_host); if (mtk_musb->is_host) { //setup fifo for host mode ep_config_from_table_for_host(mtk_musb); wake_lock(&mtk_musb->usb_lock); musb_platform_set_vbus(mtk_musb, 1); /* for no VBUS sensing IP*/ #if 1 /* wait VBUS ready */ msleep(100); /* clear session*/ devctl = musb_readb(mtk_musb->mregs,MUSB_DEVCTL); musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, (devctl&(~MUSB_DEVCTL_SESSION))); /* USB MAC OFF*/ /* VBUSVALID=0, AVALID=0, BVALID=0, SESSEND=1, IDDIG=X */ USBPHY_SET8(0x6c, 0x10); USBPHY_CLR8(0x6c, 0x2e); USBPHY_SET8(0x6d, 0x3e); DBG(0,"force PHY to idle, 0x6d=%x, 0x6c=%x\n",USBPHY_READ8(0x6d), USBPHY_READ8(0x6c)); /* wait */ msleep(5); /* restart session */ devctl = musb_readb(mtk_musb->mregs,MUSB_DEVCTL); musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, (devctl| MUSB_DEVCTL_SESSION)); /* USB MAC ONand Host Mode*/ /* VBUSVALID=1, AVALID=1, BVALID=1, SESSEND=0, IDDIG=0 */ USBPHY_CLR8(0x6c, 0x10); USBPHY_SET8(0x6c, 0x2c); USBPHY_SET8(0x6d, 0x3e); DBG(0,"force PHY to host mode, 0x6d=%x, 0x6c=%x\n",USBPHY_READ8(0x6d), USBPHY_READ8(0x6c)); #endif musb_start(mtk_musb); MUSB_HST_MODE(mtk_musb); switch_int_to_device(mtk_musb); } else { DBG(0,"devctl is %x\n",musb_readb(mtk_musb->mregs,MUSB_DEVCTL)); musb_writeb(mtk_musb->mregs,MUSB_DEVCTL,0); if (wake_lock_active(&mtk_musb->usb_lock)) wake_unlock(&mtk_musb->usb_lock); musb_platform_set_vbus(mtk_musb, 0); /* for no VBUS sensing IP */ #if 1 /* USB MAC OFF*/ /* VBUSVALID=0, AVALID=0, BVALID=0, SESSEND=1, IDDIG=X */ USBPHY_SET8(0x6c, 0x10); USBPHY_CLR8(0x6c, 0x2e); USBPHY_SET8(0x6d, 0x3e); DBG(0,"force PHY to idle, 0x6d=%x, 0x6c=%x\n", USBPHY_READ8(0x6d), USBPHY_READ8(0x6c)); #endif musb_stop(mtk_musb); //ALPS00849138 mtk_musb->xceiv->state = OTG_STATE_B_IDLE; MUSB_DEV_MODE(mtk_musb); switch_int_to_host(mtk_musb); } out: DBG(0, "work end, is_host=%d\n", mtk_musb->is_host); up(&mtk_musb->musb_lock); }
static int musb_host_test_mode(unsigned char cmd){ musb_platform_set_vbus(mtk_musb, 1); musb_otg_reset_usb (); host_test_mode(mtk_musb,cmd); return 0; }