lkml.org 
[lkml]   [2004]   [Aug]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH][linux-usb-devel] Early USB handoff
Date
From
Hello everybody,

Here is slightly improved early USB legacy handoff patch for 2.4.27
(original patch is here:
http://www.mail-archive.com/linux-usb-devel%40lists.sourceforge.net/msg2
6803.html).
Patch includes support for EHCI handoff, and not only hands off legacy
support, but also helps avoiding 'shared interrupt problem' when BIOS
leaves USB controller programmed in native mode with PCI IRQ enabled
(legacy free mode).
I've tested it on a number of machines (mostly laptops) for UHCI &
OHCI, but PC with EHCI BIOS legacy support was hard to find. I tried
Intel D865GRH, but seems like BIOS there have some problems (like lock
up during POST when flash drive plugged in), and does not adhere EHCI
handoff protocol.
Handoff is under no-usb-legacy option.

Thanks,
Aleks.
diff -ruN linux-2.4.27/Documentation/kernel-parameters.txt linux-2.4.27-fix/Documentation/kernel-parameters.txt
--- linux-2.4.27/Documentation/kernel-parameters.txt 2004-08-07 16:26:04.000000000 -0700
+++ linux-2.4.27-fix/Documentation/kernel-parameters.txt 2004-08-16 11:26:15.000000000 -0700
@@ -635,7 +635,9 @@
uart6850= [HW,SOUND]

usbfix [BUGS=IA-64]
-
+
+ no-usb-legacy [HW] Disables BIOS SMM USB Legacy/Native Support
+
video= [FB] frame buffer configuration.

vga= [BOOT] on ix386, select a particular video mode
diff -ruN linux-2.4.27/drivers/pci/quirks.c linux-2.4.27-fix/drivers/pci/quirks.c
--- linux-2.4.27/drivers/pci/quirks.c 2004-08-07 16:26:05.000000000 -0700
+++ linux-2.4.27-fix/drivers/pci/quirks.c 2004-08-16 12:26:53.000000000 -0700
@@ -726,6 +726,222 @@
pciehp_msi_quirk = 1;
}

+
+#define UHCI_USBLEGSUP 0xc0 /* legacy support */
+#define UHCI_USBCMD 0 /* command register */
+#define UHCI_USBSTS 2 /* status register */
+#define UHCI_USBINTR 4 /* interrupt register */
+#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
+#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
+#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */
+#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */
+#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */
+
+#define OHCI_CONTROL 0x04
+#define OHCI_CMDSTATUS 0x08
+#define OHCI_INTRSTATUS 0x0c
+#define OHCI_INTRENABLE 0x10
+#define OHCI_INTRDISABLE 0x14
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+
+#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
+#define EHCI_USBCMD 0 /* command register */
+#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
+#define EHCI_USBSTS 4 /* status register */
+#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
+#define EHCI_USBINTR 8 /* interrupt register */
+#define EHCI_USBLEGSUP 0 /* legacy support register */
+#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
+#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
+#define EHCI_USBLEGCTLSTS 4 /* legacy support control/status */
+#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
+
+int disable_legacy_usb __initdata = 0;
+static int __init usb_legacy_disable(char *str)
+{
+ disable_legacy_usb = 1;
+ return 0;
+}
+__setup("no-usb-legacy", usb_legacy_disable);
+
+static void __init quirk_usb_disable_ehci(struct pci_dev *pdev)
+{
+ int wait_time, delta;
+ char *base, *op_reg_base;
+ u32 hcc_params, val, temp;
+ u8 cap_length;
+
+ base = ioremap_nocache(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (base == NULL) return;
+
+ cap_length = readb( base);
+ op_reg_base = base + cap_length;
+ hcc_params = readl( base + EHCI_HCC_PARAMS);
+ hcc_params = (hcc_params >> 8) & 0xff;
+ if ( hcc_params ) {
+ pci_read_config_dword( pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ &val);
+ if ( ((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS) ) {
+ /**
+ * Ok, BIOS is in smm mode, try to hand off...
+ */
+ pci_read_config_dword( pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ &temp);
+ pci_write_config_dword( pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ temp | EHCI_USBLEGCTLSTS_SOOE);
+ val |= EHCI_USBLEGSUP_OS;
+ pci_write_config_dword( pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ val);
+
+ wait_time = 500;
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout( (HZ*10+999)/1000);
+ wait_time -= 10;
+ pci_read_config_dword( pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ &val);
+ } while ( wait_time && (val & EHCI_USBLEGSUP_BIOS) );
+ if ( !wait_time ) {
+ /**
+ * well, possibly buggy BIOS...
+ */
+ printk( KERN_WARNING "EHCI early BIOS handoff failed (BIOS bug ?)\n");
+ pci_write_config_dword( pdev,
+ hcc_params + EHCI_USBLEGSUP,
+ EHCI_USBLEGSUP_OS);
+ pci_write_config_dword( pdev,
+ hcc_params + EHCI_USBLEGCTLSTS,
+ 0);
+ }
+ }
+ }
+
+ /**
+ * halt EHCI & disable its interrupts in any case
+ */
+ val = readl( op_reg_base + EHCI_USBSTS);
+ if ( (val & EHCI_USBSTS_HALTED) == 0 ) {
+ val = readl( op_reg_base + EHCI_USBCMD);
+ val &= ~EHCI_USBCMD_RUN;
+ writel( val, op_reg_base + EHCI_USBCMD);
+
+ wait_time = 2000;
+ delta = 100;
+ do {
+ writel( 0x3f, op_reg_base + EHCI_USBSTS);
+ udelay( delta);
+ wait_time -= delta;
+ val = readl( op_reg_base + EHCI_USBSTS);
+ if ( (val == ~(u32)0) ||
+ (val & EHCI_USBSTS_HALTED)) {
+ break;
+ }
+ } while ( wait_time > 0);
+ }
+ writel( 0, op_reg_base + EHCI_USBINTR);
+ writel( 0x3f, op_reg_base + EHCI_USBSTS);
+
+ iounmap(base);
+
+ return;
+}
+
+static void __init quirk_usb_disable_smm_bios(struct pci_dev *pdev)
+{
+ int wait_time, delta;
+
+ if (!disable_legacy_usb)
+ return;
+
+ if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */
+ int i;
+ unsigned long base = 0;
+ u16 val, sts;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
+ base = pci_resource_start(pdev, i);
+ break;
+ }
+
+ if (!base)
+ return;
+
+ /*
+ * stop controller
+ */
+ sts = inw(base + UHCI_USBSTS);
+ val = inw(base + UHCI_USBCMD);
+ val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE);
+ outw(val, base + UHCI_USBCMD);
+
+ /*
+ * wait while it stops if it was running
+ */
+ if ( (sts & UHCI_USBSTS_HALTED) == 0 )
+ {
+ wait_time = 1000;
+ delta = 100;
+
+ do {
+ outw(0x1f, base + UHCI_USBSTS);
+ udelay( delta);
+ wait_time -= delta;
+ val = inw(base + UHCI_USBSTS);
+ if (val & UHCI_USBSTS_HALTED)
+ break;
+ } while ( wait_time > 0 );
+ }
+
+ /*
+ * disable interrupts & legacy support
+ */
+ outw(0, base + UHCI_USBINTR);
+ outw(0x1f, base + UHCI_USBSTS);
+ pci_read_config_word(pdev, UHCI_USBLEGSUP, &val);
+ if ( val & 0xbf )
+ pci_write_config_word(pdev, UHCI_USBLEGSUP,
+ UHCI_USBLEGSUP_DEFAULT);
+
+ } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */
+ char *base = ioremap_nocache(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (base == NULL) return;
+
+ if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
+ wait_time = 500; /* 0.5 seconds */
+ writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
+ writel(OHCI_OCR, base + OHCI_CMDSTATUS);
+ while ( (wait_time > 0) &&
+ readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
+ wait_time -= 10;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout( (HZ*10 + 999) / 1000);
+ }
+ }
+
+ /*
+ * disable interrupts
+ */
+ writel(~(u32)0, base + OHCI_INTRDISABLE);
+ writel(~(u32)0, base + OHCI_INTRSTATUS);
+
+ iounmap(base);
+ } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */
+ quirk_usb_disable_ehci( pdev);
+ }
+
+ return;
+}
+
/*
* The main table of quirks.
*/
@@ -816,6 +1032,7 @@
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc },

{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SMCH, quirk_pciehp_msi },
+ { PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, quirk_usb_disable_smm_bios },

{ 0 }
};
diff -ruN linux-2.4.27/include/asm-i386/smpboot.h linux-2.4.27-fix/include/asm-i386/smpboot.h
--- linux-2.4.27/include/asm-i386/smpboot.h 2004-08-07 16:26:06.000000000 -0700
+++ linux-2.4.27-fix/include/asm-i386/smpboot.h 2004-08-16 11:29:45.000000000 -0700
@@ -15,6 +15,7 @@
extern unsigned char int_delivery_mode;
extern unsigned int int_dest_addr_mode;
extern int cyclone_setup(char*);
+extern int disable_legacy_usb;

static inline void detect_clustered_apic(char* oem, char* prod)
{
@@ -29,6 +30,7 @@
esr_disable = 1;
/*Start cyclone clock*/
cyclone_setup(0);
+ disable_legacy_usb = 1;
/* check for ACPI tables */
} else if (!strncmp(oem, "IBM", 3) &&
(!strncmp(prod, "SERVIGIL", 8) ||
@@ -41,6 +43,7 @@
esr_disable = 1;
/*Start cyclone clock*/
cyclone_setup(0);
+ disable_legacy_usb = 1;
} else if (!strncmp(oem, "IBM NUMA", 8)){
clustered_apic_mode = CLUSTERED_APIC_NUMAQ;
apic_broadcast_id = APIC_BROADCAST_ID_APIC;
\
 
 \ /
  Last update: 2005-03-22 14:05    [W:0.036 / U:1.592 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site