コード例 #1
0
static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
                  int length, struct devrequest *req)
{
    struct QH *qh;
    struct qTD *td;
    volatile struct qTD *vtd;
    unsigned long ts;
    uint32_t *tdp;
    uint32_t endpt, token, usbsts;
    uint32_t c, toggle;
    uint32_t cmd;
    int timeout;
    int ret = 0;

    debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
          buffer, length, req);
    if (req != NULL)
        debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
              req->request, req->request,
              req->requesttype, req->requesttype,
              le16_to_cpu(req->value), le16_to_cpu(req->value),
              le16_to_cpu(req->index));

    qh = ehci_alloc(sizeof(struct QH), 32);
    if (qh == NULL) {
        debug("unable to allocate QH\n");
        return -1;
    }
    qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
    c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
         usb_pipeendpoint(pipe) == 0) ? 1 : 0;
    endpt = (8 << 28) |
            (c << 27) |
            (usb_maxpacket(dev, pipe) << 16) |
            (0 << 15) |
            (1 << 14) |
            (usb_pipespeed(pipe) << 12) |
            (usb_pipeendpoint(pipe) << 8) |
            (0 << 7) | (usb_pipedevice(pipe) << 0);
    qh->qh_endpt1 = cpu_to_hc32(endpt);
    endpt = (1 << 30) |
            (dev->portnr << 23) |
            (dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
    qh->qh_endpt2 = cpu_to_hc32(endpt);
    qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);

    td = NULL;
    tdp = &qh->qh_overlay.qt_next;

    toggle =
        usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));

    if (req != NULL) {
        td = ehci_alloc(sizeof(struct qTD), 32);
        if (td == NULL) {
            debug("unable to allocate SETUP td\n");
            goto fail;
        }
        td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
        td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
        token = (0 << 31) |
                (sizeof(*req) << 16) |
                (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
        td->qt_token = cpu_to_hc32(token);
        if (ehci_td_buffer(td, req, sizeof(*req)) != 0) {
            debug("unable construct SETUP td\n");
            ehci_free(td, sizeof(*td));
            goto fail;
        }
        *tdp = cpu_to_hc32((uint32_t) td);
        tdp = &td->qt_next;
        toggle = 1;
    }

    if (length > 0 || req == NULL) {
        td = ehci_alloc(sizeof(struct qTD), 32);
        if (td == NULL) {
            debug("unable to allocate DATA td\n");
            goto fail;
        }
        td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
        td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
        token = (toggle << 31) |
                (length << 16) |
                ((req == NULL ? 1 : 0) << 15) |
                (0 << 12) |
                (3 << 10) |
                ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
        td->qt_token = cpu_to_hc32(token);
        if (ehci_td_buffer(td, buffer, length) != 0) {
            debug("unable construct DATA td\n");
            ehci_free(td, sizeof(*td));
            goto fail;
        }
        *tdp = cpu_to_hc32((uint32_t) td);
        tdp = &td->qt_next;
    }

    if (req != NULL) {
        td = ehci_alloc(sizeof(struct qTD), 32);
        if (td == NULL) {
            debug("unable to allocate ACK td\n");
            goto fail;
        }
        td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
        td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
        token = (toggle << 31) |
                (0 << 16) |
                (1 << 15) |
                (0 << 12) |
                (3 << 10) |
                ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
        td->qt_token = cpu_to_hc32(token);
        *tdp = cpu_to_hc32((uint32_t) td);
        tdp = &td->qt_next;
    }

    qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);

    /* Flush dcache */
    ehci_flush_dcache(&qh_list);

    usbsts = ehci_readl(&hcor->or_usbsts);
    ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));

    /* Enable async. schedule. */
    cmd = ehci_readl(&hcor->or_usbcmd);
    cmd |= CMD_ASE;
    ehci_writel(&hcor->or_usbcmd, cmd);

    ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
                    100 * 1000);
    if (ret < 0) {
        printf("EHCI fail timeout STD_ASS set\n");
        goto fail;
    }

    /* Wait for TDs to be processed. */
    ts = get_timer(0);
    vtd = td;
    timeout = USB_TIMEOUT_MS(pipe);
    do {
        /* Invalidate dcache */
        ehci_invalidate_dcache(&qh_list);
        token = hc32_to_cpu(vtd->qt_token);
        if (!(token & 0x80))
            break;
        WATCHDOG_RESET();
    } while (get_timer(ts) < timeout);

    /* Check that the TD processing happened */
    if (token & 0x80) {
        printf("EHCI timed out on TD - token=%#x\n", token);
    }

    /* Disable async schedule. */
    cmd = ehci_readl(&hcor->or_usbcmd);
    cmd &= ~CMD_ASE;
    ehci_writel(&hcor->or_usbcmd, cmd);

    ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
                    100 * 1000);
    if (ret < 0) {
        printf("EHCI fail timeout STD_ASS reset\n");
        goto fail;
    }

    qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);

    token = hc32_to_cpu(qh->qh_overlay.qt_token);
    if (!(token & 0x80)) {
        debug("TOKEN=%#x\n", token);
        switch (token & 0xfc) {
        case 0:
            toggle = token >> 31;
            usb_settoggle(dev, usb_pipeendpoint(pipe),
                          usb_pipeout(pipe), toggle);
            dev->status = 0;
            break;
        case 0x40:
            dev->status = USB_ST_STALLED;
            break;
        case 0xa0:
        case 0x20:
            dev->status = USB_ST_BUF_ERR;
            break;
        case 0x50:
        case 0x10:
            dev->status = USB_ST_BABBLE_DET;
            break;
        default:
            dev->status = USB_ST_CRC_ERR;
            if ((token & 0x40) == 0x40)
                dev->status |= USB_ST_STALLED;
            break;
        }
        dev->act_len = length - ((token >> 16) & 0x7fff);
    } else {
コード例 #2
0
ファイル: sl811-hcd.c プロジェクト: Noltari/u-boot
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
		       int len,struct devrequest *setup)
{
	int done = 0;
	int devnum = usb_pipedevice(pipe);
	int ep = usb_pipeendpoint(pipe);

	dev->status = 0;

	if (devnum == root_hub_devnum)
		return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);

	PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
	       devnum, ep, buffer, len, (int)setup->requesttype,
	       (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);

	sl811_write(SL811_DEV_A, devnum);
	sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
	/* setup phase */
	usb_settoggle(dev, ep, 1, 0);
	if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
			      (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
		int dir_in = usb_pipein(pipe);
		int max = usb_maxpacket(dev, pipe);

		/* data phase */
		sl811_write(SL811_PIDEP_A,
			    PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
		usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
		while (done < len) {
			int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
						    max > len - done ? len - done : max);
			if (res < 0) {
				PDEBUG(0, "status data failed!\n");
				dev->status = -res;
				return 0;
			}
			done += res;
			usb_dotoggle(dev, ep, usb_pipeout(pipe));
			if (dir_in && res < max) /* short packet */
				break;
		}

		/* status phase */
		sl811_write(SL811_PIDEP_A,
			    PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
		usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
		if (sl811_send_packet(dev,
				      !dir_in ? usb_rcvctrlpipe(dev, ep) :
				      usb_sndctrlpipe(dev, ep),
				      0, 0) < 0) {
			PDEBUG(0, "status phase failed!\n");
			dev->status = -1;
		}
	} else {
		PDEBUG(0, "setup phase failed!\n");
		dev->status = -1;
	}

	dev->act_len = done;

	return done;
}
コード例 #3
0
ファイル: dwc2.c プロジェクト: dongzengwu/u-boot-stm32f4
int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in,
	      void *buffer, int len, bool ignore_ack)
{
	struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
	int devnum = usb_pipedevice(pipe);
	int ep = usb_pipeendpoint(pipe);
	int max = usb_maxpacket(dev, pipe);
	int eptype = dwc2_eptype[usb_pipetype(pipe)];
	int done = 0;
	int ret = 0;
	uint32_t sub;
	uint32_t xfer_len;
	uint32_t num_packets;
	int stop_transfer = 0;

	debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
	      in, len);

	do {
		/* Initialize channel */
		dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
				eptype, max);

		xfer_len = len - done;
		if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE)
			xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1;
		if (xfer_len > DWC2_DATA_BUF_SIZE)
			xfer_len = DWC2_DATA_BUF_SIZE - max + 1;

		/* Make sure that xfer_len is a multiple of max packet size. */
		if (xfer_len > 0) {
			num_packets = (xfer_len + max - 1) / max;
			if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) {
				num_packets = CONFIG_DWC2_MAX_PACKET_COUNT;
				xfer_len = num_packets * max;
			}
		} else {
			num_packets = 1;
		}

		if (in)
			xfer_len = num_packets * max;

		debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
		      *pid, xfer_len, num_packets);

		writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
		       (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
		       (*pid << DWC2_HCTSIZ_PID_OFFSET),
		       &hc_regs->hctsiz);

		if (!in)
			memcpy(aligned_buffer, (char *)buffer + done, len);

		writel(phys_to_bus((unsigned long)aligned_buffer),
		       &hc_regs->hcdma);

		/* Set host channel enable after all other setup is complete. */
		clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
				DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
				(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
				DWC2_HCCHAR_CHEN);

		ret = wait_for_chhltd(&sub, pid, ignore_ack);
		if (ret)
			break;

		if (in) {
			xfer_len -= sub;
			memcpy(buffer + done, aligned_buffer, xfer_len);
			if (sub)
				stop_transfer = 1;
		}

		done += xfer_len;

	} while ((done < len) && !stop_transfer);

	writel(0, &hc_regs->hcintmsk);
	writel(0xFFFFFFFF, &hc_regs->hcint);

	dev->status = 0;
	dev->act_len = done;

	return ret;
}
コード例 #4
0
ファイル: ehci-hcd.c プロジェクト: tomstokes/u-boot
static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
		   int length, struct devrequest *req)
{
	ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN);
	struct qTD *qtd;
	int qtd_count = 0;
	int qtd_counter = 0;
	volatile struct qTD *vtd;
	unsigned long ts;
	uint32_t *tdp;
	uint32_t endpt, maxpacket, token, usbsts;
	uint32_t c, toggle;
	uint32_t cmd;
	int timeout;
	int ret = 0;
	struct ehci_ctrl *ctrl = dev->controller;

	debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
	      buffer, length, req);
	if (req != NULL)
		debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
		      req->request, req->request,
		      req->requesttype, req->requesttype,
		      le16_to_cpu(req->value), le16_to_cpu(req->value),
		      le16_to_cpu(req->index));

#define PKT_ALIGN	512
	/*
	 * The USB transfer is split into qTD transfers. Eeach qTD transfer is
	 * described by a transfer descriptor (the qTD). The qTDs form a linked
	 * list with a queue head (QH).
	 *
	 * Each qTD transfer starts with a new USB packet, i.e. a packet cannot
	 * have its beginning in a qTD transfer and its end in the following
	 * one, so the qTD transfer lengths have to be chosen accordingly.
	 *
	 * Each qTD transfer uses up to QT_BUFFER_CNT data buffers, mapped to
	 * single pages. The first data buffer can start at any offset within a
	 * page (not considering the cache-line alignment issues), while the
	 * following buffers must be page-aligned. There is no alignment
	 * constraint on the size of a qTD transfer.
	 */
	if (req != NULL)
		/* 1 qTD will be needed for SETUP, and 1 for ACK. */
		qtd_count += 1 + 1;
	if (length > 0 || req == NULL) {
		/*
		 * Determine the qTD transfer size that will be used for the
		 * data payload (not considering the first qTD transfer, which
		 * may be longer or shorter, and the final one, which may be
		 * shorter).
		 *
		 * In order to keep each packet within a qTD transfer, the qTD
		 * transfer size is aligned to PKT_ALIGN, which is a multiple of
		 * wMaxPacketSize (except in some cases for interrupt transfers,
		 * see comment in submit_int_msg()).
		 *
		 * By default, i.e. if the input buffer is aligned to PKT_ALIGN,
		 * QT_BUFFER_CNT full pages will be used.
		 */
		int xfr_sz = QT_BUFFER_CNT;
		/*
		 * However, if the input buffer is not aligned to PKT_ALIGN, the
		 * qTD transfer size will be one page shorter, and the first qTD
		 * data buffer of each transfer will be page-unaligned.
		 */
		if ((uint32_t)buffer & (PKT_ALIGN - 1))
			xfr_sz--;
		/* Convert the qTD transfer size to bytes. */
		xfr_sz *= EHCI_PAGE_SIZE;
		/*
		 * Approximate by excess the number of qTDs that will be
		 * required for the data payload. The exact formula is way more
		 * complicated and saves at most 2 qTDs, i.e. a total of 128
		 * bytes.
		 */
		qtd_count += 2 + length / xfr_sz;
	}
/*
 * Threshold value based on the worst-case total size of the allocated qTDs for
 * a mass-storage transfer of 65535 blocks of 512 bytes.
 */
#if CONFIG_SYS_MALLOC_LEN <= 64 + 128 * 1024
#warning CONFIG_SYS_MALLOC_LEN may be too small for EHCI
#endif
	qtd = memalign(USB_DMA_MINALIGN, qtd_count * sizeof(struct qTD));
	if (qtd == NULL) {
		printf("unable to allocate TDs\n");
		return -1;
	}

	memset(qh, 0, sizeof(struct QH));
	memset(qtd, 0, qtd_count * sizeof(*qtd));

	toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));

	/*
	 * Setup QH (3.6 in ehci-r10.pdf)
	 *
	 *   qh_link ................. 03-00 H
	 *   qh_endpt1 ............... 07-04 H
	 *   qh_endpt2 ............... 0B-08 H
	 * - qh_curtd
	 *   qh_overlay.qt_next ...... 13-10 H
	 * - qh_overlay.qt_altnext
	 */
	qh->qh_link = cpu_to_hc32((uint32_t)&ctrl->qh_list | QH_LINK_TYPE_QH);
	c = (dev->speed != USB_SPEED_HIGH) && !usb_pipeendpoint(pipe);
	maxpacket = usb_maxpacket(dev, pipe);
	endpt = QH_ENDPT1_RL(8) | QH_ENDPT1_C(c) |
		QH_ENDPT1_MAXPKTLEN(maxpacket) | QH_ENDPT1_H(0) |
		QH_ENDPT1_DTC(QH_ENDPT1_DTC_DT_FROM_QTD) |
		QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) |
		QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) |
		QH_ENDPT1_DEVADDR(usb_pipedevice(pipe));
	qh->qh_endpt1 = cpu_to_hc32(endpt);
	endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
	qh->qh_endpt2 = cpu_to_hc32(endpt);
	ehci_update_endpt2_dev_n_port(dev, qh);
	qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
	qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);

	tdp = &qh->qh_overlay.qt_next;

	if (req != NULL) {
		/*
		 * Setup request qTD (3.5 in ehci-r10.pdf)
		 *
		 *   qt_next ................ 03-00 H
		 *   qt_altnext ............. 07-04 H
		 *   qt_token ............... 0B-08 H
		 *
		 *   [ buffer, buffer_hi ] loaded with "req".
		 */
		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
		token = QT_TOKEN_DT(0) | QT_TOKEN_TOTALBYTES(sizeof(*req)) |
			QT_TOKEN_IOC(0) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
			QT_TOKEN_PID(QT_TOKEN_PID_SETUP) |
			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
		qtd[qtd_counter].qt_token = cpu_to_hc32(token);
		if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req))) {
			printf("unable to construct SETUP TD\n");
			goto fail;
		}
		/* Update previous qTD! */
		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
		tdp = &qtd[qtd_counter++].qt_next;
		toggle = 1;
	}

	if (length > 0 || req == NULL) {
		uint8_t *buf_ptr = buffer;
		int left_length = length;

		do {
			/*
			 * Determine the size of this qTD transfer. By default,
			 * QT_BUFFER_CNT full pages can be used.
			 */
			int xfr_bytes = QT_BUFFER_CNT * EHCI_PAGE_SIZE;
			/*
			 * However, if the input buffer is not page-aligned, the
			 * portion of the first page before the buffer start
			 * offset within that page is unusable.
			 */
			xfr_bytes -= (uint32_t)buf_ptr & (EHCI_PAGE_SIZE - 1);
			/*
			 * In order to keep each packet within a qTD transfer,
			 * align the qTD transfer size to PKT_ALIGN.
			 */
			xfr_bytes &= ~(PKT_ALIGN - 1);
			/*
			 * This transfer may be shorter than the available qTD
			 * transfer size that has just been computed.
			 */
			xfr_bytes = min(xfr_bytes, left_length);

			/*
			 * Setup request qTD (3.5 in ehci-r10.pdf)
			 *
			 *   qt_next ................ 03-00 H
			 *   qt_altnext ............. 07-04 H
			 *   qt_token ............... 0B-08 H
			 *
			 *   [ buffer, buffer_hi ] loaded with "buffer".
			 */
			qtd[qtd_counter].qt_next =
					cpu_to_hc32(QT_NEXT_TERMINATE);
			qtd[qtd_counter].qt_altnext =
					cpu_to_hc32(QT_NEXT_TERMINATE);
			token = QT_TOKEN_DT(toggle) |
				QT_TOKEN_TOTALBYTES(xfr_bytes) |
				QT_TOKEN_IOC(req == NULL) | QT_TOKEN_CPAGE(0) |
				QT_TOKEN_CERR(3) |
				QT_TOKEN_PID(usb_pipein(pipe) ?
					QT_TOKEN_PID_IN : QT_TOKEN_PID_OUT) |
				QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
			qtd[qtd_counter].qt_token = cpu_to_hc32(token);
			if (ehci_td_buffer(&qtd[qtd_counter], buf_ptr,
						xfr_bytes)) {
				printf("unable to construct DATA TD\n");
				goto fail;
			}
			/* Update previous qTD! */
			*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
			tdp = &qtd[qtd_counter++].qt_next;
			/*
			 * Data toggle has to be adjusted since the qTD transfer
			 * size is not always an even multiple of
			 * wMaxPacketSize.
			 */
			if ((xfr_bytes / maxpacket) & 1)
				toggle ^= 1;
			buf_ptr += xfr_bytes;
			left_length -= xfr_bytes;
		} while (left_length > 0);
	}

	if (req != NULL) {
		/*
		 * Setup request qTD (3.5 in ehci-r10.pdf)
		 *
		 *   qt_next ................ 03-00 H
		 *   qt_altnext ............. 07-04 H
		 *   qt_token ............... 0B-08 H
		 */
		qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
		qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
		token = QT_TOKEN_DT(1) | QT_TOKEN_TOTALBYTES(0) |
			QT_TOKEN_IOC(1) | QT_TOKEN_CPAGE(0) | QT_TOKEN_CERR(3) |
			QT_TOKEN_PID(usb_pipein(pipe) ?
				QT_TOKEN_PID_OUT : QT_TOKEN_PID_IN) |
			QT_TOKEN_STATUS(QT_TOKEN_STATUS_ACTIVE);
		qtd[qtd_counter].qt_token = cpu_to_hc32(token);
		/* Update previous qTD! */
		*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
		tdp = &qtd[qtd_counter++].qt_next;
	}

	ctrl->qh_list.qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH);

	/* Flush dcache */
	flush_dcache_range((uint32_t)&ctrl->qh_list,
		ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
	flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1));
	flush_dcache_range((uint32_t)qtd,
			   ALIGN_END_ADDR(struct qTD, qtd, qtd_count));

	/* Set async. queue head pointer. */
	ehci_writel(&ctrl->hcor->or_asynclistaddr, (uint32_t)&ctrl->qh_list);

	usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
	ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));

	/* Enable async. schedule. */
	cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
	cmd |= CMD_ASE;
	ehci_writel(&ctrl->hcor->or_usbcmd, cmd);

	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
			100 * 1000);
	if (ret < 0) {
		printf("EHCI fail timeout STS_ASS set\n");
		goto fail;
	}

	/* Wait for TDs to be processed. */
	ts = get_timer(0);
	vtd = &qtd[qtd_counter - 1];
	timeout = USB_TIMEOUT_MS(pipe);
	do {
		/* Invalidate dcache */
		invalidate_dcache_range((uint32_t)&ctrl->qh_list,
			ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
		invalidate_dcache_range((uint32_t)qh,
			ALIGN_END_ADDR(struct QH, qh, 1));
		invalidate_dcache_range((uint32_t)qtd,
			ALIGN_END_ADDR(struct qTD, qtd, qtd_count));

		token = hc32_to_cpu(vtd->qt_token);
		if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE))
			break;
		WATCHDOG_RESET();
	} while (get_timer(ts) < timeout);

	/*
	 * Invalidate the memory area occupied by buffer
	 * Don't try to fix the buffer alignment, if it isn't properly
	 * aligned it's upper layer's fault so let invalidate_dcache_range()
	 * vow about it. But we have to fix the length as it's actual
	 * transfer length and can be unaligned. This is potentially
	 * dangerous operation, it's responsibility of the calling
	 * code to make sure enough space is reserved.
	 */
	invalidate_dcache_range((uint32_t)buffer,
		ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));

	/* Check that the TD processing happened */
	if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
		printf("EHCI timed out on TD - token=%#x\n", token);

	/* Disable async schedule. */
	cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
	cmd &= ~CMD_ASE;
	ehci_writel(&ctrl->hcor->or_usbcmd, cmd);

	ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
			100 * 1000);
	if (ret < 0) {
		printf("EHCI fail timeout STS_ASS reset\n");
		goto fail;
	}

	token = hc32_to_cpu(qh->qh_overlay.qt_token);
	if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
		debug("TOKEN=%#x\n", token);
		switch (QT_TOKEN_GET_STATUS(token) &
			~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
		case 0:
			toggle = QT_TOKEN_GET_DT(token);
			usb_settoggle(dev, usb_pipeendpoint(pipe),
				       usb_pipeout(pipe), toggle);
			dev->status = 0;
			break;
		case QT_TOKEN_STATUS_HALTED:
			dev->status = USB_ST_STALLED;
			break;
		case QT_TOKEN_STATUS_ACTIVE | QT_TOKEN_STATUS_DATBUFERR:
		case QT_TOKEN_STATUS_DATBUFERR:
			dev->status = USB_ST_BUF_ERR;
			break;
		case QT_TOKEN_STATUS_HALTED | QT_TOKEN_STATUS_BABBLEDET:
		case QT_TOKEN_STATUS_BABBLEDET:
			dev->status = USB_ST_BABBLE_DET;
			break;
		default:
			dev->status = USB_ST_CRC_ERR;
			if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
				dev->status |= USB_ST_STALLED;
			break;
		}
		dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
	} else {
		dev->act_len = 0;
#ifndef CONFIG_USB_EHCI_FARADAY
		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
		      dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts),
		      ehci_readl(&ctrl->hcor->or_portsc[0]),
		      ehci_readl(&ctrl->hcor->or_portsc[1]));
#endif
	}

	free(qtd);
	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;

fail:
	free(qtd);
	return -1;
}
コード例 #5
0
int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp)
{
	int devnum = usb_pipedevice(pipe);

	return usb_emul_find_devnum(devnum, emulp);
}
コード例 #6
0
ファイル: musb_hcd.c プロジェクト: AnAtom/u-boot-sunxi
/*
 * do a bulk transfer
 */
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
					void *buffer, int len)
{
	int dir_out = usb_pipeout(pipe);
	int ep = usb_pipeendpoint(pipe);
#ifndef MUSB_NO_MULTIPOINT
	int devnum = usb_pipedevice(pipe);
#endif
	u8  type;
	u16 csr;
	u32 txlen = 0;
	u32 nextlen = 0;
	u8  devspeed;

	/* select bulk endpoint */
	writeb(MUSB_BULK_EP, &musbr->index);

#ifndef MUSB_NO_MULTIPOINT
	/* write the address of the device */
	if (dir_out)
		writeb(devnum, &musbr->tar[MUSB_BULK_EP].txfuncaddr);
	else
		writeb(devnum, &musbr->tar[MUSB_BULK_EP].rxfuncaddr);
#endif

	/* configure the hub address and the port number as required */
	devspeed = get_dev_speed(dev);
	if ((musb_ishighspeed()) && (dev->parent != NULL) &&
		(devspeed != MUSB_TYPE_SPEED_HIGH)) {
		/*
		 * MUSB is in high speed and the destination device is full
		 * speed device. So configure the hub address and port
		 * address registers.
		 */
		config_hub_port(dev, MUSB_BULK_EP);
	} else {
#ifndef MUSB_NO_MULTIPOINT
		if (dir_out) {
			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubaddr);
			writeb(0, &musbr->tar[MUSB_BULK_EP].txhubport);
		} else {
			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubaddr);
			writeb(0, &musbr->tar[MUSB_BULK_EP].rxhubport);
		}
#endif
		devspeed = musb_cfg.musb_speed;
	}

	/* Write the saved toggle bit value */
	write_toggle(dev, ep, dir_out);

	if (dir_out) { /* bulk-out transfer */
		/* Program the TxType register */
		type = (devspeed << MUSB_TYPE_SPEED_SHIFT) |
			   (MUSB_TYPE_PROTO_BULK << MUSB_TYPE_PROTO_SHIFT) |
			   (ep & MUSB_TYPE_REMOTE_END);
		writeb(type, &musbr->txtype);

		/* Write maximum packet size to the TxMaxp register */
		writew(dev->epmaxpacketout[ep], &musbr->txmaxp);
		while (txlen < len) {
			nextlen = ((len-txlen) < dev->epmaxpacketout[ep]) ?
					(len-txlen) : dev->epmaxpacketout[ep];

#ifdef CONFIG_USB_BLACKFIN
			/* Set the transfer data size */
			writew(nextlen, &musbr->txcount);
#endif

			/* Write the data to the FIFO */
			write_fifo(MUSB_BULK_EP, nextlen,
					(void *)(((u8 *)buffer) + txlen));

			/* Set the TxPktRdy bit */
			csr = readw(&musbr->txcsr);
			writew(csr | MUSB_TXCSR_TXPKTRDY, &musbr->txcsr);

			/* Wait until the TxPktRdy bit is cleared */
			if (wait_until_txep_ready(dev, MUSB_BULK_EP) != 1) {
				readw(&musbr->txcsr);
				usb_settoggle(dev, ep, dir_out,
				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1);
				dev->act_len = txlen;
				return 0;
			}
			txlen += nextlen;
		}

		/* Keep a copy of the data toggle bit */
		csr = readw(&musbr->txcsr);
		usb_settoggle(dev, ep, dir_out,
				(csr >> MUSB_TXCSR_H_DATATOGGLE_SHIFT) & 1);
	} else { /* bulk-in transfer */
コード例 #7
0
ファイル: musb_hcd.c プロジェクト: AnAtom/u-boot-sunxi
/*
 * do a control transfer
 */
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
			int len, struct devrequest *setup)
{
	int devnum = usb_pipedevice(pipe);
	u8  devspeed;

#ifdef MUSB_NO_MULTIPOINT
	/* Control message is for the HUB? */
	if (devnum == rh_devnum) {
		int stat = musb_submit_rh_msg(dev, pipe, buffer, len, setup);
		if (stat)
			return stat;
	}
#endif

	/* select control endpoint */
	writeb(MUSB_CONTROL_EP, &musbr->index);
	readw(&musbr->txcsr);

#ifndef MUSB_NO_MULTIPOINT
	/* target addr and (for multipoint) hub addr/port */
	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].txfuncaddr);
	writeb(devnum, &musbr->tar[MUSB_CONTROL_EP].rxfuncaddr);
#endif

	/* configure the hub address and the port number as required */
	devspeed = get_dev_speed(dev);
	if ((musb_ishighspeed()) && (dev->parent != NULL) &&
		(devspeed != MUSB_TYPE_SPEED_HIGH)) {
		config_hub_port(dev, MUSB_CONTROL_EP);
		writeb(devspeed << 6, &musbr->txtype);
	} else {
		writeb(musb_cfg.musb_speed << 6, &musbr->txtype);
#ifndef MUSB_NO_MULTIPOINT
		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubaddr);
		writeb(0, &musbr->tar[MUSB_CONTROL_EP].txhubport);
		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubaddr);
		writeb(0, &musbr->tar[MUSB_CONTROL_EP].rxhubport);
#endif
	}

	/* Control transfer setup phase */
	if (ctrlreq_setup_phase(dev, setup) < 0)
		return 0;

	switch (setup->request) {
	case USB_REQ_GET_DESCRIPTOR:
	case USB_REQ_GET_CONFIGURATION:
	case USB_REQ_GET_INTERFACE:
	case USB_REQ_GET_STATUS:
	case USB_MSC_BBB_GET_MAX_LUN:
		/* control transfer in-data-phase */
		if (ctrlreq_in_data_phase(dev, len, buffer) < 0)
			return 0;
		/* control transfer out-status-phase */
		if (ctrlreq_out_status_phase(dev) < 0)
			return 0;
		break;

	case USB_REQ_SET_ADDRESS:
	case USB_REQ_SET_CONFIGURATION:
	case USB_REQ_SET_FEATURE:
	case USB_REQ_SET_INTERFACE:
	case USB_REQ_CLEAR_FEATURE:
	case USB_MSC_BBB_RESET:
		/* control transfer in status phase */
		if (ctrlreq_in_status_phase(dev) < 0)
			return 0;
		break;

	case USB_REQ_SET_DESCRIPTOR:
		/* control transfer out data phase */
		if (ctrlreq_out_data_phase(dev, len, buffer) < 0)
			return 0;
		/* control transfer in status phase */
		if (ctrlreq_in_status_phase(dev) < 0)
			return 0;
		break;

	default:
		/* unhandled control transfer */
		return -1;
	}

	dev->status = 0;
	dev->act_len = len;

#ifdef MUSB_NO_MULTIPOINT
	/* Set device address to USB_FADDR register */
	if (setup->request == USB_REQ_SET_ADDRESS)
		writeb(dev->devnum, &musbr->faddr);
#endif

	return len;
}
コード例 #8
0
ファイル: mod_host.c プロジェクト: Abioy/kasan
static struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
						 struct urb *urb)
{
	struct usbhsh_device *udev = NULL;
	struct usbhsh_device *udev0 = usbhsh_device0(hpriv);
	struct usbhsh_device *pos;
	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
	struct device *dev = usbhsh_hcd_to_dev(hcd);
	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
	unsigned long flags;
	u16 upphub, hubport;
	int i;

	/*
	 * This function should be called only while urb is pointing to device0.
	 * It will attach unused usbhsh_device to urb (usbv),
	 * and initialize device0.
	 * You can use usbhsh_device_get() to get "current" udev,
	 * and usbhsh_usbv_to_udev() is for "attached" udev.
	 */
	if (0 != usb_pipedevice(urb->pipe)) {
		dev_err(dev, "%s fail: urb isn't pointing device0\n", __func__);
		return NULL;
	}

	/********************  spin lock ********************/
	usbhs_lock(priv, flags);

	/*
	 * find unused device
	 */
	usbhsh_for_each_udev(pos, hpriv, i) {
		if (usbhsh_udev_is_used(pos))
			continue;
		udev = pos;
		break;
	}

	if (udev) {
		/*
		 * usbhsh_usbv_to_udev()
		 * usbhsh_udev_to_usbv()
		 * will be enable
		 */
		dev_set_drvdata(&usbv->dev, udev);
		udev->usbv = usbv;
	}

	usbhs_unlock(priv, flags);
	/********************  spin unlock ******************/

	if (!udev) {
		dev_err(dev, "no free usbhsh_device\n");
		return NULL;
	}

	if (usbhsh_device_has_endpoint(udev)) {
		dev_warn(dev, "udev have old endpoint\n");
		usbhsh_endpoint_detach_all(hpriv, udev);
	}

	if (usbhsh_device_has_endpoint(udev0)) {
		dev_warn(dev, "udev0 have old endpoint\n");
		usbhsh_endpoint_detach_all(hpriv, udev0);
	}

	/* uep will be attached */
	INIT_LIST_HEAD(&udev0->ep_list_head);
	INIT_LIST_HEAD(&udev->ep_list_head);

	/*
	 * set device0 config
	 */
	usbhs_set_device_config(priv,
				0, 0, 0, usbv->speed);

	/*
	 * set new device config
	 */
	upphub	= 0;
	hubport	= 0;
	if (!usbhsh_connected_to_rhdev(hcd, udev)) {
		/* if udev is not connected to rhdev, it means parent is Hub */
		struct usbhsh_device *parent = usbhsh_device_parent(udev);

		upphub	= usbhsh_device_number(hpriv, parent);
		hubport	= usbhsh_device_hubport(udev);

		dev_dbg(dev, "%s connecte to Hub [%d:%d](%p)\n", __func__,
			upphub, hubport, parent);
	}

	usbhs_set_device_config(priv,
			       usbhsh_device_number(hpriv, udev),
			       upphub, hubport, usbv->speed);

	dev_dbg(dev, "%s [%d](%p)\n", __func__,
		usbhsh_device_number(hpriv, udev), udev);

	return udev;
}