/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "musb_core.h" #ifdef CONFIG_OF #include #endif #define DRIVER_AUTHOR "Mediatek" #define DRIVER_DESC "driver for OTG USB-IF test" #define MUSB_OTG_CSR0 0x102 #define MUSB_OTG_COUNT0 0x108 #define TEST_DRIVER_NAME "mt_otg_test" #define DX_DBG #define TEST_IS_STOP 0xfff1 #define DEV_NOT_CONNECT 0xfff2 #define DEV_HNP_TIMEOUT 0xfff3 #define DEV_NOT_RESET 0xfff4 MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); /*for USB-IF OTG test*/ /* * when this func is called in EM, it will reset the USB hw. * and tester should not connet the uut to PC or connect a A-cable to it * macro for USB-IF for OTG driver */ #define OTG_CMD_E_ENABLE_VBUS 0x00 #define OTG_CMD_E_ENABLE_SRP 0x01 #define OTG_CMD_E_START_DET_SRP 0x02 #define OTG_CMD_E_START_DET_VBUS 0x03 #define OTG_CMD_P_A_UUT 0x04 #define OTG_CMD_P_B_UUT 0x05 #define HOST_CMD_TEST_SE0_NAK 0x6 #define HOST_CMD_TEST_J 0x7 #define HOST_CMD_TEST_K 0x8 #define HOST_CMD_TEST_PACKET 0x9 #define HOST_CMD_SUSPEND_RESUME 0xa #define HOST_CMD_GET_DESCRIPTOR 0xb #define HOST_CMD_SET_FEATURE 0xc #define OTG_CMD_P_B_UUT_TD59 0xd #define HOST_CMD_ENV_INIT 0xe #define HOST_CMD_ENV_EXIT 0xf #define OTG_MSG_DEV_NOT_SUPPORT 0x01 #define OTG_MSG_DEV_NOT_RESPONSE 0x02 #define OTG_MSG_HUB_NOT_SUPPORT 0x03 #define OTG_STOP_CMD 0x10 #define OTG_INIT_MSG 0x20 struct otg_message { spinlock_t lock; unsigned int msg; }; static struct otg_message g_otg_message; static atomic_t g_exec; unsigned long usb_l1intm_store; unsigned short usb_intrrxe_store; unsigned short usb_intrtxe_store; unsigned char usb_intrusbe_store; unsigned long pericfg_base; bool device_enumed; bool set_hnp; bool high_speed; bool is_td_59; struct completion stop_event; void musb_otg_reset_usb(void) { /* reset all of the USB IP, including PHY and MAC */ unsigned int usb_reset; usb_reset = __raw_readl((void __iomem *)pericfg_base); usb_reset |= 1 << 29; __raw_writel(usb_reset, (void __iomem *)pericfg_base); mdelay(10); usb_reset &= ~(1 << 29); __raw_writel(usb_reset, (void __iomem *)pericfg_base); /* power on the USB */ usb_phy_poweron(); /* enable interrupt */ musb_writel(mtk_musb->mregs, USB_L1INTM, 0x105); musb_writew(mtk_musb->mregs, MUSB_INTRTXE, 1); musb_writeb(mtk_musb->mregs, MUSB_INTRUSBE, 0xf7); } int musb_otg_env_init(void) { u8 power; /* u8 intrusb; */ /* step1: mask the PMU/PMIC EINT */ mtk_musb->usb_if = true; /* workaround for PMIC charger detection */ mtk_musb->is_host = true; /* mt65xx_eint_mask(EINT_CHR_DET_NUM); */ pmic_chrdet_int_en(0); mt_usb_init_drvvbus(); /* step5: make sure to power on the USB module */ if (mtk_musb->power) mtk_musb->power = FALSE; musb_platform_enable(mtk_musb); /* step6: clear session bit */ musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, 0); /* step7: disable and enable usb interrupt */ usb_l1intm_store = musb_readl(mtk_musb->mregs, USB_L1INTM); usb_intrrxe_store = musb_readw(mtk_musb->mregs, MUSB_INTRRXE); usb_intrtxe_store = musb_readw(mtk_musb->mregs, MUSB_INTRTXE); usb_intrusbe_store = musb_readb(mtk_musb->mregs, MUSB_INTRUSBE); musb_writel(mtk_musb->mregs, USB_L1INTM, 0); musb_writew(mtk_musb->mregs, MUSB_INTRRXE, 0); musb_writew(mtk_musb->mregs, MUSB_INTRTXE, 0); musb_writeb(mtk_musb->mregs, MUSB_INTRUSBE, 0); musb_writew(mtk_musb->mregs, MUSB_INTRRX, 0xffff); musb_writew(mtk_musb->mregs, MUSB_INTRTX, 0xffff); musb_writeb(mtk_musb->mregs, MUSB_INTRUSB, 0xff); free_irq(mtk_musb->nIrq, mtk_musb); musb_writel(mtk_musb->mregs, USB_L1INTM, 0x105); musb_writew(mtk_musb->mregs, MUSB_INTRTXE, 1); musb_writeb(mtk_musb->mregs, MUSB_INTRUSBE, 0xf7); /* setp8: set the index to 0 for ep0, maybe no need. * Designers said it is better not to use the index register. */ musb_writeb(mtk_musb->mregs, MUSB_INDEX, 0); /* setp9: init message */ g_otg_message.msg = 0; spin_lock_init(&g_otg_message.lock); init_completion(&stop_event); #ifdef DX_DBG power = musb_readb(mtk_musb->mregs, MUSB_POWER); DBG(0, "start the USB-IF test in EM,power=0x%x!\n", power); #endif return 0; } int musb_otg_env_exit(void) { DBG(0, "stop the USB-IF test in EM!\n"); musb_writel(mtk_musb->mregs, USB_L1INTM, 0); musb_writew(mtk_musb->mregs, MUSB_INTRRXE, 0); musb_writew(mtk_musb->mregs, MUSB_INTRTXE, 0); musb_writeb(mtk_musb->mregs, MUSB_INTRUSBE, 0); musb_writew(mtk_musb->mregs, MUSB_INTRRX, 0xffff); musb_writew(mtk_musb->mregs, MUSB_INTRTX, 0xffff); musb_writeb(mtk_musb->mregs, MUSB_INTRUSB, 0xff); musb_writel(mtk_musb->mregs, USB_L1INTM, usb_l1intm_store); musb_writew(mtk_musb->mregs, MUSB_INTRRXE, usb_intrrxe_store); musb_writew(mtk_musb->mregs, MUSB_INTRTXE, usb_intrtxe_store); musb_writeb(mtk_musb->mregs, MUSB_INTRUSBE, usb_intrusbe_store); mtk_musb->usb_if = false; mtk_musb->is_host = false; pmic_chrdet_int_en(1); return 0; } void musb_otg_write_fifo(u16 len, u8 *buf) { int i; DBG(0, "%s,len=%d\n", __func__, len); for (i = 0; i < len; i++) musb_writeb(mtk_musb->mregs, 0x20, *(buf + i)); } void musb_otg_read_fifo(u16 len, u8 *buf) { int i; DBG(0, "%s,len=%d\n", __func__, len); for (i = 0; i < len; i++) *(buf + i) = musb_readb(mtk_musb->mregs, 0x20); } unsigned int musb_polling_ep0_interrupt(void) { unsigned short intrtx; DBG(0, "polling ep0 interrupt\n"); do { intrtx = musb_readw(mtk_musb->mregs, MUSB_INTRTX); /* sync status */ mb(); musb_writew(mtk_musb->mregs, MUSB_INTRTX, intrtx); if (intrtx & 0x1) { /* ep0 interrupt happen */ DBG(0, "get ep0 interrupt,csr0=0x%x\n", musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0)); break; } DBG(0, "polling ep0 interrupt,csr0=0x%x\n", musb_readb(mtk_musb->mregs, MUSB_OTG_CSR0)); wait_for_completion_timeout(&stop_event, 1); if (atomic_read(&g_exec) == 0) return TEST_IS_STOP; } while (atomic_read(&g_exec) == 1); return 0; } void musb_h_setup(struct usb_ctrlrequest *setup) { unsigned short csr0; DBG(0, "%s++\n", __func__); musb_otg_write_fifo(sizeof(struct usb_ctrlrequest), (u8 *) setup); csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0, "%s,csr0=0x%x\n", __func__, csr0); csr0 |= MUSB_CSR0_H_SETUPPKT | MUSB_CSR0_TXPKTRDY; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); /* polling the Tx interrupt */ if (musb_polling_ep0_interrupt()) return; DBG(0, "%s--\n", __func__); } void musb_h_in_data(unsigned char *buf, u16 len) { /* will receive all of the data in this transfer. */ unsigned short csr0; u16 received = 0; bool bshort = false; DBG(0, "%s++\n", __func__); while ((received < len) && (!bshort)) { csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); csr0 |= MUSB_CSR0_H_REQPKT; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0, "csr0 = 0x%x!\n", csr0); if (csr0 & MUSB_CSR0_RXPKTRDY) { /* get the data from ep fifo */ u8 count = musb_readb(mtk_musb->mregs, MUSB_OTG_COUNT0); if (count < 64) bshort = true; if (received + count > len) { DBG(0, "Data is too large\n"); /* read FIFO until data end (maximum size of len) */ musb_otg_read_fifo(len - received, buf); } else { musb_otg_read_fifo(count, buf + received); } received += count; csr0 &= ~MUSB_CSR0_RXPKTRDY; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); } else DBG(0, "error, not receive the rxpktrdy interrupt!\n"); DBG(0, "%s--\n", __func__); } } void musb_h_in_status(void) { unsigned short csr0; DBG(0, "%s++\n", __func__); csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); csr0 |= MUSB_CSR0_H_REQPKT | MUSB_CSR0_H_STATUSPKT; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0, "csr0 = 0x%x!\n", csr0); if (csr0 & MUSB_CSR0_RXPKTRDY) { csr0 &= ~MUSB_CSR0_RXPKTRDY; /* whether this bit will be cleared auto, * need to clear by sw?? */ if (csr0 & MUSB_CSR0_H_STATUSPKT) csr0 &= ~MUSB_CSR0_H_STATUSPKT; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); } else if (csr0 & MUSB_CSR0_H_RXSTALL) { DBG(0, "stall!\n"); if (set_hnp) { DBG(0, "will pop up:DEV_NOT_RESPONSE!\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_RESPONSE; set_hnp = false; msleep(1000); } } DBG(0, "%s--\n", __func__); } void musb_h_out_status(void) { unsigned short csr0; DBG(0, "%s++\n", __func__); csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); csr0 |= MUSB_CSR0_H_STATUSPKT | MUSB_CSR0_TXPKTRDY; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; #ifdef DX_DBG csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0, "csr0 = 0x%x!\n", csr0); #endif DBG(0, "%s--\n", __func__); } void musb_d_reset(void) { unsigned short swrst; swrst = musb_readw(mtk_musb->mregs, 0x74); swrst |= 0x2; musb_writew(mtk_musb->mregs, 0x74, swrst); } void musb_d_setup(struct usb_ctrlrequest *setup_packet, u16 len) { musb_otg_read_fifo(len, (u8 *) setup_packet); DBG(0, "receive setup packet:0x%x 0x%x 0x%x 0x%x 0x%x\n", setup_packet->bRequest, setup_packet->bRequestType, setup_packet->wIndex, setup_packet->wValue, setup_packet->wLength); } void musb_d_out_data(struct usb_ctrlrequest *setup_packet) { unsigned short csr0; static struct usb_device_descriptor device_descriptor = { 0x12, 0x01, 0x0200, 0x00, 0x00, 0x00, 0x40, 0x0951, 0x1603, 0x0200, 0x01, 0x02, 0x03, 0x01 }; static struct usb_config_descriptor configuration_descriptor = { 0x09, 0x02, 0x0023, 0x01, 0x01, 0x00, 0x80, 0x32 }; static struct usb_interface_descriptor interface_descriptor = { 0x09, 0x04, 0x00, 0x00, 0x02, 0x08, 0x06, 0x50, 0x00 }; static struct usb_endpoint_descriptor endpoint_descriptor_in = { 0x07, 0x05, 0x81, 0x02, 0x0200, 0x00 }; static struct usb_endpoint_descriptor endpoint_descriptor_out = { 0x07, 0x05, 0x02, 0x02, 0x0200, 0x00 }; static struct usb_otg_descriptor usb_otg_descriptor = { 0x03, 0x09, 0x03 }; if (setup_packet->wValue == 0x0100) { musb_otg_write_fifo(sizeof(struct usb_device_descriptor), (u8 *) &device_descriptor); } else if (setup_packet->wValue == 0x0200) { if (setup_packet->wLength == 9) { musb_otg_write_fifo( sizeof(struct usb_config_descriptor), (u8 *) &configuration_descriptor); } else { musb_otg_write_fifo( sizeof(struct usb_config_descriptor), (u8 *) &configuration_descriptor); musb_otg_write_fifo( sizeof(struct usb_interface_descriptor), (u8 *) &interface_descriptor); musb_otg_write_fifo(7, (u8 *) &endpoint_descriptor_in); musb_otg_write_fifo(7, (u8 *) &endpoint_descriptor_out); musb_otg_write_fifo(sizeof(struct usb_otg_descriptor), (u8 *) &usb_otg_descriptor); } } csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); csr0 |= MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND; musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; } unsigned int musb_polling_bus_interrupt(unsigned int intr) { unsigned char intrusb; unsigned long timeout; if (intr == MUSB_INTR_CONNECT) timeout = jiffies + 15 * HZ; if (intr == (MUSB_INTR_CONNECT | MUSB_INTR_RESUME)) timeout = jiffies + 1; if (intr == MUSB_INTR_RESET) timeout = jiffies + 2 * HZ; do { intrusb = musb_readb(mtk_musb->mregs, MUSB_INTRUSB); /* sync status */ mb(); musb_writeb(mtk_musb->mregs, MUSB_INTRUSB, intrusb); if (intrusb & intr) { DBG(0, "interrupt happen, intrusb=0x%x, intr=0x%x\n", intrusb, intr); break; } /* check the timeout */ if ((intr == MUSB_INTR_CONNECT) && time_after(jiffies, timeout)) { DBG(0, "time out for MUSB_INTR_CONNECT\n"); return DEV_NOT_CONNECT; } if ((intr == (MUSB_INTR_CONNECT | MUSB_INTR_RESUME)) && time_after(jiffies, timeout)) { DBG(0, "time out for MUSB_INTR_CONNECT|MUSB_INTR_RESUME\n"); return DEV_HNP_TIMEOUT; } if ((intr == MUSB_INTR_RESET) && time_after(jiffies, timeout)) { DBG(0, "time out for MUSB_INTR_RESET\n"); return DEV_NOT_RESET; } /* delay for the interrupt */ if (intr != MUSB_INTR_RESET) { wait_for_completion_timeout(&stop_event, 1); if (atomic_read(&g_exec) == 0) break; } } while (atomic_read(&g_exec) == 1); if (atomic_read(&g_exec) == 0) { DBG(0, "TEST_IS_STOP\n"); return TEST_IS_STOP; } if (intrusb & MUSB_INTR_RESUME) { /* for TD.4.8, remote wakeup */ DBG(0, "MUSB_INTR_RESUME\n"); return MUSB_INTR_RESUME; } else { return intrusb; } } void musb_h_suspend(void) { unsigned char power; /* before suspend, should to send SOF for a while (USB-IF plan need) */ /* mdelay(100); */ power = musb_readb(mtk_musb->mregs, MUSB_POWER); DBG(0, "before suspend,power=0x%x\n", power); if (high_speed) power = 0x63; else power = 0x43; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); } void musb_h_remote_wakeup(void) { unsigned char power; msleep(25); power = musb_readb(mtk_musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); } bool musb_h_reset(void) { unsigned char power; power = musb_readb(mtk_musb->mregs, MUSB_POWER); power |= MUSB_POWER_RESET | MUSB_POWER_HSENAB; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); msleep(60); power &= ~MUSB_POWER_RESET; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); power = musb_readb(mtk_musb->mregs, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { DBG(0, "the device is a hs device!\n"); high_speed = true; return true; } DBG(0, "the device is a fs device!\n"); high_speed = false; return false; } void musb_d_soft_connect(bool connect) { unsigned char power; power = musb_readb(mtk_musb->mregs, MUSB_POWER); if (connect) power |= MUSB_POWER_SOFTCONN; else power &= ~MUSB_POWER_SOFTCONN; musb_writeb(mtk_musb->mregs, MUSB_POWER, power); } void musb_otg_set_session(bool set) { unsigned char devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL); if (set) devctl |= MUSB_DEVCTL_SESSION; else devctl &= ~MUSB_DEVCTL_SESSION; musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, devctl); } void musb_h_enumerate(void) { struct usb_ctrlrequest setup_packet; struct usb_device_descriptor device_descriptor; struct usb_config_descriptor configuration_descriptor; struct usb_otg_descriptor *otg_descriptor; unsigned char descriptor[65535]; /* set address */ musb_writew(mtk_musb->mregs, MUSB_TXFUNCADDR, 0); setup_packet.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_SET_ADDRESS; setup_packet.wIndex = 0; setup_packet.wValue = 1; setup_packet.wLength = 0; musb_h_setup(&setup_packet); musb_h_in_status(); musb_writew(mtk_musb->mregs, MUSB_TXFUNCADDR, 1); DBG(0, "set address OK!\n"); /* get device descriptor */ setup_packet.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; setup_packet.wIndex = 0; setup_packet.wValue = 0x0100; setup_packet.wLength = 0x40; musb_h_setup(&setup_packet); musb_h_in_data((char *)&device_descriptor, sizeof(struct usb_device_descriptor)); musb_h_out_status(); if (device_descriptor.idProduct == 0x1234) { pr_debug("device pid not match!\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_SUPPORT; /* msleep(1000); */ } DBG(0, "get device descriptor OK!device class=0x%x PID=0x%x VID=0x%x\n", device_descriptor.bDeviceClass, device_descriptor.idProduct, device_descriptor.idVendor); DBG(0, "get device descriptor OK!DescriptorType=0x%x DeviceSubClass=0x%x\n", device_descriptor.bDescriptorType, device_descriptor.bDeviceSubClass); /* get configuration descriptor */ setup_packet.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; setup_packet.wIndex = 0; setup_packet.wValue = 0x0200; setup_packet.wLength = 0x9; musb_h_setup(&setup_packet); musb_h_in_data((char *)&configuration_descriptor, sizeof(struct usb_config_descriptor)); musb_h_out_status(); DBG(0, "get configuration descriptor OK!\n"); /* get all configuration descriptor */ setup_packet.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; setup_packet.wIndex = 0; setup_packet.wValue = 0x0200; setup_packet.wLength = configuration_descriptor.wTotalLength; musb_h_setup(&setup_packet); /* * According to USB specification, * the maximum length of wTotalLength is 65535 bytes */ if (configuration_descriptor.wTotalLength <= sizeof(descriptor)) musb_h_in_data(descriptor, configuration_descriptor.wTotalLength); musb_h_out_status(); DBG(0, "get all configuration descriptor OK!\n"); /* get otg descriptor */ otg_descriptor = (struct usb_otg_descriptor *) (descriptor + configuration_descriptor.wTotalLength - 3); DBG(0, "otg descriptor::bLegth=%d,bDescriptorTye=%d,bmAttr=%d\n", otg_descriptor->bLength, otg_descriptor->bDescriptorType, otg_descriptor->bmAttributes); if (otg_descriptor->bLength == 3 && otg_descriptor->bDescriptorType == 9) { DBG(0, "get an otg descriptor!\n"); } else { DBG(0, "not an otg device, will pop Unsupported Device\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_SUPPORT; msleep(1000); } /* set hnp, need before set_configuration */ set_hnp = true; setup_packet.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_SET_FEATURE; setup_packet.wIndex = 0; setup_packet.wValue = 0x3; /* b_hnp_enable */ setup_packet.wLength = 0; musb_h_setup(&setup_packet); musb_h_in_status(); DBG(0, "set hnp OK!\n"); /* set configuration */ setup_packet.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_SET_CONFIGURATION; setup_packet.wIndex = 0; setup_packet.wValue = configuration_descriptor.iConfiguration; setup_packet.wLength = 0; musb_h_setup(&setup_packet); musb_h_in_status(); DBG(0, "set configuration OK!\n"); } void musb_d_enumerated(void) { unsigned char devctl; unsigned short csr0 = musb_readw(mtk_musb->mregs, MUSB_OTG_CSR0); DBG(0, "csr0=0x%x\n", csr0); if (csr0 & MUSB_CSR0_P_SETUPEND) { DBG(0, "SETUPEND\n"); csr0 |= MUSB_CSR0_P_SVDSETUPEND; musb_writeb(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); csr0 &= ~MUSB_CSR0_P_SVDSETUPEND; } if (csr0 & MUSB_CSR0_RXPKTRDY) { u8 count0; count0 = musb_readb(mtk_musb->mregs, MUSB_OTG_COUNT0); if (count0 == 8) { struct usb_ctrlrequest setup_packet; /* get the setup packet */ musb_d_setup(&setup_packet, count0); if (setup_packet.bRequest == USB_REQ_SET_ADDRESS) { device_enumed = false; csr0 |= MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_DATAEND; /* clear the RXPKTRDY */ musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) { DBG(0, "B-UUT:when set address, do not detect ep0 interrupt\n"); return; } musb_writeb(mtk_musb->mregs, MUSB_FADDR, (u8) setup_packet.wValue); } else if (setup_packet.bRequest == USB_REQ_GET_DESCRIPTOR) { csr0 |= MUSB_CSR0_P_SVDRXPKTRDY; /* clear the RXPKTRDY */ musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); /* device --> host */ musb_d_out_data(&setup_packet); } else if (setup_packet.bRequest == USB_REQ_SET_CONFIGURATION) { csr0 |= MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_DATAEND; /* clear the RXPKTRDY */ musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; device_enumed = true; /* will set host_req for B-device */ devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL); if (devctl & MUSB_DEVCTL_BDEVICE) { devctl |= MUSB_DEVCTL_HR; musb_writeb(mtk_musb->mregs, MUSB_DEVCTL, devctl); } } else if (setup_packet.bRequest == USB_REQ_SET_FEATURE) { csr0 |= MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_DATAEND; /* clear the RXPKTRDY */ musb_writew(mtk_musb->mregs, MUSB_OTG_CSR0, csr0); if (musb_polling_ep0_interrupt()) return; } } } } void musb_otg_test_return(void) { } static int musb_host_test_mode(unsigned char cmd); void otg_cmd_a_uut(void) { unsigned long timeout; unsigned char devctl; bool timeout_flag = false; unsigned int ret; unsigned char power; unsigned short csr0; unsigned char intrusb; unsigned short intrtx; /* 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 ((atomic_read(&g_exec) == 1) && (devctl & 0x18)) { DBG(0, "musb::not below session end!\n"); msleep(100); if (time_after(jiffies, timeout)) { timeout_flag = true; return TEST_IS_STOP; } devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL); } if (timeout_flag) { timeout_flag = false; musb_otg_reset_usb(); BG(0, "timeout for below session end, after reset usb, devctl=0x%x\n", musb_readb(mtk_musb->mregs, MUSB_DEVCTL)); } BG(0, "polling session request,begin\n"); ret = musb_polling_bus_interrupt(MUSB_INTR_SESSREQ); pBG(0, "polling session request,done,ret=0x%x\n", ret); if (ret == TEST_IS_STOP) return TEST_IS_STOP; /* session is set and VBUS will be out. */ musb_otg_set_session(true); #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 (ret == TEST_IS_STOP) return TEST_IS_STOP; if (ret == DEV_NOT_CONNECT) { DBG(0, "device is not connected in 15s\n"); g_otg_message.msg = OTG_MSG_DEV_NOT_RESPONSE; return TEST_IS_STOP; } DBG(0, "musb::connect interrupt is detected!\n"); /* the test is fail because the reset starts less than100 ms * from the B-OPT connect. the IF test needs */ msleep(100); /* 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 (ret == TEST_IS_STOP) return TEST_IS_STOP; if (ret == MUSB_INTR_RESUME) { /* for TD4.8 */ musb_h_remote_wakeup(); /* maybe need to access the B-OPT, get device descriptor */ if (atomic_read(&g_exec) == 1) wait_for_completion(&stop_event); return TEST_IS_STOP; } /* 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 (ret == TEST_IS_STOP) return TEST_IS_STOP; if (ret == DEV_NOT_RESET) { if (atomic_read(&g_exec) == 1) wait_for_completion(&stop_event); return TEST_IS_STOP; } } 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); /* sync status */ mb(); musb_writew(mtk_musb->mregs, MUSB_INTRTX, intrtx); intrusb = musb_readb(mtk_musb->mregs, MUSB_INTRUSB); /* sync status */ 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) { /* maybe receive disconnect interrupt when the session is end */ if (intrusb & MUSB_INTR_SUSPEND) { if (device_enumed) { /* return form the while loop */ break; } /* TD.4.6 */ musb_d_soft_connect(false); goto TD_4_6; } } } else wait_for_completion_timeout(&stop_event, 1); /* the enum will be repeated for 5 times */ } while (atomic_read(&g_exec) == 1); if (atomic_read(&g_exec) == 0) { /* return form the switch-case */ return TEST_IS_STOP; } DBG(0, "polling connect form B-OPT,begin\n"); /* B-OPT will connect again 100ms after A disconnect */ ret = musb_polling_bus_interrupt(MUSB_INTR_CONNECT); DBG(0, "polling connect form B-OPT,done,ret=0x%x\n", ret); if (ret == TEST_IS_STOP) return TEST_IS_STOP; musb_h_reset(); /* should reset bus again, TD.4.7 */ wait_for_completion(&stop_event); } void otg_cmd_b_uut(void) { unsigned long timeout; unsigned char devctl; bool timeout_flag = false; unsigned int ret; unsigned char power; unsigned char intrusb; unsigned short intrtx; 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; return TEST_IS_STOP; } /* 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 (ret == TEST_IS_STOP) return TEST_IS_STOP; 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); /* sync status */ mb(); musb_writew(mtk_musb->mregs, MUSB_INTRTX, intrtx); intrusb = musb_readb(mtk_musb->mregs, MUSB_INTRUSB); /* sync status */ 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 (atomic_read(&g_exec) == 1); if (atomic_read(&g_exec) == 0) return TEST_IS_STOP; 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 (ret == TEST_IS_STOP) return TEST_IS_STOP; if (ret == MUSB_INTR_RESUME) goto TD6_13; if (ret == DEV_HNP_TIMEOUT) { DBG(0, "B-UUT HNP timeout\n"); devctl = musb_readb(mtk_musb->mregs, MUSB_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; return TEST_IS_STOP; } } /* 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); if (ret == TEST_IS_STOP) return TEST_IS_STOP; 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 (ret == TEST_IS_STOP) return TEST_IS_STOP; } device_enumed = false; if (atomic_read(&g_exec) == 1) goto TD6_13; /* TD5_5 */ wait_for_completion(&stop_event); } int musb_otg_exec_cmd(unsigned int cmd) { unsigned char devctl; unsigned char intrusb; unsigned char power; unsigned short csr0; unsigned int usb_l1intp; unsigned int usb_l1ints; if (!mtk_musb) { DBG(0, "mtk_musb is NULL,error!\n"); return false; } 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); mdelay(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; atomic_set(&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 (atomic_read(&g_exec) == 1) 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(); { u32 val = 0; val = USBPHY_READ32(0x6c); val = (val & ~(0xff<<0)) | (0x1<<0); USBPHY_WRITE32(0x6c, val); val = USBPHY_READ32(0x6c); val = (val & ~(0xff<<8)) | (0x1<<8); USBPHY_WRITE32(0x6c, val); } musb_writeb(mtk_musb->mregs, 0x7B, 1); musb_otg_set_session(true); while (atomic_read(&g_exec) == 1) 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 ((atomic_read(&g_exec) == 1) && (devctl & 0x18)) { DBG(0, "musb::not below session end!\n"); msleep(100); devctl = musb_readb(mtk_musb->mregs, MUSB_DEVCTL); } while ((atomic_read(&g_exec) == 1) && (!(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 (atomic_read(&g_exec) == 1) 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 (atomic_read(&g_exec) == 1) 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"); otg_cmd_a_uut(); DBG(0, "the test as A-UUT is done\n"); break; case OTG_CMD_P_B_UUT: DBG(0, "B-UUT starts...\n"); otg_cmd_b_uut(); DBG(0, "the 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 (atomic_read(&g_exec) == 1) msleep(100); break; } DBG(0, "%s--\n", __func__); return 0; } void musb_otg_stop_cmd(void) { DBG(0, "%s++\n", __func__); atomic_set(&g_exec, 0); is_td_59 = false; complete(&stop_event); } unsigned int musb_otg_message(void) { /* for EM to pop the message */ unsigned int msg; msg = g_otg_message.msg; g_otg_message.msg = 0; return msg; } void musb_otg_message_cb(void) { /* when the OK button is clicked on EM, this func is called. */ spin_lock(&g_otg_message.lock); g_otg_message.msg = 0; spin_unlock(&g_otg_message.lock); } static int musb_otg_test_open(struct inode *inode, struct file *file) { DBG(0, "%s++\n", __func__); return 0; } static int musb_otg_test_release(struct inode *inode, struct file *file) { return 0; } ssize_t musb_otg_test_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { int ret = 0; unsigned int message = musb_otg_message(); if (message) DBG(0, "%s:message=0x%x\n", __func__, message); if (put_user((unsigned int)message, (unsigned int *)buf)) ret = -EFAULT; return ret; } ssize_t musb_otg_test_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int ret = 0; unsigned char value; if (get_user(value, (unsigned char *)buf)) ret = -EFAULT; else { if (value == OTG_STOP_CMD) { DBG(0, "%s::OTG_STOP_CMD\n", __func__); musb_otg_stop_cmd(); } else if (value == OTG_INIT_MSG) { DBG(0, "%s::OTG_INIT_MSG\n", __func__); musb_otg_message_cb(); } else { DBG(0, "musb_otg_test_write::the value is invalid,0x%x\n", value); ret = -EFAULT; } } return ret; } static long musb_otg_test_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; DBG(0, "%s :cmd=0x%x\n", __func__, cmd); ret = musb_otg_exec_cmd(cmd); return (long)ret; } static const struct file_operations musb_otg_test_fops = { .owner = THIS_MODULE, .open = musb_otg_test_open, .release = musb_otg_test_release, .read = musb_otg_test_read, .write = musb_otg_test_write, .unlocked_ioctl = musb_otg_test_ioctl, }; static struct miscdevice musb_otg_test_dev = { .minor = MISC_DYNAMIC_MINOR, /* .minor = 254, */ .name = TEST_DRIVER_NAME, .fops = &musb_otg_test_fops, .mode = 0666, }; static const u8 musb_host_test_packet[53] = { /* implicit SYNC then DATA0 to start */ /* JKJKJKJK x9 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* JJKKJJKK x8 */ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, /* JJJJKKKK x8 */ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, /* JJJJJJJKKKKKKK x8 */ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* JJJJJJJK x8 */ 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, /* JKKKKKKK x10, JK */ 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e /* implicit CRC16 then EOP to end */ }; void musb_host_load_testpacket(struct musb *musb) { unsigned short csr0 = musb_readw(musb->mregs, 0x102); DBG(0, "csr0=0x%x\n", csr0); musb->ignore_disconnect = 1; musb_otg_write_fifo(53, (u8 *) musb_host_test_packet); } void host_test_mode(struct musb *musb, unsigned int wIndex) { unsigned char temp; unsigned char power; struct usb_ctrlrequest setup_packet; struct usb_device_descriptor device_descriptor; setup_packet.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR; setup_packet.wIndex = 0; setup_packet.wValue = 0x0100; setup_packet.wLength = 0x40; musb_otg_set_session(true); msleep(200); pr_debug("devctl = 0x%x\n", musb_readb(musb->mregs, MUSB_DEVCTL)); switch (wIndex) { case HOST_CMD_TEST_SE0_NAK: DBG(0, "TEST_SE0_NAK\n"); temp = MUSB_TEST_SE0_NAK; musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; case HOST_CMD_TEST_J: DBG(0, "TEST_J\n"); temp = MUSB_TEST_J; musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; case HOST_CMD_TEST_K: DBG(0, "TEST_K\n"); temp = MUSB_TEST_K; musb_writeb(musb->mregs, MUSB_TESTMODE, temp); break; case HOST_CMD_TEST_PACKET: DBG(0, "TEST_PACKET\n"); temp = MUSB_TEST_PACKET; musb_host_load_testpacket(musb); musb_writeb(musb->mregs, MUSB_TESTMODE, temp); musb_writew(musb->mregs, 0x102, MUSB_CSR0_TXPKTRDY); break; case HOST_CMD_SUSPEND_RESUME: /* HS_HOST_PORT_SUSPEND_RESUME */ DBG(0, "HS_HOST_PORT_SUSPEND_RESUME\n"); msleep(5000); /* the host must continue sending SOFs for 15s */ DBG(0, "please begin to trigger suspend!\n"); msleep(10000); power = musb_readb(musb->mregs, MUSB_POWER); power |= MUSB_POWER_SUSPENDM | MUSB_POWER_ENSUSPEND; musb_writeb(musb->mregs, MUSB_POWER, power); msleep(5000); DBG(0, "please begin to trigger resume!\n"); msleep(10000); power &= ~MUSB_POWER_SUSPENDM; power |= MUSB_POWER_RESUME; musb_writeb(musb->mregs, MUSB_POWER, power); mdelay(25); power &= ~MUSB_POWER_RESUME; musb_writeb(musb->mregs, MUSB_POWER, power); /* SOF continue */ musb_h_setup(&setup_packet); break; case HOST_CMD_GET_DESCRIPTOR: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ DBG(0, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR\n"); /* the host issues SOFs for 15s allowing the test engineer * to raise the scope trigger just above the SOF voltage level. */ msleep(15000); musb_h_setup(&setup_packet); break; case HOST_CMD_SET_FEATURE: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ DBG(0, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR\n"); /* get device descriptor */ musb_h_setup(&setup_packet); msleep(15000); musb_h_in_data((char *)&device_descriptor, sizeof(struct usb_device_descriptor)); musb_h_out_status(); break; default: break; } /* while(1); */ } 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; } static int __init musb_otg_test_init(void) { #ifdef CONFIG_OF struct device_node *np; np = of_find_compatible_node(NULL, NULL, "mediatek,PERICFG"); if (!np) pr_debug("get PERICFG node fail"); pericfg_base = (unsigned long)of_iomap(np, 0); #else pericfg_base = PERICFG_BASE; #endif misc_register(&musb_otg_test_dev); return 0; } static void __exit musb_otg_test_exit(void) { misc_deregister(&musb_otg_test_dev); } module_init(musb_otg_test_init); module_exit(musb_otg_test_exit);