PERFORCE change 181692 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sun Aug 1 19:07:04 UTC 2010
http://p4web.freebsd.org/@@181692?ac=10
Change 181692 by hselasky at 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) ====
More information about the p4-projects
mailing list