diff options
author | Thomas Schwinge <tschwinge@gnu.org> | 2006-01-22 15:54:41 +0000 |
---|---|---|
committer | Thomas Schwinge <tschwinge@gnu.org> | 2009-06-18 00:26:29 +0200 |
commit | 4ad86505c480b2964548328a959c662e8aef5422 (patch) | |
tree | 9cc9b8f80d561ae7d7451d95a26ff8c252e86504 /linux/src/drivers/net/pci-scan.c | |
parent | 656d6634de02862e837f3a7d80a49e9ca072398d (diff) | |
download | gnumach-4ad86505c480b2964548328a959c662e8aef5422.tar.gz gnumach-4ad86505c480b2964548328a959c662e8aef5422.tar.bz2 gnumach-4ad86505c480b2964548328a959c662e8aef5422.zip |
2006-01-22 Thomas Schwinge <tschwinge@gnu.org>
* configure, i386/configure, i386/linux/configure, linux/configure,
i386/linux/device-drivers.h.in: Regenerated.
* linux/src/drivers/net/ne2k-pci.c: Resolve conflicts.
2006-01-22 Guillem Jover <guillem@hadrons.org>
* i386/linux/configure.ac: Renamed winbond-840 driver to winbond_840.
Enable the starfire, intel_gige and natsemi network drivers. Remove
"CONFIG_" from cb_chim, starfire, sundance, winbond840, hamachi,
natsemi, myson803 and ns820 driver declarations. Replace INTER_GIGE
with INTEL_GIGE.
* linux/dev/drivers/net/Space.c: Add conditional probes for natsemi,
ns820, winbond840, hamachi, sundance, starfire, myson803 and intel-gige
drivers.
* linux/src/include/asm-i386/cache.h: New file from linux 2.2.26.
* linux/dev/include/linux/malloc.h: Include <asm/cache.h>.
* linux/src/drivers/net/ns820.c (netsami_drv_id): Renamed to ...
(ns820_drv_id): ... this. Fix all callers.
* linux/src/drivers/net/intel-gige.c
(skel_netdev_probe): Renamed to ...
(igige_probe): ... this.
* linux/dev/drivers/net/eepro100.c: Remove obsoleted file.
* linux/src/drivers/net/eepro100.c (pci_id_tbl): Add PCI ID's from
linux-2.6.14-rc4.
2006-01-22 Alfred M. Szmidt <ams@gnu.org>
* i386/linux/configure.ac: Added `pci-scan.o' to the network driver
class. (ns820, myson803, sundance, winbond-840, hamachi): New drivers.
* i386/linux/Makefile.in (linux-net-files): Added `cb_shim.c',
`hamachi.c', `intel-gige.c', `myson803.c', `natsemi.c', `ns820.c',
`starfire.c', `sundance.c', `winbond-840.c' and `pci-scan.c'.
* linux/dev/include/linux/modversions.h: New file.
* linux/src/drivers/net/cb_shim.c, linux/src/drivers/net/hamachi.c,
linux/src/drivers/net/intel-gige.c, linux/src/drivers/net/myson803.c,
linux/src/drivers/net/natsemi.c, linux/src/drivers/net/ns820.c,
linux/src/drivers/net/starfire.c, linux/src/drivers/net/sundance.c,
linux/src/drivers/net/winbond-840.c,
linux/src/drivers/net/kern_compat.h, linux/src/drivers/net/pci-scan.c,
linux/src/drivers/net/pci-scan.h: New files from netdrivers 3.5 package
(http://www.scyld.com/network).
* linux/src/drivers/net/3c59x.c, linux/src/drivers/net/eepro100.c,
linux/src/drivers/net/epic100.c, linux/src/drivers/net/ne2k-pci.c,
linux/src/drivers/net/rtl8139.c, linux/src/drivers/net/tulip.c,
linux/src/drivers/net/via-rhine.c, linux/src/drivers/net/yellowfin.c:
Updated files from netdrivers 3.5 (http://www.scyld.com/network).
Diffstat (limited to 'linux/src/drivers/net/pci-scan.c')
-rw-r--r-- | linux/src/drivers/net/pci-scan.c | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/linux/src/drivers/net/pci-scan.c b/linux/src/drivers/net/pci-scan.c new file mode 100644 index 00000000..6187d5db --- /dev/null +++ b/linux/src/drivers/net/pci-scan.c @@ -0,0 +1,659 @@ +/* pci-scan.c: Linux PCI network adapter support code. */ +/* + Originally written 1999-2003 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License (GPL), incorporated herein by + reference. Drivers interacting with these functions are derivative + works and thus also must be licensed under the GPL and include an explicit + GPL notice. + + This code provides common scan and activate functions for PCI network + interfaces. + + The author may be reached as becker@scyld.com, or + Donald Becker + Scyld Computing Corporation + 914 Bay Ridge Road, Suite 220 + Annapolis MD 21403 + + Other contributers: +*/ +static const char version[] = +"pci-scan.c:v1.12 7/30/2003 Donald Becker <becker@scyld.com>" +" http://www.scyld.com/linux/drivers.html\n"; + +/* A few user-configurable values that may be modified when a module. */ + +static int msg_level = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ +static int min_pci_latency = 32; + +#if ! defined(__KERNEL__) +#define __KERNEL__ 1 +#endif +#if !defined(__OPTIMIZE__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with the proper options, including "-O". +#endif + +#if defined(MODULE) && ! defined(EXPORT_SYMTAB) +#define EXPORT_SYMTAB +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#if LINUX_VERSION_CODE < 0x20500 && defined(MODVERSIONS) +/* Another interface semantics screw-up. */ +#include <linux/module.h> +#include <linux/modversions.h> +#else +#include <linux/module.h> +#endif + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20300 +/* Bogus change in the middle of a "stable" kernel series. + Also, in 2.4.7+ slab must come before interrupt.h to avoid breakage. */ +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <asm/io.h> +#include "pci-scan.h" +#include "kern_compat.h" +#if defined(CONFIG_APM) && LINUX_VERSION_CODE < 0x20400 +#include <linux/apm_bios.h> +#endif +#ifdef CONFIG_PM +/* New in 2.4 kernels, pointlessly incompatible with earlier APM. */ +#include <linux/pm.h> +#endif + +#if (LINUX_VERSION_CODE >= 0x20100) && defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif +#if (LINUX_VERSION_CODE < 0x20100) +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#endif + +int (*register_hotswap_hook)(struct drv_id_info *did); +void (*unregister_hotswap_hook)(struct drv_id_info *did); + +#if LINUX_VERSION_CODE > 0x20118 && defined(MODULE) +MODULE_LICENSE("GPL"); +MODULE_PARM(msg_level, "i"); +MODULE_PARM(min_pci_latency, "i"); +MODULE_PARM_DESC(msg_level, "Enable additional status messages (0-7)"); +MODULE_PARM_DESC(min_pci_latency, + "Minimum value for the PCI Latency Timer settings"); +#if defined(EXPORT_SYMTAB) +EXPORT_SYMBOL_NOVERS(pci_drv_register); +EXPORT_SYMBOL_NOVERS(pci_drv_unregister); +EXPORT_SYMBOL_NOVERS(acpi_wake); +EXPORT_SYMBOL_NOVERS(acpi_set_pwr_state); +EXPORT_SYMBOL_NOVERS(register_hotswap_hook); +EXPORT_SYMBOL_NOVERS(unregister_hotswap_hook); +#endif +#endif + +/* List of registered drivers. */ +static struct drv_id_info *drv_list; +/* List of detected PCI devices, for APM events. */ +static struct dev_info { + struct dev_info *next; + void *dev; + struct drv_id_info *drv_id; + int flags; +} *dev_list; + +/* + This code is not intended to support every configuration. + It is intended to minimize duplicated code by providing the functions + needed in almost every PCI driver. + + The "no kitchen sink" policy: + Additional features and code will be added to this module only if more + than half of the drivers for common hardware would benefit from the feature. +*/ + +/* + Ideally we would detect and number all cards of a type (e.g. network) in + PCI slot order. + But that does not work with hot-swap card, CardBus cards and added drivers. + So instead we detect just the each chip table in slot order. + + This routine takes a PCI ID table, scans the PCI bus, and calls the + associated attach/probe1 routine with the hardware already activated and + single I/O or memory address already mapped. + + This routine will later be supplemented with CardBus and hot-swap PCI + support using the same table. Thus the pci_chip_tbl[] should not be + marked as __initdata. +*/ + +#if LINUX_VERSION_CODE >= 0x20200 +/* Grrrr.. complex abstaction layers with negative benefit. */ +int pci_drv_register(struct drv_id_info *drv_id, void *initial_device) +{ + int chip_idx, cards_found = 0; + struct pci_dev *pdev = NULL; + struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl; + struct drv_id_info *drv; + void *newdev; + + + /* Ignore a double-register attempt. */ + for (drv = drv_list; drv; drv = drv->next) + if (drv == drv_id) + return -EBUSY; + + while ((pdev = pci_find_class(drv_id->pci_class, pdev)) != 0) { + u32 pci_id, pci_subsys_id, pci_class_rev; + u16 pci_command, new_command; + int pci_flags; + long pciaddr; /* Bus address. */ + long ioaddr; /* Mapped address for this processor. */ + + pci_read_config_dword(pdev, PCI_VENDOR_ID, &pci_id); + /* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. */ + pci_read_config_dword(pdev, 0x2c, &pci_subsys_id); + pci_read_config_dword(pdev, PCI_REVISION_ID, &pci_class_rev); + + if (msg_level > 3) + printk(KERN_DEBUG "PCI ID %8.8x subsystem ID is %8.8x.\n", + pci_id, pci_subsys_id); + for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) { + struct pci_id_info *chip = &pci_tbl[chip_idx]; + if ((pci_id & chip->id.pci_mask) == chip->id.pci + && (pci_subsys_id&chip->id.subsystem_mask) == chip->id.subsystem + && (pci_class_rev&chip->id.revision_mask) == chip->id.revision) + break; + } + if (pci_tbl[chip_idx].name == 0) /* Compiled out! */ + continue; + + pci_flags = pci_tbl[chip_idx].pci_flags; +#if LINUX_VERSION_CODE >= 0x2030C + /* Wow. A oversized, hard-to-use abstraction. Bogus. */ + pciaddr = pdev->resource[(pci_flags >> 4) & 7].start; +#else + pciaddr = pdev->base_address[(pci_flags >> 4) & 7]; +#if defined(__alpha__) /* Really any machine with 64 bit addressing. */ + if (pci_flags & PCI_ADDR_64BITS) + pciaddr |= ((long)pdev->base_address[((pci_flags>>4)&7)+ 1]) << 32; +#endif +#endif + if (msg_level > 2) + printk(KERN_INFO "Found %s at PCI address %#lx, mapped IRQ %d.\n", + pci_tbl[chip_idx].name, pciaddr, pdev->irq); + + if ( ! (pci_flags & PCI_UNUSED_IRQ) && + (pdev->irq == 0 || pdev->irq == 255)) { + if (pdev->bus->number == 32) /* Broken CardBus activation. */ + printk(KERN_WARNING "Resources for CardBus device '%s' have" + " not been allocated.\n" + KERN_WARNING "Activation has been delayed.\n", + pci_tbl[chip_idx].name); + else + printk(KERN_WARNING "PCI device '%s' was not assigned an " + "IRQ.\n" + KERN_WARNING "It will not be activated.\n", + pci_tbl[chip_idx].name); + continue; + } + if ((pci_flags & PCI_BASE_ADDRESS_SPACE_IO)) { + ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) + continue; + } else if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, + pci_tbl[chip_idx].io_size)) == 0) { + printk(KERN_INFO "Failed to map PCI address %#lx for device " + "'%s'.\n", pciaddr, pci_tbl[chip_idx].name); + continue; + } + if ( ! (pci_flags & PCI_NO_ACPI_WAKE)) + acpi_wake(pdev); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pdev->bus->number, pdev->devfn, pci_command, new_command); + pci_write_config_word(pdev, PCI_COMMAND, new_command); + } + + newdev = drv_id->probe1(pdev, initial_device, + ioaddr, pdev->irq, chip_idx, cards_found); + if (newdev == NULL) + continue; + initial_device = 0; + cards_found++; + if (pci_flags & PCI_COMMAND_MASTER) { + pci_set_master(pdev); + if ( ! (pci_flags & PCI_NO_MIN_LATENCY)) { + u8 pci_latency; + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < min_pci_latency) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to %d clocks.\n", + pci_latency, min_pci_latency); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + min_pci_latency); + } + } + } + { + struct dev_info *devp = + kmalloc(sizeof(struct dev_info), GFP_KERNEL); + if (devp == 0) + continue; + devp->next = dev_list; + devp->dev = newdev; + devp->drv_id = drv_id; + dev_list = devp; + } + } + + if (((drv_id->flags & PCI_HOTSWAP) + && register_hotswap_hook && (*register_hotswap_hook)(drv_id) == 0) + || cards_found) { + MOD_INC_USE_COUNT; + drv_id->next = drv_list; + drv_list = drv_id; + return 0; + } else + return -ENODEV; +} +#else +int pci_drv_register(struct drv_id_info *drv_id, void *initial_device) +{ + int pci_index, cards_found = 0; + unsigned char pci_bus, pci_device_fn; + struct pci_dev *pdev; + struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl; + void *newdev; + + if ( ! pcibios_present()) + return -ENODEV; + + for (pci_index = 0; pci_index < 0xff; pci_index++) { + u32 pci_id, subsys_id, pci_class_rev; + u16 pci_command, new_command; + int chip_idx, irq, pci_flags; + long pciaddr; + long ioaddr; + u32 pci_busaddr; + u8 pci_irq_line; + + if (pcibios_find_class (drv_id->pci_class, pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &pci_id); + /* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. */ + pcibios_read_config_dword(pci_bus, pci_device_fn, 0x2c, &subsys_id); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_REVISION_ID, &pci_class_rev); + + for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) { + struct pci_id_info *chip = &pci_tbl[chip_idx]; + if ((pci_id & chip->id.pci_mask) == chip->id.pci + && (subsys_id & chip->id.subsystem_mask) == chip->id.subsystem + && (pci_class_rev&chip->id.revision_mask) == chip->id.revision) + break; + } + if (pci_tbl[chip_idx].name == 0) /* Compiled out! */ + continue; + + pci_flags = pci_tbl[chip_idx].pci_flags; + pdev = pci_find_slot(pci_bus, pci_device_fn); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + irq = pci_irq_line; + pcibios_read_config_dword(pci_bus, pci_device_fn, + ((pci_flags >> 2) & 0x1C) + 0x10, + &pci_busaddr); + pciaddr = pci_busaddr; +#if defined(__alpha__) + if (pci_flags & PCI_ADDR_64BITS) { + pcibios_read_config_dword(pci_bus, pci_device_fn, + ((pci_flags >> 2) & 0x1C) + 0x14, + &pci_busaddr); + pciaddr |= ((long)pci_busaddr)<<32; + } +#endif + + if (msg_level > 2) + printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", + pci_tbl[chip_idx].name, pciaddr, irq); + + if ( ! (pci_flags & PCI_UNUSED_IRQ) && + (irq == 0 || irq == 255)) { + if (pci_bus == 32) /* Broken CardBus activation. */ + printk(KERN_WARNING "Resources for CardBus device '%s' have" + " not been allocated.\n" + KERN_WARNING "It will not be activated.\n", + pci_tbl[chip_idx].name); + else + printk(KERN_WARNING "PCI device '%s' was not assigned an " + "IRQ.\n" + KERN_WARNING "It will not be activated.\n", + pci_tbl[chip_idx].name); + continue; + } + + if ((pciaddr & PCI_BASE_ADDRESS_SPACE_IO)) { + ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) + continue; + } else if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, + pci_tbl[chip_idx].io_size)) == 0) { + printk(KERN_INFO "Failed to map PCI address %#lx.\n", + pciaddr); + continue; + } + + if ( ! (pci_flags & PCI_NO_ACPI_WAKE)) + acpi_wake(pdev); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + newdev = drv_id->probe1(pdev, initial_device, + ioaddr, irq, chip_idx, cards_found); + + if (newdev && (pci_flags & PCI_COMMAND_MASTER) && + ! (pci_flags & PCI_NO_MIN_LATENCY)) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < min_pci_latency) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to %d clocks.\n", + pci_latency, min_pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, min_pci_latency); + } + } + if (newdev) { + struct dev_info *devp = + kmalloc(sizeof(struct dev_info), GFP_KERNEL); + if (devp) { + devp->next = dev_list; + devp->dev = newdev; + devp->drv_id = drv_id; + dev_list = devp; + } + } + initial_device = 0; + cards_found++; + } + + if (((drv_id->flags & PCI_HOTSWAP) + && register_hotswap_hook && (*register_hotswap_hook)(drv_id) == 0) + || cards_found) { + MOD_INC_USE_COUNT; + drv_id->next = drv_list; + drv_list = drv_id; + return 0; + } else + return cards_found ? 0 : -ENODEV; +} +#endif + +void pci_drv_unregister(struct drv_id_info *drv_id) +{ + struct drv_id_info **drvp; + struct dev_info **devip = &dev_list; + + if (unregister_hotswap_hook) + (*unregister_hotswap_hook)(drv_id); + + for (drvp = &drv_list; *drvp; drvp = &(*drvp)->next) + if (*drvp == drv_id) { + *drvp = (*drvp)->next; + MOD_DEC_USE_COUNT; + break; + } + while (*devip) { + struct dev_info *thisdevi = *devip; + if (thisdevi->drv_id == drv_id) { + *devip = thisdevi->next; + kfree(thisdevi); + } else + devip = &(*devip)->next; + } + + return; +} + +#if LINUX_VERSION_CODE < 0x20400 +/* + Search PCI configuration space for the specified capability registers. + Return the index, or 0 on failure. + The 2.4 kernel now includes this function. +*/ +int pci_find_capability(struct pci_dev *pdev, int findtype) +{ + u16 pci_status, cap_type; + u8 pci_cap_idx; + int cap_idx; + + pci_read_config_word(pdev, PCI_STATUS, &pci_status); + if ( ! (pci_status & PCI_STATUS_CAP_LIST)) + return 0; + pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pci_cap_idx); + cap_idx = pci_cap_idx; + for (cap_idx = pci_cap_idx; cap_idx; cap_idx = (cap_type >> 8) & 0xff) { + pci_read_config_word(pdev, cap_idx, &cap_type); + if ((cap_type & 0xff) == findtype) + return cap_idx; + } + return 0; +} +#endif + +/* Change a device from D3 (sleep) to D0 (active). + Return the old power state. + This is more complicated than you might first expect since most cards + forget all PCI config info during the transition! */ +int acpi_wake(struct pci_dev *pdev) +{ + u32 base[5], romaddr; + u16 pci_command, pwr_command; + u8 pci_latency, pci_cacheline, irq; + int i, pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM); + + if (pwr_cmd_idx == 0) + return 0; + pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command); + if ((pwr_command & 3) == 0) + return 0; + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + for (i = 0; i < 5; i++) + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4, + &base[i]); + pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &romaddr); + pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &pci_latency); + pci_read_config_byte( pdev, PCI_CACHE_LINE_SIZE, &pci_cacheline); + pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq); + + pci_write_config_word(pdev, pwr_cmd_idx + 4, 0x0000); + for (i = 0; i < 5; i++) + if (base[i]) + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4, + base[i]); + pci_write_config_dword(pdev, PCI_ROM_ADDRESS, romaddr); + pci_write_config_byte( pdev, PCI_INTERRUPT_LINE, irq); + pci_write_config_byte( pdev, PCI_CACHE_LINE_SIZE, pci_cacheline); + pci_write_config_byte( pdev, PCI_LATENCY_TIMER, pci_latency); + pci_write_config_word( pdev, PCI_COMMAND, pci_command | 5); + return pwr_command & 3; +} + +int acpi_set_pwr_state(struct pci_dev *pdev, enum acpi_pwr_state new_state) +{ + u16 pwr_command; + int pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM); + + if (pwr_cmd_idx == 0) + return 0; + pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command); + if ((pwr_command & 3) == ACPI_D3 && new_state != ACPI_D3) + acpi_wake(pdev); /* The complicated sequence. */ + pci_write_config_word(pdev, pwr_cmd_idx + 4, + (pwr_command & ~3) | new_state); + return pwr_command & 3; +} + +#if defined(CONFIG_PM) +static int handle_pm_event(struct pm_dev *dev, int event, void *data) +{ + static int down = 0; + struct dev_info *devi; + int pwr_cmd = -1; + + if (msg_level > 1) + printk(KERN_DEBUG "pci-scan: Handling power event %d for driver " + "list %s...\n", + event, drv_list->name); + switch (event) { + case PM_SUSPEND: + if (down) { + printk(KERN_DEBUG "pci-scan: Received extra suspend event\n"); + break; + } + down = 1; + for (devi = dev_list; devi; devi = devi->next) + if (devi->drv_id->pwr_event) + devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND); + break; + case PM_RESUME: + if (!down) { + printk(KERN_DEBUG "pci-scan: Received bogus resume event\n"); + break; + } + for (devi = dev_list; devi; devi = devi->next) { + if (devi->drv_id->pwr_event) { + if (msg_level > 3) + printk(KERN_DEBUG "pci-scan: Calling resume for %s " + "device.\n", devi->drv_id->name); + devi->drv_id->pwr_event(devi->dev, DRV_RESUME); + } + } + down = 0; + break; + case PM_SET_WAKEUP: pwr_cmd = DRV_PWR_WakeOn; break; + case PM_EJECT: pwr_cmd = DRV_DETACH; break; + default: + printk(KERN_DEBUG "pci-scan: Unknown power management event %d.\n", + event); + } + if (pwr_cmd >= 0) + for (devi = dev_list; devi; devi = devi->next) + if (devi->drv_id->pwr_event) + devi->drv_id->pwr_event(devi->dev, pwr_cmd); + + return 0; +} + +#elif defined(CONFIG_APM) && LINUX_VERSION_CODE < 0x20400 +static int handle_apm_event(apm_event_t event) +{ + static int down = 0; + struct dev_info *devi; + + if (msg_level > 1) + printk(KERN_DEBUG "pci-scan: Handling APM event %d for driver " + "list %s...\n", + event, drv_list->name); + return 0; + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (down) { + printk(KERN_DEBUG "pci-scan: Received extra suspend event\n"); + break; + } + down = 1; + for (devi = dev_list; devi; devi = devi->next) + if (devi->drv_id->pwr_event) + devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND); + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (!down) { + printk(KERN_DEBUG "pci-scan: Received bogus resume event\n"); + break; + } + for (devi = dev_list; devi; devi = devi->next) + if (devi->drv_id->pwr_event) + devi->drv_id->pwr_event(devi->dev, DRV_RESUME); + down = 0; + break; + } + return 0; +} +#endif /* CONFIG_APM */ + +#ifdef MODULE +int init_module(void) +{ + if (msg_level) /* Emit version even if no cards detected. */ + printk(KERN_INFO "%s", version); + +#if defined(CONFIG_PM) + pm_register(PM_PCI_DEV, 0, &handle_pm_event); +#elif defined(CONFIG_APM) && LINUX_VERSION_CODE < 0x20400 + apm_register_callback(&handle_apm_event); +#endif + return 0; +} +void cleanup_module(void) +{ +#if defined(CONFIG_PM) + pm_unregister_all(&handle_pm_event); +#elif defined(CONFIG_APM) && LINUX_VERSION_CODE < 0x20400 + apm_unregister_callback(&handle_apm_event); +#endif + if (dev_list != NULL) + printk(KERN_WARNING "pci-scan: Unfreed device references.\n"); + return; +} +#endif + + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall -Wstrict-prototypes -O6 -c pci-scan.c" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ |