X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci.c;h=d2d1879166436cfee7f82c6e0fa0493cfdc8c3b0;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=e3c97d940bab67462d521fc7d67a8178c689cb8e;hpb=bc77d24c47b89f1e0efed0b8e4be5f8aad102883;p=linux-2.6.git diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e3c97d940..d2d187916 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -9,20 +9,17 @@ * Copyright 1997 -- 2000 Martin Mares */ +#include #include #include #include #include #include +#include #include /* isa_dma_bridge_buggy */ +#include "pci.h" -#undef DEBUG - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif +#if 0 /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children @@ -67,11 +64,40 @@ pci_max_busnr(void) return max; } +#endif /* 0 */ + +static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap) +{ + u8 id; + int ttl = 48; + + while (ttl--) { + pci_bus_read_config_byte(bus, devfn, pos, &pos); + if (pos < 0x40) + break; + pos &= ~3; + pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID, + &id); + if (id == 0xff) + break; + if (id == cap) + return pos; + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap) +{ + return __pci_find_next_cap(dev->bus, dev->devfn, + pos + PCI_CAP_LIST_NEXT, cap); +} +EXPORT_SYMBOL_GPL(pci_find_next_capability); + static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_type, int cap) { u16 status; - u8 pos, id; - int ttl = 48; + u8 pos; pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status); if (!(status & PCI_STATUS_CAP_LIST)) @@ -80,24 +106,15 @@ static int __pci_bus_find_cap(struct pci_bus *bus, unsigned int devfn, u8 hdr_ty switch (hdr_type) { case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_BRIDGE: - pci_bus_read_config_byte(bus, devfn, PCI_CAPABILITY_LIST, &pos); + pos = PCI_CAPABILITY_LIST; break; case PCI_HEADER_TYPE_CARDBUS: - pci_bus_read_config_byte(bus, devfn, PCI_CB_CAPABILITY_LIST, &pos); + pos = PCI_CB_CAPABILITY_LIST; break; default: return 0; } - while (ttl-- && pos >= 0x40) { - pos &= ~3; - pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID, &id); - if (id == 0xff) - break; - if (id == cap) - return pos; - pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_NEXT, &pos); - } - return 0; + return __pci_find_next_cap(bus, devfn, pos, cap); } /** @@ -146,6 +163,7 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) return __pci_bus_find_cap(bus, devfn, hdr_type & 0x7f, cap); } +#if 0 /** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query @@ -193,6 +211,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) return 0; } +#endif /* 0 */ /** * pci_find_parent_resource - return resource region of parent bus of given region @@ -226,10 +245,43 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) return best; } +/** + * pci_restore_bars - restore a devices BAR values (e.g. after wake-up) + * @dev: PCI device to have its BARs restored + * + * Restore the BAR values for a given device, so as to make it + * accessible by its driver. + */ +void +pci_restore_bars(struct pci_dev *dev) +{ + int i, numres; + + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + numres = 6; + break; + case PCI_HEADER_TYPE_BRIDGE: + numres = 2; + break; + case PCI_HEADER_TYPE_CARDBUS: + numres = 1; + break; + default: + /* Should never get here, but just in case... */ + return; + } + + for (i = 0; i < numres; i ++) + pci_update_resource(dev, &dev->resource[i], i); +} + +int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t); + /** * pci_set_power_state - Set the power state of a PCI device * @dev: PCI device to be suspended - * @state: Power state we're entering + * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering * * Transition a device to a new power state, using the Power Management * Capabilities in the device's config space. @@ -240,21 +292,21 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) * -EIO if device does not support PCI PM. * 0 if we can successfully change the power state. */ - int -pci_set_power_state(struct pci_dev *dev, int state) +pci_set_power_state(struct pci_dev *dev, pci_power_t state) { - int pm; - u16 pmcsr; + int pm, need_restore = 0; + u16 pmcsr, pmc; /* bound the state we're entering */ - if (state > 3) state = 3; + if (state > PCI_D3hot) + state = PCI_D3hot; /* Validate current state: * Can enter D0 from any state, but if we can only go deeper * to sleep if we're already in a low power state */ - if (state > 0 && dev->current_state > state) + if (state != PCI_D0 && dev->current_state > state) return -EINVAL; else if (dev->current_state == state) return 0; /* we're already there */ @@ -263,26 +315,44 @@ pci_set_power_state(struct pci_dev *dev, int state) pm = pci_find_capability(dev, PCI_CAP_ID_PM); /* abort if the device doesn't support PM capabilities */ - if (!pm) return -EIO; + if (!pm) + return -EIO; + + pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc); + if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { + printk(KERN_DEBUG + "PCI: %s has unsupported PM cap regs version (%u)\n", + pci_name(dev), pmc & PCI_PM_CAP_VER_MASK); + return -EIO; + } /* check if this device supports the desired state */ - if (state == 1 || state == 2) { - u16 pmc; - pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc); - if (state == 1 && !(pmc & PCI_PM_CAP_D1)) return -EIO; - else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) return -EIO; - } + if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1)) + return -EIO; + else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2)) + return -EIO; - /* If we're in D3, force entire word to 0. + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + + /* If we're (effectively) in D3, force entire word to 0. * This doesn't affect PME_Status, disables PME_En, and * sets PowerState to 0. */ - if (dev->current_state >= 3) - pmcsr = 0; - else { - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + switch (dev->current_state) { + case PCI_D0: + case PCI_D1: + case PCI_D2: pmcsr &= ~PCI_PM_CTRL_STATE_MASK; pmcsr |= state; + break; + case PCI_UNKNOWN: /* Boot-up */ + if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot + && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) + need_restore = 1; + /* Fall-through: force to D0 */ + default: + pmcsr = 0; + break; } /* enter specified state */ @@ -290,66 +360,103 @@ pci_set_power_state(struct pci_dev *dev, int state) /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ - if(state == 3 || dev->current_state == 3) - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/100); - } - else if(state == 2 || dev->current_state == 2) + if (state == PCI_D3hot || dev->current_state == PCI_D3hot) + msleep(10); + else if (state == PCI_D2 || dev->current_state == PCI_D2) udelay(200); + + /* + * Give firmware a chance to be called, such as ACPI _PRx, _PSx + * Firmware method after natice method ? + */ + if (platform_pci_set_power_state) + platform_pci_set_power_state(dev, state); + dev->current_state = state; + /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT + * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning + * from D3hot to D0 _may_ perform an internal reset, thereby + * going to "D0 Uninitialized" rather than "D0 Initialized". + * For example, at least some versions of the 3c905B and the + * 3c556B exhibit this behaviour. + * + * At least some laptop BIOSen (e.g. the Thinkpad T21) leave + * devices in a D3hot state at boot. Consequently, we need to + * restore at least the BARs so that the device will be + * accessible to its driver. + */ + if (need_restore) + pci_restore_bars(dev); + return 0; } +int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state); + +/** + * pci_choose_state - Choose the power state of a PCI device + * @dev: PCI device to be suspended + * @state: target sleep state for the whole system. This is the value + * that is passed to suspend() function. + * + * Returns PCI power state suitable for given device and given system + * message. + */ + +pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) +{ + int ret; + + if (!pci_find_capability(dev, PCI_CAP_ID_PM)) + return PCI_D0; + + if (platform_pci_choose_state) { + ret = platform_pci_choose_state(dev, state); + if (ret >= 0) + state.event = ret; + } + + switch (state.event) { + case PM_EVENT_ON: + return PCI_D0; + case PM_EVENT_FREEZE: + case PM_EVENT_SUSPEND: + return PCI_D3hot; + default: + printk("They asked me for state %d\n", state.event); + BUG(); + } + return PCI_D0; +} + +EXPORT_SYMBOL(pci_choose_state); + /** * pci_save_state - save the PCI configuration space of a device before suspending * @dev: - PCI device that we're dealing with - * @buffer: - buffer to hold config space context - * - * @buffer must be large enough to hold the entire PCI 2.2 config space - * (>= 64 bytes). */ int -pci_save_state(struct pci_dev *dev, u32 *buffer) +pci_save_state(struct pci_dev *dev) { int i; - if (buffer) { - /* XXX: 100% dword access ok here? */ - for (i = 0; i < 16; i++) - pci_read_config_dword(dev, i * 4,&buffer[i]); - } + /* XXX: 100% dword access ok here? */ + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); return 0; } /** * pci_restore_state - Restore the saved state of a PCI device * @dev: - PCI device that we're dealing with - * @buffer: - saved PCI config space - * */ int -pci_restore_state(struct pci_dev *dev, u32 *buffer) +pci_restore_state(struct pci_dev *dev) { int i; - if (buffer) { - for (i = 0; i < 16; i++) - pci_write_config_dword(dev,i * 4, buffer[i]); - } - /* - * otherwise, write the context information we know from bootup. - * This works around a problem where warm-booting from Windows - * combined with a D3(hot)->D0 transition causes PCI config - * header data to be forgotten. - */ - else { - for (i = 0; i < 6; i ++) - pci_write_config_dword(dev, - PCI_BASE_ADDRESS_0 + (i * 4), - dev->resource[i].start); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } + for (i = 0; i < 16; i++) + pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]); return 0; } @@ -368,8 +475,11 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) { int err; - pci_set_power_state(dev, 0); - if ((err = pcibios_enable_device(dev, bars)) < 0) + err = pci_set_power_state(dev, PCI_D0); + if (err < 0 && err != -EIO) + return err; + err = pcibios_enable_device(dev, bars); + if (err < 0) return err; return 0; } @@ -385,10 +495,25 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) int pci_enable_device(struct pci_dev *dev) { + int err; + + if ((err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1))) + return err; + pci_fixup_device(pci_fixup_enable, dev); dev->is_enabled = 1; - return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); + return 0; } +/** + * pcibios_disable_device - disable arch specific PCI resources for device dev + * @dev: the PCI device to disable + * + * Disables architecture specific PCI resources for the device. This + * is the default implementation. Architecture implementations can + * override this. + */ +void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} + /** * pci_disable_device - Disable PCI device after use * @dev: PCI device to be disabled @@ -401,14 +526,15 @@ pci_disable_device(struct pci_dev *dev) { u16 pci_command; - dev->is_enabled = 0; - dev->is_busmaster = 0; - pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, pci_command); } + dev->is_busmaster = 0; + + pcibios_disable_device(dev); + dev->is_enabled = 0; } /** @@ -425,7 +551,7 @@ pci_disable_device(struct pci_dev *dev) * 0 if operation is successful. * */ -int pci_enable_wake(struct pci_dev *dev, u32 state, int enable) +int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { int pm; u16 value; @@ -442,7 +568,7 @@ int pci_enable_wake(struct pci_dev *dev, u32 state, int enable) pci_read_config_word(dev,pm+PCI_PM_PMC,&value); value &= PCI_PM_CAP_PME_MASK; - value >>= ffs(value); /* First bit of mask */ + value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ /* Check if it can generate PME# from requested state. */ if (!value || !(value & (1 << state))) @@ -466,7 +592,7 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) { u8 pin; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + pin = dev->pin; if (!pin) return -1; pin--; @@ -601,7 +727,7 @@ pci_set_master(struct pci_dev *dev) pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_MASTER)) { - DBG("PCI: Enabling bus mastering for device %s\n", pci_name(dev)); + pr_debug("PCI: Enabling bus mastering for device %s\n", pci_name(dev)); cmd |= PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); } @@ -679,7 +805,7 @@ pci_set_mwi(struct pci_dev *dev) pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_INVALIDATE)) { - DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", pci_name(dev)); + pr_debug("PCI: Enabling Mem-Wr-Inval for device %s\n", pci_name(dev)); cmd |= PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); } @@ -705,6 +831,31 @@ pci_clear_mwi(struct pci_dev *dev) } } +/** + * pci_intx - enables/disables PCI INTx for device dev + * @pdev: the PCI device to operate on + * @enable: boolean: whether to enable or disable PCI INTx + * + * Enables/disables PCI INTx for device dev + */ +void +pci_intx(struct pci_dev *pdev, int enable) +{ + u16 pci_command, new; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + + if (enable) { + new = pci_command & ~PCI_COMMAND_INTX_DISABLE; + } else { + new = pci_command | PCI_COMMAND_INTX_DISABLE; + } + + if (new != pci_command) { + pci_write_config_word(pdev, PCI_COMMAND, new); + } +} + #ifndef HAVE_ARCH_PCI_SET_DMA_MASK /* * These can be overridden by arch-specific implementations @@ -720,17 +871,6 @@ pci_set_dma_mask(struct pci_dev *dev, u64 mask) return 0; } -int -pci_dac_set_dma_mask(struct pci_dev *dev, u64 mask) -{ - if (!pci_dac_dma_supported(dev, mask)) - return -EIO; - - dev->dma_mask = mask; - - return 0; -} - int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) { @@ -747,8 +887,8 @@ static int __devinit pci_init(void) { struct pci_dev *dev = NULL; - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - pci_fixup_device(PCI_FIXUP_FINAL, dev); + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_fixup_device(pci_fixup_final, dev); } return 0; } @@ -778,11 +918,10 @@ struct pci_dev *isa_bridge; EXPORT_SYMBOL(isa_bridge); #endif +EXPORT_SYMBOL_GPL(pci_restore_bars); EXPORT_SYMBOL(pci_enable_device_bars); EXPORT_SYMBOL(pci_enable_device); EXPORT_SYMBOL(pci_disable_device); -EXPORT_SYMBOL(pci_max_busnr); -EXPORT_SYMBOL(pci_bus_max_busnr); EXPORT_SYMBOL(pci_find_capability); EXPORT_SYMBOL(pci_bus_find_capability); EXPORT_SYMBOL(pci_release_regions); @@ -792,8 +931,8 @@ EXPORT_SYMBOL(pci_request_region); EXPORT_SYMBOL(pci_set_master); EXPORT_SYMBOL(pci_set_mwi); EXPORT_SYMBOL(pci_clear_mwi); +EXPORT_SYMBOL_GPL(pci_intx); EXPORT_SYMBOL(pci_set_dma_mask); -EXPORT_SYMBOL(pci_dac_set_dma_mask); EXPORT_SYMBOL(pci_set_consistent_dma_mask); EXPORT_SYMBOL(pci_assign_resource); EXPORT_SYMBOL(pci_find_parent_resource);