Date: Sun, 1 Aug 2010 19:07:03 GMT From: Hans Petter Selasky <hselasky@FreeBSD.org> To: Perforce Change Reviews <perforce@FreeBSD.org> Subject: PERFORCE change 181692 for review Message-ID: <201008011907.o71J739I037130@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@181692?ac=10 Change 181692 by hselasky@hselasky_laptop001 on 2010/08/01 19:06:24 USB controller (XHCI): - implement endpoint extension and transfer insertion and removal from hardware DMA queue. Affected files ... .. //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#7 edit .. //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#9 edit .. //depot/projects/usb/src/sys/dev/usb/controller/xhcireg.h#10 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#7 (text+ko) ==== @@ -639,7 +639,8 @@ i = sc->sc_command_idx; j = sc->sc_command_ccs; - while (1) { + /* check if there are any commands on the queue */ + while ((pcmd = TAILQ_FIRST(&sc->sc_cmd_head)) != NULL) { k = (phwr->hwr_events[i].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT)) ? 1 : 0; @@ -649,11 +650,6 @@ if ((sc->sc_cmd_dp == temp) && (j == k)) break; - /* check if there are any commands on the queue */ - pcmd = TAILQ_FIRST(&sc->sc_cmd_head); - if (pcmd == NULL) - break; - TAILQ_REMOVE(&sc->sc_cmd_head, pcmd, entry); pcmd->entry.tqe_prev = NULL; @@ -662,7 +658,7 @@ usb_pc_cpu_flush(&sc->sc_hw.root_pc); - temp = pcmd->trb.dwTrb3 | htole32(XHCI_TRB_3_IOC_BIT); + temp = pcmd->trb.dwTrb3; if (j) temp |= htole32(XHCI_TRB_3_CYCLE_BIT); @@ -706,6 +702,123 @@ sc->sc_command_ccs = j; } +static usb_error_t +xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb, + uint16_t timeout_ms) +{ + XXX implement; +} + +static usb_error_t +xhci_cmd_disable_slot(struct xhci_softc *sc, uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = 0; + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT) | + XHCI_TRB_3_SLOT_SET(slot_id); + + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + +static usb_error_t +xhci_cmd_configure_ep(struct xhci_softc *sc, uint64_t input_ctx, + uint8_t deconfigure, uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = htole64(input_ctx); + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP) | + XHCI_TRB_3_SLOT_SET(slot_id); + + if (deconfigure) + dword |= XHCI_TRB_3_DCEP_BIT; + + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + +static usb_error_t +xhci_cmd_evaluate_ctx(struct xhci_softc *sc, uint64_t input_ctx, + uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = htole64(input_ctx); + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX) | + XHCI_TRB_3_SLOT_SET(slot_id); + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + +static usb_error_t +xhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve, + uint8_t ep_id, uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = 0; + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP) | + XHCI_TRB_3_SLOT_SET(slot_id) | + XHCI_TRB_3_SLOT_SET(ep_id); + + if (preserve) + dword |= XHCI_TRB_3_PRSV_BIT; + + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + +static usb_error_t +xhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t suspend, + uint8_t ep_id, uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = 0; + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP) | + XHCI_TRB_3_SLOT_SET(slot_id) | + XHCI_TRB_3_SLOT_SET(ep_id); + + if (suspend) + dword |= XHCI_TRB_3_SUSP_EP_BIT; + + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + +static usb_error_t +xhci_cmd_reset_dev(struct xhci_softc *sc, uint8_t slot_id) +{ + struct xhci_trb trb; + uint32_t dword; + + trb.qwTrb0 = 0; + trb.dwTrb2 = 0; + dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_DEVICE) | + XHCI_TRB_3_SLOT_SET(slot_id); + + trb.dwTrb3 = htole32(dword); + + return (xhci_do_command(sc, &trb, 50 /* ms */)); +} + /*------------------------------------------------------------------------* * xhci_interrupt - XHCI interrupt handler *------------------------------------------------------------------------*/ @@ -1195,9 +1308,6 @@ td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15)); td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT); - /* link the current TD with the next one */ - td->td_trb[td->ntrb].qwTrb0 = XXX; - usb_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ @@ -1205,7 +1315,224 @@ xfer->td_transfer_last = td; } +static usb_error_t +xhci_alloc_endpoint_ext(struct xhci_softc *sc, uint8_t index, + struct usb_endpoint_descriptor *edesc) +{ + struct usb_page_search buf_res; + struct xhci_endpoint_ext *pepext; + struct usb_page_cache *pc; + struct usb_page *pg; + uint8_t epno; + + epno = edesc->bEndpointAddress; + + if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) + epno |= UE_DIR_IN; + + epno = XHCI_EPNO2EPID(epno); + pc = &sc->sc_hw.devs[index].endpoint_pc[epno]; + pg = &sc->sc_hw.devs[index].endpoint_pg[epno]; + + /* need to initialize the page cache */ + pc->tag_parent = sc->sc_bus.dma_parent_tag; + + if (usb_pc_alloc_mem(pc, pg, sizeof(*pepext), XHCI_TRB_ALIGN)) { + return (USB_ERR_NOMEM); /* failure */ + } + + /* the allocated memory is zeroed */ + + usbd_get_page(pc, 0, &buf_res); + + pepext = buf_res.buffer; + + pepext->page_cache = pc; + pepext->trb_ccs = 1; + + usb_pc_cpu_flush(pepext->page_cache); + + return (0); +} + static void +xhci_free_endpoint_ext(struct xhci_softc *sc, uint8_t index, + struct usb_endpoint_descriptor *edesc) +{ + struct usb_page_cache *pc; + uint8_t epno; + + epno = edesc->bEndpointAddress; + + if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL) + epno |= UE_DIR_IN; + + epno = XHCI_EPNO2EPID(epno); + pc = &sc->sc_hw.devs[index].endpoint_pc[epno]; + + usb_pc_free_mem(pc); +} + +static void +xhci_get_endpoint_ext(struct usb_xfer *xfer, struct usb_page_search *buf) +{ + struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); + uint8_t epno; + uint8_t index; + + epno = xfer->endpointno; + if (xfer->flags_int.control_xfr) + epno |= UE_DIR_IN; + + epno = XHCI_EPNO2EPID(epno); + index = xfer->xroot->udev->device_index; + + usbd_get_page(&sc->sc_hw.devs[index].endpoint_pc[epno], 0, buf); +} + +static void +xhci_endpoint_doorbell(struct usb_xfer *xfer) +{ + struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus); + uint8_t epno; + uint8_t index; + + epno = xfer->endpointno; + if (xfer->flags_int.control_xfr) + epno |= UE_DIR_IN; + + epno = XHCI_EPNO2EPID(epno); + index = xfer->xroot->udev->device_index; + + XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(0)); +} + +static void +xhci_transfer_remove(struct usb_xfer *xfer, usb_error_t error) +{ + struct usb_page_search buf_res; + struct xhci_endpoint_ext *pepext; + + if (xfer->flags_int.bandwidth_reclaimed) { + xfer->flags_int.bandwidth_reclaimed = 0; + + xhci_get_endpoint_ext(xfer, &buf_res); + + pepext = buf_res.buffer; + + pepext->trb_used--; + + usb_pc_cpu_flush(pepext->page_cache); + + if (error) + xhci_transfer_stop_endpoint(xfer); + } +} + +static usb_error_t +xhci_transfer_insert(struct usb_xfer *xfer) +{ + struct usb_page_search buf_res; + struct xhci_td *td_first; + struct xhci_td *td_last; + struct xhci_endpoint_ext *pepext; + uint64_t addr; + uint32_t temp; + uint8_t i; + uint8_t j; + + /* check if already inserted */ + if (xfer->flags_int.bandwidth_reclaimed) + return (0); + + xhci_get_endpoint_ext(xfer, &buf_res); + + td_first = xfer->td_transfer_first; + td_last = xfer->td_transfer_last; + pepext = buf_res.buffer; + addr = buf_res.physaddr; + + if (pepext->trb_used >= (XHCI_MAX_TRANSFERS - 1)) + return (USB_ERR_NOMEM); + + pepext->trb_used++; + + xfer->flags_int.bandwidth_reclaimed = 1; + + i = pepext->trb_index; + j = pepext->trb_ccs; + + /* update TC and next pointer of last link TRB */ + + temp = td_last->td_trb[td_last->ntrb].dwTrb3; + + if (j) + temp |= htole32(XHCI_TRB_3_TC_BIT); + else + temp &= ~htole32(XHCI_TRB_3_TC_BIT); + + addr += (uintptr_t)&((struct xhci_endpoint_ext *)0)->trb[i + 1]; + + td_last->td_trb[td_last->ntrb].dwTrb3 = temp; + td_last->td_trb[td_last->ntrb].qwTrb0 = htole64(addr); + + usb_pc_cpu_flush(td_last->page_cache); + + /* update next pointer of link TRB */ + + pepext->trb[i].qwTrb0 = td_first->td_self; + pepext->trb[i].dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); + + usb_pc_cpu_flush(pepext->page_cache); + + if (j) + temp = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TC_BIT | + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); + else + temp = htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); + + pepext->trb[i].dwTrb3 = temp; + + usb_pc_cpu_flush(pepext->page_cache); + + /* advance queue */ + + i++; + + if (i == (XHCI_MAX_TRANSFERS - 1)) { + + addr = buf_res.physaddr; + + /* update next pointer of link TRB */ + + pepext->trb[i].qwTrb0 = htole64(addr); + pepext->trb[i].dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0)); + + usb_pc_cpu_flush(pepext->page_cache); + + if (j) + temp = htole32(XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_TC_BIT | + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); + else + temp = htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK)); + + pepext->trb[i].dwTrb3 = temp; + + usb_pc_cpu_flush(pepext->page_cache); + + xhci_endpoint_doorbell(xfer); + + i = 0; + j ^= 1; + } + + pepext->trb_index = i; + pepext->trb_ccs = j; + + return (0); +} + +static void xhci_root_intr(struct xhci_softc *sc) { uint16_t i; @@ -1242,6 +1569,9 @@ DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); + /* remove transfer from HW queue */ + xhci_transfer_remove(xfer, error); + /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } @@ -1273,11 +1603,20 @@ /* put transfer on interrupt queue */ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer); + + /* try to insert xfer on HW queue */ + xhci_transfer_insert(xfer); } static void xhci_device_generic_start(struct usb_xfer *xfer) { + /* try to insert xfer on HW queue */ + if (xhci_transfer_insert(xfer) != 0) { + DPRINTFN(0, "Failed to insert " + "transfer %p into HW queue.\n", xfer); + } + /* start timeout, if any */ if (xfer->timeout != 0) usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout); ==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#9 (text+ko) ==== @@ -27,11 +27,12 @@ #define _XHCI_H_ #define XHCI_MAX_DEVICES MIN(USB_MAX_DEVICES, 128) -#define XHCI_MAX_ENDPOINTS 32 +#define XHCI_MAX_ENDPOINTS 32 /* hardcoded - do not change */ #define XHCI_MAX_SCRATCHPADS 32 -#define XHCI_MAX_EVENTS (16 * 7) -#define XHCI_MAX_COMMANDS (16 * 7) +#define XHCI_MAX_EVENTS (16 * 13) +#define XHCI_MAX_COMMANDS (16 * 1) #define XHCI_MAX_RSEG 1 +#define XHCI_MAX_TRANSFERS 4 #define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */ #define XHCI_DEV_CTX_ALIGN 64 /* bytes */ @@ -55,6 +56,8 @@ #define XHCI_BAA_MASK 0xFFFFFFFFFFFFFFE0ULL }; +#define XHCI_EPNO2EPID(x) ((((x) & UE_DIR_IN) ? 1 : 0) | (2 * ((x) & UE_ADDR))) + struct xhci_slot_ctx { volatile uint32_t dwSctx0; #define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF) @@ -197,6 +200,8 @@ #define XHCI_TRB_3_IOC_BIT (1U << 5) #define XHCI_TRB_3_IDT_BIT (1U << 6) #define XHCI_TRB_3_BEI_BIT (1U << 9) +#define XHCI_TRB_3_DCEP_BIT (1U << 9) +#define XHCI_TRB_3_PRSV_BIT (1U << 9) #define XHCI_TRB_3_TRT_MASK (3U << 16) #define XHCI_TRB_3_TRT_NONE (0U << 16) #define XHCI_TRB_3_TRT_OUT (2U << 16) @@ -207,6 +212,11 @@ #define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF) #define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20) #define XHCI_TRB_3_ISO_SIA_BIT (1U << 31) +#define XHCI_TRB_3_EP_GET(x) (((x) >> 20) & 0x1F) +#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 20) +#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23) +#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF) +#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24) /* Commands */ #define XHCI_TRB_TYPE_RESERVED 0x00 @@ -324,23 +334,26 @@ }; struct xhci_endpoint_ext { - TAILQ_HEAD(, xhci_qh) head; - struct usb_page_cache root_pc; + struct xhci_trb trb[XHCI_MAX_TRANSFERS]; - struct usb_page root_pg; + struct xhci_command ep_stop_cmd; - //XXX data for QH!; + struct usb_page_cache *page_cache; - uint8_t pstreams; + uint8_t trb_used; + uint8_t trb_ccs; + uint8_t trb_index; }; struct xhci_hw_dev { struct usb_page_cache device_pc; + struct usb_page_cache endpoint_pc[XHCI_MAX_ENDPOINTS]; struct usb_page_cache input_pc; struct usb_page_cache scratch_pc[XHCI_MAX_SCRATCHPADS]; struct usb_page device_pg; + struct usb_page endpoint_pg[XHCI_MAX_ENDPOINTS]; struct usb_page input_pg; struct usb_page scratch_pg[XHCI_MAX_SCRATCHPADS]; }; ==== //depot/projects/usb/src/sys/dev/usb/controller/xhcireg.h#10 (text+ko) ====
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201008011907.o71J739I037130>