From: Kevin O'Connor Date: Sun, 18 Oct 2009 03:53:32 +0000 (-0400) Subject: Expand USB OHCI support. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=1c46a548f26cd28d478ae793e42bc1a82a977939;p=seabios.git Expand USB OHCI support. Get UHCI support to point where it works with (a modified) qemu. --- diff --git a/src/clock.c b/src/clock.c index aaa2488..0592a4e 100644 --- a/src/clock.c +++ b/src/clock.c @@ -127,6 +127,12 @@ calc_future_tsc(u32 msecs) u32 khz = GET_GLOBAL(cpu_khz); return rdtscll() + ((u64)khz * msecs); } +u64 +calc_future_tsc_usec(u32 usecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return rdtscll() + ((u64)(khz/1000) * usecs); +} /**************************************************************** diff --git a/src/config.h b/src/config.h index ae83ae3..7b472ad 100644 --- a/src/config.h +++ b/src/config.h @@ -31,7 +31,7 @@ // Support USB UHCI controllers #define CONFIG_USB_UHCI 1 // Support USB OHCI controllers -#define CONFIG_USB_OHCI 0 +#define CONFIG_USB_OHCI 1 // Support USB keyboards #define CONFIG_USB_KEYBOARD 1 // Support for IDE disk code diff --git a/src/usb-hid.c b/src/usb-hid.c index 6deb2b0..c849aa1 100644 --- a/src/usb-hid.c +++ b/src/usb-hid.c @@ -171,7 +171,7 @@ usb_check_key() { if (! CONFIG_USB_KEYBOARD) return; - void *pipe = GET_GLOBAL(keyboard_pipe); + struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe); if (!pipe) return; diff --git a/src/usb-ohci.c b/src/usb-ohci.c index 638b8a4..f1ca559 100644 --- a/src/usb-ohci.c +++ b/src/usb-ohci.c @@ -7,43 +7,117 @@ #include "util.h" // dprintf #include "pci.h" // pci_bdf_to_bus #include "config.h" // CONFIG_* -#include "ioport.h" // outw -#include "usb-ohci.h" // USBLEGSUP -#include "pci_regs.h" // PCI_BASE_ADDRESS_4 +#include "usb-ohci.h" // struct ohci_hcca +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 #include "usb.h" // struct usb_s #include "farptr.h" // GET_FLATPTR -#include "biosvar.h" // GET_GLOBAL -static void -reset_ohci(struct usb_s *cntl) -{ -} +#define FIT (1 << 31) -static void -configure_ohci(struct usb_s *cntl) +static int +start_ohci(struct usb_s *cntl, struct ohci_hcca *hcca) { - // XXX - check for SMM control? + u32 oldfminterval = readl(&cntl->ohci.regs->fminterval); + u32 oldrwc = readl(&cntl->ohci.regs->control) & OHCI_CTRL_RWC; - writel(&cntl->ohci.regs->intrdisable, OHCI_INTR_MIE); + // XXX - check if already running? - struct ohci_hcca *hcca = memalign_low(256, sizeof(*hcca)); - if (!hcca) { - dprintf(1, "No ram for ohci init\n"); - return; + // Do reset + writel(&cntl->ohci.regs->control, OHCI_USB_RESET | oldrwc); + readl(&cntl->ohci.regs->control); // flush writes + mdelay(50); + + // Do software init (min 10us, max 2ms) + u64 end = calc_future_tsc_usec(10); + writel(&cntl->ohci.regs->cmdstatus, OHCI_HCR); + for (;;) { + u32 status = readl(&cntl->ohci.regs->cmdstatus); + if (! status & OHCI_HCR) + break; + if (rdtscll() > end) { + dprintf(1, "Timeout on ohci software reset\n"); + return -1; + } } - + // Init memory + writel(&cntl->ohci.regs->ed_controlhead, (u32)cntl->ohci.control_ed); + writel(&cntl->ohci.regs->ed_bulkhead, 0); + writel(&cntl->ohci.regs->hcca, (u32)hcca); + + // Init fminterval + u32 fi = oldfminterval & 0x3fff; + writel(&cntl->ohci.regs->fminterval + , (((oldfminterval & FIT) ^ FIT) + | fi | (((6 * (fi - 210)) / 7) << 16))); + writel(&cntl->ohci.regs->periodicstart, ((9 * fi) / 10) & 0x3fff); + readl(&cntl->ohci.regs->control); // flush writes + + // XXX - verify that fminterval was setup correctly. + + // Go into operational state + writel(&cntl->ohci.regs->control + , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE + | OHCI_USB_OPER | oldrwc)); + readl(&cntl->ohci.regs->control); // flush writes + + return 0; } static void -start_ohci(struct usb_s *cntl) +stop_ohci(struct usb_s *cntl) { + u32 oldrwc = readl(&cntl->ohci.regs->control) & OHCI_CTRL_RWC; + writel(&cntl->ohci.regs->control, oldrwc); + readl(&cntl->ohci.regs->control); // flush writes } // Find any devices connected to the root hub. static int check_ohci_ports(struct usb_s *cntl) { + // Turn on power for all devices on roothub. + u32 rha = readl(&cntl->ohci.regs->roothub_a); + rha &= ~(RH_A_PSM | RH_A_OCPM); + writel(&cntl->ohci.regs->roothub_status, RH_HS_LPSC); + writel(&cntl->ohci.regs->roothub_b, RH_B_PPCM); + mdelay((rha >> 24) * 2); + + // Count and reset connected devices + int ports = rha & RH_A_NDP; + int totalcount = 0; + int i; + for (i=0; iohci.regs->roothub_portstatus[i]) & RH_PS_CCS) { + writel(&cntl->ohci.regs->roothub_portstatus[i], RH_PS_PRS); + totalcount++; + } + if (!totalcount) + // No devices connected + goto shutdown; + + mdelay(60); // XXX - should poll instead of using timer. + + totalcount = 0; + for (i=0; iohci.regs->roothub_portstatus[i]); + if ((sts & (RH_PS_CCS|RH_PS_PES)) == (RH_PS_CCS|RH_PS_PES)) { + int count = configure_usb_device(cntl, !!(sts & RH_PS_LSDA)); + if (! count) + // Shutdown port + writel(&cntl->ohci.regs->roothub_portstatus[i] + , RH_PS_CCS|RH_PS_LSDA); + totalcount += count; + } + } + if (!totalcount) + goto shutdown; + + return totalcount; + +shutdown: + // Turn off power to all ports + writel(&cntl->ohci.regs->roothub_status, RH_HS_LPS); return 0; } @@ -65,30 +139,109 @@ ohci_init(struct usb_s *cntl) pci_config_maskw(cntl->bdf, PCI_COMMAND , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); - reset_ohci(cntl); - configure_ohci(cntl); - start_ohci(cntl); + // XXX - check for and disable SMM control? - int count = check_ohci_ports(cntl); - if (! count) { - // XXX - no devices; free data structures. + // Disable interrupts + writel(&cntl->ohci.regs->intrdisable, ~0); + writel(&cntl->ohci.regs->intrstatus, ~0); + + // Allocate memory + struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca)); + struct ohci_ed *control_ed = malloc_high(sizeof(*control_ed)); + if (!hcca || !control_ed) { + dprintf(1, "No ram for ohci init\n"); return 0; } + memset(hcca, 0, sizeof(*hcca)); + memset(control_ed, 0, sizeof(*control_ed)); + control_ed->hwINFO = ED_SKIP; + cntl->ohci.control_ed = control_ed; + + int ret = start_ohci(cntl, hcca); + if (ret) + goto err; + int count = check_ohci_ports(cntl); + if (! count) + goto err; + return count; + +err: + stop_ohci(cntl); + free(hcca); + free(control_ed); return 0; } +static int +wait_ed(struct ohci_ed *ed) +{ + // XXX - 500ms just a guess + u64 end = calc_future_tsc(500); + for (;;) { + if (ed->hwHeadP == ed->hwTailP) + return 0; + if (rdtscll() > end) { + dprintf(1, "Timeout on wait_ed %p\n", ed); + return -1; + } + cpu_relax(); + } +} + int ohci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize) { if (! CONFIG_USB_OHCI) - return 0; + return -1; dprintf(5, "ohci_control %x\n", endp); - return 0; + struct usb_s *cntl = endp2cntl(endp); + int maxpacket = endp2maxsize(endp); + int lowspeed = endp2speed(endp); + int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7); + + // Setup transfer descriptors + struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3); + tds[0].hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + tds[0].hwCBP = (u32)cmd; + tds[0].hwNextTD = (u32)&tds[1]; + tds[0].hwBE = (u32)cmd + cmdsize - 1; + tds[1].hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC; + tds[1].hwCBP = datasize ? (u32)data : 0; + tds[1].hwNextTD = (u32)&tds[2]; + tds[1].hwBE = (u32)data + datasize - 1; + tds[2].hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + tds[2].hwCBP = 0; + tds[2].hwNextTD = (u32)&tds[3]; + tds[2].hwBE = 0; + + // Transfer data + struct ohci_ed *ed = cntl->ohci.control_ed; + ed->hwINFO = ED_SKIP; + barrier(); + ed->hwHeadP = (u32)&tds[0]; + ed->hwTailP = (u32)&tds[3]; + barrier(); + ed->hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0); + writel(&cntl->ohci.regs->cmdstatus, OHCI_CLF); + + int ret = wait_ed(ed); + ed->hwINFO = ED_SKIP; + udelay(1); // XXX - in case controller still accessing tds + free(tds); + return ret; } +struct ohci_pipe { + struct ohci_ed ed; + struct usb_pipe pipe; + void *data; + int count; + struct ohci_td *tds; +}; + struct usb_pipe * ohci_alloc_intr_pipe(u32 endp, int period) { @@ -96,14 +249,87 @@ ohci_alloc_intr_pipe(u32 endp, int period) return NULL; dprintf(7, "ohci_alloc_intr_pipe %x %d\n", endp, period); + struct usb_s *cntl = endp2cntl(endp); + int maxpacket = endp2maxsize(endp); + int lowspeed = endp2speed(endp); + int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7); + // XXX - just grab 20 for now. + int count = 20; + struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct ohci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) + goto err; + + struct ohci_ed *ed = &pipe->ed; + ed->hwHeadP = (u32)&tds[0]; + ed->hwTailP = (u32)&tds[count-1]; + ed->hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0); + ed->hwNextED = 0; + + int i; + for (i=0; iohci.regs->hcca; + for (i=0; iint_table); i++) + hcca->int_table[i] = (u32)ed; + + pipe->data = data; + pipe->count = count; + pipe->tds = tds; + pipe->pipe.endp = endp; + return &pipe->pipe; + +err: + free(pipe); + free(tds); + free(data); return NULL; } int -ohci_poll_intr(void *pipe, void *data) +ohci_poll_intr(struct usb_pipe *pipe, void *data) { ASSERT16(); if (! CONFIG_USB_OHCI) return -1; - return -1; + + struct ohci_pipe *p = container_of(pipe, struct ohci_pipe, pipe); + struct ohci_td *tds = GET_FLATPTR(p->tds); + struct ohci_td *head = (void*)GET_FLATPTR(p->ed.hwHeadP); + struct ohci_td *tail = (void*)GET_FLATPTR(p->ed.hwTailP); + int count = GET_FLATPTR(p->count); + int pos = (tail - tds + 1) % count; + struct ohci_td *next = &tds[pos]; + if (head == next) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + u32 endp = GET_FLATPTR(p->pipe.endp); + int maxpacket = endp2maxsize(endp); + void *pipedata = GET_FLATPTR(p->data); + void *intrdata = pipedata + maxpacket * pos; + memcpy_far(GET_SEG(SS), data + , FLATPTR_TO_SEG(intrdata), (void*)FLATPTR_TO_OFFSET(intrdata) + , maxpacket); + + // Reenable this td. + SET_FLATPTR(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC); + intrdata = pipedata + maxpacket * (tail-tds); + SET_FLATPTR(tail->hwCBP, (u32)intrdata); + SET_FLATPTR(tail->hwNextTD, (u32)next); + SET_FLATPTR(tail->hwBE, (u32)intrdata + maxpacket - 1); + + SET_FLATPTR(p->ed.hwTailP, (u32)next); + + return 0; } diff --git a/src/usb-ohci.h b/src/usb-ohci.h index 9d13f8c..e5d2127 100644 --- a/src/usb-ohci.h +++ b/src/usb-ohci.h @@ -7,7 +7,7 @@ int ohci_init(struct usb_s *cntl); int ohci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize); struct usb_pipe *ohci_alloc_intr_pipe(u32 endp, int period); -int ohci_poll_intr(void *pipe, void *data); +int ohci_poll_intr(struct usb_pipe *pipe, void *data); /**************************************************************** @@ -40,7 +40,6 @@ struct ohci_td { #define TD_CC 0xf0000000 #define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) #define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) #define TD_DONE 0x00020000 #define TD_ISO 0x00010000 @@ -92,6 +91,48 @@ struct ohci_regs { u32 roothub_portstatus[15]; } PACKED; +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_HCFS (3 << 6) +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_OPER (2 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +#define OHCI_HCR (1 << 0) +#define OHCI_CLF (1 << 1) + #define OHCI_INTR_MIE (1 << 31) +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + +#define RH_HS_LPS 0x00000001 +#define RH_HS_OCI 0x00000002 +#define RH_HS_DRWE 0x00008000 +#define RH_HS_LPSC 0x00010000 +#define RH_HS_OCIC 0x00020000 +#define RH_HS_CRWE 0x80000000 + +#define RH_B_DR 0x0000ffff +#define RH_B_PPCM 0xffff0000 + +#define RH_A_NDP (0xff << 0) +#define RH_A_PSM (1 << 8) +#define RH_A_NPS (1 << 9) +#define RH_A_DT (1 << 10) +#define RH_A_OCPM (1 << 11) +#define RH_A_NOCP (1 << 12) +#define RH_A_POTPGT (0xff << 24) + #endif // usb-ohci.h diff --git a/src/usb-uhci.c b/src/usb-uhci.c index 4d3df45..5829069 100644 --- a/src/usb-uhci.c +++ b/src/usb-uhci.c @@ -12,7 +12,6 @@ #include "pci_regs.h" // PCI_BASE_ADDRESS_4 #include "usb.h" // struct usb_s #include "farptr.h" // GET_FLATPTR -#include "biosvar.h" // GET_GLOBAL static void reset_uhci(struct usb_s *cntl) @@ -98,7 +97,7 @@ check_ports(struct usb_s *cntl) outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC1); if (port2 & USBPORTSC_CCS) outw(USBPORTSC_PR, cntl->uhci.iobase + USBPORTSC2); - mdelay(10); + mdelay(50); outw(0, cntl->uhci.iobase + USBPORTSC1); outw(0, cntl->uhci.iobase + USBPORTSC2); mdelay(10); @@ -174,7 +173,7 @@ uhci_control(u32 endp, int dir, const void *cmd, int cmdsize , void *data, int datasize) { if (! CONFIG_USB_UHCI) - return 0; + return -1; dprintf(5, "uhci_control %x\n", endp); struct usb_s *cntl = endp2cntl(endp); diff --git a/src/usb.c b/src/usb.c index edaef11..ecb8683 100644 --- a/src/usb.c +++ b/src/usb.c @@ -13,6 +13,7 @@ #include "usb-ohci.h" // ohci_init #include "usb-hid.h" // usb_keyboard_setup #include "usb.h" // struct usb_s +#include "biosvar.h" // GET_GLOBAL struct usb_s USBControllers[16] VAR16VISIBLE; @@ -46,8 +47,9 @@ alloc_intr_pipe(u32 endp, int period) int usb_poll_intr(struct usb_pipe *pipe, void *data) { - struct usb_s *cntl = endp2cntl(pipe->endp); - switch (cntl->type) { + u32 endp = GET_FLATPTR(pipe->endp); + struct usb_s *cntl = endp2cntl(endp); + switch (GET_GLOBAL(cntl->type)) { default: case USB_TYPE_UHCI: return uhci_poll_intr(pipe, data); @@ -185,10 +187,10 @@ configure_usb_device(struct usb_s *cntl, int lowspeed) if (ret) goto fail; - // XXX - free(config); + free(config); return 1; fail: - // XXX - free(config); + free(config); return 0; } diff --git a/src/usb.h b/src/usb.h index 36fde59..fe674c0 100644 --- a/src/usb.h +++ b/src/usb.h @@ -15,6 +15,7 @@ struct usb_s { } uhci; struct { struct ohci_regs *regs; + void *control_ed; } ohci; }; }; diff --git a/src/util.h b/src/util.h index c7fa261..9380190 100644 --- a/src/util.h +++ b/src/util.h @@ -219,6 +219,7 @@ void ndelay(u32 count); void udelay(u32 count); void mdelay(u32 count); u64 calc_future_tsc(u32 msecs); +u64 calc_future_tsc_usec(u32 usecs); void handle_1583(struct bregs *regs); void handle_1586(struct bregs *regs);