Commit 8c5d5989 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.6

into kroah.com:/home/greg/linux/BK/pci-2.6
parents bd24a6bd 0771a5a3
The PCI Express Port Bus Driver Guide HOWTO
Tom L Nguyen tom.l.nguyen@intel.com
11/03/2004
1. About this guide
This guide describes the basics of the PCI Express Port Bus driver
and provides information on how to enable the service drivers to
register/unregister with the PCI Express Port Bus Driver.
2. Copyright 2004 Intel Corporation
3. What is the PCI Express Port Bus Driver
A PCI Express Port is a logical PCI-PCI Bridge structure. There
are two types of PCI Express Port: the Root Port and the Switch
Port. The Root Port originates a PCI Express link from a PCI Express
Root Complex and the Switch Port connects PCI Express links to
internal logical PCI buses. The Switch Port, which has its secondary
bus representing the switch's internal routing logic, is called the
switch's Upstream Port. The switch's Downstream Port is bridging from
switch's internal routing bus to a bus representing the downstream
PCI Express link from the PCI Express Switch.
A PCI Express Port can provide up to four distinct functions,
referred to in this document as services, depending on its port type.
PCI Express Port's services include native hotplug support (HP),
power management event support (PME), advanced error reporting
support (AER), and virtual channel support (VC). These services may
be handled by a single complex driver or be individually distributed
and handled by corresponding service drivers.
4. Why use the PCI Express Port Bus Driver?
In existing Linux kernels, the Linux Device Driver Model allows a
physical device to be handled by only a single driver. The PCI
Express Port is a PCI-PCI Bridge device with multiple distinct
services. To maintain a clean and simple solution each service
may have its own software service driver. In this case several
service drivers will compete for a single PCI-PCI Bridge device.
For example, if the PCI Express Root Port native hotplug service
driver is loaded first, it claims a PCI-PCI Bridge Root Port. The
kernel therefore does not load other service drivers for that Root
Port. In other words, it is impossible to have multiple service
drivers load and run on a PCI-PCI Bridge device simultaneously
using the current driver model.
To enable multiple service drivers running simultaneously requires
having a PCI Express Port Bus driver, which manages all populated
PCI Express Ports and distributes all provided service requests
to the corresponding service drivers as required. Some key
advantages of using the PCI Express Port Bus driver are listed below:
- Allow multiple service drivers to run simultaneously on
a PCI-PCI Bridge Port device.
- Allow service drivers implemented in an independent
staged approach.
- Allow one service driver to run on multiple PCI-PCI Bridge
Port devices.
- Manage and distribute resources of a PCI-PCI Bridge Port
device to requested service drivers.
5. Configuring the PCI Express Port Bus Driver vs. Service Drivers
5.1 Including the PCI Express Port Bus Driver Support into the Kernel
Including the PCI Express Port Bus driver depends on whether the PCI
Express support is included in the kernel config. The kernel will
automatically include the PCI Express Port Bus driver as a kernel
driver when the PCI Express support is enabled in the kernel.
5.2 Enabling Service Driver Support
PCI device drivers are implemented based on Linux Device Driver Model.
All service drivers are PCI device drivers. As discussed above, it is
impossible to load any service driver once the kernel has loaded the
PCI Express Port Bus Driver. To meet the PCI Express Port Bus Driver
Model requires some minimal changes on existing service drivers that
imposes no impact on the functionality of existing service drivers.
A service driver is required to use the two APIs shown below to
register its service with the PCI Express Port Bus driver (see
section 5.2.1 & 5.2.2). It is important that a service driver
initializes the pcie_port_service_driver data structure, included in
header file /include/linux/pcieport_if.h, before calling these APIs.
Failure to do so will result an identity mismatch, which prevents
the PCI Express Port Bus driver from loading a service driver.
5.2.1 pcie_port_service_register
int pcie_port_service_register(struct pcie_port_service_driver *new)
This API replaces the Linux Driver Model's pci_module_init API. A
service driver should always calls pcie_port_service_register at
module init. Note that after service driver being loaded, calls
such as pci_enable_device(dev) and pci_set_master(dev) are no longer
necessary since these calls are executed by the PCI Port Bus driver.
5.2.2 pcie_port_service_unregister
void pcie_port_service_unregister(struct pcie_port_service_driver *new)
pcie_port_service_unregister replaces the Linux Driver Model's
pci_unregister_driver. It's always called by service driver when a
module exits.
5.2.3 Sample Code
Below is sample service driver code to initialize the port service
driver data structure.
static struct pcie_port_service_id service_id[] = { {
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.port_type = PCIE_RC_PORT,
.service_type = PCIE_PORT_SERVICE_AER,
}, { /* end: all zeroes */ }
};
static struct pcie_port_service_driver root_aerdrv = {
.name = (char *)device_name,
.id_table = &service_id[0],
.probe = aerdrv_load,
.remove = aerdrv_unload,
.suspend = aerdrv_suspend,
.resume = aerdrv_resume,
};
Below is a sample code for registering/unregistering a service
driver.
static int __init aerdrv_service_init(void)
{
int retval = 0;
retval = pcie_port_service_register(&root_aerdrv);
if (!retval) {
/*
* FIX ME
*/
}
return retval;
}
static void __exit aerdrv_service_exit(void)
{
pcie_port_service_unregister(&root_aerdrv);
}
module_init(aerdrv_service_init);
module_exit(aerdrv_service_exit);
6. Possible Resource Conflicts
Since all service drivers of a PCI-PCI Bridge Port device are
allowed to run simultaneously, below lists a few of possible resource
conflicts with proposed solutions.
6.1 MSI Vector Resource
The MSI capability structure enables a device software driver to call
pci_enable_msi to request MSI based interrupts. Once MSI interrupts
are enabled on a device, it stays in this mode until a device driver
calls pci_disable_msi to disable MSI interrupts and revert back to
INTx emulation mode. Since service drivers of the same PCI-PCI Bridge
port share the same physical device, if an individual service driver
calls pci_enable_msi/pci_disable_msi it may result unpredictable
behavior. For example, two service drivers run simultaneously on the
same physical Root Port. Both service drivers call pci_enable_msi to
request MSI based interrupts. A service driver may not know whether
any other service drivers have run on this Root Port. If either one
of them calls pci_disable_msi, it puts the other service driver
in a wrong interrupt mode.
To avoid this situation all service drivers are not permitted to
switch interrupt mode on its device. The PCI Express Port Bus driver
is responsible for determining the interrupt mode and this should be
transparent to service drivers. Service drivers need to know only
the vector IRQ assigned to the field irq of struct pcie_device, which
is passed in when the PCI Express Port Bus driver probes each service
driver. Service drivers should use (struct pcie_device*)dev->irq to
call request_irq/free_irq. In addition, the interrupt mode is stored
in the field interrupt_mode of struct pcie_device.
6.2 MSI-X Vector Resources
Similar to the MSI a device driver for an MSI-X capable device can
call pci_enable_msix to request MSI-X interrupts. All service drivers
are not permitted to switch interrupt mode on its device. The PCI
Express Port Bus driver is responsible for determining the interrupt
mode and this should be transparent to service drivers. Any attempt
by service driver to call pci_enable_msix/pci_disable_msix may
result unpredictable behavior. Service drivers should use
(struct pcie_device*)dev->irq and call request_irq/free_irq.
6.3 PCI Memory/IO Mapped Regions
Service drivers for PCI Express Power Management (PME), Advanced
Error Reporting (AER), Hot-Plug (HP) and Virtual Channel (VC) access
PCI configuration space on the PCI Express port. In all cases the
registers accessed are independent of each other. This patch assumes
that all service drivers will be well behaved and not overwrite
other service driver's configuration settings.
6.4 PCI Config Registers
Each service driver runs its PCI config operations on its own
capability structure except the PCI Express capability structure, in
which Root Control register and Device Control register are shared
between PME and AER. This patch assumes that all service drivers
will be well behaved and not overwrite other service driver's
configuration settings.
......@@ -1131,6 +1131,8 @@ config PCI_MMCONFIG
select ACPI_BOOT
default y
source "drivers/pci/pcie/Kconfig"
source "drivers/pci/Kconfig"
config ISA
......
......@@ -385,8 +385,8 @@ void __devinit pcibios_sort(void)
}
}
if (!found) {
printk(KERN_WARNING "PCI: Device %02x:%02x not found by BIOS\n",
dev->bus->number, dev->devfn);
printk(KERN_WARNING "PCI: Device %s not found by BIOS\n",
pci_name(dev));
list_del(&dev->global_list);
list_add_tail(&dev->global_list, &sorted_devices);
}
......
......@@ -56,4 +56,6 @@ $(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
# Files generated that shall be removed upon make clean
clean-files := devlist.h classlist.h
# Build PCI Express stuff if needed
obj-$(CONFIG_PCIEPORTBUS) += pcie/
......@@ -7,7 +7,7 @@
* configuration space.
*/
static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;
static DEFINE_SPINLOCK(pci_lock);
/*
* Wrappers for all PCI configuration access functions. They just check
......
......@@ -134,27 +134,6 @@ config HOTPLUG_PCI_CPCI_GENERIC
When in doubt, say N.
config HOTPLUG_PCI_PCIE
tristate "PCI Express Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you have a motherboard that supports PCI Express Native
Hotplug
To compile this driver as a module, choose M here: the
module will be called pciehp.
When in doubt, say N.
config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
bool "Use polling mechanism for hot-plug events (for testing purpose)"
depends on HOTPLUG_PCI_PCIE
help
Say Y here if you want to use the polling mechanism for hot-plug
events for early platform testing.
When in doubt, say N.
config HOTPLUG_PCI_SHPC
tristate "SHPC PCI Hotplug driver"
depends on HOTPLUG_PCI
......
......@@ -34,6 +34,7 @@
#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include <linux/pcieport_if.h>
#include "pci_hotplug.h"
#define MY_NAME "pciehp"
......@@ -311,7 +312,7 @@ enum php_ctlr_type {
typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
int pcie_init(struct controller *ctrl, struct pci_dev *pdev,
int pcie_init(struct controller *ctrl, struct pcie_device *dev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
......
......@@ -40,6 +40,7 @@
#include <asm/uaccess.h>
#include "pciehp.h"
#include "pciehprm.h"
#include <linux/interrupt.h>
/* Global variables */
int pciehp_debug;
......@@ -346,7 +347,7 @@ static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_spe
return 0;
}
static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
{
int rc;
struct controller *ctrl;
......@@ -354,7 +355,9 @@ static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
int first_device_num = 0 ; /* first PCI device number supported by this PCIE */
int num_ctlr_slots; /* number of slots supported by this HPC */
u8 value;
struct pci_dev *pdev;
dbg("%s: Called by hp_drv\n", __FUNCTION__);
ctrl = kmalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __FUNCTION__);
......@@ -363,8 +366,10 @@ static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
memset(ctrl, 0, sizeof(struct controller));
dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid);
pdev = dev->port;
rc = pcie_init(ctrl, pdev,
rc = pcie_init(ctrl, dev,
(php_intr_callback_t) pciehp_handle_attention_button,
(php_intr_callback_t) pciehp_handle_switch_change,
(php_intr_callback_t) pciehp_handle_presence_change,
......@@ -562,32 +567,52 @@ static void __exit unload_pciehpd(void)
}
int hpdriver_context = 0;
static struct pci_device_id pcied_pci_tbl[] = {
{
.class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
.class_mask = ~0,
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pcied_pci_tbl);
static void pciehp_remove (struct pcie_device *device)
{
printk("%s ENTRY\n", __FUNCTION__);
printk("%s -> Call free_irq for irq = %d\n",
__FUNCTION__, device->irq);
free_irq(device->irq, &hpdriver_context);
}
#ifdef CONFIG_PM
static int pciehp_suspend (struct pcie_device *dev, u32 state)
{
printk("%s ENTRY\n", __FUNCTION__);
return 0;
}
static int pciehp_resume (struct pcie_device *dev)
{
printk("%s ENTRY\n", __FUNCTION__);
return 0;
}
#endif
static struct pci_driver pcie_driver = {
.name = PCIE_MODULE_NAME,
.id_table = pcied_pci_tbl,
.probe = pcie_probe,
/* remove: pcie_remove_one, */
static struct pcie_port_service_id port_pci_ids[] = { {
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.port_type = PCIE_RC_PORT,
.service_type = PCIE_PORT_SERVICE_HP,
.driver_data = 0,
}, { /* end: all zeroes */ }
};
static const char device_name[] = "hpdriver";
static struct pcie_port_service_driver hpdriver_portdrv = {
.name = (char *)device_name,
.id_table = &port_pci_ids[0],
.probe = pciehp_probe,
.remove = pciehp_remove,
#ifdef CONFIG_PM
.suspend = pciehp_suspend,
.resume = pciehp_resume,
#endif /* PM */
};
static int __init pcied_init(void)
{
......@@ -603,9 +628,11 @@ static int __init pcied_init(void)
retval = pciehprm_init(PCI);
if (!retval) {
retval = pci_register_driver(&pcie_driver);
dbg("pci_register_driver = %d\n", retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
retval = pcie_port_service_register(&hpdriver_portdrv);
dbg("pcie_port_service_register = %d\n", retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
if (retval)
dbg("%s: Failure to register service\n", __FUNCTION__);
}
error_hpc_init:
......@@ -625,8 +652,8 @@ static void __exit pcied_cleanup(void)
pciehprm_cleanup();
dbg("pci_unregister_driver\n");
pci_unregister_driver(&pcie_driver);
dbg("pcie_port_service_unregister\n");
pcie_port_service_unregister(&hpdriver_portdrv);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
......
......@@ -1249,7 +1249,7 @@ static struct hpc_ops pciehp_hpc_ops = {
};
int pcie_init(struct controller * ctrl,
struct pci_dev *pdev,
struct pcie_device *dev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
......@@ -1265,6 +1265,7 @@ int pcie_init(struct controller * ctrl,
u32 slot_cap;
int cap_base, saved_cap_base;
u16 slot_status, slot_ctrl;
struct pci_dev *pdev;
DBG_ENTER_ROUTINE
......@@ -1277,7 +1278,8 @@ int pcie_init(struct controller * ctrl,
}
memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s));
pdev = dev->port;
php_ctlr->pci_dev = pdev; /* save pci_dev in context */
dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__,
......@@ -1338,7 +1340,7 @@ int pcie_init(struct controller * ctrl,
}
dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number,
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq);
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq);
for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
if (pci_resource_len(pdev, rc) > 0)
dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc,
......@@ -1355,7 +1357,7 @@ int pcie_init(struct controller * ctrl,
init_waitqueue_head(&ctrl->queue);
/* find the IRQ */
php_ctlr->irq = pdev->irq;
php_ctlr->irq = dev->irq;
dbg("HPC interrupt = %d\n", php_ctlr->irq);
/* Save interrupt callback info */
......@@ -1407,17 +1409,6 @@ int pcie_init(struct controller * ctrl,
start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */
} else {
/* Installs the interrupt handler */
dbg("%s: pcie_mch_quirk = %x\n", __FUNCTION__, pcie_mch_quirk);
if (!pcie_mch_quirk) {
rc = pci_enable_msi(pdev);
if (rc) {
info("Can't get msi for the hotplug controller\n");
info("Use INTx for the hotplug controller\n");
dbg("%s: rc = %x\n", __FUNCTION__, rc);
} else
php_ctlr->irq = pdev->irq;
}
rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl);
dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc);
if (rc) {
......
......@@ -22,7 +22,7 @@
#include "msi.h"
static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED;
static DEFINE_SPINLOCK(msi_lock);
static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL };
static kmem_cache_t* msi_cachep;
......@@ -374,19 +374,18 @@ static int msi_init(void)
if ((status = msi_cache_init()) < 0) {
pci_msi_enable = 0;
printk(KERN_INFO "WARNING: MSI INIT FAILURE\n");
printk(KERN_WARNING "PCI: MSI cache init failed\n");
return status;
}
last_alloc_vector = assign_irq_vector(AUTO_ASSIGN);
if (last_alloc_vector < 0) {
pci_msi_enable = 0;
printk(KERN_INFO "WARNING: ALL VECTORS ARE BUSY\n");
printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n");
status = -EBUSY;
return status;
}
vector_irq[last_alloc_vector] = 0;
nr_released_vectors++;
printk(KERN_INFO "MSI INIT SUCCESS\n");
return status;
}
......@@ -736,7 +735,9 @@ int pci_enable_msi(struct pci_dev* dev)
/* Check whether driver already requested for MSI-X vectors */
if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSIX)) > 0 &&
!msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) {
printk(KERN_INFO "Can't enable MSI. Device already had MSI-X vectors assigned\n");
printk(KERN_INFO "PCI: %s: Can't enable MSI. "
"Device already has MSI-X vectors assigned\n",
pci_name(dev));
dev->irq = temp;
return -EINVAL;
}
......@@ -774,9 +775,9 @@ void pci_disable_msi(struct pci_dev* dev)
}
if (entry->msi_attrib.state) {
spin_unlock_irqrestore(&msi_lock, flags);
printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on vector->%d\n",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
dev->irq);
printk(KERN_WARNING "PCI: %s: pci_disable_msi() called without "
"free_irq() on MSI vector %d\n",
pci_name(dev), dev->irq);
BUG_ON(entry->msi_attrib.state > 0);
} else {
vector_irq[dev->irq] = 0; /* free it */
......@@ -982,7 +983,9 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
/* Check whether driver already requested for MSI vector */
if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 &&
!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) {
printk(KERN_INFO "Can't enable MSI-X. Device already had MSI vector assigned\n");
printk(KERN_INFO "PCI: %s: Can't enable MSI-X. "
"Device already has an MSI vector assigned\n",
pci_name(dev));
dev->irq = temp;
return -EINVAL;
}
......@@ -1050,9 +1053,9 @@ void pci_disable_msix(struct pci_dev* dev)
spin_unlock_irqrestore(&msi_lock, flags);
if (warning) {
dev->irq = temp;
printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on all vectors\n",
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
printk(KERN_WARNING "PCI: %s: pci_disable_msix() called without "
"free_irq() on all MSI-X vectors\n",
pci_name(dev));
BUG_ON(warning > 0);
} else {
dev->irq = temp;
......@@ -1088,9 +1091,9 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev)
state = msi_desc[dev->irq]->msi_attrib.state;
spin_unlock_irqrestore(&msi_lock, flags);
if (state) {
printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on vector->%d\n",
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn), dev->irq);
printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
"called without free_irq() on MSI vector %d\n",
pci_name(dev), dev->irq);
BUG_ON(state > 0);
} else /* Release MSI vector assigned to this device */
msi_free_vector(dev, dev->irq, 0);
......@@ -1132,9 +1135,9 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev)
iounmap(base);
release_mem_region(phys_addr, PCI_MSIX_ENTRY_SIZE *
multi_msix_capable(control));
printk(KERN_DEBUG "Driver[%d:%d:%d] unloaded wo doing free_irq on all vectors\n",
dev->bus->number, PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn));
printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() "
"called without free_irq() on all MSI-X vectors\n",
pci_name(dev));
BUG_ON(warning > 0);
}
dev->irq = temp; /* Restore IOAPIC IRQ */
......
......@@ -269,7 +269,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
if ((pmc & PCI_PM_CAP_VER_MASK) != 2) {
printk(KERN_WARNING
printk(KERN_DEBUG
"PCI: %s has unsupported PM cap regs version (%u)\n",
dev->slot_name, pmc & PCI_PM_CAP_VER_MASK);
return -EIO;
......
......@@ -59,12 +59,14 @@ struct pci_visit {
extern int pci_visit_dev(struct pci_visit *fn,
struct pci_dev_wrapped *wrapped_dev,
struct pci_bus_wrapped *wrapped_parent);
extern void pci_remove_legacy_files(struct pci_bus *bus);
/* Lock for read/write access to pci device and bus lists */
extern spinlock_t pci_bus_lock;
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 Express Port Bus Configuration
#
config PCIEPORTBUS
bool "PCI Express support"
depends on PCI_GOMMCONFIG || PCI_GOANY
default n
---help---
This automatically enables PCI Express Port Bus support. Users can
choose Native Hot-Plug support, Advanced Error Reporting support,
Power Management Event support and Virtual Channel support to run
on PCI Express Ports (Root or Switch).
#
# Include service Kconfig here
#
config HOTPLUG_PCI_PCIE
tristate "PCI Express Hotplug driver"
depends on HOTPLUG_PCI && PCIEPORTBUS
help
Say Y here if you have a motherboard that supports PCI Express Native
Hotplug
To compile this driver as a module, choose M here: the
module will be called pciehp.
When in doubt, say N.
config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
bool "Use polling mechanism for hot-plug events (for testing purpose)"
depends on HOTPLUG_PCI_PCIE
help
Say Y here if you want to use the polling mechanism for hot-plug
events for early platform testing.
When in doubt, say N.
#
# Makefile for PCI-Express PORT Driver
#
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
/*
* File: portdrv.h
* Purpose: PCI Express Port Bus Driver's Internal Data Structures
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef _PORTDRV_H_
#define _PORTDRV_H_
#if !defined(PCI_CAP_ID_PME)
#define PCI_CAP_ID_PME 1
#endif
#if !defined(PCI_CAP_ID_EXP)
#define PCI_CAP_ID_EXP 0x10
#endif
#define PORT_TYPE_MASK 0xf
#define PORT_TO_SLOT_MASK 0x100
#define SLOT_HP_CAPABLE_MASK 0x40
#define PCIE_CAPABILITIES_REG 0x2
#define PCIE_SLOT_CAPABILITIES_REG 0x14
#define PCIE_PORT_DEVICE_MAXSERVICES 4
#define PCI_CFG_SPACE_SIZE 256
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
extern struct bus_type pcie_port_bus_type;
extern struct device_driver pcieport_generic_driver;
extern int pcie_port_device_probe(struct pci_dev *dev);
extern int pcie_port_device_register(struct pci_dev *dev);
#ifdef CONFIG_PM
extern int pcie_port_device_suspend(struct pcie_device *dev, u32 state);
extern int pcie_port_device_resume(struct pcie_device *dev);
#endif
extern void pcie_port_device_remove(struct pcie_device *dev);
extern void pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
#endif /* _PORTDRV_H_ */
/*
* File: portdrv_bus.c
* Purpose: PCI Express Port Bus Driver's Bus Overloading Functions
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/pcieport_if.h>
static int generic_probe (struct device *dev) { return 0;}
static int generic_remove (struct device *dev) { return 0;}
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
static int pcie_port_bus_suspend(struct device *dev, u32 state);
static int pcie_port_bus_resume(struct device *dev);
struct bus_type pcie_port_bus_type = {
.name = "pci_express",
.match = pcie_port_bus_match,
.suspend = pcie_port_bus_suspend,
.resume = pcie_port_bus_resume,
};
struct device_driver pcieport_generic_driver = {
.name = "pcieport",
.bus = &pcie_port_bus_type,
.probe = generic_probe,
.remove = generic_remove,
};
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
if ( drv->bus != &pcie_port_bus_type ||
dev->bus != &pcie_port_bus_type ||
drv == &pcieport_generic_driver) {
return 0;
}
pciedev = to_pcie_device(dev);
driver = to_service_driver(drv);
if ( (driver->id_table->vendor != PCI_ANY_ID &&
driver->id_table->vendor != pciedev->id.vendor) ||
(driver->id_table->device != PCI_ANY_ID &&
driver->id_table->device != pciedev->id.device) ||
driver->id_table->port_type != pciedev->id.port_type ||
driver->id_table->service_type != pciedev->id.service_type )
return 0;
return 1;
}
static int pcie_port_bus_suspend(struct device *dev, u32 state)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
if (!dev || !dev->driver)
return 0;
pciedev = to_pcie_device(dev);
driver = to_service_driver(dev->driver);
if (driver && driver->suspend)
driver->suspend(pciedev, state);
return 0;
}
static int pcie_port_bus_resume(struct device *dev)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
if (!dev || !dev->driver)
return 0;
pciedev = to_pcie_device(dev);
driver = to_service_driver(dev->driver);
if (driver && driver->resume)
driver->resume(pciedev);
return 0;
}
This diff is collapsed.
/*
* File: portdrv_pci.c
* Purpose: PCI Express Port Bus Driver
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/pcieport_if.h>
#include "portdrv.h"
/*
* Version Information
*/
#define DRIVER_VERSION "v1.0"
#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
#define DRIVER_DESC "PCIE Port Bus Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/* global data */
static const char device_name[] = "pcieport-driver";
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
*
* If detected invokes the pcie_port_device_register() method for
* this port device.
*
*/
static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
const struct pci_device_id *id )
{
int status;
status = pcie_port_device_probe(dev);
if (status)
return status;
if (pci_enable_device(dev) < 0)
return -ENODEV;
pci_set_master(dev);
if (!dev->irq) {
printk(KERN_WARNING
"%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n",
__FUNCTION__, dev->device, dev->vendor);
}
if (pcie_port_device_register(dev))
return -ENOMEM;
return 0;
}
static void pcie_portdrv_remove (struct pci_dev *dev)
{
struct pcie_device *pciedev;
pciedev = (struct pcie_device *)pci_get_drvdata(dev);
if (pciedev) {
pcie_port_device_remove(pciedev);
pci_set_drvdata(dev, NULL);
}
}
#ifdef CONFIG_PM
static int pcie_portdrv_suspend (struct pci_dev *dev, u32 state)
{
struct pcie_device *pciedev;
pciedev = (struct pcie_device *)pci_get_drvdata(dev);
if (pciedev)
pcie_port_device_suspend(pciedev, state);
return 0;
}
static int pcie_portdrv_resume (struct pci_dev *dev)
{
struct pcie_device *pciedev;
pciedev = (struct pcie_device *)pci_get_drvdata(dev);
if (pciedev)
pcie_port_device_resume(pciedev);
return 0;
}
#endif
/*
* LINUX Device Driver Model
*/
static const struct pci_device_id port_pci_ids[] = { {
/* handle any PCI-Express port */
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
static struct pci_driver pcie_portdrv = {
.name = (char *)device_name,
.id_table = &port_pci_ids[0],
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
#ifdef CONFIG_PM
.suspend = pcie_portdrv_suspend,
.resume = pcie_portdrv_resume,
#endif /* PM */
};
static int __init pcie_portdrv_init(void)
{
int retval = 0;
pcie_port_bus_register();
retval = pci_module_init(&pcie_portdrv);
if (retval)
pcie_port_bus_unregister();
return retval;
}
static void __exit pcie_portdrv_exit(void)
{
pci_unregister_driver(&pcie_portdrv);
pcie_port_bus_unregister();
}
module_init(pcie_portdrv_init);
module_exit(pcie_portdrv_exit);
......@@ -62,7 +62,7 @@ static void pci_create_legacy_files(struct pci_bus *b)
}
}
static void pci_remove_legacy_files(struct pci_bus *b)
void pci_remove_legacy_files(struct pci_bus *b)
{
class_device_remove_bin_file(&b->class_dev, b->legacy_io);
class_device_remove_bin_file(&b->class_dev, b->legacy_mem);
......@@ -70,7 +70,7 @@ static void pci_remove_legacy_files(struct pci_bus *b)
}
#else /* !HAVE_PCI_LEGACY */
static inline void pci_create_legacy_files(struct pci_bus *bus) { return; }
static inline void pci_remove_legacy_files(struct pci_bus *bus) { return; }
void pci_remove_legacy_files(struct pci_bus *bus) { return; }
#endif /* HAVE_PCI_LEGACY */
/*
......@@ -86,7 +86,7 @@ static ssize_t pci_bus_show_cpuaffinity(struct class_device *class_dev, char *bu
buf[ret++] = '\n';
return ret;
}
static CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL);
CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL);
/*
* PCI Bus Class
......@@ -95,10 +95,6 @@ static void release_pcibus_dev(struct class_device *class_dev)
{
struct pci_bus *pci_bus = to_pci_bus(class_dev);
pci_remove_legacy_files(pci_bus);
class_device_remove_file(&pci_bus->class_dev,
&class_device_attr_cpuaffinity);
sysfs_remove_link(&pci_bus->class_dev.kobj, "bridge");
if (pci_bus->bridge)
put_device(pci_bus->bridge);
kfree(pci_bus);
......
......@@ -61,15 +61,18 @@ int pci_remove_device_safe(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_remove_device_safe);
void pci_remove_bus(struct pci_bus *b)
void pci_remove_bus(struct pci_bus *pci_bus)
{
pci_proc_detach_bus(b);
pci_proc_detach_bus(pci_bus);
spin_lock(&pci_bus_lock);
list_del(&b->node);
list_del(&pci_bus->node);
spin_unlock(&pci_bus_lock);
class_device_unregister(&b->class_dev);
pci_remove_legacy_files(pci_bus);
class_device_remove_file(&pci_bus->class_dev,
&class_device_attr_cpuaffinity);
sysfs_remove_link(&pci_bus->class_dev.kobj, "bridge");
class_device_unregister(&pci_bus->class_dev);
}
EXPORT_SYMBOL(pci_remove_bus);
......
......@@ -5,10 +5,7 @@
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
*
* PCI ROM access routines
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
......@@ -24,11 +21,10 @@
* between the ROM and other resources, so enabling it may disable access
* to MMIO registers or other card memory.
*/
static void
pci_enable_rom(struct pci_dev *pdev)
static void pci_enable_rom(struct pci_dev *pdev)
{
u32 rom_addr;
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
rom_addr |= PCI_ROM_ADDRESS_ENABLE;
pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
......@@ -41,8 +37,7 @@ pci_enable_rom(struct pci_dev *pdev)
* Disable ROM decoding on a PCI device by turning off the last bit in the
* ROM BAR.
*/
static void
pci_disable_rom(struct pci_dev *pdev)
static void pci_disable_rom(struct pci_dev *pdev)
{
u32 rom_addr;
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
......@@ -57,7 +52,7 @@ pci_disable_rom(struct pci_dev *pdev)
* @return: kernel virtual pointer to image of ROM
*
* Map a PCI ROM into kernel space. If ROM is boot video ROM,
* the shadow BIOS copy will be returned instead of the
* the shadow BIOS copy will be returned instead of the
* actual ROM.
*/
void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
......@@ -67,10 +62,12 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
void __iomem *rom;
void __iomem *image;
int last_image;
if (res->flags & IORESOURCE_ROM_SHADOW) { /* IORESOURCE_ROM_SHADOW only set on x86 */
start = (loff_t)0xC0000; /* primary video rom always starts here */
*size = 0x20000; /* cover C000:0 through E000:0 */
/* IORESOURCE_ROM_SHADOW only set on x86 */
if (res->flags & IORESOURCE_ROM_SHADOW) {
/* primary video rom always starts here */
start = (loff_t)0xC0000;
*size = 0x20000; /* cover C000:0 through E000:0 */
} else {
if (res->flags & IORESOURCE_ROM_COPY) {
*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
......@@ -79,28 +76,32 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
/* assign the ROM an address if it doesn't have one */
if (res->parent == NULL)
pci_assign_resource(pdev, PCI_ROM_RESOURCE);
start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
if (*size == 0)
return NULL;
/* Enable ROM space decodes */
pci_enable_rom(pdev);
}
}
rom = ioremap(start, *size);
if (!rom) {
/* restore enable if ioremap fails */
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
if (!(res->flags & (IORESOURCE_ROM_ENABLE |
IORESOURCE_ROM_SHADOW |
IORESOURCE_ROM_COPY)))
pci_disable_rom(pdev);
return NULL;
}
}
/* Try to find the true size of the ROM since sometimes the PCI window */
/* size is much larger than the actual size of the ROM. */
/* True size is important if the ROM is going to be copied. */
/*
* Try to find the true size of the ROM since sometimes the PCI window
* size is much larger than the actual size of the ROM.
* True size is important if the ROM is going to be copied.
*/
image = rom;
do {
void __iomem *pds;
......@@ -136,30 +137,30 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
* @return: kernel virtual pointer to image of ROM
*
* Map a PCI ROM into kernel space. If ROM is boot video ROM,
* the shadow BIOS copy will be returned instead of the
* the shadow BIOS copy will be returned instead of the
* actual ROM.
*/
void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
void __iomem *rom;
rom = pci_map_rom(pdev, size);
if (!rom)
return NULL;
if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
return rom;
res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
if (!res->start)
if (!res->start)
return rom;
res->end = res->start + *size;
res->end = res->start + *size;
memcpy_fromio((void*)res->start, rom, *size);
pci_unmap_rom(pdev, rom);
res->flags |= IORESOURCE_ROM_COPY;
return (void __iomem *)res->start;
}
......@@ -170,16 +171,15 @@ void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
*
* Remove a mapping of a previously mapped ROM
*/
void
pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (res->flags & IORESOURCE_ROM_COPY)
return;
iounmap(rom);
/* Disable again before continuing, leave enabled if pci=rom */
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
pci_disable_rom(pdev);
......@@ -189,26 +189,28 @@ pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
* pci_remove_rom - disable the ROM and remove its sysfs attribute
* @dev: pointer to pci device struct
*
* Remove the rom file in sysfs and disable ROM decoding.
*/
void
pci_remove_rom(struct pci_dev *pdev)
void pci_remove_rom(struct pci_dev *pdev)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
if (!(res->flags & (IORESOURCE_ROM_ENABLE |
IORESOURCE_ROM_SHADOW |
IORESOURCE_ROM_COPY)))
pci_disable_rom(pdev);
}
/**
* pci_cleanup_rom - internal routine for freeing the ROM copy created
* pci_cleanup_rom - internal routine for freeing the ROM copy created
* by pci_map_rom_copy called from remove.c
* @dev: pointer to pci device struct
*
* Free the copied ROM if we allocated one.
*/
void
pci_cleanup_rom(struct pci_dev *pdev)
void pci_cleanup_rom(struct pci_dev *pdev)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (res->flags & IORESOURCE_ROM_COPY) {
......
......@@ -13,7 +13,7 @@
#include <linux/interrupt.h>
#include "pci.h"
spinlock_t pci_bus_lock = SPIN_LOCK_UNLOCKED;
DEFINE_SPINLOCK(pci_bus_lock);
static struct pci_bus * __devinit
pci_do_find_bus(struct pci_bus* bus, unsigned char busnr)
......
......@@ -2246,7 +2246,7 @@
#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d
#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e
#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f
#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b0
#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8
#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b1
#define PCI_DEVICE_ID_INTEL_ICH7_2 0x27c0
#define PCI_DEVICE_ID_INTEL_ICH7_3 0x27c1
......
/*
* File: pcieport_if.h
* Purpose: PCI Express Port Bus Driver's IF Data Structure
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef _PCIEPORT_IF_H_
#define _PCIEPORT_IF_H_
/* Port Type */
#define PCIE_RC_PORT 4 /* Root port of RC */
#define PCIE_SW_UPSTREAM_PORT 5 /* Upstream port of Switch */
#define PCIE_SW_DOWNSTREAM_PORT 6 /* Downstream port of Switch */
#define PCIE_ANY_PORT 7
/* Service Type */
#define PCIE_PORT_SERVICE_PME 1 /* Power Management Event */
#define PCIE_PORT_SERVICE_AER 2 /* Advanced Error Reporting */
#define PCIE_PORT_SERVICE_HP 4 /* Native Hotplug */
#define PCIE_PORT_SERVICE_VC 8 /* Virtual Channel */
/* Root/Upstream/Downstream Port's Interrupt Mode */
#define PCIE_PORT_INTx_MODE 0
#define PCIE_PORT_MSI_MODE 1
#define PCIE_PORT_MSIX_MODE 2
struct pcie_port_service_id {
__u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
__u32 class, class_mask; /* (class,subclass,prog-if) triplet */
__u32 port_type, service_type; /* Port Entity */
kernel_ulong_t driver_data;
};
struct pcie_device {
int irq; /* Service IRQ/MSI/MSI-X Vector */
int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */
struct pcie_port_service_id id; /* Service ID */
struct pci_dev *port; /* Root/Upstream/Downstream Port */
void *priv_data; /* Service Private Data */
struct device device; /* Generic Device Interface */
};
#define to_pcie_device(d) container_of(d, struct pcie_device, device)
static inline void set_service_data(struct pcie_device *dev, void *data)
{
dev->priv_data = data;
}
static inline void* get_service_data(struct pcie_device *dev)
{
return dev->priv_data;
}
struct pcie_port_service_driver {
const char *name;
int (*probe) (struct pcie_device *dev,
const struct pcie_port_service_id *id);
void (*remove) (struct pcie_device *dev);
int (*suspend) (struct pcie_device *dev, u32 state);
int (*resume) (struct pcie_device *dev);
const struct pcie_port_service_id *id_table;
struct device_driver driver;
};
#define to_service_driver(d) \
container_of(d, struct pcie_port_service_driver, driver)
extern int pcie_port_service_register(struct pcie_port_service_driver *new);
extern void pcie_port_service_unregister(struct pcie_port_service_driver *new);
#endif /* _PCIEPORT_IF_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment