Always make sure buspower is applied, when accessing the PCI MMIO space. Otherwise this can result in crashes. Signed-off-by: Michael Buesch <mb@xxxxxxxxx> Index: ssb-merge-new/drivers/ssb/main.c =================================================================== --- ssb-merge-new.orig/drivers/ssb/main.c 2007-08-13 17:33:23.000000000 +0200 +++ ssb-merge-new/drivers/ssb/main.c 2007-08-13 17:33:34.000000000 +0200 @@ -131,6 +131,9 @@ static void ssb_bus_suspend(struct ssb_b #ifdef CONFIG_SSB_DRIVER_PCICORE bus->pcicore.setup_done = 0; #endif +#ifdef CONFIG_SSB_DEBUG + bus->powered_up = 0; +#endif } static int ssb_device_suspend(struct device *dev, pm_message_t state) @@ -486,9 +489,14 @@ static int ssb_attach_queued_buses(void) * is too early in boot for embedded systems * (no udelay() available). So do it here in attach stage. */ + err = ssb_bus_powerup(bus, 0); + if (err) + goto error; ssb_pcicore_init(&bus->pcicore); + ssb_bus_may_powerdown(bus); err = ssb_devices_register(bus); +error: if (err) { drop_them_all = 1; list_del(&bus->list); @@ -586,11 +594,17 @@ static int ssb_bus_register(struct ssb_b goto err_pci_exit; /* Initialize basic system devices (if available) */ + err = ssb_bus_powerup(bus, 0); + if (err) + goto err_pcmcia_exit; ssb_chipcommon_init(&bus->chipco); ssb_mipscore_init(&bus->mipscore); err = ssb_fetch_invariants(bus, get_invariants); - if (err) + if (err) { + ssb_bus_may_powerdown(bus); goto err_pcmcia_exit; + } + ssb_bus_may_powerdown(bus); /* Queue it for attach. * See the comment at the ssb_is_early_boot definition. */ @@ -1012,24 +1026,27 @@ EXPORT_SYMBOL(ssb_dma_set_mask); int ssb_bus_may_powerdown(struct ssb_bus *bus) { struct ssb_chipcommon *cc; - int err; + int err = 0; /* On buses where more than one core may be working * at a time, we must not powerdown stuff if there are * still cores that may want to run. */ if (bus->bustype == SSB_BUSTYPE_SSB) - return 0; + goto out; cc = &bus->chipco; ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); if (err) goto error; - - return 0; +out: +#ifdef CONFIG_SSB_DEBUG + bus->powered_up = 0; +#endif + return err; error: ssb_printk(KERN_ERR PFX "Bus powerdown failed\n"); - return err; + goto out; } EXPORT_SYMBOL(ssb_bus_may_powerdown); @@ -1046,6 +1063,9 @@ int ssb_bus_powerup(struct ssb_bus *bus, mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST; ssb_chipco_set_clockmode(cc, mode); +#ifdef CONFIG_SSB_DEBUG + bus->powered_up = 1; +#endif return 0; error: ssb_printk(KERN_ERR PFX "Bus powerup failed\n"); Index: ssb-merge-new/drivers/ssb/pci.c =================================================================== --- ssb-merge-new.orig/drivers/ssb/pci.c 2007-08-11 20:25:54.000000000 +0200 +++ ssb-merge-new/drivers/ssb/pci.c 2007-08-13 17:33:34.000000000 +0200 @@ -499,10 +499,34 @@ out: return err; } +#ifdef CONFIG_SSB_DEBUG +static int ssb_pci_assert_buspower(struct ssb_bus *bus) +{ + if (likely(bus->powered_up)) + return 0; + + printk(KERN_ERR PFX "FATAL ERROR: Bus powered down " + "while accessing PCI MMIO space\n"); + if (bus->power_warn_count <= 10) { + bus->power_warn_count++; + dump_stack(); + } + + return -ENODEV; +} +#else /* DEBUG */ +static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) +{ + return 0; +} +#endif /* DEBUG */ + static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) { struct ssb_bus *bus = dev->bus; + if (unlikely(ssb_pci_assert_buspower(bus))) + return 0xFFFF; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return 0xFFFF; @@ -514,6 +538,8 @@ static u32 ssb_pci_read32(struct ssb_dev { struct ssb_bus *bus = dev->bus; + if (unlikely(ssb_pci_assert_buspower(bus))) + return 0xFFFFFFFF; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return 0xFFFFFFFF; @@ -525,6 +551,8 @@ static void ssb_pci_write16(struct ssb_d { struct ssb_bus *bus = dev->bus; + if (unlikely(ssb_pci_assert_buspower(bus))) + return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; @@ -536,6 +564,8 @@ static void ssb_pci_write32(struct ssb_d { struct ssb_bus *bus = dev->bus; + if (unlikely(ssb_pci_assert_buspower(bus))) + return; if (unlikely(bus->mapped_device != dev)) { if (unlikely(ssb_pci_switch_core(bus, dev))) return; Index: ssb-merge-new/include/linux/ssb/ssb.h =================================================================== --- ssb-merge-new.orig/include/linux/ssb/ssb.h 2007-08-11 20:26:09.000000000 +0200 +++ ssb-merge-new/include/linux/ssb/ssb.h 2007-08-13 17:33:34.000000000 +0200 @@ -319,8 +319,13 @@ struct ssb_bus { /* Contents of the SPROM. */ struct ssb_sprom sprom; - /* Internal. */ + /* Internal-only stuff follows. Do not touch. */ struct list_head list; +#ifdef CONFIG_SSB_DEBUG + /* Is the bus already powered up? */ + bool powered_up; + int power_warn_count; +#endif /* DEBUG */ }; /* The initialization-invariants. */ -- - To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html