Example #1
0
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);
}
Example #2
0
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, &reg->Host.hchn[ch_num].hctsizn);
	writel((uint32_t)aligned_buf, &reg->Host.hchn[ch_num].hcdman);
	writel(hcchar.d32, &reg->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(&reg->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, &reg->Host.hprt);

	gusbcfg.d32 = 0x1400;
	writel(gusbcfg.d32, &reg->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(&reg->Core.grstctl);
		if (grstctl.ahbidle)
			break;
	}
	if (i == timeout) {
		printf("DWC2 Init error AHB Idle\n");
		return;
	}

	/* Restart the Phy Clock */
	writel(0x0, &reg->ClkGate.PCGCR);
	/* Core soft reset */
	grstctl.csftrst = 1;
	writel(grstctl.d32, &reg->Core.grstctl);
	for (i = 0; i < timeout; i++) {
		udelay(1);
		grstctl.d32 = readl(&reg->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(&reg->Core.gusbcfg);
	gusbcfg.phyif = 1;
	gusbcfg.forcehstmode = 1;
	gusbcfg.forcedevmode = 0;
	writel(gusbcfg.d32, &reg->Core.gusbcfg);
	/* Wait for force host mode effect, it may takes 100ms */
	for (i = 0; i < timeout; i++) {
		udelay(10);
		gintsts.d32 = readl(&reg->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, &reg->Core.grxfsiz);
	gnptxfsiz.nptxfstaddr = DWC2_RXFIFO_DEPTH;
	gnptxfsiz.nptxfdep = DWC2_NPTXFIFO_DEPTH;
	writel(gnptxfsiz.d32, &reg->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, &reg->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, &reg->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;
}