static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { struct dwc_ctrl *ctrl = dev->controller; pUSB_OTG_REG otgReg = ctrl->otgReg; uint32_t channel_num = usb_pipeendpoint(pipe); uint32_t eptype; HOST_RET ret = HOST_OK; HCTSIZ_DATA hctsiz; HCCHAR_DATA hcchar; uint32_t hcStat; uint32_t errCnt; uint32_t packet_size; uint32_t datatoggle; eptype = usb_pipetype(pipe); // ep tye definition is different in pipe and dwc hcchar if(eptype == 2 ) eptype = 0; else if (eptype == 3) eptype = 2; debug("ehci_submit_async channel pipe %lx req %p, len %x\n", pipe, req, length); if(req == NULL) packet_size = 0x200; else packet_size = 0x40; if (req != NULL) { // setup for control hctsiz.d32 = 0; hctsiz.b.pid = DWC_HCTSIZ_SETUP; hctsiz.b.pktcnt = 1; hctsiz.b.xfersize = 8; hcchar.d32 = 0; hcchar.b.mps = packet_size; hcchar.b.epnum = channel_num; // use the same channel number as endpoint number hcchar.b.epdir = DWC_EPDIR_OUT; hcchar.b.eptype = eptype; hcchar.b.multicnt = 1; hcchar.b.devaddr = usb_pipedevice(pipe); hcchar.b.chdis = 0; hcchar.b.chen = 1; hcStat = HCSTAT_SETUP; errCnt = 0; dwc_init_channel(dev, hctsiz.d32, hcchar, (uint32_t)req); if(dwc_wait_for_complete(dev, channel_num, &hcStat, &errCnt)){ ret = HOST_ERR; goto out; } if(hcStat != HCSTAT_DONE){ ret = HOST_ERR; goto out; } } if (length || (req == NULL)) { // data for bulk & control if(req) datatoggle = DWC_HCTSIZ_DATA1; else datatoggle = ctrl->datatoggle[usb_pipein(pipe)]; debug("dwc_hcd data len %x toggle %x\n", length, datatoggle); hctsiz.d32 = 0; hctsiz.b.pid = datatoggle; hctsiz.b.pktcnt = (length+packet_size - 1)/packet_size; hctsiz.b.xfersize = length; hcchar.d32 = 0; hcchar.b.mps = packet_size; hcchar.b.epnum = channel_num; // use the same channel number as endpoint number hcchar.b.epdir = (req == NULL) ? usb_pipein(pipe) : DWC_EPDIR_IN; hcchar.b.eptype = eptype; hcchar.b.multicnt = 1; hcchar.b.devaddr = usb_pipedevice(pipe); hcchar.b.chdis = 0; hcchar.b.chen = 1; hcStat = HCSTAT_DATA; errCnt = 0; if((req == NULL)&&(hctsiz.b.pktcnt&0x01)) { ctrl->datatoggle[usb_pipein(pipe)] ^= 0x02; } dwc_init_channel(dev, hctsiz.d32, hcchar, (uint32_t)buffer); if(dwc_wait_for_complete(dev, channel_num, &hcStat, &errCnt)){ ret = HOST_ERR; goto out; } if(hcStat == HCSTAT_STALL) ctrl->datatoggle[usb_pipein(pipe)] = 0; } if (req != NULL) { // status for control debug("status len %x\n", length); hctsiz.d32 = 0; hctsiz.b.dopng = 0; hctsiz.b.pid = DWC_HCTSIZ_DATA1; hctsiz.b.pktcnt = 1; hctsiz.b.xfersize = 0; hcchar.d32 = 0; hcchar.b.mps = packet_size; hcchar.b.epnum = channel_num; // use the same channel number as endpoint number hcchar.b.epdir = (length) ? DWC_EPDIR_OUT : DWC_EPDIR_IN; hcchar.b.eptype = eptype; hcchar.b.multicnt = 1; hcchar.b.devaddr = usb_pipedevice(pipe); hcchar.b.chdis = 0; hcchar.b.chen = 1; hcStat = HCSTAT_DATA; errCnt = 0; dwc_init_channel(dev, hctsiz.d32, hcchar, 0); if(dwc_wait_for_complete(dev, channel_num, &hcStat, &errCnt)){ ret = HOST_ERR; goto out; } } out: if(ret){ debug("dwc_init channel hcziz %x, hcdma %x, hcchar %x\n", otgReg->Host.hchn[channel_num].hctsizn, otgReg->Host.hchn[channel_num].hcdman, otgReg->Host.hchn[channel_num].hccharn); } dev->act_len = length; dev->status = 0; return (ret); }
static int dwc2_transfer(struct usb_device *dev, unsigned long pipe, int size, int pid, ep_dir_t dir, uint32_t ch_num, u8 *data_buf) { struct dwc_ctrl *ctrl = dev->controller; pUSB_OTG_REG reg = ctrl->otgReg; uint32_t do_copy; int ret; uint32_t packet_cnt; uint32_t packet_size; uint32_t transferred = 0; uint32_t inpkt_length; uint32_t eptype; HCTSIZ_DATA hctsiz = { .d32 = 0 }; HCCHAR_DATA hcchar = { .d32 = 0 }; void *aligned_buf; debug("# %s #dev %p, size %d, pid %d, dir %d, buf %p\n", __func__, dev, size, pid, dir, data_buf); if (dev->speed != USB_SPEED_HIGH) { printf("Support high-speed only\n"); return -1; } if (size > DMA_SIZE) { printf("Transfer too large: %d\n", size); return -1; } packet_size = usb_maxpacket(dev, pipe); packet_cnt = DIV_ROUND_UP(size, packet_size); inpkt_length = roundup(size, packet_size); /* At least 1 packet should be programed */ packet_cnt = (packet_cnt == 0) ? 1 : packet_cnt; /* * For an IN, this field is the buffer size that the application has * reserved for the transfer. The application should program this field * as integer multiple of the maximum packet size for IN transactions. */ hctsiz.xfersize = (dir == EPDIR_OUT) ? size : inpkt_length; hctsiz.pktcnt = packet_cnt; hctsiz.pid = pid; hcchar.mps = packet_size; hcchar.epnum = usb_pipeendpoint(pipe); hcchar.epdir = dir; switch (usb_pipetype(pipe)) { case PIPE_CONTROL: eptype = 0; break; case PIPE_BULK: eptype = 2; break; default: printf("Un-supported type\n"); return -EOPNOTSUPP; } hcchar.eptype = eptype; hcchar.multicnt = 1; hcchar.devaddr = usb_pipedevice(pipe); hcchar.chdis = 0; hcchar.chen = 1; /* * Check the buffer address which should be 4-byte aligned and DMA * coherent */ //do_copy = !dma_coherent(data_buf) || ((uintptr_t)data_buf & 0x3); do_copy = 1;//(uintptr_t)data_buf & 0x3; aligned_buf = do_copy ? ctrl->align_buf : data_buf; if (do_copy && (dir == EPDIR_OUT)) memcpy(aligned_buf, data_buf, size); if (dir == EPDIR_OUT) flush_dcache_range(aligned_buf, aligned_buf + roundup(size, ARCH_DMA_MINALIGN)); writel(hctsiz.d32, ®->Host.hchn[ch_num].hctsizn); writel((uint32_t)aligned_buf, ®->Host.hchn[ch_num].hcdman); writel(hcchar.d32, ®->Host.hchn[ch_num].hccharn); ret = dwc_wait_for_complete(dev, ch_num); if (ret >= 0) { /* Calculate actual transferred length */ transferred = (dir == EPDIR_IN) ? inpkt_length - ret : ret; if (dir == EPDIR_IN) invalidate_dcache_range(aligned_buf, aligned_buf + roundup(transferred, ARCH_DMA_MINALIGN)); if (do_copy && (dir == EPDIR_IN)) memcpy(data_buf, aligned_buf, transferred); } /* Save data toggle */ hctsiz.d32 = readl(®->Host.hchn[ch_num].hctsizn); usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), (hctsiz.pid >> 1)); if (ret < 0) { printf("%s Transfer stop code: %d\n", __func__, ret); return ret; } return transferred; } int usb_lowlevel_stop(int index) { pUSB_OTG_REG reg = (pUSB_OTG_REG)rkusb_active_hcd->regbase; HPRT0_DATA hprt0 = { .d32 = 0 }; GUSBCFG_DATA gusbcfg = { .d32 = 0 }; /* Stop connect and put everything of port state in reset. */ hprt0.prtena = 1; hprt0.prtenchng = 1; hprt0.prtconndet = 1; writel(hprt0.d32, ®->Host.hprt); gusbcfg.d32 = 0x1400; writel(gusbcfg.d32, ®->Core.gusbcfg); return 0; } static void dwc2_reinit(pUSB_OTG_REG regbase) { pUSB_OTG_REG reg = regbase; GUSBCFG_DATA gusbcfg = { .d32 = 0 }; GRSTCTL_DATA grstctl = { .d32 = 0 }; GINTSTS_DATA gintsts = { .d32 = 0 }; GAHBCFG_DATA gahbcfg = { .d32 = 0 }; RXFIFOSIZE_DATA grxfsiz = { .d32 = 0 }; HCINTMSK_DATA hcintmsk = { .d32 = 0 }; TXFIFOSIZE_DATA gnptxfsiz = { .d32 = 0 }; const int timeout = 10000; int i; /* Wait for AHB idle */ for (i = 0; i < timeout; i++) { udelay(1); grstctl.d32 = readl(®->Core.grstctl); if (grstctl.ahbidle) break; } if (i == timeout) { printf("DWC2 Init error AHB Idle\n"); return; } /* Restart the Phy Clock */ writel(0x0, ®->ClkGate.PCGCR); /* Core soft reset */ grstctl.csftrst = 1; writel(grstctl.d32, ®->Core.grstctl); for (i = 0; i < timeout; i++) { udelay(1); grstctl.d32 = readl(®->Core.grstctl); if (!grstctl.csftrst) break; } if (i == timeout) { printf("DWC2 Init error reset fail\n"); return; } /* Set 16bit PHY if & Force host mode */ gusbcfg.d32 = readl(®->Core.gusbcfg); gusbcfg.phyif = 1; gusbcfg.forcehstmode = 1; gusbcfg.forcedevmode = 0; writel(gusbcfg.d32, ®->Core.gusbcfg); /* Wait for force host mode effect, it may takes 100ms */ for (i = 0; i < timeout; i++) { udelay(10); gintsts.d32 = readl(®->Core.gintsts); if (gintsts.curmod) break; } if (i == timeout) { printf("DWC2 Init error force host mode fail\n"); return; } /* * Config FIFO * The non-periodic tx fifo and rx fifo share one continuous * piece of IP-internal SRAM. */ grxfsiz.rxfdep = DWC2_RXFIFO_DEPTH; writel(grxfsiz.d32, ®->Core.grxfsiz); gnptxfsiz.nptxfstaddr = DWC2_RXFIFO_DEPTH; gnptxfsiz.nptxfdep = DWC2_NPTXFIFO_DEPTH; writel(gnptxfsiz.d32, ®->Core.gnptxfsiz); /* Init host channels */ hcintmsk.xfercomp = 1; hcintmsk.xacterr = 1; hcintmsk.stall = 1; hcintmsk.chhltd = 1; hcintmsk.bblerr = 1; for (i = 0; i < MAX_EPS_CHANNELS; i++) writel(hcintmsk.d32, ®->Host.hchn[i].hcintmaskn); /* Unmask interrupt & Use configure dma mode */ gahbcfg.glblintrmsk = 1; gahbcfg.hbstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR8; gahbcfg.dmaen = 1; writel(gahbcfg.d32, ®->Core.gahbcfg); printf("DWC2@0x%p init finished!\n", regbase); } int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) { pUSB_OTG_REG reg = (pUSB_OTG_REG)rkusb_active_hcd->regbase; struct dwc_ctrl *dwcctl; dwcctl = malloc(sizeof(struct dwc_ctrl)); if (!dwcctl) return -ENOMEM; dwcctl->otgReg = reg; dwcctl->rootdev = 0; dwcctl->align_buf = memalign(USB_DMA_MINALIGN, DMA_SIZE); if (!dwcctl->align_buf) return -ENOMEM; dwc2_reinit(reg); *controller = dwcctl; return 0; } int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length) { ep_dir_t data_dir; int pid; int ret = 0; if (usb_pipetype(pipe) != PIPE_BULK) { debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe)); return -1; } if (usb_pipein(pipe)) data_dir = EPDIR_IN; else if (usb_pipeout(pipe)) data_dir = EPDIR_OUT; else return -1; pid = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); if (pid) pid = DWC_HCTSIZ_DATA1; else pid = DWC_HCTSIZ_DATA0; ret = dwc2_transfer(dev, pipe, length, pid, data_dir, 0, buffer); if (ret < 0) return -1; dev->act_len = ret; dev->status = 0; return 0; } int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) { int ret = 0; struct dwc_ctrl *ctrl = dev->controller; ep_dir_t data_dir; if (usb_pipetype(pipe) != PIPE_CONTROL) { debug("non-control pipe (type=%lu)", usb_pipetype(pipe)); return -1; } if (usb_pipedevice(pipe) == ctrl->rootdev) { if (!ctrl->rootdev) dev->speed = USB_SPEED_HIGH; return dwc2_submit_root(dev, pipe, buffer, length, setup); } if (usb_pipein(pipe)) data_dir = EPDIR_IN; else if (usb_pipeout(pipe)) data_dir = EPDIR_OUT; else return -1; /* Setup Phase */ if (dwc2_transfer(dev, pipe, 8, PID_SETUP, EPDIR_OUT, 0, (u8 *)setup) < 0) return -1; /* Data Phase */ if (length > 0) { ret = dwc2_transfer(dev, pipe, length, PID_DATA1, data_dir, 0, buffer); if (ret < 0) return -1; } /* Status Phase */ if (dwc2_transfer(dev, pipe, 0, PID_DATA1, !data_dir, 0, NULL) < 0) return -1; dev->act_len = ret; dev->status = 0; return 0; } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length, int interval) { return 0; }