bae94d0237
Changes the pci_{enable,disable}_device() functions to work in a nested basis, so that eg, three calls to enable_device() require three calls to disable_device(). The reason for this is to simplify PCI drivers for multi-interface/capability devices. These are devices that cram more than one interface in a single function. A relevant example of that is the Wireless [USB] Host Controller Interface (similar to EHCI) [see http://www.intel.com/technology/comms/wusb/whci.htm]. In these kind of devices, multiple interfaces are accessed through a single bar and IRQ line. For that, the drivers map only the smallest area of the bar to access their register banks and use shared IRQ handlers. However, because the order at which those drivers load cannot be known ahead of time, the sequence in which the calls to pci_enable_device() and pci_disable_device() cannot be predicted. Thus: 1. driverA starts pci_enable_device() 2. driverB starts pci_enable_device() 3. driverA shutdown pci_disable_device() 4. driverB shutdown pci_disable_device() between steps 3 and 4, driver B would loose access to it's device, even if it didn't intend to. By using this modification, the device won't be disabled until all the callers to enable() have called disable(). This is implemented by replacing 'struct pci_dev->is_enabled' from a bitfield to an atomic use count. Each caller to enable increments it, each caller to disable decrements it. When the count increments from 0 to 1, __pci_enable_device() is called to actually enable the device. When it drops to zero, pci_disable_device() actually does the disabling. We keep the backend __pci_enable_device() for pci_default_resume() to use and also change the sysfs method implementation, so that userspace enabling/disabling the device doesn't disable it one time too much. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
102 lines
4.1 KiB
C
102 lines
4.1 KiB
C
/* Functions internal to the PCI core code */
|
|
|
|
extern int __must_check __pci_enable_device(struct pci_dev *);
|
|
extern int pci_uevent(struct device *dev, char **envp, int num_envp,
|
|
char *buffer, int buffer_size);
|
|
extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
|
|
extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
|
|
extern void pci_cleanup_rom(struct pci_dev *dev);
|
|
extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
|
|
resource_size_t size, resource_size_t align,
|
|
resource_size_t min, unsigned int type_mask,
|
|
void (*alignf)(void *, struct resource *,
|
|
resource_size_t, resource_size_t),
|
|
void *alignf_data);
|
|
/* Firmware callbacks */
|
|
extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
|
|
extern int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t state);
|
|
|
|
extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
|
|
extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
|
|
extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val);
|
|
extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
|
|
extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
|
|
extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
|
|
|
|
/* PCI /proc functions */
|
|
#ifdef CONFIG_PROC_FS
|
|
extern int pci_proc_attach_device(struct pci_dev *dev);
|
|
extern int pci_proc_detach_device(struct pci_dev *dev);
|
|
extern int pci_proc_detach_bus(struct pci_bus *bus);
|
|
#else
|
|
static inline int pci_proc_attach_device(struct pci_dev *dev) { return 0; }
|
|
static inline int pci_proc_detach_device(struct pci_dev *dev) { return 0; }
|
|
static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; }
|
|
#endif
|
|
|
|
/* Functions for PCI Hotplug drivers to use */
|
|
extern unsigned int pci_do_scan_bus(struct pci_bus *bus);
|
|
extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int cap);
|
|
|
|
extern void pci_remove_legacy_files(struct pci_bus *bus);
|
|
|
|
/* Lock for read/write access to pci device and bus lists */
|
|
extern struct rw_semaphore pci_bus_sem;
|
|
|
|
#ifdef CONFIG_PCI_MSI
|
|
extern int pci_msi_quirk;
|
|
#else
|
|
#define pci_msi_quirk 0
|
|
#endif
|
|
extern unsigned int pci_pm_d3_delay;
|
|
#ifdef CONFIG_PCI_MSI
|
|
void disable_msi_mode(struct pci_dev *dev, int pos, int type);
|
|
void pci_no_msi(void);
|
|
#else
|
|
static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { }
|
|
static inline void pci_no_msi(void) { }
|
|
#endif
|
|
#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PM)
|
|
int pci_save_msi_state(struct pci_dev *dev);
|
|
int pci_save_msix_state(struct pci_dev *dev);
|
|
void pci_restore_msi_state(struct pci_dev *dev);
|
|
void pci_restore_msix_state(struct pci_dev *dev);
|
|
#else
|
|
static inline int pci_save_msi_state(struct pci_dev *dev) { return 0; }
|
|
static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; }
|
|
static inline void pci_restore_msi_state(struct pci_dev *dev) {}
|
|
static inline void pci_restore_msix_state(struct pci_dev *dev) {}
|
|
#endif
|
|
static inline int pci_no_d1d2(struct pci_dev *dev)
|
|
{
|
|
unsigned int parent_dstates = 0;
|
|
|
|
if (dev->bus->self)
|
|
parent_dstates = dev->bus->self->no_d1d2;
|
|
return (dev->no_d1d2 || parent_dstates);
|
|
|
|
}
|
|
extern int pcie_mch_quirk;
|
|
extern struct device_attribute pci_dev_attrs[];
|
|
extern struct class_device_attribute class_device_attr_cpuaffinity;
|
|
|
|
/**
|
|
* pci_match_one_device - Tell if a PCI device structure has a matching
|
|
* PCI device id structure
|
|
* @id: single PCI device id structure to match
|
|
* @dev: the PCI device structure to match against
|
|
*
|
|
* Returns the matching pci_device_id structure or %NULL if there is no match.
|
|
*/
|
|
static inline const struct pci_device_id *
|
|
pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
|
|
{
|
|
if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
|
|
(id->device == PCI_ANY_ID || id->device == dev->device) &&
|
|
(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
|
|
(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
|
|
!((id->class ^ dev->class) & id->class_mask))
|
|
return id;
|
|
return NULL;
|
|
}
|
|
|