aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clock.c6
-rw-r--r--src/config.h2
-rw-r--r--src/usb-hid.c2
-rw-r--r--src/usb-ohci.c282
-rw-r--r--src/usb-ohci.h45
-rw-r--r--src/usb-uhci.c5
-rw-r--r--src/usb.c10
-rw-r--r--src/usb.h1
-rw-r--r--src/util.h1
9 files changed, 315 insertions, 39 deletions
diff --git a/src/clock.c b/src/clock.c
index aaa24885..0592a4ec 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 ae83ae39..7b472ad5 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 6deb2b09..c849aa15 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 638b8a4b..f1ca559d 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; i<ports; i++)
+ if (readl(&cntl->ohci.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; i<ports; i++) {
+ u32 sts = readl(&cntl->ohci.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; i<count-1; i++) {
+ tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC;
+ tds[i].hwCBP = (u32)data + maxpacket * i;
+ tds[i].hwNextTD = (u32)&tds[i+1];
+ tds[i].hwBE = tds[i].hwCBP + maxpacket - 1;
+ }
+
+ // XXX - need schedule - just add to primary list for now.
+ barrier();
+ struct ohci_hcca *hcca = (void*)cntl->ohci.regs->hcca;
+ for (i=0; i<ARRAY_SIZE(hcca->int_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 9d13f8ce..e5d21270 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 4d3df452..5829069c 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 edaef114..ecb86839 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 36fde59f..fe674c0b 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 c7fa2616..93801907 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);