#include <linux/slab.h>
#include <linux/pnp.h>
#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/kallsyms.h>
#include "base.h"
static void quirk_awe32_resources(struct pnp_dev *dev)
if (changed)
printk(KERN_INFO
"pnp: SB audio device quirk - increasing port range\n");
- return;
}
-static int quirk_smc_fir_enabled(struct pnp_dev *dev)
+static void quirk_supermicro_h8dce_system(struct pnp_dev *dev)
{
- unsigned long firbase;
- u8 bank, high, low, chip;
-
- if (!pnp_port_valid(dev, 1))
- return 0;
-
- firbase = pnp_port_start(dev, 1);
-
- /* Select register bank 3 */
- bank = inb(firbase + 7);
- bank &= 0xf0;
- bank |= 3;
- outb(bank, firbase + 7);
-
- high = inb(firbase + 0);
- low = inb(firbase + 1);
- chip = inb(firbase + 2);
-
- /* This corresponds to the check in smsc_ircc_present() */
- if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2))
- return 1;
-
- return 0;
-}
-
-static void quirk_smc_enable(struct pnp_dev *dev)
-{
- struct resource fir, sir, irq;
-
- pnp_activate_dev(dev);
- if (quirk_smc_fir_enabled(dev))
+ int i;
+ static struct dmi_system_id supermicro_h8dce[] = {
+ {
+ .ident = "Supermicro H8DCE",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "H8DCE"),
+ },
+ },
+ { }
+ };
+
+ if (!dmi_check_system(supermicro_h8dce))
return;
/*
- * Sometimes the BIOS claims the device is enabled, but it reports
- * the wrong FIR resources or doesn't properly configure ISA or LPC
- * bridges on the way to the device.
- *
- * HP nc6000 and nc8000/nw8000 laptops have known problems like
- * this. Fortunately, they do fix things up if we auto-configure
- * the device using its _PRS and _SRS methods.
+ * On the Supermicro H8DCE, there's a system device with resources
+ * that overlap BAR 6 of the built-in SATA PCI adapter. If the PNP
+ * system device claims them, the sata_nv driver won't be able to.
+ * More details at:
+ * https://bugzilla.redhat.com/show_bug.cgi?id=280641
+ * https://bugzilla.redhat.com/show_bug.cgi?id=313491
+ * http://lkml.org/lkml/2008/1/9/449
+ * http://thread.gmane.org/gmane.linux.acpi.devel/27312
*/
- dev_err(&dev->dev, "%s not responding at SIR 0x%lx, FIR 0x%lx; "
- "auto-configuring\n", dev->id->id,
- (unsigned long)pnp_port_start(dev, 0),
- (unsigned long)pnp_port_start(dev, 1));
-
- pnp_disable_dev(dev);
- pnp_init_resource_table(&dev->res);
- pnp_auto_config_dev(dev);
- pnp_activate_dev(dev);
- if (quirk_smc_fir_enabled(dev)) {
- dev_err(&dev->dev, "responds at SIR 0x%lx, FIR 0x%lx\n",
- (unsigned long)pnp_port_start(dev, 0),
- (unsigned long)pnp_port_start(dev, 1));
- return;
- }
-
- /*
- * The Toshiba Portege 4000 _CRS reports the FIR region first,
- * followed by the SIR region. The BIOS will configure the bridge,
- * but only if we call _SRS with SIR first, then FIR. It also
- * reports the IRQ as active high, when it is really active low.
- */
- dev_err(&dev->dev, "not responding at SIR 0x%lx, FIR 0x%lx; "
- "swapping SIR/FIR and reconfiguring\n",
- (unsigned long)pnp_port_start(dev, 0),
- (unsigned long)pnp_port_start(dev, 1));
-
- /*
- * Clear IORESOURCE_AUTO so pnp_activate_dev() doesn't reassign
- * these resources any more.
- */
- fir = dev->res.port_resource[0];
- sir = dev->res.port_resource[1];
- fir.flags &= ~IORESOURCE_AUTO;
- sir.flags &= ~IORESOURCE_AUTO;
-
- irq = dev->res.irq_resource[0];
- irq.flags &= ~IORESOURCE_AUTO;
- irq.flags &= ~IORESOURCE_BITS;
- irq.flags |= IORESOURCE_IRQ_LOWEDGE;
-
- pnp_disable_dev(dev);
- dev->res.port_resource[0] = sir;
- dev->res.port_resource[1] = fir;
- dev->res.irq_resource[0] = irq;
- pnp_activate_dev(dev);
-
- if (quirk_smc_fir_enabled(dev)) {
- dev_err(&dev->dev, "responds at SIR 0x%lx, FIR 0x%lx\n",
- (unsigned long)pnp_port_start(dev, 0),
- (unsigned long)pnp_port_start(dev, 1));
- return;
+ for (i = 0; i < PNP_MAX_MEM; i++) {
+ if (pnp_mem_valid(dev, i) && pnp_mem_len(dev, i) &&
+ (pnp_mem_start(dev, i) & 0xdfef0000) == 0xdfef0000) {
+ dev_warn(&dev->dev, "disabling 0x%llx-0x%llx to prevent"
+ " conflict with sata_nv PCI device\n",
+ (unsigned long long) pnp_mem_start(dev, i),
+ (unsigned long long) (pnp_mem_start(dev, i) +
+ pnp_mem_len(dev, i) - 1));
+ pnp_mem_flags(dev, i) = 0;
+ }
}
-
- dev_err(&dev->dev, "giving up; try \"smsc-ircc2.nopnp\" and "
- "email bjorn.helgaas@hp.com\n");
}
/*
{"CTL0043", quirk_sb16audio_resources},
{"CTL0044", quirk_sb16audio_resources},
{"CTL0045", quirk_sb16audio_resources},
- {"SMCf010", quirk_smc_enable},
+ {"PNP0c01", quirk_supermicro_h8dce_system},
+ {"PNP0c02", quirk_supermicro_h8dce_system},
{""}
};
void pnp_fixup_device(struct pnp_dev *dev)
{
int i = 0;
+ void (*quirk)(struct pnp_dev *);
while (*pnp_fixups[i].id) {
if (compare_pnp_id(dev->id, pnp_fixups[i].id)) {
- pnp_dbg("Calling quirk for %s", dev->dev.bus_id);
- pnp_fixups[i].quirk_function(dev);
+ quirk = pnp_fixups[i].quirk_function;
+
+#ifdef DEBUG
+ dev_dbg(&dev->dev, "calling quirk 0x%p", quirk);
+ print_fn_descriptor_symbol(": %s()\n",
+ (unsigned long) *quirk);
+#endif
+ (*quirk)(dev);
}
i++;
}