USB on ES40
Miod Vallat <[email protected]> Tue, 18 Feb 2014 21:30:41 +0000
| Newsgroups | gmane.os.openbsd.alpha |
|---|---|
| Message-ID | <[email protected]> |
Hello,
there seem to be a few alpha systems where the USB ports do not behave
correctly. One of these is the ES40, where it is often necessary to
disable ohci to be able to use the system.
I have spent some time pulling hair[0] and trying to figure out why
the USB controller would trigger an interrupt storm, even with no
devices plugged.
It turns out that, for some better deeply buried reason, the USB
interrupt on all high-end alpha systems (at least Tsunami [1] and Titan
[2] systems, but probably also Marvel [3]) is routed through the ISA
bridge, and is shown to the kernel as an ISA interrupt.
Some systems (XP1000, DS20) do not lie further and expose a PCI
interrupt (level triggered) with an ISA interrupt vector number, and
everything is fine. But other systems, such as the ES40, work so hard
masquerading the USB interrupt as an ISA interrupt than it seems to
become an edge-triggered interrupt. There lies madness, and this is why
interrupt storms occur on ES40 due to the USB controller, unless it is
disabled in the kernel (boot -fl c to the rescue!)
The following diff attempts to recognize systems where the USB
interrupt is edge triggered, and prevent the interrupt controller logic
to smartly set it as level triggered. This allows me to use the USB
ports of my ES40 (tested with an USB keyboard and a MAXINT-in-one smart
card reader).
If your alpha has both an sio0 device and an ohci0 device, please test
this (against HEAD... build GENERIC.MP if you have a multiprocessor
system!), and report whether there is a change in USB behaviour.
Thanks,
Miod
[0] Don't worry, my non-white hair situation has been critical for
years, with no hope even in the long term undyed future.
[1] CS20, DS10/20, ES40, XP900/1000, DP264, UP* too
[2] DS15/25, ES45
[3] ES47/80, GS160, oh wait we don't run on these yet, but if you have
one and are willing to test kernels, pleas*e get in touch with me...
Index: pci/sio_pic.c
===================================================================
RCS file: /cvs/src/sys/arch/alpha/pci/sio_pic.c,v
retrieving revision 1.36
diff -u -p -r1.36 sio_pic.c
--- pci/sio_pic.c 18 Feb 2014 19:37:33 -0000 1.36
+++ pci/sio_pic.c 18 Feb 2014 21:12:53 -0000
@@ -116,6 +116,15 @@ u_int8_t initial_ocw1[2];
u_int8_t initial_elcr[2];
#endif
+/*
+ * Overrides for ELCR settings.
+ * These are used on ES40 and similar systems suffering from a PCI USB HCI
+ * interrupt being routed through the ISA logic with actual logic to
+ * make it behave an edge-triggered interrupt, although PCI interrupts are
+ * supposed to be level-triggered.
+ */
+u_int8_t elcr_override[2] = { 0x00, 0x00 };
+
void sio_setirqstat(int, int, int);
int sio_intr_alloc(void *, int, int, int *);
int sio_intr_check(void *, int, int);
@@ -135,6 +144,9 @@ bus_space_handle_t sio_ioh_elcr;
int
i82378_setup_elcr()
{
+ int device, maxndevs;
+ pcitag_t tag;
+ pcireg_t id;
int rv;
/*
@@ -145,12 +157,30 @@ i82378_setup_elcr()
rv = bus_space_map(sio_iot, 0x4d0, 2, 0, &sio_ioh_elcr);
- if (rv == 0) {
- sio_read_elcr = i82378_read_elcr;
- sio_write_elcr = i82378_write_elcr;
+ if (rv != 0)
+ return 0;
+
+ sio_read_elcr = i82378_read_elcr;
+ sio_write_elcr = i82378_write_elcr;
+
+ /*
+ * Search PCI configuration space for an ALI M5237 USB controller
+ * on the first bus.
+ */
+
+ maxndevs = pci_bus_maxdevs(sio_pc, 0);
+
+ for (device = 0; device < maxndevs; device++) {
+ tag = pci_make_tag(sio_pc, 0, device, 0);
+ id = pci_conf_read(sio_pc, tag, PCI_ID_REG);
+
+ if (id == PCI_ID_CODE(PCI_VENDOR_ALI, PCI_PRODUCT_ALI_M5237)) {
+ elcr_override[10 / 8] |= 1 << (10 % 8);
+ break;
+ }
}
- return (rv);
+ return (0);
}
u_int8_t
@@ -203,31 +233,13 @@ cy82c693_setup_elcr()
tag = pci_make_tag(sio_pc, 0, device, 0);
id = pci_conf_read(sio_pc, tag, PCI_ID_REG);
- /* Invalid vendor ID value? */
- if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
- continue;
- /* XXX Not invalid, but we've done this ~forever. */
- if (PCI_VENDOR(id) == 0)
- continue;
-
- if (PCI_VENDOR(id) != PCI_VENDOR_CONTAQ ||
- PCI_PRODUCT(id) != PCI_PRODUCT_CONTAQ_82C693)
- continue;
-
- /*
- * Found one!
- */
-
-#if 0
- printf("cy82c693_setup_elcr: found 82C693 at device %d\n",
- device);
-#endif
-
- sio_cy82c693_handle = cy82c693_init(sio_iot);
- sio_read_elcr = cy82c693_read_elcr;
- sio_write_elcr = cy82c693_write_elcr;
-
- return (0);
+ if (id ==
+ PCI_ID_CODE(PCI_VENDOR_CONTAQ, PCI_PRODUCT_CONTAQ_82C693)) {
+ sio_cy82c693_handle = cy82c693_init(sio_iot);
+ sio_read_elcr = cy82c693_read_elcr;
+ sio_write_elcr = cy82c693_write_elcr;
+ return (0);
+ }
}
/*
@@ -300,13 +312,18 @@ sio_setirqstat(irq, enabled, type)
ocw1[icu] |= 1 << bit;
/*
- * interrupt type select: set bit to get level-triggered.
+ * interrupt type select: set bit to get level-triggered...
*/
if (type == IST_LEVEL)
elcr[icu] |= 1 << bit;
else
elcr[icu] &= ~(1 << bit);
+ /*
+ * ...unless we pretend to know better.
+ */
+ elcr[icu] &= ~elcr_override[icu];
+
#ifdef not_here
/* see the init function... */
ocw1[0] &= ~0x04; /* always enable IRQ2 on first PIC */
@@ -325,9 +342,6 @@ sio_intr_setup(pc, iot)
pci_chipset_tag_t pc;
bus_space_tag_t iot;
{
-#ifdef notyet
- char *cp;
-#endif
int i;
sio_iot = iot;