[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

BIOS to OS handover of ehci ports



This patch adds support for the handover of ehci ports between the BIOS and
OS.

This is useful for people with the new breed of legacy free machines. We got
a batch of new computers in at work with no dedicated PS/2 connectors for the
keyboard and mouse and discovered that the usb keyboard wasn't being detected
on boot by OpenBSD. However, if we unplug the keyboard and attach it to a
different port it is attached. Nothing happens if it is moved back to the
original port. Rebooting with the keyboard in a different port results in the
same story, but it shifts to the port the kb was plugged into.

This patch was inspired by a change in freebsd by Ian Dowse.

I need testing wherever possible, and especially on non i386 architectures
such as amd64 and macppc.

DG

Index: cardbus/ehci_cardbus.c
===================================================================
RCS file: /cvs/openbsd/src/sys/dev/cardbus/ehci_cardbus.c,v
retrieving revision 1.1
diff -u -p -r1.1 ehci_cardbus.c
--- cardbus/ehci_cardbus.c	7 Dec 2004 05:42:41 -0000	1.1
+++ cardbus/ehci_cardbus.c	21 Dec 2004 08:43:32 -0000
@@ -228,6 +228,8 @@ XXX	(ct->ct_cf->cardbus_mem_open)(cc, 0,
 		return;
 	}
 
+	sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_shutdown, &sc->sc);
+
 	/* Attach usb device. */
 	sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus,
 				       usbctlprint);
Index: pci/ehci_pci.c
===================================================================
RCS file: /cvs/openbsd/src/sys/dev/pci/ehci_pci.c,v
retrieving revision 1.3
diff -u -p -r1.3 ehci_pci.c
--- pci/ehci_pci.c	30 May 2004 01:25:17 -0000	1.3
+++ pci/ehci_pci.c	21 Dec 2004 08:43:32 -0000
@@ -69,10 +69,6 @@ extern int ehcidebug;
 #define DPRINTF(x)
 #endif
 
-int	ehci_pci_match(struct device *, void *, void *);
-void	ehci_pci_attach(struct device *, struct device *, void *);
-int	ehci_pci_detach(device_ptr_t, int);
-
 struct ehci_pci_softc {
 	ehci_softc_t		sc;
 	pci_chipset_tag_t	sc_pc;
@@ -80,6 +76,13 @@ struct ehci_pci_softc {
 	void 			*sc_ih;		/* interrupt vectoring */
 };
 
+int	ehci_pci_match(struct device *, void *, void *);
+void	ehci_pci_attach(struct device *, struct device *, void *);
+int	ehci_pci_detach(device_ptr_t, int);
+void	ehci_pci_givecontroller(struct ehci_pci_softc *);
+void	ehci_pci_takecontroller(struct ehci_pci_softc *);
+void	ehci_pci_shutdown(void *);
+
 struct cfattach ehci_pci_ca = {
 	sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach,
 	ehci_pci_detach, ehci_activate
@@ -203,6 +206,7 @@ ehci_pci_attach(struct device *parent, s
 	}
 	sc->sc.sc_ncomp = ncomp;
 
+	ehci_pci_takecontroller(sc);
 	r = ehci_init(&sc->sc);
 	if (r != USBD_NORMAL_COMPLETION) {
 		printf("%s: init failed, error=%d\n", devname, r);
@@ -210,6 +214,8 @@ ehci_pci_attach(struct device *parent, s
 		return;
 	}
 
+	sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_pci_shutdown, sc);
+
 	/* Attach usb device. */
 	sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus,
 				       usbctlprint);
@@ -233,4 +239,64 @@ ehci_pci_detach(device_ptr_t self, int f
 		sc->sc.sc_size = 0;
 	}
 	return (0);
+}
+
+void
+ehci_pci_givecontroller(struct ehci_pci_softc *sc)
+{
+	u_int32_t cparams, eec, legsup;
+	int eecp;
+
+	cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
+	for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+	    eecp = EHCI_EECP_NEXT(eec)) {
+		eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
+		if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+			continue;
+		legsup = eec;
+		pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
+		    legsup & ~EHCI_LEGSUP_OSOWNED);
+	}
+}
+
+void
+ehci_pci_takecontroller(struct ehci_pci_softc *sc)
+{
+	u_int32_t cparams, eec, legsup;
+	int eecp, i;
+
+	cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
+	/* Synchronise with the BIOS if it owns the controller. */
+	for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+	    eecp = EHCI_EECP_NEXT(eec)) {
+		eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
+		if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
+			continue;
+		legsup = eec;
+		pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
+		    legsup | EHCI_LEGSUP_OSOWNED);
+		if (legsup & EHCI_LEGSUP_BIOSOWNED) {
+			DPRINTF(("%s: waiting for BIOS to give up control\n",
+			    USBDEVNAME(sc->sc.sc_bus.bdev)));
+			for (i = 0; i < 5000; i++) {
+				legsup = pci_conf_read(sc->sc_pc, sc->sc_tag,
+				    eecp);
+				if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0)
+					break;
+				DELAY(1000);
+			}
+			if (legsup & EHCI_LEGSUP_BIOSOWNED)
+				printf("%s: timed out waiting for BIOS\n",
+				    USBDEVNAME(sc->sc.sc_bus.bdev));
+		}
+	}
+}
+
+void
+ehci_pci_shutdown(void *v)
+{
+	struct ehci_pci_softc *sc = (struct ehci_pci_softc *)v;
+
+	ehci_shutdown(&sc->sc);
+	ehci_pci_givecontroller(sc);
 }
Index: usb/ehci.c
===================================================================
RCS file: /cvs/openbsd/src/sys/dev/usb/ehci.c,v
retrieving revision 1.32
diff -u -p -r1.32 ehci.c
--- usb/ehci.c	31 Oct 2004 10:29:45 -0000	1.32
+++ usb/ehci.c	21 Dec 2004 08:43:32 -0000
@@ -144,7 +144,6 @@ struct ehci_pipe {
 	} u;
 };
 
-Static void		ehci_shutdown(void *);
 Static void		ehci_power(int, void *);
 
 Static usbd_status	ehci_open(usbd_pipe_handle);
@@ -416,7 +415,6 @@ ehci_init(ehci_softc_t *sc)
 	sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
 
 	sc->sc_powerhook = powerhook_establish(ehci_power, sc);
-	sc->sc_shutdownhook = shutdownhook_establish(ehci_shutdown, sc);
 
 	sc->sc_eintrs = EHCI_NORMAL_INTRS;
 
Index: usb/ehcireg.h
===================================================================
RCS file: /cvs/openbsd/src/sys/dev/usb/ehcireg.h,v
retrieving revision 1.11
diff -u -p -r1.11 ehcireg.h
--- usb/ehcireg.h	25 Oct 2004 22:30:04 -0000	1.11
+++ usb/ehcireg.h	21 Dec 2004 08:43:32 -0000
@@ -64,9 +64,16 @@
 
 #define PCI_EHCI_PORTWAKECAP	0x62	/* RW Port wake caps (opt)  */
 
-/* Regs ar EECP + offset */
-#define PCI_EHCI_USBLEGSUP	0x00
-#define PCI_EHCI_USBLEGCTLSTS	0x04
+/* EHCI Extended Capabilities */
+#define EHCI_EC_LEGSUP		0x01
+
+#define EHCI_EECP_NEXT(x)	(((x) >> 8) & 0xff)
+#define EHCI_EECP_ID(x)		((x) & 0xff)
+
+#define EHCI_LEGSUP_LEGSUP	0x01
+#define  EHCI_LEGSUP_OSOWNED	0x01000000 /* OS owned semaphore */
+#define  EHCI_LEGSUP_BIOSOWNED	0x00010000 /* BIOS owned semaphore */
+#define PCI_LEGSUP_USBLEGCTLSTS	0x04
 
 /*** EHCI capability registers ***/
 
Index: usb/ehcivar.h
===================================================================
RCS file: /cvs/openbsd/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.6
diff -u -p -r1.6 ehcivar.h
--- usb/ehcivar.h	20 Oct 2004 12:45:31 -0000	1.6
+++ usb/ehcivar.h	21 Dec 2004 08:43:32 -0000
@@ -154,3 +154,4 @@ usbd_status	ehci_init(ehci_softc_t *);
 int		ehci_intr(void *);
 int		ehci_detach(ehci_softc_t *, int);
 int		ehci_activate(device_ptr_t, enum devact);
+void		ehci_shutdown(void *);



Visit your host, monkey.org