diff options
author | David Brownell <david-b@pacbell.net> | 2005-09-23 20:14:37 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:38 -0400 |
commit | 7586269c0b52970f60bb69fcb86e765fc1d72309 (patch) | |
tree | a0cb04f9527101e8b9331350e4dd9d57e6e019e7 /drivers/pci | |
parent | 38ffdd62b0f466ef9126ac9f060ade5f218b4887 (diff) |
[PATCH] USB: move handoff code
This moves the PCI quirk handling for USB host controllers from the
PCI directory to the USB directory. Follow-on patches will need to:
(a) merge these copies with the originals in the HCD reset methods.
they don't wholly agree, despite doing the very same thing; and
(b) eventually change it so "usb-handoff" is the default, to help
get more robust USB/BIOS/input/... interactions.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/Makefile | 2
drivers/pci/quirks.c | 253 ---------------------------------------
drivers/usb/Makefile | 1
drivers/usb/host/Makefile | 5
drivers/usb/host/pci-quirks.c | 272 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 280 insertions(+), 253 deletions(-)
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/quirks.c | 253 |
1 files changed, 3 insertions, 250 deletions
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ee1605906a3e..bbd9c2323d8c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
@@ -7,6 +7,9 @@ | |||
7 | * | 7 | * |
8 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> | 8 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> |
9 | * | 9 | * |
10 | * Init/reset quirks for USB host controllers should be in the | ||
11 | * USB quirks file, where their drivers can access reuse it. | ||
12 | * | ||
10 | * The bridge optimization stuff has been removed. If you really | 13 | * The bridge optimization stuff has been removed. If you really |
11 | * have a silly BIOS which is unable to set your host bridge right, | 14 | * have a silly BIOS which is unable to set your host bridge right, |
12 | * use the PowerTweak utility (see http://powertweak.sourceforge.net). | 15 | * use the PowerTweak utility (see http://powertweak.sourceforge.net). |
@@ -645,28 +648,6 @@ static void quirk_via_irq(struct pci_dev *dev) | |||
645 | DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq); | 648 | DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq); |
646 | 649 | ||
647 | /* | 650 | /* |
648 | * PIIX3 USB: We have to disable USB interrupts that are | ||
649 | * hardwired to PIRQD# and may be shared with an | ||
650 | * external device. | ||
651 | * | ||
652 | * Legacy Support Register (LEGSUP): | ||
653 | * bit13: USB PIRQ Enable (USBPIRQDEN), | ||
654 | * bit4: Trap/SMI On IRQ Enable (USBSMIEN). | ||
655 | * | ||
656 | * We mask out all r/wc bits, too. | ||
657 | */ | ||
658 | static void __devinit quirk_piix3_usb(struct pci_dev *dev) | ||
659 | { | ||
660 | u16 legsup; | ||
661 | |||
662 | pci_read_config_word(dev, 0xc0, &legsup); | ||
663 | legsup &= 0x50ef; | ||
664 | pci_write_config_word(dev, 0xc0, legsup); | ||
665 | } | ||
666 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb ); | ||
667 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, quirk_piix3_usb ); | ||
668 | |||
669 | /* | ||
670 | * VIA VT82C598 has its device ID settable and many BIOSes | 651 | * VIA VT82C598 has its device ID settable and many BIOSes |
671 | * set it to the ID of VT82C597 for backward compatibility. | 652 | * set it to the ID of VT82C597 for backward compatibility. |
672 | * We need to switch it off to be able to recognize the real | 653 | * We need to switch it off to be able to recognize the real |
@@ -1039,234 +1020,6 @@ static void __init quirk_sis_96x_smbus(struct pci_dev *dev) | |||
1039 | pci_read_config_byte(dev, 0x77, &val); | 1020 | pci_read_config_byte(dev, 0x77, &val); |
1040 | } | 1021 | } |
1041 | 1022 | ||
1042 | |||
1043 | #define UHCI_USBLEGSUP 0xc0 /* legacy support */ | ||
1044 | #define UHCI_USBCMD 0 /* command register */ | ||
1045 | #define UHCI_USBSTS 2 /* status register */ | ||
1046 | #define UHCI_USBINTR 4 /* interrupt register */ | ||
1047 | #define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ | ||
1048 | #define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ | ||
1049 | #define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ | ||
1050 | #define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ | ||
1051 | #define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ | ||
1052 | |||
1053 | #define OHCI_CONTROL 0x04 | ||
1054 | #define OHCI_CMDSTATUS 0x08 | ||
1055 | #define OHCI_INTRSTATUS 0x0c | ||
1056 | #define OHCI_INTRENABLE 0x10 | ||
1057 | #define OHCI_INTRDISABLE 0x14 | ||
1058 | #define OHCI_OCR (1 << 3) /* ownership change request */ | ||
1059 | #define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ | ||
1060 | #define OHCI_INTR_OC (1 << 30) /* ownership change */ | ||
1061 | |||
1062 | #define EHCI_HCC_PARAMS 0x08 /* extended capabilities */ | ||
1063 | #define EHCI_USBCMD 0 /* command register */ | ||
1064 | #define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ | ||
1065 | #define EHCI_USBSTS 4 /* status register */ | ||
1066 | #define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */ | ||
1067 | #define EHCI_USBINTR 8 /* interrupt register */ | ||
1068 | #define EHCI_USBLEGSUP 0 /* legacy support register */ | ||
1069 | #define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ | ||
1070 | #define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */ | ||
1071 | #define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ | ||
1072 | #define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ | ||
1073 | |||
1074 | int usb_early_handoff __devinitdata = 0; | ||
1075 | static int __init usb_handoff_early(char *str) | ||
1076 | { | ||
1077 | usb_early_handoff = 1; | ||
1078 | return 0; | ||
1079 | } | ||
1080 | __setup("usb-handoff", usb_handoff_early); | ||
1081 | |||
1082 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) | ||
1083 | { | ||
1084 | unsigned long base = 0; | ||
1085 | int wait_time, delta; | ||
1086 | u16 val, sts; | ||
1087 | int i; | ||
1088 | |||
1089 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | ||
1090 | if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { | ||
1091 | base = pci_resource_start(pdev, i); | ||
1092 | break; | ||
1093 | } | ||
1094 | |||
1095 | if (!base) | ||
1096 | return; | ||
1097 | |||
1098 | /* | ||
1099 | * stop controller | ||
1100 | */ | ||
1101 | sts = inw(base + UHCI_USBSTS); | ||
1102 | val = inw(base + UHCI_USBCMD); | ||
1103 | val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); | ||
1104 | outw(val, base + UHCI_USBCMD); | ||
1105 | |||
1106 | /* | ||
1107 | * wait while it stops if it was running | ||
1108 | */ | ||
1109 | if ((sts & UHCI_USBSTS_HALTED) == 0) | ||
1110 | { | ||
1111 | wait_time = 1000; | ||
1112 | delta = 100; | ||
1113 | |||
1114 | do { | ||
1115 | outw(0x1f, base + UHCI_USBSTS); | ||
1116 | udelay(delta); | ||
1117 | wait_time -= delta; | ||
1118 | val = inw(base + UHCI_USBSTS); | ||
1119 | if (val & UHCI_USBSTS_HALTED) | ||
1120 | break; | ||
1121 | } while (wait_time > 0); | ||
1122 | } | ||
1123 | |||
1124 | /* | ||
1125 | * disable interrupts & legacy support | ||
1126 | */ | ||
1127 | outw(0, base + UHCI_USBINTR); | ||
1128 | outw(0x1f, base + UHCI_USBSTS); | ||
1129 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); | ||
1130 | if (val & 0xbf) | ||
1131 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); | ||
1132 | |||
1133 | } | ||
1134 | |||
1135 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | ||
1136 | { | ||
1137 | void __iomem *base; | ||
1138 | int wait_time; | ||
1139 | |||
1140 | base = ioremap_nocache(pci_resource_start(pdev, 0), | ||
1141 | pci_resource_len(pdev, 0)); | ||
1142 | if (base == NULL) return; | ||
1143 | |||
1144 | if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { | ||
1145 | wait_time = 500; /* 0.5 seconds */ | ||
1146 | writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); | ||
1147 | writel(OHCI_OCR, base + OHCI_CMDSTATUS); | ||
1148 | while (wait_time > 0 && | ||
1149 | readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { | ||
1150 | wait_time -= 10; | ||
1151 | msleep(10); | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | /* | ||
1156 | * disable interrupts | ||
1157 | */ | ||
1158 | writel(~(u32)0, base + OHCI_INTRDISABLE); | ||
1159 | writel(~(u32)0, base + OHCI_INTRSTATUS); | ||
1160 | |||
1161 | iounmap(base); | ||
1162 | } | ||
1163 | |||
1164 | static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | ||
1165 | { | ||
1166 | int wait_time, delta; | ||
1167 | void __iomem *base, *op_reg_base; | ||
1168 | u32 hcc_params, val, temp; | ||
1169 | u8 cap_length; | ||
1170 | |||
1171 | base = ioremap_nocache(pci_resource_start(pdev, 0), | ||
1172 | pci_resource_len(pdev, 0)); | ||
1173 | if (base == NULL) return; | ||
1174 | |||
1175 | cap_length = readb(base); | ||
1176 | op_reg_base = base + cap_length; | ||
1177 | hcc_params = readl(base + EHCI_HCC_PARAMS); | ||
1178 | hcc_params = (hcc_params >> 8) & 0xff; | ||
1179 | if (hcc_params) { | ||
1180 | pci_read_config_dword(pdev, | ||
1181 | hcc_params + EHCI_USBLEGSUP, | ||
1182 | &val); | ||
1183 | if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { | ||
1184 | /* | ||
1185 | * Ok, BIOS is in smm mode, try to hand off... | ||
1186 | */ | ||
1187 | pci_read_config_dword(pdev, | ||
1188 | hcc_params + EHCI_USBLEGCTLSTS, | ||
1189 | &temp); | ||
1190 | pci_write_config_dword(pdev, | ||
1191 | hcc_params + EHCI_USBLEGCTLSTS, | ||
1192 | temp | EHCI_USBLEGCTLSTS_SOOE); | ||
1193 | val |= EHCI_USBLEGSUP_OS; | ||
1194 | pci_write_config_dword(pdev, | ||
1195 | hcc_params + EHCI_USBLEGSUP, | ||
1196 | val); | ||
1197 | |||
1198 | wait_time = 500; | ||
1199 | do { | ||
1200 | msleep(10); | ||
1201 | wait_time -= 10; | ||
1202 | pci_read_config_dword(pdev, | ||
1203 | hcc_params + EHCI_USBLEGSUP, | ||
1204 | &val); | ||
1205 | } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); | ||
1206 | if (!wait_time) { | ||
1207 | /* | ||
1208 | * well, possibly buggy BIOS... | ||
1209 | */ | ||
1210 | printk(KERN_WARNING "EHCI early BIOS handoff " | ||
1211 | "failed (BIOS bug ?)\n"); | ||
1212 | pci_write_config_dword(pdev, | ||
1213 | hcc_params + EHCI_USBLEGSUP, | ||
1214 | EHCI_USBLEGSUP_OS); | ||
1215 | pci_write_config_dword(pdev, | ||
1216 | hcc_params + EHCI_USBLEGCTLSTS, | ||
1217 | 0); | ||
1218 | } | ||
1219 | } | ||
1220 | } | ||
1221 | |||
1222 | /* | ||
1223 | * halt EHCI & disable its interrupts in any case | ||
1224 | */ | ||
1225 | val = readl(op_reg_base + EHCI_USBSTS); | ||
1226 | if ((val & EHCI_USBSTS_HALTED) == 0) { | ||
1227 | val = readl(op_reg_base + EHCI_USBCMD); | ||
1228 | val &= ~EHCI_USBCMD_RUN; | ||
1229 | writel(val, op_reg_base + EHCI_USBCMD); | ||
1230 | |||
1231 | wait_time = 2000; | ||
1232 | delta = 100; | ||
1233 | do { | ||
1234 | writel(0x3f, op_reg_base + EHCI_USBSTS); | ||
1235 | udelay(delta); | ||
1236 | wait_time -= delta; | ||
1237 | val = readl(op_reg_base + EHCI_USBSTS); | ||
1238 | if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { | ||
1239 | break; | ||
1240 | } | ||
1241 | } while (wait_time > 0); | ||
1242 | } | ||
1243 | writel(0, op_reg_base + EHCI_USBINTR); | ||
1244 | writel(0x3f, op_reg_base + EHCI_USBSTS); | ||
1245 | |||
1246 | iounmap(base); | ||
1247 | |||
1248 | return; | ||
1249 | } | ||
1250 | |||
1251 | |||
1252 | |||
1253 | static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) | ||
1254 | { | ||
1255 | if (!usb_early_handoff) | ||
1256 | return; | ||
1257 | |||
1258 | if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */ | ||
1259 | quirk_usb_handoff_uhci(pdev); | ||
1260 | } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */ | ||
1261 | quirk_usb_handoff_ohci(pdev); | ||
1262 | } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */ | ||
1263 | quirk_usb_disable_ehci(pdev); | ||
1264 | } | ||
1265 | |||
1266 | return; | ||
1267 | } | ||
1268 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); | ||
1269 | |||
1270 | /* | 1023 | /* |
1271 | * ... This is further complicated by the fact that some SiS96x south | 1024 | * ... This is further complicated by the fact that some SiS96x south |
1272 | * bridges pretend to be 85C503/5513 instead. In that case see if we | 1025 | * bridges pretend to be 85C503/5513 instead. In that case see if we |