Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

59
drivers/pci/Kconfig Normal file
View File

@@ -0,0 +1,59 @@
#
# PCI configuration
#
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
depends on PCI
depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64
help
This allows device drivers to enable MSI (Message Signaled
Interrupts). Message Signaled Interrupts enable a device to
generate an interrupt using an inbound Memory Write on its
PCI bus instead of asserting a device IRQ pin.
If you don't know what to do here, say N.
config PCI_LEGACY_PROC
bool "Legacy /proc/pci interface"
depends on PCI
---help---
This feature enables a procfs file -- /proc/pci -- that provides a
summary of PCI devices in the system.
This feature has been deprecated as of v2.5.53, in favor of using the
tool lspci(8). This feature may be removed at a future date.
lspci can provide the same data, as well as much more. lspci is a part of
the pci-utils package, which should be installed by your distribution.
See <file:Documentation/Changes> for information on where to get the latest
version.
When in doubt, say N.
config PCI_NAMES
bool "PCI device name database"
depends on PCI
---help---
By default, the kernel contains a database of all known PCI device
names to make the information in /proc/pci, /proc/ioports and
similar files comprehensible to the user.
This database increases size of the kernel image by about 80KB. This
memory is freed after the system boots up if CONFIG_HOTPLUG is not set.
Anyway, if you are building an installation floppy or kernel for an
embedded system where kernel image size really matters, you can disable
this feature and you'll get device ID numbers instead of names.
When in doubt, say Y.
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
help
Say Y here if you want the PCI core to produce a bunch of debug
messages to the system log. Select this if you are having a
problem with PCI support and want to see more of what is going on.
When in doubt, say N.

65
drivers/pci/Makefile Normal file
View File

@@ -0,0 +1,65 @@
#
# Makefile for the PCI bus specific drivers.
#
obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
names.o pci-driver.o search.o pci-sysfs.o \
rom.o
obj-$(CONFIG_PROC_FS) += proc.o
ifndef CONFIG_SPARC64
obj-y += setup-res.o
endif
obj-$(CONFIG_HOTPLUG) += hotplug.o
# Build the PCI Hotplug drivers if we were asked to
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
#
# Some architectures use the generic PCI setup functions
#
obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
obj-$(CONFIG_PARISC) += setup-bus.o
obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
obj-$(CONFIG_PPC32) += setup-irq.o
obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
obj-$(CONFIG_PCI_MSI) += msi.o
#
# ACPI Related PCI FW Functions
#
obj-$(CONFIG_ACPI) += pci-acpi.o
# Cardbus & CompactPCI use setup-bus
obj-$(CONFIG_HOTPLUG) += setup-bus.o
ifndef CONFIG_X86
obj-y += syscall.o
endif
ifeq ($(CONFIG_PCI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
hostprogs-y := gen-devlist
# Dependencies on generated files need to be listed explicitly
$(obj)/names.o: $(obj)/devlist.h $(obj)/classlist.h
$(obj)/classlist.h: $(obj)/devlist.h
# And that's how to generate them
quiet_cmd_devlist = DEVLIST $@
cmd_devlist = ( cd $(obj); ./gen-devlist ) < $<
$(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
$(call cmd,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/

62
drivers/pci/access.c Normal file
View File

@@ -0,0 +1,62 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/ioport.h>
/*
* This interrupt-safe spinlock protects all accesses to PCI
* configuration space.
*/
static DEFINE_SPINLOCK(pci_lock);
/*
* Wrappers for all PCI configuration access functions. They just check
* alignment, do locking and call the low-level functions pointed to
* by pci_dev->ops.
*/
#define PCI_byte_BAD 0
#define PCI_word_BAD (pos & 1)
#define PCI_dword_BAD (pos & 3)
#define PCI_OP_READ(size,type,len) \
int pci_bus_read_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type *value) \
{ \
int res; \
unsigned long flags; \
u32 data = 0; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->read(bus, devfn, pos, len, &data); \
*value = (type)data; \
spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
#define PCI_OP_WRITE(size,type,len) \
int pci_bus_write_config_##size \
(struct pci_bus *bus, unsigned int devfn, int pos, type value) \
{ \
int res; \
unsigned long flags; \
if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \
spin_lock_irqsave(&pci_lock, flags); \
res = bus->ops->write(bus, devfn, pos, len, value); \
spin_unlock_irqrestore(&pci_lock, flags); \
return res; \
}
PCI_OP_READ(byte, u8, 1)
PCI_OP_READ(word, u16, 2)
PCI_OP_READ(dword, u32, 4)
PCI_OP_WRITE(byte, u8, 1)
PCI_OP_WRITE(word, u16, 2)
PCI_OP_WRITE(dword, u32, 4)
EXPORT_SYMBOL(pci_bus_read_config_byte);
EXPORT_SYMBOL(pci_bus_read_config_word);
EXPORT_SYMBOL(pci_bus_read_config_dword);
EXPORT_SYMBOL(pci_bus_write_config_byte);
EXPORT_SYMBOL(pci_bus_write_config_word);
EXPORT_SYMBOL(pci_bus_write_config_dword);

151
drivers/pci/bus.c Normal file
View File

@@ -0,0 +1,151 @@
/*
* drivers/pci/bus.c
*
* From setup-res.c, by:
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
* Ivan Kokshaysky (ink@jurassic.park.msu.ru)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include "pci.h"
/**
* pci_bus_alloc_resource - allocate a resource from a parent bus
* @bus: PCI bus
* @res: resource to allocate
* @size: size of resource to allocate
* @align: alignment of resource to allocate
* @min: minimum /proc/iomem address to allocate
* @type_mask: IORESOURCE_* type flags
* @alignf: resource alignment function
* @alignf_data: data argument for resource alignment function
*
* Given the PCI bus a device resides on, the size, minimum address,
* alignment and type, try to find an acceptable resource allocation
* for a specific device resource.
*/
int
pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
unsigned long size, unsigned long align, unsigned long min,
unsigned int type_mask,
void (*alignf)(void *, struct resource *,
unsigned long, unsigned long),
void *alignf_data)
{
int i, ret = -ENOMEM;
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
struct resource *r = bus->resource[i];
if (!r)
continue;
/* type_mask must match */
if ((res->flags ^ r->flags) & type_mask)
continue;
/* We cannot allocate a non-prefetching resource
from a pre-fetching area */
if ((r->flags & IORESOURCE_PREFETCH) &&
!(res->flags & IORESOURCE_PREFETCH))
continue;
/* Ok, try it out.. */
ret = allocate_resource(r, res, size, min, -1, align,
alignf, alignf_data);
if (ret == 0)
break;
}
return ret;
}
/**
* add a single device
* @dev: device to add
*
* This adds a single pci device to the global
* device list and adds sysfs and procfs entries
*/
void __devinit pci_bus_add_device(struct pci_dev *dev)
{
device_add(&dev->dev);
spin_lock(&pci_bus_lock);
list_add_tail(&dev->global_list, &pci_devices);
spin_unlock(&pci_bus_lock);
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
}
/**
* pci_bus_add_devices - insert newly discovered PCI devices
* @bus: bus to check for new devices
*
* Add newly discovered PCI devices (which are on the bus->devices
* list) to the global PCI device list, add the sysfs and procfs
* entries. Where a bridge is found, add the discovered bus to
* the parents list of child buses, and recurse (breadth-first
* to be compatible with 2.4)
*
* Call hotplug for each new devices.
*/
void __devinit pci_bus_add_devices(struct pci_bus *bus)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
* Skip already-present devices (which are on the
* global device list.)
*/
if (!list_empty(&dev->global_list))
continue;
pci_bus_add_device(dev);
}
list_for_each_entry(dev, &bus->devices, bus_list) {
BUG_ON(list_empty(&dev->global_list));
/*
* If there is an unattached subordinate bus, attach
* it and then scan for unattached PCI devices.
*/
if (dev->subordinate && list_empty(&dev->subordinate->node)) {
spin_lock(&pci_bus_lock);
list_add_tail(&dev->subordinate->node, &dev->bus->children);
spin_unlock(&pci_bus_lock);
pci_bus_add_devices(dev->subordinate);
sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
}
}
}
void pci_enable_bridges(struct pci_bus *bus)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->subordinate) {
pci_enable_device(dev);
pci_set_master(dev);
pci_enable_bridges(dev->subordinate);
}
}
}
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);
EXPORT_SYMBOL(pci_enable_bridges);

132
drivers/pci/gen-devlist.c Normal file
View File

@@ -0,0 +1,132 @@
/*
* Generate devlist.h and classlist.h from the PCI ID file.
*
* (c) 1999--2002 Martin Mares <mj@ucw.cz>
*/
#include <stdio.h>
#include <string.h>
#define MAX_NAME_SIZE 200
static void
pq(FILE *f, const char *c, int len)
{
int i = 1;
while (*c && i != len) {
if (*c == '"')
fprintf(f, "\\\"");
else {
fputc(*c, f);
if (*c == '?' && c[1] == '?') {
/* Avoid trigraphs */
fprintf(f, "\" \"");
}
}
c++;
i++;
}
}
int
main(void)
{
char line[1024], *c, *bra, vend[8];
int vendors = 0;
int mode = 0;
int lino = 0;
int vendor_len = 0;
FILE *devf, *clsf;
devf = fopen("devlist.h", "w");
clsf = fopen("classlist.h", "w");
if (!devf || !clsf) {
fprintf(stderr, "Cannot create output file!\n");
return 1;
}
while (fgets(line, sizeof(line)-1, stdin)) {
lino++;
if ((c = strchr(line, '\n')))
*c = 0;
if (!line[0] || line[0] == '#')
continue;
if (line[1] == ' ') {
if (line[0] == 'C' && strlen(line) > 4 && line[4] == ' ') {
vend[0] = line[2];
vend[1] = line[3];
vend[2] = 0;
mode = 2;
} else goto err;
}
else if (line[0] == '\t') {
if (line[1] == '\t')
continue;
switch (mode) {
case 1:
if (strlen(line) > 5 && line[5] == ' ') {
c = line + 5;
while (*c == ' ')
*c++ = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
/* Too long, try cutting off long description */
bra = strchr(c, '[');
if (bra && bra > c && bra[-1] == ' ')
bra[-1] = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
fprintf(stderr, "Line %d: Device name too long. Name truncated.\n", lino);
fprintf(stderr, "%s\n", c);
/*return 1;*/
}
}
fprintf(devf, "\tDEVICE(%s,%s,\"", vend, line+1);
pq(devf, c, MAX_NAME_SIZE - vendor_len - 1);
fputs("\")\n", devf);
} else goto err;
break;
case 2:
if (strlen(line) > 3 && line[3] == ' ') {
c = line + 3;
while (*c == ' ')
*c++ = 0;
fprintf(clsf, "CLASS(%s%s, \"%s\")\n", vend, line+1, c);
} else goto err;
break;
default:
goto err;
}
} else if (strlen(line) > 4 && line[4] == ' ') {
c = line + 4;
while (*c == ' ')
*c++ = 0;
if (vendors)
fputs("ENDVENDOR()\n\n", devf);
vendors++;
strcpy(vend, line);
vendor_len = strlen(c);
if (vendor_len + 24 > MAX_NAME_SIZE) {
fprintf(stderr, "Line %d: Vendor name too long\n", lino);
return 1;
}
fprintf(devf, "VENDOR(%s,\"", vend);
pq(devf, c, 0);
fputs("\")\n", devf);
mode = 1;
} else {
err:
fprintf(stderr, "Line %d: Syntax error in mode %d: %s\n", lino, mode, line);
return 1;
}
}
fputs("ENDVENDOR()\n\
\n\
#undef VENDOR\n\
#undef DEVICE\n\
#undef ENDVENDOR\n", devf);
fputs("\n#undef CLASS\n", clsf);
fclose(devf);
fclose(clsf);
return 0;
}

163
drivers/pci/hotplug.c Normal file
View File

@@ -0,0 +1,163 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/module.h>
#include "pci.h"
int pci_hotplug (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct pci_dev *pdev;
char *scratch;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
pdev = to_pci_dev(dev);
if (!pdev)
return -ENODEV;
scratch = buffer;
/* stuff we want to pass to /sbin/hotplug */
envp[i++] = scratch;
length += scnprintf (scratch, buffer_size - length, "PCI_CLASS=%04X",
pdev->class);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
envp[i++] = scratch;
length += scnprintf (scratch, buffer_size - length, "PCI_ID=%04X:%04X",
pdev->vendor, pdev->device);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
envp[i++] = scratch;
length += scnprintf (scratch, buffer_size - length,
"PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
pdev->subsystem_device);
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
++length;
scratch += length;
envp[i++] = scratch;
length += scnprintf (scratch, buffer_size - length, "PCI_SLOT_NAME=%s",
pci_name(pdev));
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
static int pci_visit_bus (struct pci_visit * fn, struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_parent)
{
struct list_head *ln;
struct pci_dev *dev;
struct pci_dev_wrapped wrapped_dev;
int result = 0;
pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(wrapped_bus->bus),
wrapped_bus->bus->number);
if (fn->pre_visit_pci_bus) {
result = fn->pre_visit_pci_bus(wrapped_bus, wrapped_parent);
if (result)
return result;
}
ln = wrapped_bus->bus->devices.next;
while (ln != &wrapped_bus->bus->devices) {
dev = pci_dev_b(ln);
ln = ln->next;
memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
wrapped_dev.dev = dev;
result = pci_visit_dev(fn, &wrapped_dev, wrapped_bus);
if (result)
return result;
}
if (fn->post_visit_pci_bus)
result = fn->post_visit_pci_bus(wrapped_bus, wrapped_parent);
return result;
}
static int pci_visit_bridge (struct pci_visit * fn,
struct pci_dev_wrapped *wrapped_dev,
struct pci_bus_wrapped *wrapped_parent)
{
struct pci_bus *bus;
struct pci_bus_wrapped wrapped_bus;
int result = 0;
pr_debug("PCI: Scanning bridge %s\n", pci_name(wrapped_dev->dev));
if (fn->visit_pci_dev) {
result = fn->visit_pci_dev(wrapped_dev, wrapped_parent);
if (result)
return result;
}
bus = wrapped_dev->dev->subordinate;
if (bus) {
memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
wrapped_bus.bus = bus;
result = pci_visit_bus(fn, &wrapped_bus, wrapped_dev);
}
return result;
}
/**
* pci_visit_dev - scans the pci buses.
* Every bus and every function is presented to a custom
* function that can act upon it.
*/
int pci_visit_dev(struct pci_visit *fn, struct pci_dev_wrapped *wrapped_dev,
struct pci_bus_wrapped *wrapped_parent)
{
struct pci_dev* dev = wrapped_dev ? wrapped_dev->dev : NULL;
int result = 0;
if (!dev)
return 0;
if (fn->pre_visit_pci_dev) {
result = fn->pre_visit_pci_dev(wrapped_dev, wrapped_parent);
if (result)
return result;
}
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
result = pci_visit_bridge(fn, wrapped_dev,
wrapped_parent);
if (result)
return result;
break;
default:
pr_debug("PCI: Scanning device %s\n", pci_name(dev));
if (fn->visit_pci_dev) {
result = fn->visit_pci_dev (wrapped_dev,
wrapped_parent);
if (result)
return result;
}
}
if (fn->post_visit_pci_dev)
result = fn->post_visit_pci_dev(wrapped_dev, wrapped_parent);
return result;
}
EXPORT_SYMBOL(pci_visit_dev);

197
drivers/pci/hotplug/Kconfig Normal file
View File

@@ -0,0 +1,197 @@
#
# PCI Hotplug support
#
menu "PCI Hotplug Support"
config HOTPLUG_PCI
tristate "Support for PCI Hotplug (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL
select HOTPLUG
---help---
Say Y here if you have a motherboard with a PCI Hotplug controller.
This allows you to add and remove PCI cards while the machine is
powered up and running. The file system pcihpfs must be mounted
in order to interact with any PCI Hotplug controllers.
To compile this driver as a module, choose M here: the
module will be called pci_hotplug.
When in doubt, say N.
config HOTPLUG_PCI_FAKE
tristate "Fake PCI Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you want to use the fake PCI hotplug driver. It can
be used to simulate PCI hotplug events if even if your system is
not PCI hotplug capable.
This driver will "emulate" removing PCI devices from the system.
If the "power" file is written to with "0" then the specified PCI
device will be completely removed from the kernel.
WARNING, this does NOT turn off the power to the PCI device.
This is a "logical" removal, not a physical or electrical
removal.
Use this module at your own risk. You have been warned!
To compile this driver as a module, choose M here: the
module will be called fakephp.
When in doubt, say N.
config HOTPLUG_PCI_COMPAQ
tristate "Compaq PCI Hotplug driver"
depends on HOTPLUG_PCI && X86 && PCI_BIOS
help
Say Y here if you have a motherboard with a Compaq PCI Hotplug
controller.
To compile this driver as a module, choose M here: the
module will be called cpqphp.
When in doubt, say N.
config HOTPLUG_PCI_COMPAQ_NVRAM
bool "Save configuration into NVRAM on Compaq servers"
depends on HOTPLUG_PCI_COMPAQ
help
Say Y here if you have a Compaq server that has a PCI Hotplug
controller. This will allow the PCI Hotplug driver to store the PCI
system configuration options in NVRAM.
When in doubt, say N.
config HOTPLUG_PCI_IBM
tristate "IBM PCI Hotplug driver"
depends on HOTPLUG_PCI && X86_IO_APIC && X86 && PCI_BIOS
help
Say Y here if you have a motherboard with a IBM PCI Hotplug
controller.
To compile this driver as a module, choose M here: the
module will be called ibmphp.
When in doubt, say N.
config HOTPLUG_PCI_ACPI
tristate "ACPI PCI Hotplug driver"
depends on ACPI_BUS && HOTPLUG_PCI
help
Say Y here if you have a system that supports PCI Hotplug using
ACPI.
To compile this driver as a module, choose M here: the
module will be called acpiphp.
When in doubt, say N.
config HOTPLUG_PCI_ACPI_IBM
tristate "ACPI PCI Hotplug driver IBM extensions"
depends on HOTPLUG_PCI_ACPI
help
Say Y here if you have an IBM system that supports PCI Hotplug using
ACPI.
To compile this driver as a module, choose M here: the
module will be called acpiphp_ibm.
When in doubt, say N.
config HOTPLUG_PCI_CPCI
bool "CompactPCI Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you have a CompactPCI system card with CompactPCI
hotswap support per the PICMG 2.1 specification.
When in doubt, say N.
config HOTPLUG_PCI_CPCI_ZT5550
tristate "Ziatech ZT5550 CompactPCI Hotplug driver"
depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
help
Say Y here if you have an Performance Technologies (formerly Intel,
formerly just Ziatech) Ziatech ZT5550 CompactPCI system card.
To compile this driver as a module, choose M here: the
module will be called cpcihp_zt5550.
When in doubt, say N.
config HOTPLUG_PCI_CPCI_GENERIC
tristate "Generic port I/O CompactPCI Hotplug driver"
depends on HOTPLUG_PCI && HOTPLUG_PCI_CPCI && X86
help
Say Y here if you have a CompactPCI system card that exposes the #ENUM
hotswap signal as a bit in a system register that can be read through
standard port I/O.
To compile this driver as a module, choose M here: the
module will be called cpcihp_generic.
When in doubt, say N.
config HOTPLUG_PCI_SHPC
tristate "SHPC PCI Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you have a motherboard with a SHPC PCI Hotplug
controller.
To compile this driver as a module, choose M here: the
module will be called shpchp.
When in doubt, say N.
config HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
bool "Use polling mechanism for hot-plug events (for testing purpose)"
depends on HOTPLUG_PCI_SHPC
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_PHPRM_LEGACY
bool "For AMD SHPC only: Use $HRT for resource/configuration"
depends on HOTPLUG_PCI_SHPC && !ACPI_BUS
help
Say Y here for AMD SHPC. You have to select this option if you are
using this driver on platform with AMD SHPC.
config HOTPLUG_PCI_RPA
tristate "RPA PCI Hotplug driver"
depends on HOTPLUG_PCI && PPC_PSERIES && PPC64 && !HOTPLUG_PCI_FAKE
help
Say Y here if you have a a RPA system that supports PCI Hotplug.
To compile this driver as a module, choose M here: the
module will be called rpaphp.
When in doubt, say N.
config HOTPLUG_PCI_RPA_DLPAR
tristate "RPA Dynamic Logical Partitioning for I/O slots"
depends on HOTPLUG_PCI_RPA
help
Say Y here if your system supports Dynamic Logical Partitioning
for I/O slots.
To compile this driver as a module, choose M here: the
module will be called rpadlpar_io.
When in doubt, say N.
config HOTPLUG_PCI_SGI
tristate "SGI PCI Hotplug Support"
depends on HOTPLUG_PCI && IA64_SGI_SN2
help
Say Y here if you have an SGI IA64 Altix system.
When in doubt, say N.
endmenu

View File

@@ -0,0 +1,74 @@
#
# Makefile for the Linux kernel pci hotplug controller drivers.
#
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
pci_hotplug-objs := pci_hotplug_core.o
ifdef CONFIG_HOTPLUG_PCI_CPCI
pci_hotplug-objs += cpci_hotplug_core.o \
cpci_hotplug_pci.o
endif
cpqphp-objs := cpqphp_core.o \
cpqphp_ctrl.o \
cpqphp_sysfs.o \
cpqphp_pci.o
cpqphp-$(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM) += cpqphp_nvram.o
cpqphp-objs += $(cpqphp-y)
ibmphp-objs := ibmphp_core.o \
ibmphp_ebda.o \
ibmphp_pci.o \
ibmphp_res.o \
ibmphp_hpc.o
acpiphp-objs := acpiphp_core.o \
acpiphp_glue.o \
acpiphp_pci.o \
acpiphp_res.o
rpaphp-objs := rpaphp_core.o \
rpaphp_pci.o \
rpaphp_slot.o \
rpaphp_vio.o
rpadlpar_io-objs := rpadlpar_core.o \
rpadlpar_sysfs.o
pciehp-objs := pciehp_core.o \
pciehp_ctrl.o \
pciehp_pci.o \
pciehp_hpc.o
ifdef CONFIG_ACPI_BUS
pciehp-objs += pciehprm_acpi.o
else
pciehp-objs += pciehprm_nonacpi.o
endif
shpchp-objs := shpchp_core.o \
shpchp_ctrl.o \
shpchp_pci.o \
shpchp_sysfs.o \
shpchp_hpc.o
ifdef CONFIG_ACPI_BUS
shpchp-objs += shpchprm_acpi.o
else
ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
shpchp-objs += shpchprm_legacy.o
else
shpchp-objs += shpchprm_nonacpi.o
endif
endif

View File

@@ -0,0 +1,268 @@
/*
* ACPI PCI Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
* Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
* Copyright (C) 2002,2003 NEC Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <gregkh@us.ibm.com>,
* <t-kochi@bq.jp.nec.com>
*
*/
#ifndef _ACPIPHP_H
#define _ACPIPHP_H
#include <linux/acpi.h>
#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
#include "pci_hotplug.h"
#define dbg(format, arg...) \
do { \
if (acpiphp_debug) \
printk(KERN_DEBUG "%s: " format, \
MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
/* name size which is used for entries in pcihpfs */
#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */
struct acpiphp_bridge;
struct acpiphp_slot;
struct pci_resource;
/*
* struct slot - slot information for each *physical* slot
*/
struct slot {
u8 number;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
struct acpiphp_slot *acpi_slot;
};
/*
* struct pci_resource - describes pci resource (mem, pfmem, io, bus)
*/
struct pci_resource {
struct pci_resource * next;
u64 base;
u32 length;
};
/**
* struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters
* @cache_line_size in DWORD
* @latency_timer in PCI clock
* @enable_SERR 0 or 1
* @enable_PERR 0 or 1
*/
struct hpp_param {
u8 cache_line_size;
u8 latency_timer;
u8 enable_SERR;
u8 enable_PERR;
};
/**
* struct acpiphp_bridge - PCI bridge information
*
* for each bridge device in ACPI namespace
*/
struct acpiphp_bridge {
struct list_head list;
acpi_handle handle;
struct acpiphp_slot *slots;
int type;
int nr_slots;
u8 seg;
u8 bus;
u8 sub;
u32 flags;
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
struct pci_bus *pci_bus;
/* PCI-to-PCI bridge device */
struct pci_dev *pci_dev;
/* ACPI 2.0 _HPP parameters */
struct hpp_param hpp;
spinlock_t res_lock;
/* available resources on this bus */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
};
/**
* struct acpiphp_slot - PCI slot information
*
* PCI slot information for each *physical* PCI slot
*/
struct acpiphp_slot {
struct acpiphp_slot *next;
struct acpiphp_bridge *bridge; /* parent */
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct semaphore crit_sect;
u32 id; /* slot id (serial #) for hotplug core */
u8 device; /* pci device# */
u32 sun; /* ACPI _SUN (slot unique number) */
u32 slotno; /* slot number relative to bridge */
u32 flags; /* see below */
};
/**
* struct acpiphp_func - PCI function information
*
* PCI function information for each object in ACPI namespace
* typically 8 objects per slot (i.e. for each PCI function)
*/
struct acpiphp_func {
struct acpiphp_slot *slot; /* parent */
struct list_head sibling;
struct pci_dev *pci_dev;
acpi_handle handle;
u8 function; /* pci function# */
u32 flags; /* see below */
/* resources used for this function */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
};
/**
* struct acpiphp_attention_info - device specific attention registration
*
* ACPI has no generic method of setting/getting attention status
* this allows for device specific driver registration
*/
struct acpiphp_attention_info
{
int (*set_attn)(struct hotplug_slot *slot, u8 status);
int (*get_attn)(struct hotplug_slot *slot, u8 *status);
struct module *owner;
};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
/* PCI BRIDGE type */
#define BRIDGE_TYPE_HOST 0
#define BRIDGE_TYPE_P2P 1
/* ACPI _STA method value (ignore bit 4; battery present) */
#define ACPI_STA_PRESENT (0x00000001)
#define ACPI_STA_ENABLED (0x00000002)
#define ACPI_STA_SHOW_IN_UI (0x00000004)
#define ACPI_STA_FUNCTIONING (0x00000008)
#define ACPI_STA_ALL (0x0000000f)
/* bridge flags */
#define BRIDGE_HAS_STA (0x00000001)
#define BRIDGE_HAS_EJ0 (0x00000002)
#define BRIDGE_HAS_HPP (0x00000004)
#define BRIDGE_HAS_PS0 (0x00000010)
#define BRIDGE_HAS_PS1 (0x00000020)
#define BRIDGE_HAS_PS2 (0x00000040)
#define BRIDGE_HAS_PS3 (0x00000080)
/* slot flags */
#define SLOT_POWEREDON (0x00000001)
#define SLOT_ENABLED (0x00000002)
#define SLOT_MULTIFUNCTION (0x00000004)
/* function flags */
#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
#define FUNC_HAS_PS0 (0x00000010)
#define FUNC_HAS_PS1 (0x00000020)
#define FUNC_HAS_PS2 (0x00000040)
#define FUNC_HAS_PS3 (0x00000080)
/* function prototypes */
/* acpiphp_core.c */
extern int acpiphp_register_attention(struct acpiphp_attention_info*info);
extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
/* acpiphp_glue.c */
extern int acpiphp_glue_init (void);
extern void acpiphp_glue_exit (void);
extern int acpiphp_get_num_slots (void);
extern struct acpiphp_slot *get_slot_from_id (int id);
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
extern int acpiphp_enable_slot (struct acpiphp_slot *slot);
extern int acpiphp_disable_slot (struct acpiphp_slot *slot);
extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
/* acpiphp_pci.c */
extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn);
extern int acpiphp_configure_slot (struct acpiphp_slot *slot);
extern int acpiphp_configure_function (struct acpiphp_func *func);
extern void acpiphp_unconfigure_function (struct acpiphp_func *func);
extern int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge);
extern int acpiphp_init_func_resource (struct acpiphp_func *func);
/* acpiphp_res.c */
extern struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size);
extern struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size);
extern struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size);
extern int acpiphp_resource_sort_and_combine (struct pci_resource **head);
extern struct pci_resource *acpiphp_make_resource (u64 base, u32 length);
extern void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to);
extern void acpiphp_free_resource (struct pci_resource **res);
extern void acpiphp_dump_resource (struct acpiphp_bridge *bridge); /* debug */
extern void acpiphp_dump_func_resource (struct acpiphp_func *func); /* debug */
/* variables */
extern int acpiphp_debug;
#endif /* _ACPIPHP_H */

View File

@@ -0,0 +1,453 @@
/*
* ACPI PCI Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
* Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
* Copyright (C) 2002,2003 NEC Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <gregkh@us.ibm.com>,
* <t-kochi@bq.jp.nec.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include "pci_hotplug.h"
#include "acpiphp.h"
static LIST_HEAD(slot_list);
#define MY_NAME "acpiphp"
static int debug;
int acpiphp_debug;
/* local variables */
static int num_slots;
static struct acpiphp_attention_info *attention_info;
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>"
#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
module_param(debug, bool, 0644);
/* export the attention callback registration methods */
EXPORT_SYMBOL_GPL(acpiphp_register_attention);
EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
.get_address = get_address,
};
/**
* acpiphp_register_attention - set attention LED callback
* @info: must be completely filled with LED callbacks
*
* Description: this is used to register a hardware specific ACPI
* driver that manipulates the attention LED. All the fields in
* info must be set.
**/
int acpiphp_register_attention(struct acpiphp_attention_info *info)
{
int retval = -EINVAL;
if (info && info->owner && info->set_attn &&
info->get_attn && !attention_info) {
retval = 0;
attention_info = info;
}
return retval;
}
/**
* acpiphp_unregister_attention - unset attention LED callback
* @info: must match the pointer used to register
*
* Description: this is used to un-register a hardware specific acpi
* driver that manipulates the attention LED. The pointer to the
* info struct must be the same as the one used to set it.
**/
int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
{
int retval = -EINVAL;
if (info && attention_info == info) {
attention_info = NULL;
retval = 0;
}
return retval;
}
/**
* enable_slot - power on and enable a slot
* @hotplug_slot: slot to enable
*
* Actual tasks are done in acpiphp_enable_slot()
*
*/
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/* enable the specified slot */
return acpiphp_enable_slot(slot->acpi_slot);
}
/**
* disable_slot - disable and power off a slot
* @hotplug_slot: slot to disable
*
* Actual tasks are done in acpiphp_disable_slot()
*
*/
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/* disable the specified slot */
return acpiphp_disable_slot(slot->acpi_slot);
}
/**
* set_attention_status - set attention LED
* @hotplug_slot: slot to set attention LED on
* @status: value to set attention LED to (0 or 1)
*
* attention status LED, so we use a callback that
* was registered with us. This allows hardware specific
* ACPI implementations to blink the light for us.
**/
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
int retval = -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
if (attention_info && try_module_get(attention_info->owner)) {
retval = attention_info->set_attn(hotplug_slot, status);
module_put(attention_info->owner);
} else
attention_info = NULL;
return retval;
}
/**
* get_power_status - get power status of a slot
* @hotplug_slot: slot to get status
* @value: pointer to store status
*
* Some platforms may not implement _STA method properly.
* In that case, the value returned may not be reliable.
*
*/
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
*value = acpiphp_get_power_status(slot->acpi_slot);
return 0;
}
/**
* get_attention_status - get attention LED status
* @hotplug_slot: slot to get status from
* @value: returns with value of attention LED
*
* ACPI doesn't have known method to determine the state
* of the attention status LED, so we use a callback that
* was registered with us. This allows hardware specific
* ACPI implementations to determine its state
**/
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
int retval = -EINVAL;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
if (attention_info && try_module_get(attention_info->owner)) {
retval = attention_info->get_attn(hotplug_slot, value);
module_put(attention_info->owner);
} else
attention_info = NULL;
return retval;
}
/**
* get_latch_status - get latch status of a slot
* @hotplug_slot: slot to get status
* @value: pointer to store status
*
* ACPI doesn't provide any formal means to access latch status.
* Instead, we fake latch status from _STA
*
*/
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
*value = acpiphp_get_latch_status(slot->acpi_slot);
return 0;
}
/**
* get_adapter_status - get adapter status of a slot
* @hotplug_slot: slot to get status
* @value: pointer to store status
*
* ACPI doesn't provide any formal means to access adapter status.
* Instead, we fake adapter status from _STA
*
*/
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
*value = acpiphp_get_adapter_status(slot->acpi_slot);
return 0;
}
/**
* get_address - get pci address of a slot
* @hotplug_slot: slot to get status
* @busdev: pointer to struct pci_busdev (seg, bus, dev)
*
*/
static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
*value = acpiphp_get_address(slot->acpi_slot);
return 0;
}
static int __init init_acpi(void)
{
int retval;
/* initialize internal data structure etc. */
retval = acpiphp_glue_init();
/* read initial number of slots */
if (!retval) {
num_slots = acpiphp_get_num_slots();
if (num_slots == 0)
retval = -ENODEV;
}
return retval;
}
/**
* make_slot_name - make a slot name that appears in pcihpfs
* @slot: slot to name
*
*/
static void make_slot_name(struct slot *slot)
{
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%u",
slot->acpi_slot->sun);
}
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
/**
* init_slots - initialize 'struct slot' structures for each slot
*
*/
static int __init init_slots(void)
{
struct slot *slot;
int retval = -ENOMEM;
int i;
for (i = 0; i < num_slots; ++i) {
slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
if (!slot)
goto error;
memset(slot, 0, sizeof(struct slot));
slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot->hotplug_slot)
goto error_slot;
memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
if (!slot->hotplug_slot->info)
goto error_hpslot;
memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info));
slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
if (!slot->hotplug_slot->name)
goto error_info;
slot->number = i;
slot->hotplug_slot->private = slot;
slot->hotplug_slot->release = &release_slot;
slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
slot->acpi_slot = get_slot_from_id(i);
slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
slot->hotplug_slot->info->attention_status = 0;
slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
make_slot_name(slot);
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_name;
}
/* add slot to our internal list */
list_add(&slot->slot_list, &slot_list);
info("Slot [%s] registered\n", slot->hotplug_slot->name);
}
return 0;
error_name:
kfree(slot->hotplug_slot->name);
error_info:
kfree(slot->hotplug_slot->info);
error_hpslot:
kfree(slot->hotplug_slot);
error_slot:
kfree(slot);
error:
return retval;
}
static void __exit cleanup_slots (void)
{
struct list_head *tmp, *n;
struct slot *slot;
list_for_each_safe (tmp, n, &slot_list) {
/* memory will be freed in release_slot callback */
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot);
}
}
static int __init acpiphp_init(void)
{
int retval;
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
acpiphp_debug = debug;
/* read all the ACPI info from the system */
retval = init_acpi();
if (retval)
return retval;
return init_slots();
}
static void __exit acpiphp_exit(void)
{
cleanup_slots();
/* deallocate internal data structures etc. */
acpiphp_glue_exit();
}
module_init(acpiphp_init);
module_exit(acpiphp_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,499 @@
/*
* ACPI PCI Hot Plug IBM Extension
*
* Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
* Copyright (C) 2004 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <vernux@us.ibm.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <acpi/acpi_bus.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <asm/uaccess.h>
#include <linux/moduleparam.h>
#include "acpiphp.h"
#include "pci_hotplug.h"
#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
static int debug;
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
#define MY_NAME "acpiphp_ibm"
#undef dbg
#define dbg(format, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: " format, \
MY_NAME , ## arg); \
} while (0)
#define FOUND_APCI 0x61504349
/* these are the names for the IBM ACPI pseudo-device */
#define IBM_HARDWARE_ID1 "IBM37D0"
#define IBM_HARDWARE_ID2 "IBM37D4"
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
/* union apci_descriptor - allows access to the
* various device descriptors that are embedded in the
* aPCI table
*/
union apci_descriptor {
struct {
char sig[4];
u8 len;
} header;
struct {
u8 type;
u8 len;
u16 slot_id;
u8 bus_id;
u8 dev_num;
u8 slot_num;
u8 slot_attr[2];
u8 attn;
u8 status[2];
u8 sun;
u8 res[3];
} slot;
struct {
u8 type;
u8 len;
} generic;
};
/* struct notification - keeps info about the device
* that cause the ACPI notification event
*/
struct notification {
struct acpi_device *device;
u8 event;
};
static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
static int ibm_get_table_from_acpi(char **bufp);
static ssize_t ibm_read_apci_table(struct kobject *kobj,
char *buffer, loff_t pos, size_t size);
static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
u32 lvl, void *context, void **rv);
static int __init ibm_acpiphp_init(void);
static void __exit ibm_acpiphp_exit(void);
static acpi_handle ibm_acpi_handle;
static struct notification ibm_note;
static struct bin_attribute ibm_apci_table_attr = {
.attr = {
.name = "apci_table",
.owner = THIS_MODULE,
.mode = S_IRUGO,
},
.read = ibm_read_apci_table,
.write = NULL,
};
static struct acpiphp_attention_info ibm_attention_info =
{
.set_attn = ibm_set_attention_status,
.get_attn = ibm_get_attention_status,
.owner = THIS_MODULE,
};
/**
* ibm_slot_from_id - workaround for bad ibm hardware
* @id: the slot number that linux refers to the slot by
*
* Description: this method returns the aCPI slot descriptor
* corresponding to the Linux slot number. This descriptor
* has info about the aPCI slot id and attention status.
* This descriptor must be freed using kfree when done.
**/
static union apci_descriptor *ibm_slot_from_id(int id)
{
int ind = 0, size;
union apci_descriptor *ret = NULL, *des;
char *table;
size = ibm_get_table_from_acpi(&table);
des = (union apci_descriptor *)table;
if (memcmp(des->header.sig, "aPCI", 4) != 0)
goto ibm_slot_done;
des = (union apci_descriptor *)&table[ind += des->header.len];
while (ind < size && (des->generic.type != 0x82 ||
des->slot.slot_num != id)) {
des = (union apci_descriptor *)&table[ind += des->generic.len];
}
if (ind < size && des->slot.slot_num == id)
ret = des;
ibm_slot_done:
if (ret) {
ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
memcpy(ret, des, sizeof(union apci_descriptor));
}
kfree(table);
return ret;
}
/**
* ibm_set_attention_status - callback method to set the attention LED
* @slot: the hotplug_slot to work with
* @status: what to set the LED to (0 or 1)
*
* Description: this method is registered with the acpiphp module as a
* callback to do the device specific task of setting the LED status
**/
static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
{
union acpi_object args[2];
struct acpi_object_list params = { .pointer = args, .count = 2 };
acpi_status stat;
unsigned long rc;
union apci_descriptor *ibm_slot;
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
dbg("%s: set slot %d (%d) attention status to %d\n", __FUNCTION__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
(status ? 1 : 0));
args[0].type = ACPI_TYPE_INTEGER;
args[0].integer.value = ibm_slot->slot.slot_id;
args[1].type = ACPI_TYPE_INTEGER;
args[1].integer.value = (status) ? 1 : 0;
kfree(ibm_slot);
stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
if (ACPI_FAILURE(stat)) {
err("APLS evaluation failed: 0x%08x\n", stat);
return -ENODEV;
} else if (!rc) {
err("APLS method failed: 0x%08lx\n", rc);
return -ERANGE;
}
return 0;
}
/**
* ibm_get_attention_status - callback method to get attention LED status
* @slot: the hotplug_slot to work with
* @status: returns what the LED is set to (0 or 1)
*
* Description: this method is registered with the acpiphp module as a
* callback to do the device specific task of getting the LED status
*
* Because there is no direct method of getting the LED status directly
* from an ACPI call, we read the aPCI table and parse out our
* slot descriptor to read the status from that.
**/
static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
{
union apci_descriptor *ibm_slot;
ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
*status = 1;
else
*status = 0;
dbg("%s: get slot %d (%d) attention status is %d\n", __FUNCTION__,
ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
*status);
kfree(ibm_slot);
return 0;
}
/**
* ibm_handle_events - listens for ACPI events for the IBM37D0 device
* @handle: an ACPI handle to the device that caused the event
* @event: the event info (device specific)
* @context: passed context (our notification struct)
*
* Description: this method is registered as a callback with the ACPI
* subsystem it is called when this device has an event to notify the OS of
*
* The events actually come from the device as two events that get
* synthesized into one event with data by this function. The event
* ID comes first and then the slot number that caused it. We report
* this as one event to the OS.
*
* From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
* only re-enable the interrupt that causes this event AFTER this method
* has returned, thereby enforcing serial access for the notification struct.
**/
static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
{
u8 detail = event & 0x0f;
u8 subevent = event & 0xf0;
struct notification *note = context;
dbg("%s: Received notification %02x\n", __FUNCTION__, event);
if (subevent == 0x80) {
dbg("%s: generationg bus event\n", __FUNCTION__);
acpi_bus_generate_event(note->device, note->event, detail);
} else
note->event = event;
}
/**
* ibm_get_table_from_acpi - reads the APLS buffer from ACPI
* @bufp: address to pointer to allocate for the table
*
* Description: this method reads the APLS buffer in from ACPI and
* stores the "stripped" table into a single buffer
* it allocates and passes the address back in bufp
*
* If NULL is passed in as buffer, this method only calculates
* the size of the table and returns that without filling
* in the buffer
*
* returns < 0 on error or the size of the table on success
**/
static int ibm_get_table_from_acpi(char **bufp)
{
union acpi_object *package;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
char *lbuf = NULL;
int i, size = -EIO;
status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
if (ACPI_FAILURE(status)) {
err("%s: APCI evaluation failed\n", __FUNCTION__);
return -ENODEV;
}
package = (union acpi_object *) buffer.pointer;
if(!(package) ||
(package->type != ACPI_TYPE_PACKAGE) ||
!(package->package.elements)) {
err("%s: Invalid APCI object\n", __FUNCTION__);
goto read_table_done;
}
for(size = 0, i = 0; i < package->package.count; i++) {
if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
err("%s: Invalid APCI element %d\n", __FUNCTION__, i);
goto read_table_done;
}
size += package->package.elements[i].buffer.length;
}
if (bufp == NULL)
goto read_table_done;
lbuf = kmalloc(size, GFP_KERNEL);
dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
__FUNCTION__, package->package.count, size, lbuf);
if (lbuf) {
*bufp = lbuf;
memset(lbuf, 0, size);
} else {
size = -ENOMEM;
goto read_table_done;
}
size = 0;
for (i=0; i<package->package.count; i++) {
memcpy(&lbuf[size],
package->package.elements[i].buffer.pointer,
package->package.elements[i].buffer.length);
size += package->package.elements[i].buffer.length;
}
read_table_done:
kfree(buffer.pointer);
return size;
}
/**
* ibm_read_apci_table - callback for the sysfs apci_table file
* @kobj: the kobject this binary attribute is a part of
* @buffer: the kernel space buffer to fill
* @pos: the offset into the file
* @size: the number of bytes requested
*
* Description: gets registered with sysfs as the reader callback
* to be executed when /sys/bus/pci/slots/apci_table gets read
*
* Since we don't get notified on open and close for this file,
* things get really tricky here...
* our solution is to only allow reading the table in all at once
**/
static ssize_t ibm_read_apci_table(struct kobject *kobj,
char *buffer, loff_t pos, size_t size)
{
int bytes_read = -EINVAL;
char *table = NULL;
dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size);
if (pos == 0) {
bytes_read = ibm_get_table_from_acpi(&table);
if (bytes_read > 0 && bytes_read <= size)
memcpy(buffer, table, bytes_read);
kfree(table);
}
return bytes_read;
}
/**
* ibm_find_acpi_device - callback to find our ACPI device
* @handle: the ACPI handle of the device we are inspecting
* @lvl: depth into the namespace tree
* @context: a pointer to our handle to fill when we find the device
* @rv: a return value to fill if desired
*
* Description: used as a callback when calling acpi_walk_namespace
* to find our device. When this method returns non-zero
* acpi_walk_namespace quits its search and returns our value
**/
static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
u32 lvl, void *context, void **rv)
{
acpi_handle *phandle = (acpi_handle *)context;
acpi_status status;
struct acpi_device_info info;
struct acpi_buffer info_buffer = {
.length = sizeof(struct acpi_device_info),
.pointer = &info,
};
status = acpi_get_object_info(handle, &info_buffer);
if (ACPI_FAILURE(status)) {
err("%s: Failed to get device information", __FUNCTION__);
return 0;
}
info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0';
if(info.current_status && (info.valid & ACPI_VALID_HID) &&
(!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) ||
!strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) {
dbg("found hardware: %s, handle: %p\n", info.hardware_id.value,
handle);
*phandle = handle;
/* returning non-zero causes the search to stop
* and returns this value to the caller of
* acpi_walk_namespace, but it also causes some warnings
* in the acpi debug code to print...
*/
return FOUND_APCI;
}
return 0;
}
static int __init ibm_acpiphp_init(void)
{
int retval = 0;
acpi_status status;
struct acpi_device *device;
struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
dbg("%s\n", __FUNCTION__);
if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ibm_find_acpi_device,
&ibm_acpi_handle, NULL) != FOUND_APCI) {
err("%s: acpi_walk_namespace failed\n", __FUNCTION__);
retval = -ENODEV;
goto init_return;
}
dbg("%s: found IBM aPCI device\n", __FUNCTION__);
if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
err("%s: acpi_bus_get_device failed\n", __FUNCTION__);
retval = -ENODEV;
goto init_return;
}
if (acpiphp_register_attention(&ibm_attention_info)) {
retval = -ENODEV;
goto init_return;
}
ibm_note.device = device;
status = acpi_install_notify_handler(
ibm_acpi_handle,
ACPI_DEVICE_NOTIFY,
ibm_handle_events,
&ibm_note);
if (ACPI_FAILURE(status)) {
err("%s: Failed to register notification handler\n",
__FUNCTION__);
retval = -EBUSY;
goto init_cleanup;
}
ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
return retval;
init_cleanup:
acpiphp_unregister_attention(&ibm_attention_info);
init_return:
return retval;
}
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj;
dbg("%s\n", __FUNCTION__);
if (acpiphp_unregister_attention(&ibm_attention_info))
err("%s: attention info deregistration failed", __FUNCTION__);
status = acpi_remove_notify_handler(
ibm_acpi_handle,
ACPI_DEVICE_NOTIFY,
ibm_handle_events);
if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n",
__FUNCTION__);
// remove the /sys entries
if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
err("%s: removal of sysfs file apci_table failed\n",
__FUNCTION__);
}
module_init(ibm_acpiphp_init);
module_exit(ibm_acpiphp_exit);

View File

@@ -0,0 +1,449 @@
/*
* ACPI PCI HotPlug PCI configuration space management
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2002 IBM Corp.
* Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
* Copyright (C) 2002 NEC Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <t-kochi@bq.jp.nec.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/acpi.h>
#include "../pci.h"
#include "pci_hotplug.h"
#include "acpiphp.h"
#define MY_NAME "acpiphp_pci"
/* allocate mem/pmem/io resource to a new function */
static int init_config_space (struct acpiphp_func *func)
{
u32 bar, len;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
int count;
struct acpiphp_bridge *bridge;
struct pci_resource *res;
struct pci_bus *pbus;
int bus, device, function;
unsigned int devfn;
u16 tmp;
bridge = func->slot->bridge;
pbus = bridge->pci_bus;
bus = bridge->bus;
device = func->slot->device;
function = func->function;
devfn = PCI_DEVFN(device, function);
for (count = 0; address[count]; count++) { /* for 6 BARs */
pci_bus_write_config_dword(pbus, devfn,
address[count], 0xFFFFFFFF);
pci_bus_read_config_dword(pbus, devfn, address[count], &bar);
if (!bar) /* This BAR is not implemented */
continue;
dbg("Device %02x.%02x BAR %d wants %x\n", device, function, count, bar);
if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
len = len & ~(len - 1);
dbg("len in IO %x, BAR %d\n", len, count);
spin_lock(&bridge->res_lock);
res = acpiphp_get_io_resource(&bridge->io_head, len);
spin_unlock(&bridge->res_lock);
if (!res) {
err("cannot allocate requested io for %02x:%02x.%d len %x\n",
bus, device, function, len);
return -1;
}
pci_bus_write_config_dword(pbus, devfn,
address[count],
(u32)res->base);
res->next = func->io_head;
func->io_head = res;
} else {
/* This is Memory */
if (bar & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
len = bar & 0xFFFFFFF0;
len = ~len + 1;
dbg("len in PFMEM %x, BAR %d\n", len, count);
spin_lock(&bridge->res_lock);
res = acpiphp_get_resource(&bridge->p_mem_head, len);
spin_unlock(&bridge->res_lock);
if (!res) {
err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
bus, device, function, len);
return -1;
}
pci_bus_write_config_dword(pbus, devfn,
address[count],
(u32)res->base);
if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
dbg("inside the pfmem 64 case, count %d\n", count);
count += 1;
pci_bus_write_config_dword(pbus, devfn,
address[count],
(u32)(res->base >> 32));
}
res->next = func->p_mem_head;
func->p_mem_head = res;
} else {
/* regular memory */
len = bar & 0xFFFFFFF0;
len = ~len + 1;
dbg("len in MEM %x, BAR %d\n", len, count);
spin_lock(&bridge->res_lock);
res = acpiphp_get_resource(&bridge->mem_head, len);
spin_unlock(&bridge->res_lock);
if (!res) {
err("cannot allocate requested pfmem for %02x:%02x.%d len %x\n",
bus, device, function, len);
return -1;
}
pci_bus_write_config_dword(pbus, devfn,
address[count],
(u32)res->base);
if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
dbg("inside mem 64 case, reg. mem, count %d\n", count);
count += 1;
pci_bus_write_config_dword(pbus, devfn,
address[count],
(u32)(res->base >> 32));
}
res->next = func->mem_head;
func->mem_head = res;
}
}
}
/* disable expansion rom */
pci_bus_write_config_dword(pbus, devfn, PCI_ROM_ADDRESS, 0x00000000);
/* set PCI parameters from _HPP */
pci_bus_write_config_byte(pbus, devfn, PCI_CACHE_LINE_SIZE,
bridge->hpp.cache_line_size);
pci_bus_write_config_byte(pbus, devfn, PCI_LATENCY_TIMER,
bridge->hpp.latency_timer);
pci_bus_read_config_word(pbus, devfn, PCI_COMMAND, &tmp);
if (bridge->hpp.enable_SERR)
tmp |= PCI_COMMAND_SERR;
if (bridge->hpp.enable_PERR)
tmp |= PCI_COMMAND_PARITY;
pci_bus_write_config_word(pbus, devfn, PCI_COMMAND, tmp);
return 0;
}
/* detect_used_resource - subtract resource under dev from bridge */
static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev)
{
int count;
dbg("Device %s\n", pci_name(dev));
for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) {
struct pci_resource *res;
struct pci_resource **head;
unsigned long base = dev->resource[count].start;
unsigned long len = dev->resource[count].end - base + 1;
unsigned long flags = dev->resource[count].flags;
if (!flags)
continue;
dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base,
base + len - 1, flags);
if (flags & IORESOURCE_IO) {
head = &bridge->io_head;
} else if (flags & IORESOURCE_PREFETCH) {
head = &bridge->p_mem_head;
} else {
head = &bridge->mem_head;
}
spin_lock(&bridge->res_lock);
res = acpiphp_get_resource_with_base(head, base, len);
spin_unlock(&bridge->res_lock);
if (res)
kfree(res);
}
return 0;
}
/**
* acpiphp_detect_pci_resource - detect resources under bridge
* @bridge: detect all resources already used under this bridge
*
* collect all resources already allocated for all devices under a bridge.
*/
int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge)
{
struct list_head *l;
struct pci_dev *dev;
list_for_each (l, &bridge->pci_bus->devices) {
dev = pci_dev_b(l);
detect_used_resource(bridge, dev);
}
return 0;
}
/**
* acpiphp_init_slot_resource - gather resource usage information of a slot
* @slot: ACPI slot object to be checked, should have valid pci_dev member
*
* TBD: PCI-to-PCI bridge case
* use pci_dev->resource[]
*/
int acpiphp_init_func_resource (struct acpiphp_func *func)
{
u64 base;
u32 bar, len;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
int count;
struct pci_resource *res;
struct pci_dev *dev;
dev = func->pci_dev;
dbg("Hot-pluggable device %s\n", pci_name(dev));
for (count = 0; address[count]; count++) { /* for 6 BARs */
pci_read_config_dword(dev, address[count], &bar);
if (!bar) /* This BAR is not implemented */
continue;
pci_write_config_dword(dev, address[count], 0xFFFFFFFF);
pci_read_config_dword(dev, address[count], &len);
if (len & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
base = bar & 0xFFFFFFFC;
len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF);
len = len & ~(len - 1);
dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1);
res = acpiphp_make_resource(base, len);
if (!res)
goto no_memory;
res->next = func->io_head;
func->io_head = res;
} else {
/* This is Memory */
base = bar & 0xFFFFFFF0;
if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
len &= 0xFFFFFFF0;
len = ~len + 1;
if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
dbg("prefetch mem 64\n");
count += 1;
}
dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1);
res = acpiphp_make_resource(base, len);
if (!res)
goto no_memory;
res->next = func->p_mem_head;
func->p_mem_head = res;
} else {
/* regular memory */
len &= 0xFFFFFFF0;
len = ~len + 1;
if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
dbg("mem 64\n");
count += 1;
}
dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1);
res = acpiphp_make_resource(base, len);
if (!res)
goto no_memory;
res->next = func->mem_head;
func->mem_head = res;
}
}
pci_write_config_dword(dev, address[count], bar);
}
#if 1
acpiphp_dump_func_resource(func);
#endif
return 0;
no_memory:
err("out of memory\n");
acpiphp_free_resource(&func->io_head);
acpiphp_free_resource(&func->mem_head);
acpiphp_free_resource(&func->p_mem_head);
return -1;
}
/**
* acpiphp_configure_slot - allocate PCI resources
* @slot: slot to be configured
*
* initializes a PCI functions on a device inserted
* into the slot
*
*/
int acpiphp_configure_slot (struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
struct list_head *l;
u8 hdr;
u32 dvid;
int retval = 0;
int is_multi = 0;
pci_bus_read_config_byte(slot->bridge->pci_bus,
PCI_DEVFN(slot->device, 0),
PCI_HEADER_TYPE, &hdr);
if (hdr & 0x80)
is_multi = 1;
list_for_each (l, &slot->funcs) {
func = list_entry(l, struct acpiphp_func, sibling);
if (is_multi || func->function == 0) {
pci_bus_read_config_dword(slot->bridge->pci_bus,
PCI_DEVFN(slot->device,
func->function),
PCI_VENDOR_ID, &dvid);
if (dvid != 0xffffffff) {
retval = init_config_space(func);
if (retval)
break;
}
}
}
return retval;
}
/**
* acpiphp_configure_function - configure PCI function
* @func: function to be configured
*
* initializes a PCI functions on a device inserted
* into the slot
*
*/
int acpiphp_configure_function (struct acpiphp_func *func)
{
/* all handled by the pci core now */
return 0;
}
/**
* acpiphp_unconfigure_function - unconfigure PCI function
* @func: function to be unconfigured
*
*/
void acpiphp_unconfigure_function (struct acpiphp_func *func)
{
struct acpiphp_bridge *bridge;
/* if pci_dev is NULL, ignore it */
if (!func->pci_dev)
return;
pci_remove_bus_device(func->pci_dev);
/* free all resources */
bridge = func->slot->bridge;
spin_lock(&bridge->res_lock);
acpiphp_move_resource(&func->io_head, &bridge->io_head);
acpiphp_move_resource(&func->mem_head, &bridge->mem_head);
acpiphp_move_resource(&func->p_mem_head, &bridge->p_mem_head);
acpiphp_move_resource(&func->bus_head, &bridge->bus_head);
spin_unlock(&bridge->res_lock);
}

View File

@@ -0,0 +1,700 @@
/*
* ACPI PCI HotPlug Utility functions
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
* Copyright (C) 2002 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
* Copyright (C) 2002 NEC Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <gregkh@us.ibm.com>, <t-kochi@bq.jp.nec.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/pci.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#include <linux/fcntl.h>
#include <linux/list.h>
#include "pci_hotplug.h"
#include "acpiphp.h"
#define MY_NAME "acpiphp_res"
/*
* sort_by_size - sort nodes by their length, smallest first
*/
static int sort_by_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return 1;
if (!((*head)->next))
return 0;
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length > (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length > current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return 0;
}
#if 0
/*
* sort_by_max_size - sort nodes by their length, largest first
*/
static int sort_by_max_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return 1;
if (!((*head)->next))
return 0;
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length < (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length < current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return 0;
}
#endif
/**
* get_io_resource - get resource for I/O ports
*
* this function sorts the resource list by size and then
* returns the first node of "size" length that is not in the
* ISA aliasing window. If it finds a node larger than "size"
* it will split it up.
*
* size must be a power of two.
*
* difference from get_resource is handling of ISA aliasing space.
*
*/
struct pci_resource *acpiphp_get_io_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u64 temp_qword;
if (!(*head))
return NULL;
if (acpiphp_resource_sort_and_combine(head))
return NULL;
if (sort_by_size(head))
return NULL;
for (node = *head; node; node = node->next) {
if (node->length < size)
continue;
if (node->base & (size - 1)) {
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_qword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_qword - node->base)) < size)
continue;
split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
if (!split_node)
return NULL;
node->base = temp_qword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = acpiphp_make_resource(node->base + size, node->length - size);
if (!split_node)
return NULL;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
/* For IO make sure it's not in the ISA aliasing space */
if ((node->base & 0x300L) && !(node->base & 0xfffff000))
continue;
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return node;
}
#if 0
/**
* get_max_resource - get the largest resource
*
* Gets the largest node that is at least "size" big from the
* list pointed to by head. It aligns the node on top and bottom
* to "size" alignment before returning it.
*/
static struct pci_resource *acpiphp_get_max_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *max;
struct pci_resource *temp;
struct pci_resource *split_node;
u64 temp_qword;
if (!(*head))
return NULL;
if (acpiphp_resource_sort_and_combine(head))
return NULL;
if (sort_by_max_size(head))
return NULL;
for (max = *head;max; max = max->next) {
/* If not big enough we could probably just bail,
instead we'll continue to the next. */
if (max->length < size)
continue;
if (max->base & (size - 1)) {
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_qword = (max->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((max->length - (temp_qword - max->base)) < size)
continue;
split_node = acpiphp_make_resource(max->base, temp_qword - max->base);
if (!split_node)
return NULL;
max->base = temp_qword;
max->length -= split_node->length;
/* Put it next in the list */
split_node->next = max->next;
max->next = split_node;
}
if ((max->base + max->length) & (size - 1)) {
/* this one isn't end aligned properly at the top
so we'll make a new entry and split it up */
temp_qword = ((max->base + max->length) & ~(size - 1));
split_node = acpiphp_make_resource(temp_qword,
max->length + max->base - temp_qword);
if (!split_node)
return NULL;
max->length -= split_node->length;
/* Put it in the list */
split_node->next = max->next;
max->next = split_node;
}
/* Make sure it didn't shrink too much when we aligned it */
if (max->length < size)
continue;
/* Now take it out of the list */
temp = (struct pci_resource*) *head;
if (temp == max) {
*head = max->next;
} else {
while (temp && temp->next != max) {
temp = temp->next;
}
temp->next = max->next;
}
max->next = NULL;
return max;
}
/* If we get here, we couldn't find one */
return NULL;
}
#endif
/**
* get_resource - get resource (mem, pfmem)
*
* this function sorts the resource list by size and then
* returns the first node of "size" length. If it finds a node
* larger than "size" it will split it up.
*
* size must be a power of two.
*
*/
struct pci_resource *acpiphp_get_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u64 temp_qword;
if (!(*head))
return NULL;
if (acpiphp_resource_sort_and_combine(head))
return NULL;
if (sort_by_size(head))
return NULL;
for (node = *head; node; node = node->next) {
dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
__FUNCTION__, size, node, (u32)node->base, node->length);
if (node->length < size)
continue;
if (node->base & (size - 1)) {
dbg("%s: not aligned\n", __FUNCTION__);
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_qword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_qword - node->base)) < size)
continue;
split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
if (!split_node)
return NULL;
node->base = temp_qword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
dbg("%s: too big\n", __FUNCTION__);
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = acpiphp_make_resource(node->base + size, node->length - size);
if (!split_node)
return NULL;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
dbg("%s: got one!!!\n", __FUNCTION__);
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return node;
}
/**
* get_resource_with_base - get resource with specific base address
*
* this function
* returns the first node of "size" length located at specified base address.
* If it finds a node larger than "size" it will split it up.
*
* size must be a power of two.
*
*/
struct pci_resource *acpiphp_get_resource_with_base (struct pci_resource **head, u64 base, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u64 temp_qword;
if (!(*head))
return NULL;
if (acpiphp_resource_sort_and_combine(head))
return NULL;
for (node = *head; node; node = node->next) {
dbg(": 1st req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
(u32)base, size, node, (u32)node->base, node->length);
if (node->base > base)
continue;
if ((node->base + node->length) < (base + size))
continue;
if (node->base < base) {
dbg(": split 1\n");
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_qword = base;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_qword - node->base)) < size)
continue;
split_node = acpiphp_make_resource(node->base, temp_qword - node->base);
if (!split_node)
return NULL;
node->base = temp_qword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
}
dbg(": 2nd req_base=%x req_size =%x node=%p, base=%x, length=%x\n",
(u32)base, size, node, (u32)node->base, node->length);
/* Don't need to check if too small since we already did */
if (node->length > size) {
dbg(": split 2\n");
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = acpiphp_make_resource(node->base + size, node->length - size);
if (!split_node)
return NULL;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
dbg(": got one!!!\n");
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return node;
}
/**
* acpiphp_resource_sort_and_combine
*
* Sorts all of the nodes in the list in ascending order by
* their base addresses. Also does garbage collection by
* combining adjacent nodes.
*
* returns 0 if success
*/
int acpiphp_resource_sort_and_combine (struct pci_resource **head)
{
struct pci_resource *node1;
struct pci_resource *node2;
int out_of_order = 1;
if (!(*head))
return 1;
dbg("*head->next = %p\n",(*head)->next);
if (!(*head)->next)
return 0; /* only one item on the list, already sorted! */
dbg("*head->base = 0x%x\n",(u32)(*head)->base);
dbg("*head->next->base = 0x%x\n", (u32)(*head)->next->base);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->base > (*head)->next->base)) {
node1 = *head;
(*head) = (*head)->next;
node1->next = (*head)->next;
(*head)->next = node1;
out_of_order++;
}
node1 = (*head);
while (node1->next && node1->next->next) {
if (node1->next->base > node1->next->next->base) {
out_of_order++;
node2 = node1->next;
node1->next = node1->next->next;
node1 = node1->next;
node2->next = node1->next;
node1->next = node2;
} else
node1 = node1->next;
}
} /* End of out_of_order loop */
node1 = *head;
while (node1 && node1->next) {
if ((node1->base + node1->length) == node1->next->base) {
/* Combine */
dbg("8..\n");
node1->length += node1->next->length;
node2 = node1->next;
node1->next = node1->next->next;
kfree(node2);
} else
node1 = node1->next;
}
return 0;
}
/**
* acpiphp_make_resource - make resource structure
* @base: base address of a resource
* @length: length of a resource
*/
struct pci_resource *acpiphp_make_resource (u64 base, u32 length)
{
struct pci_resource *res;
res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (res) {
memset(res, 0, sizeof(struct pci_resource));
res->base = base;
res->length = length;
}
return res;
}
/**
* acpiphp_move_resource - move linked resources from one to another
* @from: head of linked resource list
* @to: head of linked resource list
*/
void acpiphp_move_resource (struct pci_resource **from, struct pci_resource **to)
{
struct pci_resource *tmp;
while (*from) {
tmp = (*from)->next;
(*from)->next = *to;
*to = *from;
*from = tmp;
}
/* *from = NULL is guaranteed */
}
/**
* acpiphp_free_resource - free all linked resources
* @res: head of linked resource list
*/
void acpiphp_free_resource (struct pci_resource **res)
{
struct pci_resource *tmp;
while (*res) {
tmp = (*res)->next;
kfree(*res);
*res = tmp;
}
/* *res = NULL is guaranteed */
}
/* debug support functions; will go away sometime :) */
static void dump_resource(struct pci_resource *head)
{
struct pci_resource *p;
int cnt;
p = head;
cnt = 0;
while (p) {
dbg("[%02d] %08x - %08x\n",
cnt++, (u32)p->base, (u32)p->base + p->length - 1);
p = p->next;
}
}
void acpiphp_dump_resource(struct acpiphp_bridge *bridge)
{
dbg("I/O resource:\n");
dump_resource(bridge->io_head);
dbg("MEM resource:\n");
dump_resource(bridge->mem_head);
dbg("PMEM resource:\n");
dump_resource(bridge->p_mem_head);
dbg("BUS resource:\n");
dump_resource(bridge->bus_head);
}
void acpiphp_dump_func_resource(struct acpiphp_func *func)
{
dbg("I/O resource:\n");
dump_resource(func->io_head);
dbg("MEM resource:\n");
dump_resource(func->mem_head);
dbg("PMEM resource:\n");
dump_resource(func->p_mem_head);
dbg("BUS resource:\n");
dump_resource(func->bus_head);
}

View File

@@ -0,0 +1,96 @@
/*
* CompactPCI Hot Plug Core Functions
*
* Copyright (C) 2002 SOMA Networks, Inc.
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <scottm@somanetworks.com>
*/
#ifndef _CPCI_HOTPLUG_H
#define _CPCI_HOTPLUG_H
#include <linux/types.h>
#include <linux/pci.h>
/* PICMG 2.12 R2.0 HS CSR bits: */
#define HS_CSR_INS 0x0080
#define HS_CSR_EXT 0x0040
#define HS_CSR_PI 0x0030
#define HS_CSR_LOO 0x0008
#define HS_CSR_PIE 0x0004
#define HS_CSR_EIM 0x0002
#define HS_CSR_DHA 0x0001
struct slot {
u8 number;
unsigned int devfn;
struct pci_bus *bus;
struct pci_dev *dev;
unsigned int extracting;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
struct cpci_hp_controller_ops {
int (*query_enum) (void);
int (*enable_irq) (void);
int (*disable_irq) (void);
int (*check_irq) (void *dev_id);
int (*hardware_test) (struct slot* slot, u32 value);
u8 (*get_power) (struct slot* slot);
int (*set_power) (struct slot* slot, int value);
};
struct cpci_hp_controller {
unsigned int irq;
unsigned long irq_flags;
char *devname;
void *dev_id;
char *name;
struct cpci_hp_controller_ops *ops;
};
extern int cpci_hp_register_controller(struct cpci_hp_controller *controller);
extern int cpci_hp_unregister_controller(struct cpci_hp_controller *controller);
extern int cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last);
extern int cpci_hp_unregister_bus(struct pci_bus *bus);
extern int cpci_hp_start(void);
extern int cpci_hp_stop(void);
/*
* Internal function prototypes, these functions should not be used by
* board/chassis drivers.
*/
extern u8 cpci_get_attention_status(struct slot *slot);
extern u8 cpci_get_latch_status(struct slot *slot);
extern u8 cpci_get_adapter_status(struct slot *slot);
extern u16 cpci_get_hs_csr(struct slot * slot);
extern int cpci_set_attention_status(struct slot *slot, int status);
extern int cpci_check_and_clear_ins(struct slot * slot);
extern int cpci_check_ext(struct slot * slot);
extern int cpci_clear_ext(struct slot * slot);
extern int cpci_led_on(struct slot * slot);
extern int cpci_led_off(struct slot * slot);
extern int cpci_configure_slot(struct slot *slot);
extern int cpci_unconfigure_slot(struct slot *slot);
#endif /* _CPCI_HOTPLUG_H */

View File

@@ -0,0 +1,792 @@
/*
* CompactPCI Hot Plug Driver
*
* Copyright (C) 2002 SOMA Networks, Inc.
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <scottm@somanetworks.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
#include "pci_hotplug.h"
#include "cpci_hotplug.h"
#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "CompactPCI Hot Plug Core"
#define MY_NAME "cpci_hotplug"
#define dbg(format, arg...) \
do { \
if(cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
static spinlock_t list_lock;
static LIST_HEAD(slot_list);
static int slots;
int cpci_debug;
static struct cpci_hp_controller *controller;
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
static struct semaphore thread_exit; /* guard ensure thread has exited before calling it quits */
static int thread_finished = 1;
static int enable_slot(struct hotplug_slot *slot);
static int disable_slot(struct hotplug_slot *slot);
static int set_attention_status(struct hotplug_slot *slot, u8 value);
static int get_power_status(struct hotplug_slot *slot, u8 * value);
static int get_attention_status(struct hotplug_slot *slot, u8 * value);
static struct hotplug_slot_ops cpci_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
};
static int
update_latch_status(struct hotplug_slot *hotplug_slot, u8 value)
{
struct hotplug_slot_info info;
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
info.latch_status = value;
return pci_hp_change_slot_info(hotplug_slot, &info);
}
static int
update_adapter_status(struct hotplug_slot *hotplug_slot, u8 value)
{
struct hotplug_slot_info info;
memcpy(&info, hotplug_slot->info, sizeof(struct hotplug_slot_info));
info.adapter_status = value;
return pci_hp_change_slot_info(hotplug_slot, &info);
}
static int
enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
if(controller->ops->set_power) {
retval = controller->ops->set_power(slot, 1);
}
return retval;
}
static int
disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s", __FUNCTION__, hotplug_slot->name);
/* Unconfigure device */
dbg("%s - unconfiguring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
if((retval = cpci_unconfigure_slot(slot))) {
err("%s - could not unconfigure slot %s",
__FUNCTION__, slot->hotplug_slot->name);
return retval;
}
dbg("%s - finished unconfiguring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
/* Clear EXT (by setting it) */
if(cpci_clear_ext(slot)) {
err("%s - could not clear EXT for slot %s",
__FUNCTION__, slot->hotplug_slot->name);
retval = -ENODEV;
}
cpci_led_on(slot);
if(controller->ops->set_power) {
retval = controller->ops->set_power(slot, 0);
}
if(update_adapter_status(slot->hotplug_slot, 0)) {
warn("failure to update adapter file");
}
slot->extracting = 0;
return retval;
}
static u8
cpci_get_power_status(struct slot *slot)
{
u8 power = 1;
if(controller->ops->get_power) {
power = controller->ops->get_power(slot);
}
return power;
}
static int
get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
{
struct slot *slot = hotplug_slot->private;
*value = cpci_get_power_status(slot);
return 0;
}
static int
get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
{
struct slot *slot = hotplug_slot->private;
*value = cpci_get_attention_status(slot);
return 0;
}
static int
set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
return cpci_set_attention_status(hotplug_slot->private, status);
}
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
#define SLOT_NAME_SIZE 6
static void
make_slot_name(struct slot *slot)
{
snprintf(slot->hotplug_slot->name,
SLOT_NAME_SIZE, "%02x:%02x", slot->bus->number, slot->number);
}
int
cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
{
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
char *name;
int status = -ENOMEM;
int i;
if(!(controller && bus)) {
return -ENODEV;
}
/*
* Create a structure for each slot, and register that slot
* with the pci_hotplug subsystem.
*/
for (i = first; i <= last; ++i) {
slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
if (!slot)
goto error;
memset(slot, 0, sizeof (struct slot));
hotplug_slot =
kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
if (!hotplug_slot)
goto error_slot;
memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
slot->hotplug_slot = hotplug_slot;
info = kmalloc(sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!info)
goto error_hpslot;
memset(info, 0, sizeof (struct hotplug_slot_info));
hotplug_slot->info = info;
name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
if (!name)
goto error_info;
hotplug_slot->name = name;
slot->bus = bus;
slot->number = i;
slot->devfn = PCI_DEVFN(i, 0);
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
make_slot_name(slot);
hotplug_slot->ops = &cpci_hotplug_slot_ops;
/*
* Initialize the slot info structure with some known
* good values.
*/
dbg("initializing slot %s", slot->hotplug_slot->name);
info->power_status = cpci_get_power_status(slot);
info->attention_status = cpci_get_attention_status(slot);
dbg("registering slot %s", slot->hotplug_slot->name);
status = pci_hp_register(slot->hotplug_slot);
if (status) {
err("pci_hp_register failed with error %d", status);
goto error_name;
}
/* Add slot to our internal list */
spin_lock(&list_lock);
list_add(&slot->slot_list, &slot_list);
slots++;
spin_unlock(&list_lock);
}
return 0;
error_name:
kfree(name);
error_info:
kfree(info);
error_hpslot:
kfree(hotplug_slot);
error_slot:
kfree(slot);
error:
return status;
}
int
cpci_hp_unregister_bus(struct pci_bus *bus)
{
struct slot *slot;
struct list_head *tmp;
struct list_head *next;
int status;
spin_lock(&list_lock);
if(!slots) {
spin_unlock(&list_lock);
return -1;
}
list_for_each_safe(tmp, next, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
if(slot->bus == bus) {
dbg("deregistering slot %s", slot->hotplug_slot->name);
status = pci_hp_deregister(slot->hotplug_slot);
if(status) {
err("pci_hp_deregister failed with error %d",
status);
return status;
}
list_del(&slot->slot_list);
slots--;
}
}
spin_unlock(&list_lock);
return 0;
}
/* This is the interrupt mode interrupt handler */
static irqreturn_t
cpci_hp_intr(int irq, void *data, struct pt_regs *regs)
{
dbg("entered cpci_hp_intr");
/* Check to see if it was our interrupt */
if((controller->irq_flags & SA_SHIRQ) &&
!controller->ops->check_irq(controller->dev_id)) {
dbg("exited cpci_hp_intr, not our interrupt");
return IRQ_NONE;
}
/* Disable ENUM interrupt */
controller->ops->disable_irq();
/* Trigger processing by the event thread */
dbg("Signal event_semaphore");
up(&event_semaphore);
dbg("exited cpci_hp_intr");
return IRQ_HANDLED;
}
/*
* According to PICMG 2.12 R2.0, section 6.3.2, upon
* initialization, the system driver shall clear the
* INS bits of the cold-inserted devices.
*/
static int
init_slots(void)
{
struct slot *slot;
struct list_head *tmp;
struct pci_dev* dev;
dbg("%s - enter", __FUNCTION__);
spin_lock(&list_lock);
if(!slots) {
spin_unlock(&list_lock);
return -1;
}
list_for_each(tmp, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
dbg("%s - looking at slot %s",
__FUNCTION__, slot->hotplug_slot->name);
if(cpci_check_and_clear_ins(slot)) {
dbg("%s - cleared INS for slot %s",
__FUNCTION__, slot->hotplug_slot->name);
dev = pci_find_slot(slot->bus->number, PCI_DEVFN(slot->number, 0));
if(dev) {
if(update_adapter_status(slot->hotplug_slot, 1)) {
warn("failure to update adapter file");
}
if(update_latch_status(slot->hotplug_slot, 1)) {
warn("failure to update latch file");
}
slot->dev = dev;
} else {
err("%s - no driver attached to device in slot %s",
__FUNCTION__, slot->hotplug_slot->name);
}
}
}
spin_unlock(&list_lock);
dbg("%s - exit", __FUNCTION__);
return 0;
}
static int
check_slots(void)
{
struct slot *slot;
struct list_head *tmp;
int extracted;
int inserted;
spin_lock(&list_lock);
if(!slots) {
spin_unlock(&list_lock);
err("no slots registered, shutting down");
return -1;
}
extracted = inserted = 0;
list_for_each(tmp, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
dbg("%s - looking at slot %s",
__FUNCTION__, slot->hotplug_slot->name);
if(cpci_check_and_clear_ins(slot)) {
u16 hs_csr;
/* Some broken hardware (e.g. PLX 9054AB) asserts ENUM# twice... */
if(slot->dev) {
warn("slot %s already inserted", slot->hotplug_slot->name);
inserted++;
continue;
}
/* Process insertion */
dbg("%s - slot %s inserted",
__FUNCTION__, slot->hotplug_slot->name);
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (1) = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
/* Configure device */
dbg("%s - configuring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
if(cpci_configure_slot(slot)) {
err("%s - could not configure slot %s",
__FUNCTION__, slot->hotplug_slot->name);
continue;
}
dbg("%s - finished configuring slot %s",
__FUNCTION__, slot->hotplug_slot->name);
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (2) = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
if(update_latch_status(slot->hotplug_slot, 1)) {
warn("failure to update latch file");
}
if(update_adapter_status(slot->hotplug_slot, 1)) {
warn("failure to update adapter file");
}
cpci_led_off(slot);
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR (3) = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
inserted++;
} else if(cpci_check_ext(slot)) {
u16 hs_csr;
/* Process extraction request */
dbg("%s - slot %s extracted",
__FUNCTION__, slot->hotplug_slot->name);
/* GSM, debug */
hs_csr = cpci_get_hs_csr(slot);
dbg("%s - slot %s HS_CSR = %04x",
__FUNCTION__, slot->hotplug_slot->name, hs_csr);
if(!slot->extracting) {
if(update_latch_status(slot->hotplug_slot, 0)) {
warn("failure to update latch file");
}
slot->extracting = 1;
}
extracted++;
}
}
spin_unlock(&list_lock);
if(inserted || extracted) {
return extracted;
}
else {
err("cannot find ENUM# source, shutting down");
return -1;
}
}
/* This is the interrupt mode worker thread body */
static int
event_thread(void *data)
{
int rc;
struct slot *slot;
struct list_head *tmp;
lock_kernel();
daemonize("cpci_hp_eventd");
unlock_kernel();
dbg("%s - event thread started", __FUNCTION__);
while(1) {
dbg("event thread sleeping");
down_interruptible(&event_semaphore);
dbg("event thread woken, thread_finished = %d",
thread_finished);
if(thread_finished || signal_pending(current))
break;
while(controller->ops->query_enum()) {
rc = check_slots();
if (rc > 0)
/* Give userspace a chance to handle extraction */
msleep(500);
else if (rc < 0) {
dbg("%s - error checking slots", __FUNCTION__);
thread_finished = 1;
break;
}
}
/* Check for someone yanking out a board */
list_for_each(tmp, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
if(slot->extracting) {
/*
* Hmmm, we're likely hosed at this point, should we
* bother trying to tell the driver or not?
*/
err("card in slot %s was improperly removed",
slot->hotplug_slot->name);
if(update_adapter_status(slot->hotplug_slot, 0)) {
warn("failure to update adapter file");
}
slot->extracting = 0;
}
}
/* Re-enable ENUM# interrupt */
dbg("%s - re-enabling irq", __FUNCTION__);
controller->ops->enable_irq();
}
dbg("%s - event thread signals exit", __FUNCTION__);
up(&thread_exit);
return 0;
}
/* This is the polling mode worker thread body */
static int
poll_thread(void *data)
{
int rc;
struct slot *slot;
struct list_head *tmp;
lock_kernel();
daemonize("cpci_hp_polld");
unlock_kernel();
while(1) {
if(thread_finished || signal_pending(current))
break;
while(controller->ops->query_enum()) {
rc = check_slots();
if(rc > 0)
/* Give userspace a chance to handle extraction */
msleep(500);
else if (rc < 0) {
dbg("%s - error checking slots", __FUNCTION__);
thread_finished = 1;
break;
}
}
/* Check for someone yanking out a board */
list_for_each(tmp, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
if(slot->extracting) {
/*
* Hmmm, we're likely hosed at this point, should we
* bother trying to tell the driver or not?
*/
err("card in slot %s was improperly removed",
slot->hotplug_slot->name);
if(update_adapter_status(slot->hotplug_slot, 0)) {
warn("failure to update adapter file");
}
slot->extracting = 0;
}
}
msleep(100);
}
dbg("poll thread signals exit");
up(&thread_exit);
return 0;
}
static int
cpci_start_thread(void)
{
int pid;
/* initialize our semaphores */
init_MUTEX_LOCKED(&event_semaphore);
init_MUTEX_LOCKED(&thread_exit);
thread_finished = 0;
if(controller->irq) {
pid = kernel_thread(event_thread, NULL, 0);
} else {
pid = kernel_thread(poll_thread, NULL, 0);
}
if(pid < 0) {
err("Can't start up our thread");
return -1;
}
dbg("Our thread pid = %d", pid);
return 0;
}
static void
cpci_stop_thread(void)
{
thread_finished = 1;
dbg("thread finish command given");
if(controller->irq) {
up(&event_semaphore);
}
dbg("wait for thread to exit");
down(&thread_exit);
}
int
cpci_hp_register_controller(struct cpci_hp_controller *new_controller)
{
int status = 0;
if(!controller) {
controller = new_controller;
if(controller->irq) {
if(request_irq(controller->irq,
cpci_hp_intr,
controller->irq_flags,
MY_NAME, controller->dev_id)) {
err("Can't get irq %d for the hotplug cPCI controller", controller->irq);
status = -ENODEV;
}
dbg("%s - acquired controller irq %d", __FUNCTION__,
controller->irq);
}
} else {
err("cPCI hotplug controller already registered");
status = -1;
}
return status;
}
int
cpci_hp_unregister_controller(struct cpci_hp_controller *old_controller)
{
int status = 0;
if(controller) {
if(!thread_finished) {
cpci_stop_thread();
}
if(controller->irq) {
free_irq(controller->irq, controller->dev_id);
}
controller = NULL;
} else {
status = -ENODEV;
}
return status;
}
int
cpci_hp_start(void)
{
static int first = 1;
int status;
dbg("%s - enter", __FUNCTION__);
if(!controller) {
return -ENODEV;
}
spin_lock(&list_lock);
if(!slots) {
spin_unlock(&list_lock);
return -ENODEV;
}
spin_unlock(&list_lock);
if(first) {
status = init_slots();
if(status) {
return status;
}
first = 0;
}
status = cpci_start_thread();
if(status) {
return status;
}
dbg("%s - thread started", __FUNCTION__);
if(controller->irq) {
/* Start enum interrupt processing */
dbg("%s - enabling irq", __FUNCTION__);
controller->ops->enable_irq();
}
dbg("%s - exit", __FUNCTION__);
return 0;
}
int
cpci_hp_stop(void)
{
if(!controller) {
return -ENODEV;
}
if(controller->irq) {
/* Stop enum interrupt processing */
dbg("%s - disabling irq", __FUNCTION__);
controller->ops->disable_irq();
}
cpci_stop_thread();
return 0;
}
static void __exit
cleanup_slots(void)
{
struct list_head *tmp;
struct slot *slot;
/*
* Unregister all of our slots with the pci_hotplug subsystem,
* and free up all memory that we had allocated.
*/
spin_lock(&list_lock);
if(!slots) {
goto null_cleanup;
}
list_for_each(tmp, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
null_cleanup:
spin_unlock(&list_lock);
return;
}
int __init
cpci_hotplug_init(int debug)
{
spin_lock_init(&list_lock);
cpci_debug = debug;
info(DRIVER_DESC " version: " DRIVER_VERSION);
return 0;
}
void __exit
cpci_hotplug_exit(void)
{
/*
* Clean everything up.
*/
cleanup_slots();
}
EXPORT_SYMBOL_GPL(cpci_hp_register_controller);
EXPORT_SYMBOL_GPL(cpci_hp_unregister_controller);
EXPORT_SYMBOL_GPL(cpci_hp_register_bus);
EXPORT_SYMBOL_GPL(cpci_hp_unregister_bus);
EXPORT_SYMBOL_GPL(cpci_hp_start);
EXPORT_SYMBOL_GPL(cpci_hp_stop);

View File

@@ -0,0 +1,661 @@
/*
* CompactPCI Hot Plug Driver PCI functions
*
* Copyright (C) 2002 by SOMA Networks, Inc.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <scottm@somanetworks.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include "../pci.h"
#include "pci_hotplug.h"
#include "cpci_hotplug.h"
#if !defined(MODULE)
#define MY_NAME "cpci_hotplug"
#else
#define MY_NAME THIS_MODULE->name
#endif
extern int cpci_debug;
#define dbg(format, arg...) \
do { \
if(cpci_debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
u8 cpci_get_attention_status(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return 0;
}
return hs_csr & 0x0008 ? 1 : 0;
}
int cpci_set_attention_status(struct slot* slot, int status)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return 0;
}
if(status) {
hs_csr |= HS_CSR_LOO;
} else {
hs_csr &= ~HS_CSR_LOO;
}
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
return 0;
}
return 1;
}
u16 cpci_get_hs_csr(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0xFFFF;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return 0xFFFF;
}
return hs_csr;
}
#if 0
u16 cpci_set_hs_csr(struct slot* slot, u16 hs_csr)
{
int hs_cap;
u16 new_hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0xFFFF;
}
/* Write out the new value */
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
return 0xFFFF;
}
/* Read back what we just wrote out */
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&new_hs_csr)) {
return 0xFFFF;
}
return new_hs_csr;
}
#endif
int cpci_check_and_clear_ins(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
int ins = 0;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return 0;
}
if(hs_csr & HS_CSR_INS) {
/* Clear INS (by setting it) */
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
ins = 0;
}
ins = 1;
}
return ins;
}
int cpci_check_ext(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
int ext = 0;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return 0;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return 0;
}
if(hs_csr & HS_CSR_EXT) {
ext = 1;
}
return ext;
}
int cpci_clear_ext(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return -ENODEV;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return -ENODEV;
}
if(hs_csr & HS_CSR_EXT) {
/* Clear EXT (by setting it) */
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
return -ENODEV;
}
}
return 0;
}
int cpci_led_on(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return -ENODEV;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return -ENODEV;
}
if((hs_csr & HS_CSR_LOO) != HS_CSR_LOO) {
/* Set LOO */
hs_csr |= HS_CSR_LOO;
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
err("Could not set LOO for slot %s",
slot->hotplug_slot->name);
return -ENODEV;
}
}
return 0;
}
int cpci_led_off(struct slot* slot)
{
int hs_cap;
u16 hs_csr;
hs_cap = pci_bus_find_capability(slot->bus,
slot->devfn,
PCI_CAP_ID_CHSWP);
if(!hs_cap) {
return -ENODEV;
}
if(pci_bus_read_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
&hs_csr)) {
return -ENODEV;
}
if(hs_csr & HS_CSR_LOO) {
/* Clear LOO */
hs_csr &= ~HS_CSR_LOO;
if(pci_bus_write_config_word(slot->bus,
slot->devfn,
hs_cap + 2,
hs_csr)) {
err("Could not clear LOO for slot %s",
slot->hotplug_slot->name);
return -ENODEV;
}
}
return 0;
}
/*
* Device configuration functions
*/
static int cpci_configure_dev(struct pci_bus *bus, struct pci_dev *dev)
{
u8 irq_pin;
int r;
dbg("%s - enter", __FUNCTION__);
/* NOTE: device already setup from prior scan */
/* FIXME: How would we know if we need to enable the expansion ROM? */
pci_write_config_word(dev, PCI_ROM_ADDRESS, 0x00L);
/* Assign resources */
dbg("assigning resources for %02x:%02x.%x",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
for (r = 0; r < 6; r++) {
struct resource *res = dev->resource + r;
if(res->flags)
pci_assign_resource(dev, r);
}
dbg("finished assigning resources for %02x:%02x.%x",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
/* Does this function have an interrupt at all? */
dbg("checking for function interrupt");
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
if(irq_pin) {
dbg("function uses interrupt pin %d", irq_pin);
}
/*
* Need to explicitly set irq field to 0 so that it'll get assigned
* by the pcibios platform dependent code called by pci_enable_device.
*/
dev->irq = 0;
dbg("enabling device");
pci_enable_device(dev); /* XXX check return */
dbg("now dev->irq = %d", dev->irq);
if(irq_pin && dev->irq) {
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
}
/* Can't use pci_insert_device at the moment, do it manually for now */
pci_proc_attach_device(dev);
dbg("notifying drivers");
//pci_announce_device_to_drivers(dev);
dbg("%s - exit", __FUNCTION__);
return 0;
}
static int cpci_configure_bridge(struct pci_bus* bus, struct pci_dev* dev)
{
int rc;
struct pci_bus* child;
struct resource* r;
u8 max, n;
u16 command;
dbg("%s - enter", __FUNCTION__);
/* Do basic bridge initialization */
rc = pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40);
if(rc) {
printk(KERN_ERR "%s - write of PCI_LATENCY_TIMER failed\n", __FUNCTION__);
}
rc = pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40);
if(rc) {
printk(KERN_ERR "%s - write of PCI_SEC_LATENCY_TIMER failed\n", __FUNCTION__);
}
rc = pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4);
if(rc) {
printk(KERN_ERR "%s - write of PCI_CACHE_LINE_SIZE failed\n", __FUNCTION__);
}
/*
* Set parent bridge's subordinate field so that configuration space
* access will work in pci_scan_bridge and friends.
*/
max = pci_max_busnr();
bus->subordinate = max + 1;
pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, max + 1);
/* Scan behind bridge */
n = pci_scan_bridge(bus, dev, max, 2);
child = pci_find_bus(0, max + 1);
if (!child)
return -ENODEV;
pci_proc_attach_bus(child);
/*
* Update parent bridge's subordinate field if there were more bridges
* behind the bridge that was scanned.
*/
if(n > max) {
bus->subordinate = n;
pci_write_config_byte(bus->self, PCI_SUBORDINATE_BUS, n);
}
/*
* Update the bridge resources of the bridge to accommodate devices
* behind it.
*/
pci_bus_size_bridges(child);
pci_bus_assign_resources(child);
/* Enable resource mapping via command register */
command = PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
r = child->resource[0];
if(r && r->start) {
command |= PCI_COMMAND_IO;
}
r = child->resource[1];
if(r && r->start) {
command |= PCI_COMMAND_MEMORY;
}
r = child->resource[2];
if(r && r->start) {
command |= PCI_COMMAND_MEMORY;
}
rc = pci_write_config_word(dev, PCI_COMMAND, command);
if(rc) {
err("Error setting command register");
return rc;
}
/* Set bridge control register */
command = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA;
rc = pci_write_config_word(dev, PCI_BRIDGE_CONTROL, command);
if(rc) {
err("Error setting bridge control register");
return rc;
}
dbg("%s - exit", __FUNCTION__);
return 0;
}
static int configure_visit_pci_dev(struct pci_dev_wrapped *wrapped_dev,
struct pci_bus_wrapped *wrapped_bus)
{
int rc;
struct pci_dev *dev = wrapped_dev->dev;
struct pci_bus *bus = wrapped_bus->bus;
struct slot* slot;
dbg("%s - enter", __FUNCTION__);
/*
* We need to fix up the hotplug representation with the Linux
* representation.
*/
if(wrapped_dev->data) {
slot = (struct slot*) wrapped_dev->data;
slot->dev = dev;
}
/* If it's a bridge, scan behind it for devices */
if(dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
rc = cpci_configure_bridge(bus, dev);
if(rc)
return rc;
}
/* Actually configure device */
if(dev) {
rc = cpci_configure_dev(bus, dev);
if(rc)
return rc;
}
dbg("%s - exit", __FUNCTION__);
return 0;
}
static int unconfigure_visit_pci_dev_phase2(struct pci_dev_wrapped *wrapped_dev,
struct pci_bus_wrapped *wrapped_bus)
{
struct pci_dev *dev = wrapped_dev->dev;
struct slot* slot;
dbg("%s - enter", __FUNCTION__);
if(!dev)
return -ENODEV;
/* Remove the Linux representation */
if(pci_remove_device_safe(dev)) {
err("Could not remove device\n");
return -1;
}
/*
* Now remove the hotplug representation.
*/
if(wrapped_dev->data) {
slot = (struct slot*) wrapped_dev->data;
slot->dev = NULL;
} else {
dbg("No hotplug representation for %02x:%02x.%x",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
}
dbg("%s - exit", __FUNCTION__);
return 0;
}
static int unconfigure_visit_pci_bus_phase2(struct pci_bus_wrapped *wrapped_bus,
struct pci_dev_wrapped *wrapped_dev)
{
struct pci_bus *bus = wrapped_bus->bus;
struct pci_bus *parent = bus->self->bus;
dbg("%s - enter", __FUNCTION__);
/* The cleanup code for proc entries regarding buses should be in the kernel... */
if(bus->procdir)
dbg("detach_pci_bus %s", bus->procdir->name);
pci_proc_detach_bus(bus);
/* The cleanup code should live in the kernel... */
bus->self->subordinate = NULL;
/* unlink from parent bus */
list_del(&bus->node);
/* Now, remove */
if(bus)
kfree(bus);
/* Update parent's subordinate field */
if(parent) {
u8 n = pci_bus_max_busnr(parent);
if(n < parent->subordinate) {
parent->subordinate = n;
pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, n);
}
}
dbg("%s - exit", __FUNCTION__);
return 0;
}
static struct pci_visit configure_functions = {
.visit_pci_dev = configure_visit_pci_dev,
};
static struct pci_visit unconfigure_functions_phase2 = {
.post_visit_pci_bus = unconfigure_visit_pci_bus_phase2,
.post_visit_pci_dev = unconfigure_visit_pci_dev_phase2
};
int cpci_configure_slot(struct slot* slot)
{
int rc = 0;
dbg("%s - enter", __FUNCTION__);
if(slot->dev == NULL) {
dbg("pci_dev null, finding %02x:%02x:%x",
slot->bus->number, PCI_SLOT(slot->devfn), PCI_FUNC(slot->devfn));
slot->dev = pci_find_slot(slot->bus->number, slot->devfn);
}
/* Still NULL? Well then scan for it! */
if(slot->dev == NULL) {
int n;
dbg("pci_dev still null");
/*
* This will generate pci_dev structures for all functions, but
* we will only call this case when lookup fails.
*/
n = pci_scan_slot(slot->bus, slot->devfn);
dbg("%s: pci_scan_slot returned %d", __FUNCTION__, n);
if(n > 0)
pci_bus_add_devices(slot->bus);
slot->dev = pci_find_slot(slot->bus->number, slot->devfn);
if(slot->dev == NULL) {
err("Could not find PCI device for slot %02x", slot->number);
return 0;
}
}
dbg("slot->dev = %p", slot->dev);
if(slot->dev) {
struct pci_dev *dev;
struct pci_dev_wrapped wrapped_dev;
struct pci_bus_wrapped wrapped_bus;
int i;
memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
for (i = 0; i < 8; i++) {
dev = pci_find_slot(slot->bus->number,
PCI_DEVFN(PCI_SLOT(slot->dev->devfn), i));
if(!dev)
continue;
wrapped_dev.dev = dev;
wrapped_bus.bus = slot->dev->bus;
if(i)
wrapped_dev.data = NULL;
else
wrapped_dev.data = (void*) slot;
rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus);
}
}
dbg("%s - exit, rc = %d", __FUNCTION__, rc);
return rc;
}
int cpci_unconfigure_slot(struct slot* slot)
{
int rc = 0;
int i;
struct pci_dev_wrapped wrapped_dev;
struct pci_bus_wrapped wrapped_bus;
struct pci_dev *dev;
dbg("%s - enter", __FUNCTION__);
if(!slot->dev) {
err("No device for slot %02x\n", slot->number);
return -ENODEV;
}
memset(&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
memset(&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
for (i = 0; i < 8; i++) {
dev = pci_find_slot(slot->bus->number,
PCI_DEVFN(PCI_SLOT(slot->devfn), i));
if(dev) {
wrapped_dev.dev = dev;
wrapped_bus.bus = dev->bus;
if(i)
wrapped_dev.data = NULL;
else
wrapped_dev.data = (void*) slot;
dbg("%s - unconfigure phase 2", __FUNCTION__);
rc = pci_visit_dev(&unconfigure_functions_phase2,
&wrapped_dev,
&wrapped_bus);
if(rc)
break;
}
}
dbg("%s - exit, rc = %d", __FUNCTION__, rc);
return rc;
}

View File

@@ -0,0 +1,223 @@
/*
* cpcihp_generic.c
*
* Generic port I/O CompactPCI driver
*
* Copyright 2002 SOMA Networks, Inc.
* Copyright 2001 Intel San Luis Obispo
* Copyright 2000,2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This generic CompactPCI hotplug driver should allow using the PCI hotplug
* mechanism on any CompactPCI board that exposes the #ENUM signal as a bit
* in a system register that can be read through standard port I/O.
*
* Send feedback to <scottm@somanetworks.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include "cpci_hotplug.h"
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "Generic port I/O CompactPCI Hot Plug Driver"
#if !defined(MODULE)
#define MY_NAME "cpcihp_generic"
#else
#define MY_NAME THIS_MODULE->name
#endif
#define dbg(format, arg...) \
do { \
if(debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
static int debug;
static char *bridge;
static u8 bridge_busnr;
static u8 bridge_slot;
static struct pci_bus *bus;
static u8 first_slot;
static u8 last_slot;
static u16 port;
static unsigned int enum_bit;
static u8 enum_mask;
static struct cpci_hp_controller_ops generic_hpc_ops;
static struct cpci_hp_controller generic_hpc;
static int __init validate_parameters(void)
{
char* str;
char* p;
unsigned long tmp;
if(!bridge) {
info("not configured, disabling.");
return 1;
}
str = bridge;
if(!*str)
return -EINVAL;
tmp = simple_strtoul(str, &p, 16);
if(p == str || tmp > 0xff) {
err("Invalid hotplug bus bridge device bus number");
return -EINVAL;
}
bridge_busnr = (u8) tmp;
dbg("bridge_busnr = 0x%02x", bridge_busnr);
if(*p != ':') {
err("Invalid hotplug bus bridge device");
return -EINVAL;
}
str = p + 1;
tmp = simple_strtoul(str, &p, 16);
if(p == str || tmp > 0x1f) {
err("Invalid hotplug bus bridge device slot number");
return -EINVAL;
}
bridge_slot = (u8) tmp;
dbg("bridge_slot = 0x%02x", bridge_slot);
dbg("first_slot = 0x%02x", first_slot);
dbg("last_slot = 0x%02x", last_slot);
if(!(first_slot && last_slot)) {
err("Need to specify first_slot and last_slot");
return -EINVAL;
}
if(last_slot < first_slot) {
err("first_slot must be less than last_slot");
return -EINVAL;
}
dbg("port = 0x%04x", port);
dbg("enum_bit = 0x%02x", enum_bit);
if(enum_bit > 7) {
err("Invalid #ENUM bit");
return -EINVAL;
}
enum_mask = 1 << enum_bit;
return 0;
}
static int query_enum(void)
{
u8 value;
value = inb_p(port);
return ((value & enum_mask) == enum_mask);
}
static int __init cpcihp_generic_init(void)
{
int status;
struct resource* r;
struct pci_dev* dev;
info(DRIVER_DESC " version: " DRIVER_VERSION);
status = validate_parameters();
if(status != 0)
return status;
r = request_region(port, 1, "#ENUM hotswap signal register");
if(!r)
return -EBUSY;
dev = pci_find_slot(bridge_busnr, PCI_DEVFN(bridge_slot, 0));
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
err("Invalid bridge device %s", bridge);
return -EINVAL;
}
bus = dev->subordinate;
memset(&generic_hpc, 0, sizeof (struct cpci_hp_controller));
generic_hpc_ops.query_enum = query_enum;
generic_hpc.ops = &generic_hpc_ops;
status = cpci_hp_register_controller(&generic_hpc);
if(status != 0) {
err("Could not register cPCI hotplug controller");
return -ENODEV;
}
dbg("registered controller");
status = cpci_hp_register_bus(bus, first_slot, last_slot);
if(status != 0) {
err("Could not register cPCI hotplug bus");
goto init_bus_register_error;
}
dbg("registered bus");
status = cpci_hp_start();
if(status != 0) {
err("Could not started cPCI hotplug system");
goto init_start_error;
}
dbg("started cpci hp system");
return 0;
init_start_error:
cpci_hp_unregister_bus(bus);
init_bus_register_error:
cpci_hp_unregister_controller(&generic_hpc);
err("status = %d", status);
return status;
}
static void __exit cpcihp_generic_exit(void)
{
cpci_hp_stop();
cpci_hp_unregister_bus(bus);
cpci_hp_unregister_controller(&generic_hpc);
release_region(port, 1);
}
module_init(cpcihp_generic_init);
module_exit(cpcihp_generic_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
module_param(bridge, charp, 0);
MODULE_PARM_DESC(bridge, "Hotswap bus bridge device, <bus>:<slot> (bus and slot are in hexadecimal)");
module_param(first_slot, byte, 0);
MODULE_PARM_DESC(first_slot, "Hotswap bus first slot number");
module_param(last_slot, byte, 0);
MODULE_PARM_DESC(last_slot, "Hotswap bus last slot number");
module_param(port, ushort, 0);
MODULE_PARM_DESC(port, "#ENUM signal I/O port");
module_param(enum_bit, uint, 0);
MODULE_PARM_DESC(enum_bit, "#ENUM signal bit (0-7)");

View File

@@ -0,0 +1,305 @@
/*
* cpcihp_zt5550.c
*
* Intel/Ziatech ZT5550 CompactPCI Host Controller driver
*
* Copyright 2002 SOMA Networks, Inc.
* Copyright 2001 Intel San Luis Obispo
* Copyright 2000,2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <scottm@somanetworks.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include "cpci_hotplug.h"
#include "cpcihp_zt5550.h"
#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "ZT5550 CompactPCI Hot Plug Driver"
#define MY_NAME "cpcihp_zt5550"
#define dbg(format, arg...) \
do { \
if(debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
} while(0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
static int debug;
static int poll;
static struct cpci_hp_controller_ops zt5550_hpc_ops;
static struct cpci_hp_controller zt5550_hpc;
/* Primary cPCI bus bridge device */
static struct pci_dev *bus0_dev;
static struct pci_bus *bus0;
/* Host controller device */
static struct pci_dev *hc_dev;
/* Host controller register addresses */
static void __iomem *hc_registers;
static void __iomem *csr_hc_index;
static void __iomem *csr_hc_data;
static void __iomem *csr_int_status;
static void __iomem *csr_int_mask;
static int zt5550_hc_config(struct pci_dev *pdev)
{
/* Since we know that no boards exist with two HC chips, treat it as an error */
if(hc_dev) {
err("too many host controller devices?");
return -EBUSY;
}
hc_dev = pdev;
dbg("hc_dev = %p", hc_dev);
dbg("pci resource start %lx", pci_resource_start(hc_dev, 1));
dbg("pci resource len %lx", pci_resource_len(hc_dev, 1));
if(!request_mem_region(pci_resource_start(hc_dev, 1),
pci_resource_len(hc_dev, 1), MY_NAME)) {
err("cannot reserve MMIO region");
return -ENOMEM;
}
hc_registers =
ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
if(!hc_registers) {
err("cannot remap MMIO region %lx @ %lx",
pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1));
release_mem_region(pci_resource_start(hc_dev, 1),
pci_resource_len(hc_dev, 1));
return -ENODEV;
}
csr_hc_index = hc_registers + CSR_HCINDEX;
csr_hc_data = hc_registers + CSR_HCDATA;
csr_int_status = hc_registers + CSR_INTSTAT;
csr_int_mask = hc_registers + CSR_INTMASK;
/*
* Disable host control, fault and serial interrupts
*/
dbg("disabling host control, fault and serial interrupts");
writeb((u8) HC_INT_MASK_REG, csr_hc_index);
writeb((u8) ALL_INDEXED_INTS_MASK, csr_hc_data);
dbg("disabled host control, fault and serial interrupts");
/*
* Disable timer0, timer1 and ENUM interrupts
*/
dbg("disabling timer0, timer1 and ENUM interrupts");
writeb((u8) ALL_DIRECT_INTS_MASK, csr_int_mask);
dbg("disabled timer0, timer1 and ENUM interrupts");
return 0;
}
static int zt5550_hc_cleanup(void)
{
if(!hc_dev)
return -ENODEV;
iounmap(hc_registers);
release_mem_region(pci_resource_start(hc_dev, 1),
pci_resource_len(hc_dev, 1));
return 0;
}
static int zt5550_hc_query_enum(void)
{
u8 value;
value = inb_p(ENUM_PORT);
return ((value & ENUM_MASK) == ENUM_MASK);
}
static int zt5550_hc_check_irq(void *dev_id)
{
int ret;
u8 reg;
ret = 0;
if(dev_id == zt5550_hpc.dev_id) {
reg = readb(csr_int_status);
if(reg)
ret = 1;
}
return ret;
}
static int zt5550_hc_enable_irq(void)
{
u8 reg;
if(hc_dev == NULL) {
return -ENODEV;
}
reg = readb(csr_int_mask);
reg = reg & ~ENUM_INT_MASK;
writeb(reg, csr_int_mask);
return 0;
}
static int zt5550_hc_disable_irq(void)
{
u8 reg;
if(hc_dev == NULL) {
return -ENODEV;
}
reg = readb(csr_int_mask);
reg = reg | ENUM_INT_MASK;
writeb(reg, csr_int_mask);
return 0;
}
static int zt5550_hc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
int status;
status = zt5550_hc_config(pdev);
if(status != 0) {
return status;
}
dbg("returned from zt5550_hc_config");
memset(&zt5550_hpc, 0, sizeof (struct cpci_hp_controller));
zt5550_hpc_ops.query_enum = zt5550_hc_query_enum;
zt5550_hpc.ops = &zt5550_hpc_ops;
if(!poll) {
zt5550_hpc.irq = hc_dev->irq;
zt5550_hpc.irq_flags = SA_SHIRQ;
zt5550_hpc.dev_id = hc_dev;
zt5550_hpc_ops.enable_irq = zt5550_hc_enable_irq;
zt5550_hpc_ops.disable_irq = zt5550_hc_disable_irq;
zt5550_hpc_ops.check_irq = zt5550_hc_check_irq;
} else {
info("using ENUM# polling mode");
}
status = cpci_hp_register_controller(&zt5550_hpc);
if(status != 0) {
err("could not register cPCI hotplug controller");
goto init_hc_error;
}
dbg("registered controller");
/* Look for first device matching cPCI bus's bridge vendor and device IDs */
if(!(bus0_dev = pci_get_device(PCI_VENDOR_ID_DEC,
PCI_DEVICE_ID_DEC_21154, NULL))) {
status = -ENODEV;
goto init_register_error;
}
bus0 = bus0_dev->subordinate;
pci_dev_put(bus0_dev);
status = cpci_hp_register_bus(bus0, 0x0a, 0x0f);
if(status != 0) {
err("could not register cPCI hotplug bus");
goto init_register_error;
}
dbg("registered bus");
status = cpci_hp_start();
if(status != 0) {
err("could not started cPCI hotplug system");
cpci_hp_unregister_bus(bus0);
goto init_register_error;
}
dbg("started cpci hp system");
return 0;
init_register_error:
cpci_hp_unregister_controller(&zt5550_hpc);
init_hc_error:
err("status = %d", status);
zt5550_hc_cleanup();
return status;
}
static void __devexit zt5550_hc_remove_one(struct pci_dev *pdev)
{
cpci_hp_stop();
cpci_hp_unregister_bus(bus0);
cpci_hp_unregister_controller(&zt5550_hpc);
zt5550_hc_cleanup();
}
static struct pci_device_id zt5550_hc_pci_tbl[] = {
{ PCI_VENDOR_ID_ZIATECH, PCI_DEVICE_ID_ZIATECH_5550_HC, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, zt5550_hc_pci_tbl);
static struct pci_driver zt5550_hc_driver = {
.name = "zt5550_hc",
.id_table = zt5550_hc_pci_tbl,
.probe = zt5550_hc_init_one,
.remove = __devexit_p(zt5550_hc_remove_one),
};
static int __init zt5550_init(void)
{
struct resource* r;
info(DRIVER_DESC " version: " DRIVER_VERSION);
r = request_region(ENUM_PORT, 1, "#ENUM hotswap signal register");
if(!r)
return -EBUSY;
return pci_register_driver(&zt5550_hc_driver);
}
static void __exit
zt5550_exit(void)
{
pci_unregister_driver(&zt5550_hc_driver);
release_region(ENUM_PORT, 1);
}
module_init(zt5550_init);
module_exit(zt5550_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
module_param(poll, bool, 0644);
MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not");

View File

@@ -0,0 +1,79 @@
/*
* cpcihp_zt5550.h
*
* Intel/Ziatech ZT5550 CompactPCI Host Controller driver definitions
*
* Copyright 2002 SOMA Networks, Inc.
* Copyright 2001 Intel San Luis Obispo
* Copyright 2000,2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <scottm@somanetworks.com>
*/
#ifndef _CPCIHP_ZT5550_H
#define _CPCIHP_ZT5550_H
/* Direct registers */
#define CSR_HCINDEX 0x00
#define CSR_HCDATA 0x04
#define CSR_INTSTAT 0x08
#define CSR_INTMASK 0x09
#define CSR_CNT0CMD 0x0C
#define CSR_CNT1CMD 0x0E
#define CSR_CNT0 0x10
#define CSR_CNT1 0x14
/* Masks for interrupt bits in CSR_INTMASK direct register */
#define CNT0_INT_MASK 0x01
#define CNT1_INT_MASK 0x02
#define ENUM_INT_MASK 0x04
#define ALL_DIRECT_INTS_MASK 0x07
/* Indexed registers (through CSR_INDEX, CSR_DATA) */
#define HC_INT_MASK_REG 0x04
#define HC_STATUS_REG 0x08
#define HC_CMD_REG 0x0C
#define ARB_CONFIG_GNT_REG 0x10
#define ARB_CONFIG_CFG_REG 0x12
#define ARB_CONFIG_REG 0x10
#define ISOL_CONFIG_REG 0x18
#define FAULT_STATUS_REG 0x20
#define FAULT_CONFIG_REG 0x24
#define WD_CONFIG_REG 0x2C
#define HC_DIAG_REG 0x30
#define SERIAL_COMM_REG 0x34
#define SERIAL_OUT_REG 0x38
#define SERIAL_IN_REG 0x3C
/* Masks for interrupt bits in HC_INT_MASK_REG indexed register */
#define SERIAL_INT_MASK 0x01
#define FAULT_INT_MASK 0x02
#define HCF_INT_MASK 0x04
#define ALL_INDEXED_INTS_MASK 0x07
/* Digital I/O port storing ENUM# */
#define ENUM_PORT 0xE1
/* Mask to get to the ENUM# bit on the bus */
#define ENUM_MASK 0x40
#endif /* _CPCIHP_ZT5550_H */

View File

@@ -0,0 +1,721 @@
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#ifndef _CPQPHP_H
#define _CPQPHP_H
#include "pci_hotplug.h"
#include <linux/interrupt.h>
#include <asm/io.h> /* for read? and write? functions */
#include <linux/delay.h> /* for delays */
#define MY_NAME "cpqphp"
#define dbg(fmt, arg...) do { if (cpqhp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
struct smbios_system_slot {
u8 type;
u8 length;
u16 handle;
u8 name_string_num;
u8 slot_type;
u8 slot_width;
u8 slot_current_usage;
u8 slot_length;
u16 slot_number;
u8 properties1;
u8 properties2;
} __attribute__ ((packed));
/* offsets to the smbios generic type based on the above structure layout */
enum smbios_system_slot_offsets {
SMBIOS_SLOT_GENERIC_TYPE = offsetof(struct smbios_system_slot, type),
SMBIOS_SLOT_GENERIC_LENGTH = offsetof(struct smbios_system_slot, length),
SMBIOS_SLOT_GENERIC_HANDLE = offsetof(struct smbios_system_slot, handle),
SMBIOS_SLOT_NAME_STRING_NUM = offsetof(struct smbios_system_slot, name_string_num),
SMBIOS_SLOT_TYPE = offsetof(struct smbios_system_slot, slot_type),
SMBIOS_SLOT_WIDTH = offsetof(struct smbios_system_slot, slot_width),
SMBIOS_SLOT_CURRENT_USAGE = offsetof(struct smbios_system_slot, slot_current_usage),
SMBIOS_SLOT_LENGTH = offsetof(struct smbios_system_slot, slot_length),
SMBIOS_SLOT_NUMBER = offsetof(struct smbios_system_slot, slot_number),
SMBIOS_SLOT_PROPERTIES1 = offsetof(struct smbios_system_slot, properties1),
SMBIOS_SLOT_PROPERTIES2 = offsetof(struct smbios_system_slot, properties2),
};
struct smbios_generic {
u8 type;
u8 length;
u16 handle;
} __attribute__ ((packed));
/* offsets to the smbios generic type based on the above structure layout */
enum smbios_generic_offsets {
SMBIOS_GENERIC_TYPE = offsetof(struct smbios_generic, type),
SMBIOS_GENERIC_LENGTH = offsetof(struct smbios_generic, length),
SMBIOS_GENERIC_HANDLE = offsetof(struct smbios_generic, handle),
};
struct smbios_entry_point {
char anchor[4];
u8 ep_checksum;
u8 ep_length;
u8 major_version;
u8 minor_version;
u16 max_size_entry;
u8 ep_rev;
u8 reserved[5];
char int_anchor[5];
u8 int_checksum;
u16 st_length;
u32 st_address;
u16 number_of_entrys;
u8 bcd_rev;
} __attribute__ ((packed));
/* offsets to the smbios entry point based on the above structure layout */
enum smbios_entry_point_offsets {
ANCHOR = offsetof(struct smbios_entry_point, anchor[0]),
EP_CHECKSUM = offsetof(struct smbios_entry_point, ep_checksum),
EP_LENGTH = offsetof(struct smbios_entry_point, ep_length),
MAJOR_VERSION = offsetof(struct smbios_entry_point, major_version),
MINOR_VERSION = offsetof(struct smbios_entry_point, minor_version),
MAX_SIZE_ENTRY = offsetof(struct smbios_entry_point, max_size_entry),
EP_REV = offsetof(struct smbios_entry_point, ep_rev),
INT_ANCHOR = offsetof(struct smbios_entry_point, int_anchor[0]),
INT_CHECKSUM = offsetof(struct smbios_entry_point, int_checksum),
ST_LENGTH = offsetof(struct smbios_entry_point, st_length),
ST_ADDRESS = offsetof(struct smbios_entry_point, st_address),
NUMBER_OF_ENTRYS = offsetof(struct smbios_entry_point, number_of_entrys),
BCD_REV = offsetof(struct smbios_entry_point, bcd_rev),
};
struct ctrl_reg { /* offset */
u8 slot_RST; /* 0x00 */
u8 slot_enable; /* 0x01 */
u16 misc; /* 0x02 */
u32 led_control; /* 0x04 */
u32 int_input_clear; /* 0x08 */
u32 int_mask; /* 0x0a */
u8 reserved0; /* 0x10 */
u8 reserved1; /* 0x11 */
u8 reserved2; /* 0x12 */
u8 gen_output_AB; /* 0x13 */
u32 non_int_input; /* 0x14 */
u32 reserved3; /* 0x18 */
u32 reserved4; /* 0x1a */
u32 reserved5; /* 0x20 */
u8 reserved6; /* 0x24 */
u8 reserved7; /* 0x25 */
u16 reserved8; /* 0x26 */
u8 slot_mask; /* 0x28 */
u8 reserved9; /* 0x29 */
u8 reserved10; /* 0x2a */
u8 reserved11; /* 0x2b */
u8 slot_SERR; /* 0x2c */
u8 slot_power; /* 0x2d */
u8 reserved12; /* 0x2e */
u8 reserved13; /* 0x2f */
u8 next_curr_freq; /* 0x30 */
u8 reset_freq_mode; /* 0x31 */
} __attribute__ ((packed));
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
SLOT_RST = offsetof(struct ctrl_reg, slot_RST),
SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable),
MISC = offsetof(struct ctrl_reg, misc),
LED_CONTROL = offsetof(struct ctrl_reg, led_control),
INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear),
INT_MASK = offsetof(struct ctrl_reg, int_mask),
CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0),
CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1),
CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1),
GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB),
NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input),
CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3),
CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4),
CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5),
CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6),
CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7),
CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8),
SLOT_MASK = offsetof(struct ctrl_reg, slot_mask),
CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9),
CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10),
CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11),
SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR),
SLOT_POWER = offsetof(struct ctrl_reg, slot_power),
NEXT_CURR_FREQ = offsetof(struct ctrl_reg, next_curr_freq),
RESET_FREQ_MODE = offsetof(struct ctrl_reg, reset_freq_mode),
};
struct hrt {
char sig0;
char sig1;
char sig2;
char sig3;
u16 unused_IRQ;
u16 PCIIRQ;
u8 number_of_entries;
u8 revision;
u16 reserved1;
u32 reserved2;
} __attribute__ ((packed));
/* offsets to the hotplug resource table registers based on the above structure layout */
enum hrt_offsets {
SIG0 = offsetof(struct hrt, sig0),
SIG1 = offsetof(struct hrt, sig1),
SIG2 = offsetof(struct hrt, sig2),
SIG3 = offsetof(struct hrt, sig3),
UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
PCIIRQ = offsetof(struct hrt, PCIIRQ),
NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
REVISION = offsetof(struct hrt, revision),
HRT_RESERVED1 = offsetof(struct hrt, reserved1),
HRT_RESERVED2 = offsetof(struct hrt, reserved2),
};
struct slot_rt {
u8 dev_func;
u8 primary_bus;
u8 secondary_bus;
u8 max_bus;
u16 io_base;
u16 io_length;
u16 mem_base;
u16 mem_length;
u16 pre_mem_base;
u16 pre_mem_length;
} __attribute__ ((packed));
/* offsets to the hotplug slot resource table registers based on the above structure layout */
enum slot_rt_offsets {
DEV_FUNC = offsetof(struct slot_rt, dev_func),
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
MAX_BUS = offsetof(struct slot_rt, max_bus),
IO_BASE = offsetof(struct slot_rt, io_base),
IO_LENGTH = offsetof(struct slot_rt, io_length),
MEM_BASE = offsetof(struct slot_rt, mem_base),
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
};
struct pci_func {
struct pci_func *next;
u8 bus;
u8 device;
u8 function;
u8 is_a_board;
u16 status;
u8 configured;
u8 switch_save;
u8 presence_save;
u32 base_length[0x06];
u8 base_type[0x06];
u16 reserved2;
u32 config_space[0x20];
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct timer_list *p_task_event;
struct pci_dev* pci_dev;
};
struct slot {
struct slot *next;
u8 bus;
u8 device;
u8 number;
u8 is_a_board;
u8 configured;
u8 state;
u8 switch_save;
u8 presence_save;
u32 capabilities;
u16 reserved2;
struct timer_list task_event;
u8 hp_slot;
struct controller *ctrl;
void __iomem *p_sm_slot;
struct hotplug_slot *hotplug_slot;
};
struct pci_resource {
struct pci_resource * next;
u32 base;
u32 length;
};
struct event_info {
u32 event_type;
u8 hp_slot;
};
struct controller {
struct controller *next;
u32 ctrl_int_comp;
struct semaphore crit_sect; /* critical section semaphore */
void __iomem *hpc_reg; /* cookie for our pci controller location */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev *pci_dev;
struct pci_bus *pci_bus;
struct event_info event_queue[10];
struct slot *slot;
u8 next_event;
u8 interrupt;
u8 cfgspc_irq;
u8 bus; /* bus number for the pci hotplug controller */
u8 rev;
u8 slot_device_offset;
u8 first_slot;
u8 add_support;
u8 push_flag;
enum pci_bus_speed speed;
enum pci_bus_speed speed_capability;
u8 push_button; /* 0 = no pushbutton, 1 = pushbutton present */
u8 slot_switch_type; /* 0 = no switch, 1 = switch present */
u8 defeature_PHP; /* 0 = PHP not supported, 1 = PHP supported */
u8 alternate_base_address; /* 0 = not supported, 1 = supported */
u8 pci_config_space; /* Index/data access to working registers 0 = not supported, 1 = supported */
u8 pcix_speed_capability; /* PCI-X */
u8 pcix_support; /* PCI-X */
u16 vendor_id;
struct work_struct int_task_event;
wait_queue_head_t queue; /* sleep & wake process */
};
struct irq_mapping {
u8 barber_pole;
u8 valid_INT;
u8 interrupt[4];
};
struct resource_lists {
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct irq_mapping *irqs;
};
#define ROM_PHY_ADDR 0x0F0000
#define ROM_PHY_LEN 0x00ffff
#define PCI_HPC_ID 0xA0F7
#define PCI_SUB_HPC_ID 0xA2F7
#define PCI_SUB_HPC_ID2 0xA2F8
#define PCI_SUB_HPC_ID3 0xA2F9
#define PCI_SUB_HPC_ID_INTC 0xA2FA
#define PCI_SUB_HPC_ID4 0xA2FD
#define INT_BUTTON_IGNORE 0
#define INT_PRESENCE_ON 1
#define INT_PRESENCE_OFF 2
#define INT_SWITCH_CLOSE 3
#define INT_SWITCH_OPEN 4
#define INT_POWER_FAULT 5
#define INT_POWER_FAULT_CLEAR 6
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2
#define POWERON_STATE 3
#define POWEROFF_STATE 4
#define PCISLOT_INTERLOCK_CLOSED 0x00000001
#define PCISLOT_ADAPTER_PRESENT 0x00000002
#define PCISLOT_POWERED 0x00000004
#define PCISLOT_66_MHZ_OPERATION 0x00000008
#define PCISLOT_64_BIT_OPERATION 0x00000010
#define PCISLOT_REPLACE_SUPPORTED 0x00000020
#define PCISLOT_ADD_SUPPORTED 0x00000040
#define PCISLOT_INTERLOCK_SUPPORTED 0x00000080
#define PCISLOT_66_MHZ_SUPPORTED 0x00000100
#define PCISLOT_64_BIT_SUPPORTED 0x00000200
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
#define INTERLOCK_OPEN 0x00000002
#define ADD_NOT_SUPPORTED 0x00000003
#define CARD_FUNCTIONING 0x00000005
#define ADAPTER_NOT_SAME 0x00000006
#define NO_ADAPTER_PRESENT 0x00000009
#define NOT_ENOUGH_RESOURCES 0x0000000B
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
#define POWER_FAILURE 0x0000000E
#define REMOVE_NOT_SUPPORTED 0x00000003
/*
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
#define msg_HPC_non_compaq_or_intel "The PCI hot plug controller is not supported by this driver.\n"
#define msg_HPC_not_supported "this system is not supported by this version of cpqphpd. Upgrade to a newer version of cpqphpd\n"
#define msg_unable_to_save "unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
/* sysfs functions for the hotplug controller info */
extern void cpqhp_create_ctrl_files (struct controller *ctrl);
/* controller functions */
extern void cpqhp_pushbutton_thread (unsigned long event_pointer);
extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data, struct pt_regs *regs);
extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start);
extern int cpqhp_event_start_thread (void);
extern void cpqhp_event_stop_thread (void);
extern struct pci_func *cpqhp_slot_create (unsigned char busnumber);
extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func);
extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func);
extern int cpqhp_hardware_test (struct controller *ctrl, int test_num);
/* resource functions */
extern int cpqhp_resource_sort_and_combine (struct pci_resource **head);
/* pci functions */
extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot);
extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug);
extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func);
extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func);
extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func);
extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func);
extern void cpqhp_destroy_board_resources (struct pci_func * func);
extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
extern void cpqhp_destroy_resource_list (struct resource_lists * resources);
extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func);
extern int cpqhp_unconfigure_device (struct pci_func* func);
/* Global variables */
extern int cpqhp_debug;
extern int cpqhp_legacy_mode;
extern struct controller *cpqhp_ctrl_list;
extern struct pci_func *cpqhp_slot_list[256];
/* these can be gotten rid of, but for debugging they are purty */
extern u8 cpqhp_nic_irq;
extern u8 cpqhp_disk_irq;
/* inline functions */
/*
* return_resource
*
* Puts node back in the resource list pointed to by head
*
*/
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
{
if (!node || !head)
return;
node->next = *head;
*head = node;
}
static inline void set_SOGO(struct controller *ctrl)
{
u16 misc;
misc = readw(ctrl->hpc_reg + MISC);
misc = (misc | 0x0001) & 0xFFFB;
writew(misc, ctrl->hpc_reg + MISC);
}
static inline void amber_LED_on(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control |= (0x01010000L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
}
static inline void amber_LED_off(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x01010000L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
}
static inline int read_amber_LED(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= (0x01010000L << slot);
return led_control ? 1 : 0;
}
static inline void green_LED_on(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control |= 0x0101L << slot;
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
}
static inline void green_LED_off(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x0101L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
}
static inline void green_LED_blink(struct controller *ctrl, u8 slot)
{
u32 led_control;
led_control = readl(ctrl->hpc_reg + LED_CONTROL);
led_control &= ~(0x0101L << slot);
led_control |= (0x0001L << slot);
writel(led_control, ctrl->hpc_reg + LED_CONTROL);
}
static inline void slot_disable(struct controller *ctrl, u8 slot)
{
u8 slot_enable;
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
slot_enable &= ~(0x01 << slot);
writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
}
static inline void slot_enable(struct controller *ctrl, u8 slot)
{
u8 slot_enable;
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
slot_enable |= (0x01 << slot);
writeb(slot_enable, ctrl->hpc_reg + SLOT_ENABLE);
}
static inline u8 is_slot_enabled(struct controller *ctrl, u8 slot)
{
u8 slot_enable;
slot_enable = readb(ctrl->hpc_reg + SLOT_ENABLE);
slot_enable &= (0x01 << slot);
return slot_enable ? 1 : 0;
}
static inline u8 read_slot_enable(struct controller *ctrl)
{
return readb(ctrl->hpc_reg + SLOT_ENABLE);
}
/*
* get_controller_speed - find the current frequency/mode of controller.
*
* @ctrl: controller to get frequency/mode for.
*
* Returns controller speed.
*
*/
static inline u8 get_controller_speed(struct controller *ctrl)
{
u8 curr_freq;
u16 misc;
if (ctrl->pcix_support) {
curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ);
if ((curr_freq & 0xB0) == 0xB0)
return PCI_SPEED_133MHz_PCIX;
if ((curr_freq & 0xA0) == 0xA0)
return PCI_SPEED_100MHz_PCIX;
if ((curr_freq & 0x90) == 0x90)
return PCI_SPEED_66MHz_PCIX;
if (curr_freq & 0x10)
return PCI_SPEED_66MHz;
return PCI_SPEED_33MHz;
}
misc = readw(ctrl->hpc_reg + MISC);
return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
}
/*
* get_adapter_speed - find the max supported frequency/mode of adapter.
*
* @ctrl: hotplug controller.
* @hp_slot: hotplug slot where adapter is installed.
*
* Returns adapter speed.
*
*/
static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot)
{
u32 temp_dword = readl(ctrl->hpc_reg + NON_INT_INPUT);
dbg("slot: %d, PCIXCAP: %8x\n", hp_slot, temp_dword);
if (ctrl->pcix_support) {
if (temp_dword & (0x10000 << hp_slot))
return PCI_SPEED_133MHz_PCIX;
if (temp_dword & (0x100 << hp_slot))
return PCI_SPEED_66MHz_PCIX;
}
if (temp_dword & (0x01 << hp_slot))
return PCI_SPEED_66MHz;
return PCI_SPEED_33MHz;
}
static inline void enable_slot_power(struct controller *ctrl, u8 slot)
{
u8 slot_power;
slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
slot_power |= (0x01 << slot);
writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
}
static inline void disable_slot_power(struct controller *ctrl, u8 slot)
{
u8 slot_power;
slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
slot_power &= ~(0x01 << slot);
writeb(slot_power, ctrl->hpc_reg + SLOT_POWER);
}
static inline int cpq_get_attention_status(struct controller *ctrl, struct slot *slot)
{
u8 hp_slot;
hp_slot = slot->device - ctrl->slot_device_offset;
return read_amber_LED(ctrl, hp_slot);
}
static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot)
{
u8 hp_slot;
hp_slot = slot->device - ctrl->slot_device_offset;
return is_slot_enabled(ctrl, hp_slot);
}
static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot)
{
u32 status;
u8 hp_slot;
hp_slot = slot->device - ctrl->slot_device_offset;
dbg("%s: slot->device = %d, ctrl->slot_device_offset = %d \n",
__FUNCTION__, slot->device, ctrl->slot_device_offset);
status = (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot));
return(status == 0) ? 1 : 0;
}
static inline int get_presence_status(struct controller *ctrl, struct slot *slot)
{
int presence_save = 0;
u8 hp_slot;
u32 tempdword;
hp_slot = slot->device - ctrl->slot_device_offset;
tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02;
return presence_save;
}
#define SLOT_NAME_SIZE 10
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
{
snprintf(buffer, buffer_size, "%d", slot->number);
}
static inline int wait_for_ctrl_irq(struct controller *ctrl)
{
DECLARE_WAITQUEUE(wait, current);
int retval = 0;
dbg("%s - start\n", __FUNCTION__);
add_wait_queue(&ctrl->queue, &wait);
/* Sleep for up to 1 second to wait for the LED to change. */
msleep_interruptible(1000);
remove_wait_queue(&ctrl->queue, &wait);
if (signal_pending(current))
retval = -EINTR;
dbg("%s - end\n", __FUNCTION__);
return retval;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,666 @@
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "cpqphp.h"
#include "cpqphp_nvram.h"
#define ROM_INT15_PHY_ADDR 0x0FF859
#define READ_EV 0xD8A4
#define WRITE_EV 0xD8A5
struct register_foo {
union {
unsigned long lword; /* eax */
unsigned short word; /* ax */
struct {
unsigned char low; /* al */
unsigned char high; /* ah */
} byte;
} data;
unsigned char opcode; /* see below */
unsigned long length; /* if the reg. is a pointer, how much data */
} __attribute__ ((packed));
struct all_reg {
struct register_foo eax_reg;
struct register_foo ebx_reg;
struct register_foo ecx_reg;
struct register_foo edx_reg;
struct register_foo edi_reg;
struct register_foo esi_reg;
struct register_foo eflags_reg;
} __attribute__ ((packed));
struct ev_hrt_header {
u8 Version;
u8 num_of_ctrl;
u8 next;
};
struct ev_hrt_ctrl {
u8 bus;
u8 device;
u8 function;
u8 mem_avail;
u8 p_mem_avail;
u8 io_avail;
u8 bus_avail;
u8 next;
};
static u8 evbuffer_init;
static u8 evbuffer_length;
static u8 evbuffer[1024];
static void __iomem *compaq_int15_entry_point;
static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */
/* This is a series of function that deals with
setting & getting the hotplug resource table in some environment variable.
*/
/*
* We really shouldn't be doing this unless there is a _very_ good reason to!!!
* greg k-h
*/
static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail)
{
u8 **tByte;
if ((*used + 1) > *avail)
return(1);
*((u8*)*p_buffer) = value;
tByte = (u8**)p_buffer;
(*tByte)++;
*used+=1;
return(0);
}
static u32 add_dword( u32 **p_buffer, u32 value, u32 *used, u32 *avail)
{
if ((*used + 4) > *avail)
return(1);
**p_buffer = value;
(*p_buffer)++;
*used+=4;
return(0);
}
/*
* check_for_compaq_ROM
*
* this routine verifies that the ROM OEM string is 'COMPAQ'
*
* returns 0 for non-Compaq ROM, 1 for Compaq ROM
*/
static int check_for_compaq_ROM (void __iomem *rom_start)
{
u8 temp1, temp2, temp3, temp4, temp5, temp6;
int result = 0;
temp1 = readb(rom_start + 0xffea + 0);
temp2 = readb(rom_start + 0xffea + 1);
temp3 = readb(rom_start + 0xffea + 2);
temp4 = readb(rom_start + 0xffea + 3);
temp5 = readb(rom_start + 0xffea + 4);
temp6 = readb(rom_start + 0xffea + 5);
if ((temp1 == 'C') &&
(temp2 == 'O') &&
(temp3 == 'M') &&
(temp4 == 'P') &&
(temp5 == 'A') &&
(temp6 == 'Q')) {
result = 1;
}
dbg ("%s - returned %d\n", __FUNCTION__, result);
return result;
}
static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
{
unsigned long flags;
int op = operation;
int ret_val;
if (!compaq_int15_entry_point)
return -ENODEV;
spin_lock_irqsave(&int15_lock, flags);
__asm__ (
"xorl %%ebx,%%ebx\n" \
"xorl %%edx,%%edx\n" \
"pushf\n" \
"push %%cs\n" \
"cli\n" \
"call *%6\n"
: "=c" (*buf_size), "=a" (ret_val)
: "a" (op), "c" (*buf_size), "S" (ev_name),
"D" (buffer), "m" (compaq_int15_entry_point)
: "%ebx", "%edx");
spin_unlock_irqrestore(&int15_lock, flags);
return((ret_val & 0xFF00) >> 8);
}
/*
* load_HRT
*
* Read the hot plug Resource Table from NVRAM
*/
static int load_HRT (void __iomem *rom_start)
{
u32 available;
u32 temp_dword;
u8 temp_byte = 0xFF;
u32 rc;
if (!check_for_compaq_ROM(rom_start)) {
return -ENODEV;
}
available = 1024;
// Now load the EV
temp_dword = available;
rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
evbuffer_length = temp_dword;
// We're maintaining the resource lists so write FF to invalidate old info
temp_dword = 1;
rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
return rc;
}
/*
* store_HRT
*
* Save the hot plug Resource Table in NVRAM
*/
static u32 store_HRT (void __iomem *rom_start)
{
u32 *buffer;
u32 *pFill;
u32 usedbytes;
u32 available;
u32 temp_dword;
u32 rc;
u8 loop;
u8 numCtrl = 0;
struct controller *ctrl;
struct pci_resource *resNode;
struct ev_hrt_header *p_EV_header;
struct ev_hrt_ctrl *p_ev_ctrl;
available = 1024;
if (!check_for_compaq_ROM(rom_start)) {
return(1);
}
buffer = (u32*) evbuffer;
if (!buffer)
return(1);
pFill = buffer;
usedbytes = 0;
p_EV_header = (struct ev_hrt_header *) pFill;
ctrl = cpqhp_ctrl_list;
// The revision of this structure
rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available);
if (rc)
return(rc);
// The number of controllers
rc = add_byte( &pFill, 1, &usedbytes, &available);
if (rc)
return(rc);
while (ctrl) {
p_ev_ctrl = (struct ev_hrt_ctrl *) pFill;
numCtrl++;
// The bus number
rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available);
if (rc)
return(rc);
// The device Number
rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
if (rc)
return(rc);
// The function Number
rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
if (rc)
return(rc);
// Skip the number of available entries
rc = add_dword( &pFill, 0, &usedbytes, &available);
if (rc)
return(rc);
// Figure out memory Available
resNode = ctrl->mem_head;
loop = 0;
while (resNode) {
loop ++;
// base
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
// length
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
// Fill in the number of entries
p_ev_ctrl->mem_avail = loop;
// Figure out prefetchable memory Available
resNode = ctrl->p_mem_head;
loop = 0;
while (resNode) {
loop ++;
// base
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
// length
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
// Fill in the number of entries
p_ev_ctrl->p_mem_avail = loop;
// Figure out IO Available
resNode = ctrl->io_head;
loop = 0;
while (resNode) {
loop ++;
// base
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
// length
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
// Fill in the number of entries
p_ev_ctrl->io_avail = loop;
// Figure out bus Available
resNode = ctrl->bus_head;
loop = 0;
while (resNode) {
loop ++;
// base
rc = add_dword( &pFill, resNode->base, &usedbytes, &available);
if (rc)
return(rc);
// length
rc = add_dword( &pFill, resNode->length, &usedbytes, &available);
if (rc)
return(rc);
resNode = resNode->next;
}
// Fill in the number of entries
p_ev_ctrl->bus_avail = loop;
ctrl = ctrl->next;
}
p_EV_header->num_of_ctrl = numCtrl;
// Now store the EV
temp_dword = usedbytes;
rc = access_EV(WRITE_EV, "CQTHPS", (u8*) buffer, &temp_dword);
dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
evbuffer_length = temp_dword;
if (rc) {
err(msg_unable_to_save);
return(1);
}
return(0);
}
void compaq_nvram_init (void __iomem *rom_start)
{
if (rom_start) {
compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
}
dbg("int15 entry = %p\n", compaq_int15_entry_point);
/* initialize our int15 lock */
spin_lock_init(&int15_lock);
}
int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
{
u8 bus, device, function;
u8 nummem, numpmem, numio, numbus;
u32 rc;
u8 *p_byte;
struct pci_resource *mem_node;
struct pci_resource *p_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct ev_hrt_ctrl *p_ev_ctrl;
struct ev_hrt_header *p_EV_header;
if (!evbuffer_init) {
// Read the resource list information in from NVRAM
if (load_HRT(rom_start))
memset (evbuffer, 0, 1024);
evbuffer_init = 1;
}
// If we saved information in NVRAM, use it now
p_EV_header = (struct ev_hrt_header *) evbuffer;
// The following code is for systems where version 1.0 of this
// driver has been loaded, but doesn't support the hardware.
// In that case, the driver would incorrectly store something
// in NVRAM.
if ((p_EV_header->Version == 2) ||
((p_EV_header->Version == 1) && !ctrl->push_flag)) {
p_byte = &(p_EV_header->next);
p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next);
p_byte += 3;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
bus = p_ev_ctrl->bus;
device = p_ev_ctrl->device;
function = p_ev_ctrl->function;
while ((bus != ctrl->bus) ||
(device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
(function != PCI_FUNC(ctrl->pci_dev->devfn))) {
nummem = p_ev_ctrl->mem_avail;
numpmem = p_ev_ctrl->p_mem_avail;
numio = p_ev_ctrl->io_avail;
numbus = p_ev_ctrl->bus_avail;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
// Skip forward to the next entry
p_byte += (nummem + numpmem + numio + numbus) * 8;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
p_byte += 3;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
bus = p_ev_ctrl->bus;
device = p_ev_ctrl->device;
function = p_ev_ctrl->function;
}
nummem = p_ev_ctrl->mem_avail;
numpmem = p_ev_ctrl->p_mem_avail;
numio = p_ev_ctrl->io_avail;
numbus = p_ev_ctrl->bus_avail;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length))
return 2;
while (nummem--) {
mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
break;
mem_node->base = *(u32*)p_byte;
dbg("mem base = %8.8x\n",mem_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(mem_node);
return 2;
}
mem_node->length = *(u32*)p_byte;
dbg("mem length = %8.8x\n",mem_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(mem_node);
return 2;
}
mem_node->next = ctrl->mem_head;
ctrl->mem_head = mem_node;
}
while (numpmem--) {
p_mem_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
break;
p_mem_node->base = *(u32*)p_byte;
dbg("pre-mem base = %8.8x\n",p_mem_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(p_mem_node);
return 2;
}
p_mem_node->length = *(u32*)p_byte;
dbg("pre-mem length = %8.8x\n",p_mem_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(p_mem_node);
return 2;
}
p_mem_node->next = ctrl->p_mem_head;
ctrl->p_mem_head = p_mem_node;
}
while (numio--) {
io_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
break;
io_node->base = *(u32*)p_byte;
dbg("io base = %8.8x\n",io_node->base);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(io_node);
return 2;
}
io_node->length = *(u32*)p_byte;
dbg("io length = %8.8x\n",io_node->length);
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(io_node);
return 2;
}
io_node->next = ctrl->io_head;
ctrl->io_head = io_node;
}
while (numbus--) {
bus_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
break;
bus_node->base = *(u32*)p_byte;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(bus_node);
return 2;
}
bus_node->length = *(u32*)p_byte;
p_byte += 4;
if (p_byte > ((u8*)p_EV_header + evbuffer_length)) {
kfree(bus_node);
return 2;
}
bus_node->next = ctrl->bus_head;
ctrl->bus_head = bus_node;
}
// If all of the following fail, we don't have any resources for
// hot plug add
rc = 1;
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
if (rc)
return(rc);
} else {
if ((evbuffer[0] != 0) && (!ctrl->push_flag))
return 1;
}
return 0;
}
int compaq_nvram_store (void __iomem *rom_start)
{
int rc = 1;
if (rom_start == NULL)
return -ENODEV;
if (evbuffer_init) {
rc = store_HRT(rom_start);
if (rc) {
err(msg_unable_to_save);
}
}
return rc;
}

View File

@@ -0,0 +1,57 @@
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#ifndef _CPQPHP_NVRAM_H
#define _CPQPHP_NVRAM_H
#ifndef CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
static inline void compaq_nvram_init (void __iomem *rom_start)
{
return;
}
static inline int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl)
{
return 0;
}
static inline int compaq_nvram_store (void __iomem *rom_start)
{
return 0;
}
#else
extern void compaq_nvram_init (void __iomem *rom_start);
extern int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl);
extern int compaq_nvram_store (void __iomem *rom_start);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,143 @@
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include "cpqphp.h"
/* A few routines that create sysfs entries for the hot plug controller */
static ssize_t show_ctrl (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
out += sprintf(buf, "Free resources: memory\n");
index = 11;
res = ctrl->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: prefetchable memory\n");
index = 11;
res = ctrl->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: IO\n");
index = 11;
res = ctrl->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: bus numbers\n");
index = 11;
res = ctrl->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
return out - buf;
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
static ssize_t show_dev (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
struct pci_func *new_slot;
struct slot *slot;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
slot=ctrl->slot;
while (slot) {
new_slot = cpqhp_slot_find(slot->bus, slot->device, 0);
if (!new_slot)
break;
out += sprintf(out, "assigned resources: memory\n");
index = 11;
res = new_slot->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: prefetchable memory\n");
index = 11;
res = new_slot->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: IO\n");
index = 11;
res = new_slot->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: bus numbers\n");
index = 11;
res = new_slot->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
slot=slot->next;
}
return out - buf;
}
static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
void cpqhp_create_ctrl_files (struct controller *ctrl)
{
device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
}

View File

@@ -0,0 +1,358 @@
/*
* Fake PCI Hot Plug Controller Driver
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003 IBM Corp.
* Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
*
* Based on ideas and code from:
* Vladimir Kondratiev <vladimir.kondratiev@intel.com>
* Rolf Eike Beer <eike-kernel@sf-tec.de>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Send feedback to <greg@kroah.com>
*/
/*
*
* This driver will "emulate" removing PCI devices from the system. If
* the "power" file is written to with "0" then the specified PCI device
* will be completely removed from the kernel.
*
* WARNING, this does NOT turn off the power to the PCI device. This is
* a "logical" removal, not a physical or electrical removal.
*
* Use this module at your own risk, you have been warned!
*
* Enabling PCI devices is left as an exercise for the reader...
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include "pci_hotplug.h"
#include "../pci.h"
#if !defined(MODULE)
#define MY_NAME "fakephp"
#else
#define MY_NAME THIS_MODULE->name
#endif
#define dbg(format, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: " format, \
MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
struct dummy_slot {
struct list_head node;
struct hotplug_slot *slot;
struct pci_dev *dev;
};
static int debug;
static LIST_HEAD(slot_list);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
};
static void dummy_release(struct hotplug_slot *slot)
{
struct dummy_slot *dslot = slot->private;
list_del(&dslot->node);
kfree(dslot->slot->info);
kfree(dslot->slot);
pci_dev_put(dslot->dev);
kfree(dslot);
}
static int add_slot(struct pci_dev *dev)
{
struct dummy_slot *dslot;
struct hotplug_slot *slot;
int retval = -ENOMEM;
slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot)
goto error;
memset(slot, 0, sizeof(*slot));
slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
if (!slot->info)
goto error_slot;
memset(slot->info, 0, sizeof(struct hotplug_slot_info));
slot->info->power_status = 1;
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
slot->name = &dev->dev.bus_id[0];
dbg("slot->name = %s\n", slot->name);
dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;
slot->ops = &dummy_hotplug_slot_ops;
slot->release = &dummy_release;
slot->private = dslot;
retval = pci_hp_register(slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
}
dslot->slot = slot;
dslot->dev = pci_dev_get(dev);
list_add (&dslot->node, &slot_list);
return retval;
error_dslot:
kfree(dslot);
error_info:
kfree(slot->info);
error_slot:
kfree(slot);
error:
return retval;
}
static int __init pci_scan_buses(void)
{
struct pci_dev *dev = NULL;
int retval = 0;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
retval = add_slot(dev);
if (retval) {
pci_dev_put(dev);
break;
}
}
return retval;
}
static void remove_slot(struct dummy_slot *dslot)
{
int retval;
dbg("removing slot %s\n", dslot->slot->name);
retval = pci_hp_deregister(dslot->slot);
if (retval)
err("Problem unregistering a slot %s\n", dslot->slot->name);
}
/**
* Rescan slot.
* Tries hard not to re-enable already existing devices
* also handles scanning of subfunctions
*
* @param temp Device template. Should be set: bus and devfn.
*/
static void pci_rescan_slot(struct pci_dev *temp)
{
struct pci_bus *bus = temp->bus;
struct pci_dev *dev;
int func;
u8 hdr_type;
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
temp->hdr_type = hdr_type & 0x7f;
if (!pci_find_slot(bus->number, temp->devfn)) {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
pci_bus_add_device(dev);
add_slot(dev);
}
}
/* multifunction device? */
if (!(hdr_type & 0x80))
return;
/* continue scanning for other functions */
for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
continue;
temp->hdr_type = hdr_type & 0x7f;
if (!pci_find_slot(bus->number, temp->devfn)) {
dev = pci_scan_single_device(bus, temp->devfn);
if (dev) {
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
pci_bus_add_device(dev);
add_slot(dev);
}
}
}
}
}
/**
* Rescan PCI bus.
* call pci_rescan_slot for each possible function of the bus
*
* @param bus
*/
static void pci_rescan_bus(const struct pci_bus *bus)
{
unsigned int devfn;
struct pci_dev *dev;
dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
return;
memset(dev, 0, sizeof(dev));
dev->bus = (struct pci_bus*)bus;
dev->sysdata = bus->sysdata;
for (devfn = 0; devfn < 0x100; devfn += 8) {
dev->devfn = devfn;
pci_rescan_slot(dev);
}
kfree(dev);
}
/* recursively scan all buses */
static void pci_rescan_buses(const struct list_head *list)
{
const struct list_head *l;
list_for_each(l,list) {
const struct pci_bus *b = pci_bus_b(l);
pci_rescan_bus(b);
pci_rescan_buses(&b->children);
}
}
/* initiate rescan of all pci buses */
static inline void pci_rescan(void) {
pci_rescan_buses(&pci_root_buses);
}
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
/* mis-use enable_slot for rescanning of the pci bus */
pci_rescan();
return -ENODEV;
}
/* find the hotplug_slot for the pci_dev */
static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
{
struct dummy_slot *dslot;
list_for_each_entry(dslot, &slot_list, node) {
if (dslot->dev == dev)
return dslot->slot;
}
return NULL;
}
static int disable_slot(struct hotplug_slot *slot)
{
struct dummy_slot *dslot;
struct hotplug_slot *hslot;
struct pci_dev *dev;
int func;
if (!slot)
return -ENODEV;
dslot = slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
/* don't disable bridged devices just yet, we can't handle them easily... */
if (dslot->dev->subordinate) {
err("Can't remove PCI devices with other PCI devices behind it yet.\n");
return -ENODEV;
}
/* search for subfunctions and disable them first */
if (!(dslot->dev->devfn & 7)) {
for (func = 1; func < 8; func++) {
dev = pci_find_slot(dslot->dev->bus->number,
dslot->dev->devfn + func);
if (dev) {
hslot = get_slot_from_dev(dev);
if (hslot)
disable_slot(hslot);
else {
err("Hotplug slot not found for subfunction of PCI device\n");
return -ENODEV;
}
} else
dbg("No device in slot found\n");
}
}
/* remove the device from the pci core */
pci_remove_bus_device(dslot->dev);
/* blow away this sysfs entry and other parts. */
remove_slot(dslot);
return 0;
}
static void cleanup_slots (void)
{
struct list_head *tmp;
struct list_head *next;
struct dummy_slot *dslot;
list_for_each_safe (tmp, next, &slot_list) {
dslot = list_entry (tmp, struct dummy_slot, node);
remove_slot(dslot);
}
}
static int __init dummyphp_init(void)
{
info(DRIVER_DESC "\n");
return pci_scan_buses();
}
static void __exit dummyphp_exit(void)
{
cleanup_slots();
}
module_init(dummyphp_init);
module_exit(dummyphp_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

View File

@@ -0,0 +1,763 @@
#ifndef __IBMPHP_H
#define __IBMPHP_H
/*
* IBM Hot Plug Controller Driver
*
* Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
*
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001-2003 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <gregkh@us.ibm.com>
*
*/
#include "pci_hotplug.h"
extern int ibmphp_debug;
#if !defined(MODULE)
#define MY_NAME "ibmphpd"
#else
#define MY_NAME THIS_MODULE->name
#endif
#define debug(fmt, arg...) do { if (ibmphp_debug == 1) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define debug_pci(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
/* EBDA stuff */
/***********************************************************
* SLOT CAPABILITY *
***********************************************************/
#define EBDA_SLOT_133_MAX 0x20
#define EBDA_SLOT_100_MAX 0x10
#define EBDA_SLOT_66_MAX 0x02
#define EBDA_SLOT_PCIX_CAP 0x08
/************************************************************
* RESOURE TYPE *
************************************************************/
#define EBDA_RSRC_TYPE_MASK 0x03
#define EBDA_IO_RSRC_TYPE 0x00
#define EBDA_MEM_RSRC_TYPE 0x01
#define EBDA_PFM_RSRC_TYPE 0x03
#define EBDA_RES_RSRC_TYPE 0x02
/*************************************************************
* IO RESTRICTION TYPE *
*************************************************************/
#define EBDA_IO_RESTRI_MASK 0x0c
#define EBDA_NO_RESTRI 0x00
#define EBDA_AVO_VGA_ADDR 0x04
#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08
#define EBDA_AVO_ISA_ADDR 0x0c
/**************************************************************
* DEVICE TYPE DEF *
**************************************************************/
#define EBDA_DEV_TYPE_MASK 0x10
#define EBDA_PCI_DEV 0x10
#define EBDA_NON_PCI_DEV 0x00
/***************************************************************
* PRIMARY DEF DEFINITION *
***************************************************************/
#define EBDA_PRI_DEF_MASK 0x20
#define EBDA_PRI_PCI_BUS_INFO 0x20
#define EBDA_NORM_DEV_RSRC_INFO 0x00
//--------------------------------------------------------------
// RIO TABLE DATA STRUCTURE
//--------------------------------------------------------------
struct rio_table_hdr {
u8 ver_num;
u8 scal_count;
u8 riodev_count;
u16 offset;
};
//-------------------------------------------------------------
// SCALABILITY DETAIL
//-------------------------------------------------------------
struct scal_detail {
u8 node_id;
u32 cbar;
u8 port0_node_connect;
u8 port0_port_connect;
u8 port1_node_connect;
u8 port1_port_connect;
u8 port2_node_connect;
u8 port2_port_connect;
u8 chassis_num;
// struct list_head scal_detail_list;
};
//--------------------------------------------------------------
// RIO DETAIL
//--------------------------------------------------------------
struct rio_detail {
u8 rio_node_id;
u32 bbar;
u8 rio_type;
u8 owner_id;
u8 port0_node_connect;
u8 port0_port_connect;
u8 port1_node_connect;
u8 port1_port_connect;
u8 first_slot_num;
u8 status;
u8 wpindex;
u8 chassis_num;
struct list_head rio_detail_list;
};
struct opt_rio {
u8 rio_type;
u8 chassis_num;
u8 first_slot_num;
u8 middle_num;
struct list_head opt_rio_list;
};
struct opt_rio_lo {
u8 rio_type;
u8 chassis_num;
u8 first_slot_num;
u8 middle_num;
u8 pack_count;
struct list_head opt_rio_lo_list;
};
/****************************************************************
* HPC DESCRIPTOR NODE *
****************************************************************/
struct ebda_hpc_list {
u8 format;
u16 num_ctlrs;
short phys_addr;
// struct list_head ebda_hpc_list;
};
/*****************************************************************
* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS *
* STRUCTURE *
*****************************************************************/
struct ebda_hpc_slot {
u8 slot_num;
u32 slot_bus_num;
u8 ctl_index;
u8 slot_cap;
};
struct ebda_hpc_bus {
u32 bus_num;
u8 slots_at_33_conv;
u8 slots_at_66_conv;
u8 slots_at_66_pcix;
u8 slots_at_100_pcix;
u8 slots_at_133_pcix;
};
/********************************************************************
* THREE TYPE OF HOT PLUG CONTROLER *
********************************************************************/
struct isa_ctlr_access {
u16 io_start;
u16 io_end;
};
struct pci_ctlr_access {
u8 bus;
u8 dev_fun;
};
struct wpeg_i2c_ctlr_access {
ulong wpegbbar;
u8 i2c_addr;
};
#define HPC_DEVICE_ID 0x0246
#define HPC_SUBSYSTEM_ID 0x0247
#define HPC_PCI_OFFSET 0x40
/*************************************************************************
* RSTC DESCRIPTOR NODE *
*************************************************************************/
struct ebda_rsrc_list {
u8 format;
u16 num_entries;
u16 phys_addr;
struct ebda_rsrc_list *next;
};
/***************************************************************************
* PCI RSRC NODE *
***************************************************************************/
struct ebda_pci_rsrc {
u8 rsrc_type;
u8 bus_num;
u8 dev_fun;
u32 start_addr;
u32 end_addr;
u8 marked; /* for NVRAM */
struct list_head ebda_pci_rsrc_list;
};
/***********************************************************
* BUS_INFO DATE STRUCTURE *
***********************************************************/
struct bus_info {
u8 slot_min;
u8 slot_max;
u8 slot_count;
u8 busno;
u8 controller_id;
u8 current_speed;
u8 current_bus_mode;
u8 index;
u8 slots_at_33_conv;
u8 slots_at_66_conv;
u8 slots_at_66_pcix;
u8 slots_at_100_pcix;
u8 slots_at_133_pcix;
struct list_head bus_info_list;
};
/***********************************************************
* GLOBAL VARIABLES *
***********************************************************/
extern struct list_head ibmphp_ebda_pci_rsrc_head;
extern struct list_head ibmphp_slot_head;
/***********************************************************
* FUNCTION PROTOTYPES *
***********************************************************/
extern void ibmphp_free_ebda_hpc_queue (void);
extern int ibmphp_access_ebda (void);
extern struct slot *ibmphp_get_slot_from_physical_num (u8);
extern int ibmphp_get_total_hp_slots (void);
extern void ibmphp_free_ibm_slot (struct slot *);
extern void ibmphp_free_bus_info_queue (void);
extern void ibmphp_free_ebda_pci_rsrc_queue (void);
extern struct bus_info *ibmphp_find_same_bus_num (u32);
extern int ibmphp_get_bus_index (u8);
extern u16 ibmphp_get_total_controllers (void);
extern int ibmphp_register_pci (void);
/* passed parameters */
#define MEM 0
#define IO 1
#define PFMEM 2
/* bit masks */
#define RESTYPE 0x03
#define IOMASK 0x00 /* will need to take its complement */
#define MMASK 0x01
#define PFMASK 0x03
#define PCIDEVMASK 0x10 /* we should always have PCI devices */
#define PRIMARYBUSMASK 0x20
/* pci specific defines */
#define PCI_VENDOR_ID_NOTVALID 0xFFFF
#define PCI_HEADER_TYPE_MULTIDEVICE 0x80
#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81
#define LATENCY 0x64
#define CACHE 64
#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */
#define IOBRIDGE 0x1000 /* 4k */
#define MEMBRIDGE 0x100000 /* 1M */
/* irqs */
#define SCSI_IRQ 0x09
#define LAN_IRQ 0x0A
#define OTHER_IRQ 0x0B
/* Data Structures */
/* type is of the form x x xx xx
* | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory
* | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid
* | | VGA and their aliases, 11 - Avoid ISA
* | - 1 - PCI device, 0 - non pci device
* - 1 - Primary PCI Bus Information (0 if Normal device)
* the IO restrictions [2:3] are only for primary buses
*/
/* we need this struct because there could be several resource blocks
* allocated per primary bus in the EBDA
*/
struct range_node {
int rangeno;
u32 start;
u32 end;
struct range_node *next;
};
struct bus_node {
u8 busno;
int noIORanges;
struct range_node *rangeIO;
int noMemRanges;
struct range_node *rangeMem;
int noPFMemRanges;
struct range_node *rangePFMem;
int needIOUpdate;
int needMemUpdate;
int needPFMemUpdate;
struct resource_node *firstIO; /* first IO resource on the Bus */
struct resource_node *firstMem; /* first memory resource on the Bus */
struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */
struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */
struct list_head bus_list;
};
struct resource_node {
int rangeno;
u8 busno;
u8 devfunc;
u32 start;
u32 end;
u32 len;
int type; /* MEM, IO, PFMEM */
u8 fromMem; /* this is to indicate that the range is from
* from the Memory bucket rather than from PFMem */
struct resource_node *next;
struct resource_node *nextRange; /* for the other mem range on bus */
};
struct res_needed {
u32 mem;
u32 pfmem;
u32 io;
u8 not_correct; /* needed for return */
int devices[32]; /* for device numbers behind this bridge */
};
/* functions */
extern int ibmphp_rsrc_init (void);
extern int ibmphp_add_resource (struct resource_node *);
extern int ibmphp_remove_resource (struct resource_node *);
extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int);
extern int ibmphp_check_resource (struct resource_node *, u8);
extern int ibmphp_remove_bus (struct bus_node *, u8);
extern void ibmphp_free_resources (void);
extern int ibmphp_add_pfmem_from_mem (struct resource_node *);
extern struct bus_node *ibmphp_find_res_bus (u8);
extern void ibmphp_print_test (void); /* for debugging purposes */
extern void ibmphp_hpc_initvars (void);
extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *);
extern int ibmphp_hpc_writeslot (struct slot *, u8);
extern void ibmphp_lock_operations (void);
extern void ibmphp_unlock_operations (void);
extern int ibmphp_hpc_start_poll_thread (void);
extern void ibmphp_hpc_stop_poll_thread (void);
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// HPC return codes
//----------------------------------------------------------------------------
#define FALSE 0x00
#define TRUE 0x01
#define HPC_ERROR 0xFF
//-----------------------------------------------------------------------------
// BUS INFO
//-----------------------------------------------------------------------------
#define BUS_SPEED 0x30
#define BUS_MODE 0x40
#define BUS_MODE_PCIX 0x01
#define BUS_MODE_PCI 0x00
#define BUS_SPEED_2 0x20
#define BUS_SPEED_1 0x10
#define BUS_SPEED_33 0x00
#define BUS_SPEED_66 0x01
#define BUS_SPEED_100 0x02
#define BUS_SPEED_133 0x03
#define BUS_SPEED_66PCIX 0x04
#define BUS_SPEED_66UNKNOWN 0x05
#define BUS_STATUS_AVAILABLE 0x01
#define BUS_CONTROL_AVAILABLE 0x02
#define SLOT_LATCH_REGS_SUPPORTED 0x10
#define PRGM_MODEL_REV_LEVEL 0xF0
#define MAX_ADAPTER_NONE 0x09
//----------------------------------------------------------------------------
// HPC 'write' operations/commands
//----------------------------------------------------------------------------
// Command Code State Write to reg
// Machine at index
//------------------------- ---- ------- ------------
#define HPC_CTLR_ENABLEIRQ 0x00 // N 15
#define HPC_CTLR_DISABLEIRQ 0x01 // N 15
#define HPC_SLOT_OFF 0x02 // Y 0-14
#define HPC_SLOT_ON 0x03 // Y 0-14
#define HPC_SLOT_ATTNOFF 0x04 // N 0-14
#define HPC_SLOT_ATTNON 0x05 // N 0-14
#define HPC_CTLR_CLEARIRQ 0x06 // N 15
#define HPC_CTLR_RESET 0x07 // Y 15
#define HPC_CTLR_IRQSTEER 0x08 // N 15
#define HPC_BUS_33CONVMODE 0x09 // Y 31-34
#define HPC_BUS_66CONVMODE 0x0A // Y 31-34
#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34
#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34
#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34
#define HPC_ALLSLOT_OFF 0x11 // Y 15
#define HPC_ALLSLOT_ON 0x12 // Y 15
#define HPC_SLOT_BLINKLED 0x13 // N 0-14
//----------------------------------------------------------------------------
// read commands
//----------------------------------------------------------------------------
#define READ_SLOTSTATUS 0x01
#define READ_EXTSLOTSTATUS 0x02
#define READ_BUSSTATUS 0x03
#define READ_CTLRSTATUS 0x04
#define READ_ALLSTAT 0x05
#define READ_ALLSLOT 0x06
#define READ_SLOTLATCHLOWREG 0x07
#define READ_REVLEVEL 0x08
#define READ_HPCOPTIONS 0x09
//----------------------------------------------------------------------------
// slot status
//----------------------------------------------------------------------------
#define HPC_SLOT_POWER 0x01
#define HPC_SLOT_CONNECT 0x02
#define HPC_SLOT_ATTN 0x04
#define HPC_SLOT_PRSNT2 0x08
#define HPC_SLOT_PRSNT1 0x10
#define HPC_SLOT_PWRGD 0x20
#define HPC_SLOT_BUS_SPEED 0x40
#define HPC_SLOT_LATCH 0x80
//----------------------------------------------------------------------------
// HPC_SLOT_POWER status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_POWER_OFF 0x00
#define HPC_SLOT_POWER_ON 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_CONNECT status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_CONNECTED 0x00
#define HPC_SLOT_DISCONNECTED 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_ATTN status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_ATTN_OFF 0x00
#define HPC_SLOT_ATTN_ON 0x01
#define HPC_SLOT_ATTN_BLINK 0x02
//----------------------------------------------------------------------------
// HPC_SLOT_PRSNT status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_EMPTY 0x00
#define HPC_SLOT_PRSNT_7 0x01
#define HPC_SLOT_PRSNT_15 0x02
#define HPC_SLOT_PRSNT_25 0x03
//----------------------------------------------------------------------------
// HPC_SLOT_PWRGD status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_PWRGD_FAULT_NONE 0x00
#define HPC_SLOT_PWRGD_GOOD 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_BUS_SPEED status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_BUS_SPEED_OK 0x00
#define HPC_SLOT_BUS_SPEED_MISM 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_LATCH status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open
#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed
//----------------------------------------------------------------------------
// extended slot status
//----------------------------------------------------------------------------
#define HPC_SLOT_PCIX 0x01
#define HPC_SLOT_SPEED1 0x02
#define HPC_SLOT_SPEED2 0x04
#define HPC_SLOT_BLINK_ATTN 0x08
#define HPC_SLOT_RSRVD1 0x10
#define HPC_SLOT_RSRVD2 0x20
#define HPC_SLOT_BUS_MODE 0x40
#define HPC_SLOT_RSRVD3 0x80
//----------------------------------------------------------------------------
// HPC_XSLOT_PCIX_CAP status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_PCIX_NO 0x00
#define HPC_SLOT_PCIX_YES 0x01
//----------------------------------------------------------------------------
// HPC_XSLOT_SPEED status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_SPEED_33 0x00
#define HPC_SLOT_SPEED_66 0x01
#define HPC_SLOT_SPEED_133 0x02
//----------------------------------------------------------------------------
// HPC_XSLOT_ATTN_BLINK status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_ATTN_BLINK_OFF 0x00
#define HPC_SLOT_ATTN_BLINK_ON 0x01
//----------------------------------------------------------------------------
// HPC_XSLOT_BUS_MODE status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_BUS_MODE_OK 0x00
#define HPC_SLOT_BUS_MODE_MISM 0x01
//----------------------------------------------------------------------------
// Controller status
//----------------------------------------------------------------------------
#define HPC_CTLR_WORKING 0x01
#define HPC_CTLR_FINISHED 0x02
#define HPC_CTLR_RESULT0 0x04
#define HPC_CTLR_RESULT1 0x08
#define HPC_CTLR_RESULE2 0x10
#define HPC_CTLR_RESULT3 0x20
#define HPC_CTLR_IRQ_ROUTG 0x40
#define HPC_CTLR_IRQ_PENDG 0x80
//----------------------------------------------------------------------------
// HPC_CTLR_WROKING status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_WORKING_NO 0x00
#define HPC_CTLR_WORKING_YES 0x01
//----------------------------------------------------------------------------
// HPC_CTLR_FINISHED status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_FINISHED_NO 0x00
#define HPC_CTLR_FINISHED_YES 0x01
//----------------------------------------------------------------------------
// HPC_CTLR_RESULT status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_RESULT_SUCCESS 0x00
#define HPC_CTLR_RESULT_FAILED 0x01
#define HPC_CTLR_RESULT_RSVD 0x02
#define HPC_CTLR_RESULT_NORESP 0x03
//----------------------------------------------------------------------------
// macro for slot info
//----------------------------------------------------------------------------
#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \
? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF))
#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \
? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
? HPC_SLOT_ATTN_BLINK \
: ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \
? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \
: ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7)))
#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \
? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE))
#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \
? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK))
#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \
? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN))
#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \
? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO))
#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \
? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \
: HPC_SLOT_SPEED_66) \
: HPC_SLOT_SPEED_33))
#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \
? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK))
//--------------------------------------------------------------------------
// macro for bus info
//---------------------------------------------------------------------------
#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \
? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \
: ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33))
#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI)
#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE))
#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20)
#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE))
#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED))
//----------------------------------------------------------------------------
// macro for controller info
//----------------------------------------------------------------------------
#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \
? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO))
#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \
? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO))
#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \
? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \
: HPC_CTLR_RESULT_RSVD) \
: ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \
: HPC_CTLR_RESULT_SUCCESS)))
// command that affect the state machine of HPC
#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \
(c == HPC_SLOT_ON) || \
(c == HPC_CTLR_RESET) || \
(c == HPC_BUS_33CONVMODE) || \
(c == HPC_BUS_66CONVMODE) || \
(c == HPC_BUS_66PCIXMODE) || \
(c == HPC_BUS_100PCIXMODE) || \
(c == HPC_BUS_133PCIXMODE) || \
(c == HPC_ALLSLOT_OFF) || \
(c == HPC_ALLSLOT_ON))
/* Core part of the driver */
#define ENABLE 1
#define DISABLE 0
#define CARD_INFO 0x07
#define PCIX133 0x07
#define PCIX66 0x05
#define PCI66 0x04
extern struct pci_bus *ibmphp_pci_bus;
/* Variables */
struct pci_func {
struct pci_dev *dev; /* from the OS */
u8 busno;
u8 device;
u8 function;
struct resource_node *io[6];
struct resource_node *mem[6];
struct resource_node *pfmem[6];
struct pci_func *next;
int devices[32]; /* for bridge config */
u8 irq[4]; /* for interrupt config */
u8 bus; /* flag for unconfiguring, to say if PPB */
};
struct slot {
u8 bus;
u8 device;
u8 number;
u8 real_physical_slot_num;
char name[100];
u32 capabilities;
u8 supported_speed;
u8 supported_bus_mode;
struct hotplug_slot *hotplug_slot;
struct controller *ctrl;
struct pci_func *func;
u8 irq[4];
u8 flag; /* this is for disable slot and polling */
int bit_mode; /* 0 = 32, 1 = 64 */
u8 ctlr_index;
struct bus_info *bus_on;
struct list_head ibm_slot_list;
u8 status;
u8 ext_status;
u8 busstatus;
};
struct controller {
struct ebda_hpc_slot *slots;
struct ebda_hpc_bus *buses;
struct pci_dev *ctrl_dev; /* in case where controller is PCI */
u8 starting_slot_num; /* starting and ending slot #'s this ctrl controls*/
u8 ending_slot_num;
u8 revision;
u8 options; /* which options HPC supports */
u8 status;
u8 ctlr_id;
u8 slot_count;
u8 bus_count;
u8 ctlr_relative_id;
u32 irq;
union {
struct isa_ctlr_access isa_ctlr;
struct pci_ctlr_access pci_ctlr;
struct wpeg_i2c_ctlr_access wpeg_ctlr;
} u;
u8 ctlr_type;
struct list_head ebda_hpc_list;
};
/* Functions */
extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */
extern int ibmphp_do_disable_slot (struct slot *slot_cur);
extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */
extern int ibmphp_configure_card (struct pci_func *, u8);
extern int ibmphp_unconfigure_card (struct slot **, int);
extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
#endif //__IBMPHP_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
/*
* PCI HotPlug Core Functions
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#ifndef _PCI_HOTPLUG_H
#define _PCI_HOTPLUG_H
/* These values come from the PCI Hotplug Spec */
enum pci_bus_speed {
PCI_SPEED_33MHz = 0x00,
PCI_SPEED_66MHz = 0x01,
PCI_SPEED_66MHz_PCIX = 0x02,
PCI_SPEED_100MHz_PCIX = 0x03,
PCI_SPEED_133MHz_PCIX = 0x04,
PCI_SPEED_66MHz_PCIX_ECC = 0x05,
PCI_SPEED_100MHz_PCIX_ECC = 0x06,
PCI_SPEED_133MHz_PCIX_ECC = 0x07,
PCI_SPEED_66MHz_PCIX_266 = 0x09,
PCI_SPEED_100MHz_PCIX_266 = 0x0a,
PCI_SPEED_133MHz_PCIX_266 = 0x0b,
PCI_SPEED_66MHz_PCIX_533 = 0x11,
PCI_SPEED_100MHz_PCIX_533 = 0x12,
PCI_SPEED_133MHz_PCIX_533 = 0x13,
PCI_SPEED_UNKNOWN = 0xff,
};
/* These values come from the PCI Express Spec */
enum pcie_link_width {
PCIE_LNK_WIDTH_RESRV = 0x00,
PCIE_LNK_X1 = 0x01,
PCIE_LNK_X2 = 0x02,
PCIE_LNK_X4 = 0x04,
PCIE_LNK_X8 = 0x08,
PCIE_LNK_X12 = 0x0C,
PCIE_LNK_X16 = 0x10,
PCIE_LNK_X32 = 0x20,
PCIE_LNK_WIDTH_UNKNOWN = 0xFF,
};
enum pcie_link_speed {
PCIE_2PT5GB = 0x14,
PCIE_LNK_SPEED_UNKNOWN = 0xFF,
};
struct hotplug_slot;
struct hotplug_slot_attribute {
struct attribute attr;
ssize_t (*show)(struct hotplug_slot *, char *);
ssize_t (*store)(struct hotplug_slot *, const char *, size_t);
};
#define to_hotplug_attr(n) container_of(n, struct hotplug_slot_attribute, attr);
/**
* struct hotplug_slot_ops -the callbacks that the hotplug pci core can use
* @owner: The module owner of this structure
* @enable_slot: Called when the user wants to enable a specific pci slot
* @disable_slot: Called when the user wants to disable a specific pci slot
* @set_attention_status: Called to set the specific slot's attention LED to
* the specified value
* @hardware_test: Called to run a specified hardware test on the specified
* slot.
* @get_power_status: Called to get the current power status of a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_attention_status: Called to get the current attention status of a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_latch_status: Called to get the current latch status of a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_address: Called to get pci address of a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_max_bus_speed: Called to get the max bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
* @get_cur_bus_speed: Called to get the current bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
*
* The table of function pointers that is passed to the hotplug pci core by a
* hotplug pci driver. These functions are called by the hotplug pci core when
* the user wants to do something to a specific slot (query it for information,
* set an LED, enable / disable power, etc.)
*/
struct hotplug_slot_ops {
struct module *owner;
int (*enable_slot) (struct hotplug_slot *slot);
int (*disable_slot) (struct hotplug_slot *slot);
int (*set_attention_status) (struct hotplug_slot *slot, u8 value);
int (*hardware_test) (struct hotplug_slot *slot, u32 value);
int (*get_power_status) (struct hotplug_slot *slot, u8 *value);
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
int (*get_address) (struct hotplug_slot *slot, u32 *value);
int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
};
/**
* struct hotplug_slot_info - used to notify the hotplug pci core of the state of the slot
* @power: if power is enabled or not (1/0)
* @attention_status: if the attention light is enabled or not (1/0)
* @latch_status: if the latch (if any) is open or closed (1/0)
* @adapter_present: if there is a pci board present in the slot or not (1/0)
* @address: (domain << 16 | bus << 8 | dev)
*
* Used to notify the hotplug pci core of the status of a specific slot.
*/
struct hotplug_slot_info {
u8 power_status;
u8 attention_status;
u8 latch_status;
u8 adapter_status;
u32 address;
enum pci_bus_speed max_bus_speed;
enum pci_bus_speed cur_bus_speed;
};
/**
* struct hotplug_slot - used to register a physical slot with the hotplug pci core
* @name: the name of the slot being registered. This string must
* be unique amoung slots registered on this system.
* @ops: pointer to the &struct hotplug_slot_ops to be used for this slot
* @info: pointer to the &struct hotplug_slot_info for the inital values for
* this slot.
* @release: called during pci_hp_deregister to free memory allocated in a
* hotplug_slot structure.
* @private: used by the hotplug pci controller driver to store whatever it
* needs.
*/
struct hotplug_slot {
char *name;
struct hotplug_slot_ops *ops;
struct hotplug_slot_info *info;
void (*release) (struct hotplug_slot *slot);
void *private;
/* Variables below this are for use only by the hotplug pci core. */
struct list_head slot_list;
struct kobject kobj;
};
#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)
extern int pci_hp_register (struct hotplug_slot *slot);
extern int pci_hp_deregister (struct hotplug_slot *slot);
extern int pci_hp_change_slot_info (struct hotplug_slot *slot,
struct hotplug_slot_info *info);
extern struct subsystem pci_hotplug_slots_subsys;
#endif

View File

@@ -0,0 +1,715 @@
/*
* PCI HotPlug Controller Core
*
* Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001-2002 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
* Filesystem portion based on work done by Pat Mochel on ddfs/driverfs
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include "pci_hotplug.h"
#define MY_NAME "pci_hotplug"
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
/* local variables */
static int debug;
#define DRIVER_VERSION "0.5"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC "PCI Hot Plug PCI Core"
//////////////////////////////////////////////////////////////////
static LIST_HEAD(pci_hotplug_slot_list);
struct subsystem pci_hotplug_slots_subsys;
static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct hotplug_slot *slot = to_hotplug_slot(kobj);
struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
return attribute->show ? attribute->show(slot, buf) : 0;
}
static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
struct attribute *attr, const char *buf, size_t len)
{
struct hotplug_slot *slot = to_hotplug_slot(kobj);
struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
return attribute->store ? attribute->store(slot, buf, len) : 0;
}
static struct sysfs_ops hotplug_slot_sysfs_ops = {
.show = hotplug_slot_attr_show,
.store = hotplug_slot_attr_store,
};
static void hotplug_slot_release(struct kobject *kobj)
{
struct hotplug_slot *slot = to_hotplug_slot(kobj);
if (slot->release)
slot->release(slot);
}
static struct kobj_type hotplug_slot_ktype = {
.sysfs_ops = &hotplug_slot_sysfs_ops,
.release = &hotplug_slot_release,
};
decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
"33 MHz PCI", /* 0x00 */
"66 MHz PCI", /* 0x01 */
"66 MHz PCIX", /* 0x02 */
"100 MHz PCIX", /* 0x03 */
"133 MHz PCIX", /* 0x04 */
NULL, /* 0x05 */
NULL, /* 0x06 */
NULL, /* 0x07 */
NULL, /* 0x08 */
"66 MHz PCIX 266", /* 0x09 */
"100 MHz PCIX 266", /* 0x0a */
"133 MHz PCIX 266", /* 0x0b */
NULL, /* 0x0c */
NULL, /* 0x0d */
NULL, /* 0x0e */
NULL, /* 0x0f */
NULL, /* 0x10 */
"66 MHz PCIX 533", /* 0x11 */
"100 MHz PCIX 533", /* 0x12 */
"133 MHz PCIX 533", /* 0x13 */
"25 GBps PCI-E", /* 0x14 */
};
#ifdef CONFIG_HOTPLUG_PCI_CPCI
extern int cpci_hotplug_init(int debug);
extern void cpci_hotplug_exit(void);
#else
static inline int cpci_hotplug_init(int debug) { return 0; }
static inline void cpci_hotplug_exit(void) { }
#endif
/* Weee, fun with macros... */
#define GET_STATUS(name,type) \
static int get_##name (struct hotplug_slot *slot, type *value) \
{ \
struct hotplug_slot_ops *ops = slot->ops; \
int retval = 0; \
if (try_module_get(ops->owner)) { \
if (ops->get_##name) \
retval = ops->get_##name (slot, value); \
else \
*value = slot->info->name; \
module_put(ops->owner); \
} \
return retval; \
}
GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)
static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
{
int retval;
u8 value;
retval = get_power_status (slot, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
exit:
return retval;
}
static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
size_t count)
{
unsigned long lpower;
u8 power;
int retval = 0;
lpower = simple_strtoul (buf, NULL, 10);
power = (u8)(lpower & 0xff);
dbg ("power = %d\n", power);
if (!try_module_get(slot->ops->owner)) {
retval = -ENODEV;
goto exit;
}
switch (power) {
case 0:
if (slot->ops->disable_slot)
retval = slot->ops->disable_slot(slot);
break;
case 1:
if (slot->ops->enable_slot)
retval = slot->ops->enable_slot(slot);
break;
default:
err ("Illegal value specified for power\n");
retval = -EINVAL;
}
module_put(slot->ops->owner);
exit:
if (retval)
return retval;
return count;
}
static struct hotplug_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};
static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
{
int retval;
u8 value;
retval = get_attention_status (slot, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
exit:
return retval;
}
static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
size_t count)
{
unsigned long lattention;
u8 attention;
int retval = 0;
lattention = simple_strtoul (buf, NULL, 10);
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);
if (!try_module_get(slot->ops->owner)) {
retval = -ENODEV;
goto exit;
}
if (slot->ops->set_attention_status)
retval = slot->ops->set_attention_status(slot, attention);
module_put(slot->ops->owner);
exit:
if (retval)
return retval;
return count;
}
static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};
static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
{
int retval;
u8 value;
retval = get_latch_status (slot, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
exit:
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};
static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
{
int retval;
u8 value;
retval = get_adapter_status (slot, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
exit:
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};
static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
{
int retval;
u32 address;
retval = get_address (slot, &address);
if (retval)
goto exit;
retval = sprintf (buf, "%04x:%02x:%02x\n",
(address >> 16) & 0xffff,
(address >> 8) & 0xff,
address & 0xff);
exit:
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_address = {
.attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
.show = address_read_file,
};
static char *unknown_speed = "Unknown bus speed";
static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
retval = get_max_bus_speed (slot, &value);
if (retval)
goto exit;
if (value == PCI_SPEED_UNKNOWN)
speed_string = unknown_speed;
else
speed_string = pci_bus_speed_strings[value];
retval = sprintf (buf, "%s\n", speed_string);
exit:
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};
static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
retval = get_cur_bus_speed (slot, &value);
if (retval)
goto exit;
if (value == PCI_SPEED_UNKNOWN)
speed_string = unknown_speed;
else
speed_string = pci_bus_speed_strings[value];
retval = sprintf (buf, "%s\n", speed_string);
exit:
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};
static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
size_t count)
{
unsigned long ltest;
u32 test;
int retval = 0;
ltest = simple_strtoul (buf, NULL, 10);
test = (u32)(ltest & 0xffffffff);
dbg ("test = %d\n", test);
if (!try_module_get(slot->ops->owner)) {
retval = -ENODEV;
goto exit;
}
if (slot->ops->hardware_test)
retval = slot->ops->hardware_test(slot, test);
module_put(slot->ops->owner);
exit:
if (retval)
return retval;
return count;
}
static struct hotplug_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};
static int has_power_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
(slot->ops->disable_slot) ||
(slot->ops->get_power_status))
return 0;
return -ENOENT;
}
static int has_attention_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
(slot->ops->get_attention_status))
return 0;
return -ENOENT;
}
static int has_latch_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
return 0;
return -ENOENT;
}
static int has_adapter_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
return 0;
return -ENOENT;
}
static int has_address_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_address)
return 0;
return -ENOENT;
}
static int has_max_bus_speed_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
return 0;
return -ENOENT;
}
static int has_cur_bus_speed_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
return 0;
return -ENOENT;
}
static int has_test_file (struct hotplug_slot *slot)
{
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
return 0;
return -ENOENT;
}
static int fs_add_slot (struct hotplug_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
if (has_attention_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
if (has_latch_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
if (has_adapter_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
if (has_address_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr);
if (has_max_bus_speed_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
if (has_cur_bus_speed_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
if (has_test_file(slot) == 0)
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr);
return 0;
}
static void fs_remove_slot (struct hotplug_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
if (has_attention_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
if (has_latch_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
if (has_address_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
if (has_cur_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
if (has_test_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
}
static struct hotplug_slot *get_slot_from_name (const char *name)
{
struct hotplug_slot *slot;
struct list_head *tmp;
list_for_each (tmp, &pci_hotplug_slot_list) {
slot = list_entry (tmp, struct hotplug_slot, slot_list);
if (strcmp(slot->name, name) == 0)
return slot;
}
return NULL;
}
/**
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
* @slot: pointer to the &struct hotplug_slot to register
*
* Registers a hotplug slot with the pci hotplug subsystem, which will allow
* userspace interaction to the slot.
*
* Returns 0 if successful, anything else for an error.
*/
int pci_hp_register (struct hotplug_slot *slot)
{
int result;
if (slot == NULL)
return -ENODEV;
if ((slot->info == NULL) || (slot->ops == NULL))
return -EINVAL;
if (slot->release == NULL) {
dbg("Why are you trying to register a hotplug slot"
"without a proper release function?\n");
return -EINVAL;
}
kobject_set_name(&slot->kobj, "%s", slot->name);
kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
/* this can fail if we have already registered a slot with the same name */
if (kobject_register(&slot->kobj)) {
err("Unable to register kobject");
return -EINVAL;
}
list_add (&slot->slot_list, &pci_hotplug_slot_list);
result = fs_add_slot (slot);
dbg ("Added slot %s to the list\n", slot->name);
return result;
}
/**
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
* @slot: pointer to the &struct hotplug_slot to deregister
*
* The @slot must have been registered with the pci hotplug subsystem
* previously with a call to pci_hp_register().
*
* Returns 0 if successful, anything else for an error.
*/
int pci_hp_deregister (struct hotplug_slot *slot)
{
struct hotplug_slot *temp;
if (slot == NULL)
return -ENODEV;
temp = get_slot_from_name (slot->name);
if (temp != slot) {
return -ENODEV;
}
list_del (&slot->slot_list);
fs_remove_slot (slot);
dbg ("Removed slot %s from the list\n", slot->name);
kobject_unregister(&slot->kobj);
return 0;
}
/**
* pci_hp_change_slot_info - changes the slot's information structure in the core
* @slot: pointer to the slot whose info has changed
* @info: pointer to the info copy into the slot's info structure
*
* @slot must have been registered with the pci
* hotplug subsystem previously with a call to pci_hp_register().
*
* Returns 0 if successful, anything else for an error.
*/
int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info)
{
if ((slot == NULL) || (info == NULL))
return -ENODEV;
/*
* check all fields in the info structure, and update timestamps
* for the files referring to the fields that have now changed.
*/
if ((has_power_file(slot) == 0) &&
(slot->info->power_status != info->power_status))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr);
if ((has_attention_file(slot) == 0) &&
(slot->info->attention_status != info->attention_status))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
if ((has_latch_file(slot) == 0) &&
(slot->info->latch_status != info->latch_status))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
if ((has_adapter_file(slot) == 0) &&
(slot->info->adapter_status != info->adapter_status))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
if ((has_address_file(slot) == 0) &&
(slot->info->address != info->address))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr);
if ((has_max_bus_speed_file(slot) == 0) &&
(slot->info->max_bus_speed != info->max_bus_speed))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
if ((has_cur_bus_speed_file(slot) == 0) &&
(slot->info->cur_bus_speed != info->cur_bus_speed))
sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
return 0;
}
static int __init pci_hotplug_init (void)
{
int result;
kset_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
result = subsystem_register(&pci_hotplug_slots_subsys);
if (result) {
err("Register subsys with error %d\n", result);
goto exit;
}
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
goto err_subsys;
}
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
goto exit;
err_subsys:
subsystem_unregister(&pci_hotplug_slots_subsys);
exit:
return result;
}
static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
subsystem_unregister(&pci_hotplug_slots_subsys);
}
module_init(pci_hotplug_init);
module_exit(pci_hotplug_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);

View File

@@ -0,0 +1,352 @@
/*
* PCI Express Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHP_H
#define _PCIEHP_H
#include <linux/types.h>
#include <linux/pci.h>
#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"
extern int pciehp_poll_mode;
extern int pciehp_poll_time;
extern int pciehp_debug;
/*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
#define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct pci_func {
struct pci_func *next;
u8 bus;
u8 device;
u8 function;
u8 is_a_board;
u16 status;
u8 configured;
u8 switch_save;
u8 presence_save;
u32 base_length[0x06];
u8 base_type[0x06];
u16 reserved2;
u32 config_space[0x20];
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev* pci_dev;
};
struct slot {
struct slot *next;
u8 bus;
u8 device;
u32 number;
u8 is_a_board;
u8 configured;
u8 state;
u8 switch_save;
u8 presence_save;
u32 capabilities;
u16 reserved2;
struct timer_list task_event;
u8 hp_slot;
struct controller *ctrl;
struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
struct pci_resource {
struct pci_resource * next;
u32 base;
u32 length;
};
struct event_info {
u32 event_type;
u8 hp_slot;
};
struct controller {
struct controller *next;
struct semaphore crit_sect; /* critical section semaphore */
void *hpc_ctlr_handle; /* HPC controller handle */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev *pci_dev;
struct pci_bus *pci_bus;
struct event_info event_queue[10];
struct slot *slot;
struct hpc_ops *hpc_ops;
wait_queue_head_t queue; /* sleep & wake process */
u8 next_event;
u8 seg;
u8 bus;
u8 device;
u8 function;
u8 rev;
u8 slot_device_offset;
u8 add_support;
enum pci_bus_speed speed;
u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
u8 slot_bus; /* Bus where the slots handled by this controller sit */
u8 ctrlcap;
u16 vendor_id;
};
struct irq_mapping {
u8 barber_pole;
u8 valid_INT;
u8 interrupt[4];
};
struct resource_lists {
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct irq_mapping *irqs;
};
#define INT_BUTTON_IGNORE 0
#define INT_PRESENCE_ON 1
#define INT_PRESENCE_OFF 2
#define INT_SWITCH_CLOSE 3
#define INT_SWITCH_OPEN 4
#define INT_POWER_FAULT 5
#define INT_POWER_FAULT_CLEAR 6
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2
#define POWERON_STATE 3
#define POWEROFF_STATE 4
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
/* Error messages */
#define INTERLOCK_OPEN 0x00000002
#define ADD_NOT_SUPPORTED 0x00000003
#define CARD_FUNCTIONING 0x00000005
#define ADAPTER_NOT_SAME 0x00000006
#define NO_ADAPTER_PRESENT 0x00000009
#define NOT_ENOUGH_RESOURCES 0x0000000B
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
#define WRONG_BUS_FREQUENCY 0x0000000D
#define POWER_FAILURE 0x0000000E
#define REMOVE_NOT_SUPPORTED 0x00000003
#define DISABLE_CARD 1
/* Field definitions in Slot Capabilities Register */
#define ATTN_BUTTN_PRSN 0x00000001
#define PWR_CTRL_PRSN 0x00000002
#define MRL_SENS_PRSN 0x00000004
#define ATTN_LED_PRSN 0x00000008
#define PWR_LED_PRSN 0x00000010
#define HP_SUPR_RM_SUP 0x00000020
#define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN)
#define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN)
#define MRL_SENS(cap) (cap & MRL_SENS_PRSN)
#define ATTN_LED(cap) (cap & ATTN_LED_PRSN)
#define PWR_LED(cap) (cap & PWR_LED_PRSN)
#define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP)
/*
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
#define msg_HPC_non_pcie "The PCI hot plug controller is not supported by this driver.\n"
#define msg_HPC_not_supported "This system is not supported by this version of pciephd module. Upgrade to a newer version of pciehpd\n"
#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
/* controller functions */
extern int pciehprm_find_available_resources (struct controller *ctrl);
extern int pciehp_event_start_thread (void);
extern void pciehp_event_stop_thread (void);
extern struct pci_func *pciehp_slot_create (unsigned char busnumber);
extern struct pci_func *pciehp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
extern int pciehp_enable_slot (struct slot *slot);
extern int pciehp_disable_slot (struct slot *slot);
extern u8 pciehp_handle_attention_button (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_switch_change (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_presence_change (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_power_fault (u8 hp_slot, void *inst_id);
/* extern void long_delay (int delay); */
/* resource functions */
extern int pciehp_resource_sort_and_combine (struct pci_resource **head);
/* pci functions */
extern int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
/*extern int pciehp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
extern int pciehp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
extern int pciehp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag);
extern int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
extern void pciehp_destroy_board_resources (struct pci_func * func);
extern int pciehp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
extern void pciehp_destroy_resource_list (struct resource_lists * resources);
extern int pciehp_configure_device (struct controller* ctrl, struct pci_func* func);
extern int pciehp_unconfigure_device (struct pci_func* func);
/* Global variables */
extern struct controller *pciehp_ctrl_list;
extern struct pci_func *pciehp_slot_list[256];
/* Inline functions */
static inline struct slot *pciehp_find_slot(struct controller *ctrl, u8 device)
{
struct slot *p_slot, *tmp_slot = NULL;
p_slot = ctrl->slot;
dbg("p_slot = %p\n", p_slot);
while (p_slot && (p_slot->device != device)) {
tmp_slot = p_slot;
p_slot = p_slot->next;
dbg("In while loop, p_slot = %p\n", p_slot);
}
if (p_slot == NULL) {
err("ERROR: pciehp_find_slot device=0x%x\n", device);
p_slot = tmp_slot;
}
return p_slot;
}
static inline int wait_for_ctrl_irq(struct controller *ctrl)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
dbg("%s : start\n", __FUNCTION__);
add_wait_queue(&ctrl->queue, &wait);
if (!pciehp_poll_mode)
/* Sleep for up to 1 second */
msleep_interruptible(1000);
else
msleep_interruptible(2500);
remove_wait_queue(&ctrl->queue, &wait);
if (signal_pending(current))
retval = -EINTR;
dbg("%s : end\n", __FUNCTION__);
return retval;
}
/* Puts node back in the resource list pointed to by head */
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
{
if (!node || !head)
return;
node->next = *head;
*head = node;
}
#define SLOT_NAME_SIZE 10
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
{
snprintf(buffer, buffer_size, "%d", slot->number);
}
enum php_ctlr_type {
PCI,
ISA,
ACPI
};
typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
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,
php_intr_callback_t power_fault_callback);
/* This has no meaning for PCI Express, as there is only 1 slot per port */
int pcie_get_ctlr_slot_config(struct controller *ctrl,
int *num_ctlr_slots,
int *first_device_num,
int *physical_slot_num,
u8 *ctrlcap);
struct hpc_ops {
int (*power_on_slot) (struct slot *slot);
int (*power_off_slot) (struct slot *slot);
int (*get_power_status) (struct slot *slot, u8 *status);
int (*get_attention_status) (struct slot *slot, u8 *status);
int (*set_attention_status) (struct slot *slot, u8 status);
int (*get_latch_status) (struct slot *slot, u8 *status);
int (*get_adapter_status) (struct slot *slot, u8 *status);
int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_max_lnk_width) (struct slot *slot, enum pcie_link_width *value);
int (*get_cur_lnk_width) (struct slot *slot, enum pcie_link_width *value);
int (*query_power_fault) (struct slot *slot);
void (*green_led_on) (struct slot *slot);
void (*green_led_off) (struct slot *slot);
void (*green_led_blink) (struct slot *slot);
void (*release_ctlr) (struct controller *ctrl);
int (*check_lnk_status) (struct controller *ctrl);
};
#endif /* _PCIEHP_H */

View File

@@ -0,0 +1,662 @@
/*
* PCI Express Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "pciehp.h"
#include "pciehprm.h"
#include <linux/interrupt.h>
/* Global variables */
int pciehp_debug;
int pciehp_poll_mode;
int pciehp_poll_time;
struct controller *pciehp_ctrl_list;
struct pci_func *pciehp_slot_list[256];
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(pciehp_debug, bool, 0644);
module_param(pciehp_poll_mode, bool, 0644);
module_param(pciehp_poll_time, int, 0644);
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
#define PCIE_MODULE_NAME "pciehp"
static int pcie_start_thread (void);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
static int init_slots(struct controller *ctrl)
{
struct slot *new_slot;
u8 number_of_slots;
u8 slot_device;
u32 slot_number;
int result = -ENOMEM;
dbg("%s\n",__FUNCTION__);
number_of_slots = ctrl->num_slots;
slot_device = ctrl->slot_device_offset;
slot_number = ctrl->first_slot;
while (number_of_slots) {
new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
if (!new_slot)
goto error;
memset(new_slot, 0, sizeof(struct slot));
new_slot->hotplug_slot =
kmalloc(sizeof(*(new_slot->hotplug_slot)),
GFP_KERNEL);
if (!new_slot->hotplug_slot)
goto error_slot;
memset(new_slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
new_slot->hotplug_slot->info =
kmalloc(sizeof(*(new_slot->hotplug_slot->info)),
GFP_KERNEL);
if (!new_slot->hotplug_slot->info)
goto error_hpslot;
memset(new_slot->hotplug_slot->info, 0,
sizeof(struct hotplug_slot_info));
new_slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE,
GFP_KERNEL);
if (!new_slot->hotplug_slot->name)
goto error_info;
new_slot->ctrl = ctrl;
new_slot->bus = ctrl->slot_bus;
new_slot->device = slot_device;
new_slot->hpc_ops = ctrl->hpc_ops;
new_slot->number = ctrl->first_slot;
new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
/* register this slot with the hotplug pci core */
new_slot->hotplug_slot->private = new_slot;
make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
new_slot->hotplug_slot->ops = &pciehp_hotplug_slot_ops;
new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n",
new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
result = pci_hp_register (new_slot->hotplug_slot);
if (result) {
err ("pci_hp_register failed with error %d\n", result);
goto error_name;
}
new_slot->next = ctrl->slot;
ctrl->slot = new_slot;
number_of_slots--;
slot_device++;
slot_number += ctrl->slot_num_inc;
}
return 0;
error_name:
kfree(new_slot->hotplug_slot->name);
error_info:
kfree(new_slot->hotplug_slot->info);
error_hpslot:
kfree(new_slot->hotplug_slot);
error_slot:
kfree(new_slot);
error:
return result;
}
static int cleanup_slots (struct controller * ctrl)
{
struct slot *old_slot, *next_slot;
old_slot = ctrl->slot;
ctrl->slot = NULL;
while (old_slot) {
next_slot = old_slot->next;
pci_hp_deregister (old_slot->hotplug_slot);
kfree(old_slot->hotplug_slot->info);
kfree(old_slot->hotplug_slot->name);
kfree(old_slot->hotplug_slot);
kfree(old_slot);
old_slot = next_slot;
}
return(0);
}
static int get_ctlr_slot_config(struct controller *ctrl)
{
int num_ctlr_slots; /* Not needed; PCI Express has 1 slot per port*/
int first_device_num; /* Not needed */
int physical_slot_num;
u8 ctrlcap;
int rc;
rc = pcie_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &ctrlcap);
if (rc) {
err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
return (-1);
}
ctrl->num_slots = num_ctlr_slots; /* PCI Express has 1 slot per port */
ctrl->slot_device_offset = first_device_num;
ctrl->first_slot = physical_slot_num;
ctrl->ctrlcap = ctrlcap;
dbg("%s: bus(0x%x) num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) ctrlcap(%x) for b:d (%x:%x)\n",
__FUNCTION__, ctrl->slot_bus, num_ctlr_slots, first_device_num, physical_slot_num, ctrlcap,
ctrl->bus, ctrl->device);
return (0);
}
/*
* set_attention_status - Turns the Amber LED for a slot on, off or blink
*/
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
hotplug_slot->info->attention_status = status;
if (ATTN_LED(slot->ctrl->ctrlcap))
slot->hpc_ops->set_attention_status(slot, status);
return 0;
}
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return pciehp_enable_slot(slot);
}
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return pciehp_disable_slot(slot);
}
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_power_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->power_status;
return 0;
}
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_attention_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->attention_status;
return 0;
}
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_latch_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->latch_status;
return 0;
}
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_adapter_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->adapter_status;
return 0;
}
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_id *id)
{
int rc;
struct controller *ctrl;
struct slot *t_slot;
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__);
goto err_out_none;
}
memset(ctrl, 0, sizeof(struct controller));
dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid);
pdev = dev->port;
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,
(php_intr_callback_t) pciehp_handle_power_fault);
if (rc) {
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
goto err_out_free_ctrl;
}
ctrl->pci_dev = pdev;
pci_set_drvdata(pdev, ctrl);
ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("%s: out of memory\n", __FUNCTION__);
rc = -ENOMEM;
goto err_out_unmap_mmio_region;
}
dbg("%s: ctrl->pci_bus %p\n", __FUNCTION__, ctrl->pci_bus);
memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
ctrl->bus = pdev->bus->number; /* ctrl bus */
ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */
ctrl->device = PCI_SLOT(pdev->devfn);
ctrl->function = PCI_FUNC(pdev->devfn);
dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__,
ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
/*
* Save configuration headers for this and subordinate PCI buses
*/
rc = get_ctlr_slot_config(ctrl);
if (rc) {
err(msg_initialization_err, rc);
goto err_out_free_ctrl_bus;
}
first_device_num = ctrl->slot_device_offset;
num_ctlr_slots = ctrl->num_slots;
/* Store PCI Config Space for all devices on this bus */
dbg("%s: Before calling pciehp_save_config, ctrl->bus %x,ctrl->slot_bus %x\n",
__FUNCTION__,ctrl->bus, ctrl->slot_bus);
rc = pciehp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
goto err_out_free_ctrl_bus;
}
/* Get IO, memory, and IRQ resources for new devices */
rc = pciehprm_find_available_resources(ctrl);
ctrl->add_support = !rc;
if (rc) {
dbg("pciehprm_find_available_resources = %#x\n", rc);
err("unable to locate PCI configuration resources for hot plug add.\n");
goto err_out_free_ctrl_bus;
}
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
err(msg_initialization_err, 6);
goto err_out_free_ctrl_slot;
}
t_slot = pciehp_find_slot(ctrl, first_device_num);
dbg("%s: t_slot %p\n", __FUNCTION__, t_slot);
/* Finish setting up the hot plug ctrl device */
ctrl->next_event = 0;
if (!pciehp_ctrl_list) {
pciehp_ctrl_list = ctrl;
ctrl->next = NULL;
} else {
ctrl->next = pciehp_ctrl_list;
pciehp_ctrl_list = ctrl;
}
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
dbg("%s: adpater value %x\n", __FUNCTION__, value);
if ((POWER_CTRL(ctrl->ctrlcap)) && !value) {
rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
if (rc) {
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
goto err_out_free_ctrl_slot;
} else
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return 0;
err_out_free_ctrl_slot:
cleanup_slots(ctrl);
err_out_free_ctrl_bus:
kfree(ctrl->pci_bus);
err_out_unmap_mmio_region:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
kfree(ctrl);
err_out_none:
return -ENODEV;
}
static int pcie_start_thread(void)
{
int loop;
int retval = 0;
dbg("Initialize + Start the notification/polling mechanism \n");
retval = pciehp_event_start_thread();
if (retval) {
dbg("pciehp_event_start_thread() failed\n");
return retval;
}
dbg("Initialize slot lists\n");
/* One slot list for each bus in the system */
for (loop = 0; loop < 256; loop++) {
pciehp_slot_list[loop] = NULL;
}
return retval;
}
static inline void __exit
free_pciehp_res(struct pci_resource *res)
{
struct pci_resource *tres;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
}
static void __exit unload_pciehpd(void)
{
struct pci_func *next;
struct pci_func *TempSlot;
int loop;
struct controller *ctrl;
struct controller *tctrl;
ctrl = pciehp_ctrl_list;
while (ctrl) {
cleanup_slots(ctrl);
free_pciehp_res(ctrl->io_head);
free_pciehp_res(ctrl->mem_head);
free_pciehp_res(ctrl->p_mem_head);
free_pciehp_res(ctrl->bus_head);
kfree (ctrl->pci_bus);
ctrl->hpc_ops->release_ctlr(ctrl);
tctrl = ctrl;
ctrl = ctrl->next;
kfree(tctrl);
}
for (loop = 0; loop < 256; loop++) {
next = pciehp_slot_list[loop];
while (next != NULL) {
free_pciehp_res(next->io_head);
free_pciehp_res(next->mem_head);
free_pciehp_res(next->p_mem_head);
free_pciehp_res(next->bus_head);
TempSlot = next;
next = next->next;
kfree(TempSlot);
}
}
/* Stop the notification mechanism */
pciehp_event_stop_thread();
}
int hpdriver_context = 0;
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 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)
{
int retval = 0;
#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
pciehp_poll_mode = 1;
#endif
retval = pcie_start_thread();
if (retval)
goto error_hpc_init;
retval = pciehprm_init(PCI);
if (!retval) {
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:
if (retval) {
pciehprm_cleanup();
pciehp_event_stop_thread();
} else
pciehprm_print_pirt();
return retval;
}
static void __exit pcied_cleanup(void)
{
dbg("unload_pciehpd()\n");
unload_pciehpd();
pciehprm_cleanup();
dbg("pcie_port_service_unregister\n");
pcie_port_service_unregister(&hpdriver_portdrv);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
module_init(pcied_init);
module_exit(pcied_cleanup);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,827 @@
/*
* PCI Express Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
#include "../pci.h"
#include "pciehp.h"
#ifndef CONFIG_IA64
#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
#endif
int pciehp_configure_device (struct controller* ctrl, struct pci_func* func)
{
unsigned char bus;
struct pci_bus *child;
int num;
if (func->pci_dev == NULL)
func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
/* Still NULL ? Well then scan for it ! */
if (func->pci_dev == NULL) {
dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__);
num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
if (num)
pci_bus_add_devices(ctrl->pci_dev->subordinate);
func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
if (func->pci_dev == NULL) {
dbg("ERROR: pci_dev still null\n");
return 0;
}
}
if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
pci_do_scan_bus(child);
}
return 0;
}
int pciehp_unconfigure_device(struct pci_func* func)
{
int rc = 0;
int j;
struct pci_bus *pbus;
dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus,
func->device, func->function);
pbus = func->pci_dev->bus;
for (j=0; j<8 ; j++) {
struct pci_dev* temp = pci_find_slot(func->bus,
(func->device << 3) | j);
if (temp) {
pci_remove_bus_device(temp);
}
}
/*
* Some PCI Express root ports require fixup after hot-plug operation.
*/
if (pcie_mch_quirk)
pci_fixup_device(pci_fixup_final, pbus->self);
return rc;
}
/*
* pciehp_set_irq
*
* @bus_num: bus number of PCI device
* @dev_num: device number of PCI device
* @slot: pointer to u8 where slot number will be returned
*/
int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
{
#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
int rc;
u16 temp_word;
struct pci_dev fakedev;
struct pci_bus fakebus;
fakedev.devfn = dev_num << 3;
fakedev.bus = &fakebus;
fakebus.number = bus_num;
dbg("%s: dev %d, bus %d, pin %d, num %d\n",
__FUNCTION__, dev_num, bus_num, int_pin, irq_num);
rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
dbg("%s: rc %d\n", __FUNCTION__, rc);
if (!rc)
return !rc;
/* set the Edge Level Control Register (ELCR) */
temp_word = inb(0x4d0);
temp_word |= inb(0x4d1) << 8;
temp_word |= 0x01 << irq_num;
/* This should only be for x86 as it sets the Edge Level Control Register */
outb((u8) (temp_word & 0xFF), 0x4d0);
outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
#endif
return 0;
}
/* More PCI configuration routines; this time centered around hotplug controller */
/*
* pciehp_save_config
*
* Reads configuration for all slots in a PCI bus and saves info.
*
* Note: For non-hot plug busses, the slot # saved is the device #
*
* returns 0 if success
*/
int pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
{
int rc;
u8 class_code;
u8 header_type;
u32 ID;
u8 secondary_bus;
struct pci_func *new_slot;
int sub_bus;
int max_functions;
int function;
u8 DevError;
int device = 0;
int cloop = 0;
int stop_it;
int index;
int is_hot_plug = num_ctlr_slots || first_device_num;
struct pci_bus lpci_bus, *pci_bus;
int FirstSupported, LastSupported;
dbg("%s: Enter\n", __FUNCTION__);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
num_ctlr_slots, first_device_num);
/* Decide which slots are supported */
if (is_hot_plug) {
/*********************************
* is_hot_plug is the slot mask
*********************************/
FirstSupported = first_device_num;
LastSupported = FirstSupported + num_ctlr_slots - 1;
} else {
FirstSupported = 0;
LastSupported = 0x1F;
}
dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported,
LastSupported);
/* Save PCI configuration space for all devices in supported slots */
dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number);
pci_bus->number = busnumber;
dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device);
for (device = FirstSupported; device <= LastSupported; device++) {
ID = 0xFFFFFFFF;
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0),
PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
dbg("%s: ID = %x\n", __FUNCTION__, ID);
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
/* If multi-function device, set max_functions to 8 */
if (header_type & 0x80)
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
DevError = 0;
dbg("%s: In do loop\n", __FUNCTION__);
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */
/* Recurse the subordinate bus
* get the subordinate bus number
*/
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
PCI_SECONDARY_BUS, &secondary_bus);
if (rc) {
return rc;
} else {
sub_bus = (int) secondary_bus;
/* Save secondary bus cfg spc with this recursive call. */
rc = pciehp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
}
}
index = 0;
new_slot = pciehp_slot_find(busnumber, device, index++);
dbg("%s: new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, index-1);
while (new_slot && (new_slot->function != (u8) function)) {
new_slot = pciehp_slot_find(busnumber, device, index++);
dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, index-1);
}
if (!new_slot) {
/* Setup slot structure. */
new_slot = pciehp_slot_create(busnumber);
dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, function);
if (new_slot == NULL)
return(1);
}
new_slot->bus = (u8) busnumber;
new_slot->device = (u8) device;
new_slot->function = (u8) function;
new_slot->is_a_board = 1;
new_slot->switch_save = 0x10;
/* In case of unsupported board */
new_slot->status = DevError;
new_slot->pci_dev = pci_find_slot(new_slot->bus,
(new_slot->device << 3) | new_slot->function);
dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
for (cloop = 0; cloop < 0x20; cloop++) {
rc = pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(device, function),
cloop << 2,
(u32 *) &(new_slot->config_space [cloop]));
/* dbg("new_slot->config_space[%x] = %x\n",
cloop, new_slot->config_space[cloop]); */
if (rc)
return rc;
}
function++;
stop_it = 0;
/* this loop skips to the next present function
* reading in Class Code and Header type.
*/
while ((function < max_functions)&&(!stop_it)) {
dbg("%s: In while loop \n", __FUNCTION__);
rc = pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(device, function),
PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
dbg("Nothing there\n");
} else { /* Something there */
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
stop_it++;
}
}
} while (function < max_functions);
/* End of IF (device in slot?) */
} else if (is_hot_plug) {
/* Setup slot structure with entry for empty slot */
new_slot = pciehp_slot_create(busnumber);
if (new_slot == NULL) {
return(1);
}
dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot,
new_slot->bus, new_slot->device, new_slot->function);
new_slot->bus = (u8) busnumber;
new_slot->device = (u8) device;
new_slot->function = 0;
new_slot->is_a_board = 0;
new_slot->presence_save = 0;
new_slot->switch_save = 0;
}
} /* End of FOR loop */
dbg("%s: Exit\n", __FUNCTION__);
return(0);
}
/*
* pciehp_save_slot_config
*
* Saves configuration info for all PCI devices in a given slot
* including subordinate busses.
*
* returns 0 if success
*/
int pciehp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot)
{
int rc;
u8 class_code;
u8 header_type;
u32 ID;
u8 secondary_bus;
int sub_bus;
int max_functions;
int function;
int cloop = 0;
int stop_it;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = new_slot->bus;
ID = 0xFFFFFFFF;
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0),
PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
0x0B, &class_code);
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
PCI_HEADER_TYPE, &header_type);
if (header_type & 0x80) /* Multi-function device */
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
/* Recurse the subordinate bus */
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_SECONDARY_BUS, &secondary_bus);
sub_bus = (int) secondary_bus;
/* Save the config headers for the secondary bus. */
rc = pciehp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
} /* End of IF */
new_slot->status = 0;
for (cloop = 0; cloop < 0x20; cloop++) {
pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(new_slot->device, function),
cloop << 2,
(u32 *) &(new_slot->config_space [cloop]));
}
function++;
stop_it = 0;
/* this loop skips to the next present function
* reading in the Class Code and the Header type.
*/
while ((function < max_functions) && (!stop_it)) {
pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
} else { /* Something there */
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
0x0B, &class_code);
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_HEADER_TYPE, &header_type);
stop_it++;
}
}
} while (function < max_functions);
} /* End of IF (device in slot?) */
else {
return 2;
}
return 0;
}
/*
* pciehp_save_used_resources
*
* Stores used resource information for existing boards. this is
* for boards that were in the system when this driver was loaded.
* this function is for hot plug ADD
*
* returns 0 if success
* if disable == 1(DISABLE_CARD),
* it loops for all functions of the slot and disables them.
* else, it just get resources of the function and return.
*/
int pciehp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable)
{
u8 cloop;
u8 header_type;
u8 secondary_bus;
u8 temp_byte;
u16 command;
u16 save_command;
u16 w_base, w_length;
u32 temp_register;
u32 save_base;
u32 base, length;
u64 base64 = 0;
int index = 0;
unsigned int devfn;
struct pci_resource *mem_node = NULL;
struct pci_resource *p_mem_node = NULL;
struct pci_resource *t_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
if (disable)
func = pciehp_slot_find(func->bus, func->device, index++);
while ((func != NULL) && func->is_a_board) {
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Save the command register */
pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
if (disable) {
/* disable card */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
}
/* Check for Bridge */
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n",
func->bus, func->device, save_command);
if (disable) {
/* Clear Bridge Control Register */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
}
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
bus_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(temp_byte - secondary_bus + 1);
bus_node->next = func->bus_head;
func->bus_head = bus_node;
/* Save IO base and Limit registers */
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte);
base = temp_byte;
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
length = temp_byte;
if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
io_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
io_node->length = (ulong)(length - base + 0x10) << 8;
io_node->next = func->io_head;
func->io_head = io_node;
}
/* Save memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
mem_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)w_base << 16;
mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
/* Save prefetchable memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
p_mem_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)w_base << 16;
p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
} else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n",
func->bus, func->device, save_command);
/* Figure out IO and memory base lengths */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
temp_register = 0xFFFFFFFF;
pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
if (!disable)
pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base);
if (!temp_register)
continue;
base = temp_register;
if ((base & PCI_BASE_ADDRESS_SPACE_IO) &&
(!disable || (save_command & PCI_COMMAND_IO))) {
/* IO base */
/* set temp_register = amount of IO space requested */
base = base & 0xFFFFFFFCL;
base = (~base) + 1;
io_node = kmalloc(sizeof (struct pci_resource),
GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
io_node->length = (ulong)base;
dbg("sur adapter: IO bar=0x%x(length=0x%x)\n",
io_node->base, io_node->length);
io_node->next = func->io_head;
func->io_head = io_node;
} else { /* map Memory */
int prefetchable = 1;
/* struct pci_resources **res_node; */
char *res_type_str = "PMEM";
u32 temp_register2;
t_mem_node = kmalloc(sizeof (struct pci_resource),
GFP_KERNEL);
if (!t_mem_node)
return -ENOMEM;
if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
(!disable || (save_command & PCI_COMMAND_MEMORY))) {
prefetchable = 0;
mem_node = t_mem_node;
res_type_str++;
} else
p_mem_node = t_mem_node;
base = base & 0xFFFFFFF0L;
base = (~base) + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
if (prefetchable) {
p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
res_type_str,
p_mem_node->base,
p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
res_type_str,
mem_node->base,
mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
base64 = temp_register2;
base64 = (base64 << 32) | save_base;
if (temp_register2) {
dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
res_type_str, temp_register2, (u32)base64);
base64 &= 0x00000000FFFFFFFFL;
}
if (prefetchable) {
p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
res_type_str,
p_mem_node->base,
p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
res_type_str,
mem_node->base,
mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
cloop += 4;
break;
default:
dbg("asur: reserved BAR type=0x%x\n",
temp_register);
break;
}
}
} /* End of base register loop */
} else { /* Some other unknown header type */
dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n",
func->bus, func->device);
}
/* find the next device in this slot */
if (!disable)
break;
func = pciehp_slot_find(func->bus, func->device, index++);
}
return 0;
}
/**
* kfree_resource_list: release memory of all list members
* @res: resource list to free
*/
static inline void
return_resource_list(struct pci_resource **func, struct pci_resource **res)
{
struct pci_resource *node;
struct pci_resource *t_node;
node = *func;
*func = NULL;
while (node) {
t_node = node->next;
return_resource(res, node);
node = t_node;
}
}
/*
* pciehp_return_board_resources
*
* this routine returns all resources allocated to a board to
* the available pool.
*
* returns 0 if success
*/
int pciehp_return_board_resources(struct pci_func * func,
struct resource_lists * resources)
{
int rc;
dbg("%s\n", __FUNCTION__);
if (!func)
return 1;
return_resource_list(&(func->io_head),&(resources->io_head));
return_resource_list(&(func->mem_head),&(resources->mem_head));
return_resource_list(&(func->p_mem_head),&(resources->p_mem_head));
return_resource_list(&(func->bus_head),&(resources->bus_head));
rc = pciehp_resource_sort_and_combine(&(resources->mem_head));
rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head));
rc |= pciehp_resource_sort_and_combine(&(resources->io_head));
rc |= pciehp_resource_sort_and_combine(&(resources->bus_head));
return rc;
}
/**
* kfree_resource_list: release memory of all list members
* @res: resource list to free
*/
static inline void
kfree_resource_list(struct pci_resource **r)
{
struct pci_resource *res, *tres;
res = *r;
*r = NULL;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
}
/**
* pciehp_destroy_resource_list: put node back in the resource list
* @resources: list to put nodes back
*/
void pciehp_destroy_resource_list(struct resource_lists * resources)
{
kfree_resource_list(&(resources->io_head));
kfree_resource_list(&(resources->mem_head));
kfree_resource_list(&(resources->p_mem_head));
kfree_resource_list(&(resources->bus_head));
}
/**
* pciehp_destroy_board_resources: put node back in the resource list
* @resources: list to put nodes back
*/
void pciehp_destroy_board_resources(struct pci_func * func)
{
kfree_resource_list(&(func->io_head));
kfree_resource_list(&(func->mem_head));
kfree_resource_list(&(func->p_mem_head));
kfree_resource_list(&(func->bus_head));
}

View File

@@ -0,0 +1,52 @@
/*
* PCIEHPRM : PCIEHP Resource Manager for ACPI/non-ACPI platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHPRM_H_
#define _PCIEHPRM_H_
#ifdef CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI
#include "pciehprm_nonacpi.h"
#endif
int pciehprm_init(enum php_ctlr_type ct);
void pciehprm_cleanup(void);
int pciehprm_print_pirt(void);
int pciehprm_find_available_resources(struct controller *ctrl);
int pciehprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
void pciehprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
#ifdef DEBUG
#define RES_CHECK(this, bits) \
{ if (((this) & (bits - 1))) \
printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
#else
#define RES_CHECK(this, bits)
#endif
#endif /* _PCIEHPRM_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,501 @@
/*
* PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "pciehp.h"
#include "pciehprm.h"
#include "pciehprm_nonacpi.h"
void pciehprm_cleanup(void)
{
return;
}
int pciehprm_print_pirt(void)
{
return 0;
}
int pciehprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
*sun = (u8) (ctrl->first_slot);
return 0;
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void phprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static int phprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int phprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
pciehp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int phprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
phprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = phprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int pciehprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
pciehp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func, new_func;
int busn = ctrl->slot_bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
/*
if (devn == ctrl->device && funn == ctrl->function)
continue;
*/
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
dbg("%s: vid = %x bus %x dev %x fun %x\n", __FUNCTION__,
vid, busn, devn, funn);
func = pciehp_slot_find(busn, devn, funn);
dbg("%s: func = %p\n", __FUNCTION__,func);
if (!func) {
memset(&new_func, 0, sizeof(struct pci_func));
new_func.bus = busn;
new_func.device = devn;
new_func.function = funn;
new_func.is_a_board = 1;
configure_existing_function(ctrl, &new_func);
phprm_dump_func_res(&new_func);
} else {
configure_existing_function(ctrl, func);
phprm_dump_func_res(func);
}
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
}
}
}
return 0;
}
static void phprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
/*
* phprm_find_available_resources
*
* Finds available memory, IO, and IRQ resources for programming
* devices which may be added to the system
* this function is for hot plug ADD!
*
* returns 0 if success
*/
int pciehprm_find_available_resources(struct controller *ctrl)
{
struct pci_func func;
u32 rc;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ctrl->bus;
func.device = ctrl->device;
func.function = ctrl->function;
func.is_a_board = 1;
/* Get resources for this PCI bridge */
rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD);
dbg("%s: pciehp_save_used_resources rc = %d\n", __FUNCTION__, rc);
if (func.mem_head)
func.mem_head->next = ctrl->mem_head;
ctrl->mem_head = func.mem_head;
if (func.p_mem_head)
func.p_mem_head->next = ctrl->p_mem_head;
ctrl->p_mem_head = func.p_mem_head;
if (func.io_head)
func.io_head->next = ctrl->io_head;
ctrl->io_head = func.io_head;
if(func.bus_head)
func.bus_head->next = ctrl->bus_head;
ctrl->bus_head = func.bus_head;
if (ctrl->bus_head)
pciehprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
dbg("%s: before bind_pci_resources_to slots\n", __FUNCTION__);
bind_pci_resources_to_slots (ctrl);
dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
return (rc);
}
int pciehprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__,
func->bus, func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void pciehprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_pciehprm_init_pci(void)
{
return 0;
}
int pciehprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_pciehprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}

View File

@@ -0,0 +1,56 @@
/*
* PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHPRM_NONACPI_H_
#define _PCIEHPRM_NONACPI_H_
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _PCIEHPRM_NONACPI_H_ */

View File

@@ -0,0 +1,375 @@
/*
* PCI Hot Plug Controller Skeleton Driver - 0.2
*
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2003 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This driver is to be used as a skeleton driver to be show how to interface
* with the pci hotplug core easily.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/init.h>
#include "pci_hotplug.h"
struct slot {
u8 number;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
static LIST_HEAD(slot_list);
#define MY_NAME "pcihp_skeleton"
#define dbg(format, arg...) \
do { \
if (debug) \
printk (KERN_DEBUG "%s: " format "\n", \
MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
static int debug;
static int num_slots;
#define DRIVER_VERSION "0.3"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "Hot Plug PCI Controller Skeleton Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int hardware_test (struct hotplug_slot *slot, u32 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static struct hotplug_slot_ops skel_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.hardware_test = hardware_test,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
};
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in code here to enable the specified slot
*/
return retval;
}
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in code here to disable the specified slot
*/
return retval;
}
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
switch (status) {
case 0:
/*
* Fill in code here to turn light off
*/
break;
case 1:
default:
/*
* Fill in code here to turn light on
*/
break;
}
return retval;
}
static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
switch (value) {
case 0:
/* Specify a test here */
break;
case 1:
/* Specify another test here */
break;
}
return retval;
}
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in logic to get the current power status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in logic to get the current attention status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in logic to get the current latch status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = hotplug_slot->private;
int retval = 0;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
/*
* Fill in logic to get the current adapter status of the specific
* slot and store it in the *value location.
*/
return retval;
}
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
#define SLOT_NAME_SIZE 10
static void make_slot_name(struct slot *slot)
{
/*
* Stupid way to make a filename out of the slot name.
* replace this if your hardware provides a better way to name slots.
*/
snprintf(slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", slot->number);
}
/**
* init_slots - initialize 'struct slot' structures for each slot
*
*/
static int __init init_slots(void)
{
struct slot *slot;
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
char *name;
int retval = -ENOMEM;
int i;
/*
* Create a structure for each slot, and register that slot
* with the pci_hotplug subsystem.
*/
for (i = 0; i < num_slots; ++i) {
slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
if (!slot)
goto error;
memset(slot, 0, sizeof(struct slot));
hotplug_slot = kmalloc(sizeof(struct hotplug_slot),
GFP_KERNEL);
if (!hotplug_slot)
goto error_slot;
memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
slot->hotplug_slot = hotplug_slot;
info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
if (!info)
goto error_hpslot;
memset(info, 0, sizeof (struct hotplug_slot_info));
hotplug_slot->info = info;
name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
if (!name)
goto error_info;
hotplug_slot->name = name;
slot->number = i;
hotplug_slot->private = slot;
hotplug_slot->release = &release_slot;
make_slot_name(slot);
hotplug_slot->ops = &skel_hotplug_slot_ops;
/*
* Initilize the slot info structure with some known
* good values.
*/
info->power_status = get_power_status(slot);
info->attention_status = get_attention_status(slot);
info->latch_status = get_latch_status(slot);
info->adapter_status = get_adapter_status(slot);
dbg("registering slot %d\n", i);
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_name;
}
/* add slot to our internal list */
list_add(&slot->slot_list, &slot_list);
}
return 0;
error_name:
kfree(name);
error_info:
kfree(info);
error_hpslot:
kfree(hotplug_slot);
error_slot:
kfree(slot);
error:
return retval;
}
static void __exit cleanup_slots(void)
{
struct list_head *tmp;
struct list_head *next;
struct slot *slot;
/*
* Unregister all of our slots with the pci_hotplug subsystem.
* Memory will be freed in release_slot() callback after slot's
* lifespan is finished.
*/
list_for_each_safe(tmp, next, &slot_list) {
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
pci_hp_deregister(slot->hotplug_slot);
}
}
static int __init pcihp_skel_init(void)
{
int retval;
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/*
* Do specific initialization stuff for your driver here
* Like initializing your controller hardware (if any) and
* determining the number of slots you have in the system
* right now.
*/
num_slots = 5;
return init_slots();
}
static void __exit pcihp_skel_exit(void)
{
/*
* Clean everything up.
*/
cleanup_slots();
}
module_init(pcihp_skel_init);
module_exit(pcihp_skel_exit);

View File

@@ -0,0 +1,24 @@
/*
* Interface for Dynamic Logical Partitioning of I/O Slots on
* RPA-compliant PPC64 platform.
*
* John Rose <johnrose@austin.ibm.com>
* October 2003
*
* Copyright (C) 2003 IBM.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _RPADLPAR_IO_H_
#define _RPADLPAR_IO_H_
extern int dlpar_sysfs_init(void);
extern void dlpar_sysfs_exit(void);
extern int dlpar_add_slot(char *drc_name);
extern int dlpar_remove_slot(char *drc_name);
#endif

View File

@@ -0,0 +1,503 @@
/*
* Interface for Dynamic Logical Partitioning of I/O Slots on
* RPA-compliant PPC64 platform.
*
* John Rose <johnrose@austin.ibm.com>
* Linda Xie <lxie@us.ibm.com>
*
* October 2003
*
* Copyright (C) 2003 IBM.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <asm/semaphore.h>
#include <asm/rtas.h>
#include "../pci.h"
#include "rpaphp.h"
#include "rpadlpar.h"
static DECLARE_MUTEX(rpadlpar_sem);
#define NODE_TYPE_VIO 1
#define NODE_TYPE_SLOT 2
#define NODE_TYPE_PHB 3
static struct device_node *find_php_slot_vio_node(char *drc_name)
{
struct device_node *child;
struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
char *loc_code;
if (!parent)
return NULL;
for (child = of_get_next_child(parent, NULL);
child; child = of_get_next_child(parent, child)) {
loc_code = get_property(child, "ibm,loc-code", NULL);
if (loc_code && !strncmp(loc_code, drc_name, strlen(drc_name)))
return child;
}
return NULL;
}
/* Find dlpar-capable pci node that contains the specified name and type */
static struct device_node *find_php_slot_pci_node(char *drc_name,
char *drc_type)
{
struct device_node *np = NULL;
char *name;
char *type;
int rc;
while ((np = of_find_node_by_type(np, "pci"))) {
rc = rpaphp_get_drc_props(np, NULL, &name, &type, NULL);
if (rc == 0)
if (!strcmp(drc_name, name) && !strcmp(drc_type, type))
break;
}
return np;
}
static struct device_node *find_newly_added_node(char *drc_name, int *node_type)
{
struct device_node *dn;
dn = find_php_slot_pci_node(drc_name, "SLOT");
if (dn) {
*node_type = NODE_TYPE_SLOT;
return dn;
}
dn = find_php_slot_pci_node(drc_name, "PHB");
if (dn) {
*node_type = NODE_TYPE_PHB;
return dn;
}
dn = find_php_slot_vio_node(drc_name);
if (dn) {
*node_type = NODE_TYPE_VIO;
return dn;
}
return NULL;
}
static struct slot *find_slot(char *drc_name)
{
struct list_head *tmp, *n;
struct slot *slot;
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
if (strcmp(slot->location, drc_name) == 0)
return slot;
}
return NULL;
}
static void rpadlpar_claim_one_bus(struct pci_bus *b)
{
struct list_head *ld;
struct pci_bus *child_bus;
for (ld = b->devices.next; ld != &b->devices; ld = ld->next) {
struct pci_dev *dev = pci_dev_b(ld);
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
if (r->parent || !r->start || !r->flags)
continue;
rpaphp_claim_resource(dev, i);
}
}
list_for_each_entry(child_bus, &b->children, node)
rpadlpar_claim_one_bus(child_bus);
}
static int pci_add_secondary_bus(struct device_node *dn,
struct pci_dev *bridge_dev)
{
struct pci_controller *hose = dn->phb;
struct pci_bus *child;
u8 sec_busno;
/* Get busno of downstream bus */
pci_read_config_byte(bridge_dev, PCI_SECONDARY_BUS, &sec_busno);
/* Allocate and add to children of bridge_dev->bus */
child = pci_add_new_bus(bridge_dev->bus, bridge_dev, sec_busno);
if (!child) {
printk(KERN_ERR "%s: could not add secondary bus\n", __FUNCTION__);
return -ENOMEM;
}
sprintf(child->name, "PCI Bus #%02x", child->number);
/* Fixup subordinate bridge bases and resources */
pcibios_fixup_bus(child);
/* Claim new bus resources */
rpadlpar_claim_one_bus(bridge_dev->bus);
if (hose->last_busno < child->number)
hose->last_busno = child->number;
dn->bussubno = child->number;
/* ioremap() for child bus, which may or may not succeed */
remap_bus_range(child);
return 0;
}
static struct pci_dev *dlpar_pci_add_bus(struct device_node *dn)
{
struct pci_controller *hose = dn->phb;
struct pci_dev *dev = NULL;
/* Scan phb bus for EADS device, adding new one to bus->devices */
if (!pci_scan_single_device(hose->bus, dn->devfn)) {
printk(KERN_ERR "%s: found no device on bus\n", __FUNCTION__);
return NULL;
}
/* Add new devices to global lists. Register in proc, sysfs. */
pci_bus_add_devices(hose->bus);
/* Confirm new bridge dev was created */
dev = rpaphp_find_pci_dev(dn);
if (!dev) {
printk(KERN_ERR "%s: failed to add pci device\n", __FUNCTION__);
return NULL;
}
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
printk(KERN_ERR "%s: unexpected header type %d\n",
__FUNCTION__, dev->hdr_type);
return NULL;
}
if (pci_add_secondary_bus(dn, dev))
return NULL;
return dev;
}
static int dlpar_pci_remove_bus(struct pci_dev *bridge_dev)
{
struct pci_bus *secondary_bus;
if (!bridge_dev) {
printk(KERN_ERR "%s: unexpected null device\n",
__FUNCTION__);
return -EINVAL;
}
secondary_bus = bridge_dev->subordinate;
if (unmap_bus_range(secondary_bus)) {
printk(KERN_ERR "%s: failed to unmap bus range\n",
__FUNCTION__);
return -ERANGE;
}
pci_remove_bus_device(bridge_dev);
return 0;
}
static inline int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
{
struct pci_dev *dev;
/* Add pci bus */
dev = dlpar_pci_add_bus(dn);
if (!dev) {
printk(KERN_ERR "%s: unable to add bus %s\n", __FUNCTION__,
drc_name);
return -EIO;
}
return 0;
}
static int dlpar_remove_root_bus(struct pci_controller *phb)
{
struct pci_bus *phb_bus;
int rc;
phb_bus = phb->bus;
if (!(list_empty(&phb_bus->children) &&
list_empty(&phb_bus->devices))) {
return -EBUSY;
}
rc = pcibios_remove_root_bus(phb);
if (rc)
return -EIO;
device_unregister(phb_bus->bridge);
pci_remove_bus(phb_bus);
return 0;
}
static int dlpar_remove_phb(struct slot *slot)
{
struct pci_controller *phb;
struct device_node *dn;
int rc = 0;
dn = slot->dn;
if (!dn) {
printk(KERN_ERR "%s: unexpected NULL slot device node\n",
__FUNCTION__);
return -EIO;
}
phb = dn->phb;
if (!phb) {
printk(KERN_ERR "%s: unexpected NULL phb pointer\n",
__FUNCTION__);
return -EIO;
}
if (rpaphp_remove_slot(slot)) {
printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
__FUNCTION__, slot->location);
return -EIO;
}
rc = dlpar_remove_root_bus(phb);
if (rc < 0)
return rc;
return 0;
}
static int dlpar_add_phb(struct device_node *dn)
{
struct pci_controller *phb;
phb = init_phb_dynamic(dn);
if (!phb)
return -EINVAL;
return 0;
}
/**
* dlpar_add_slot - DLPAR add an I/O Slot
* @drc_name: drc-name of newly added slot
*
* Make the hotplug module and the kernel aware
* of a newly added I/O Slot.
* Return Codes -
* 0 Success
* -ENODEV Not a valid drc_name
* -EINVAL Slot already added
* -ERESTARTSYS Signalled before obtaining lock
* -EIO Internal PCI Error
*/
int dlpar_add_slot(char *drc_name)
{
struct device_node *dn = NULL;
int node_type;
int rc = 0;
if (down_interruptible(&rpadlpar_sem))
return -ERESTARTSYS;
/* Check for existing hotplug slot */
if (find_slot(drc_name)) {
rc = -EINVAL;
goto exit;
}
dn = find_newly_added_node(drc_name, &node_type);
if (!dn) {
rc = -ENODEV;
goto exit;
}
switch (node_type) {
case NODE_TYPE_VIO:
/* Just add hotplug slot */
break;
case NODE_TYPE_SLOT:
rc = dlpar_add_pci_slot(drc_name, dn);
break;
case NODE_TYPE_PHB:
rc = dlpar_add_phb(dn);
break;
default:
printk("%s: unexpected node type\n", __FUNCTION__);
return -EIO;
}
if (!rc && rpaphp_add_slot(dn)) {
printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
__FUNCTION__, drc_name);
rc = -EIO;
}
exit:
up(&rpadlpar_sem);
return rc;
}
/**
* dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
* @drc_name: drc-name of newly added slot
*
* Remove the kernel and hotplug representations
* of an I/O Slot.
* Return Codes:
* 0 Success
* -EIO Internal Error
*/
int dlpar_remove_vio_slot(struct slot *slot, char *drc_name)
{
/* Remove hotplug slot */
if (rpaphp_remove_slot(slot)) {
printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
__FUNCTION__, drc_name);
return -EIO;
}
return 0;
}
/**
* dlpar_remove_slot - DLPAR remove a PCI I/O Slot
* @drc_name: drc-name of newly added slot
*
* Remove the kernel and hotplug representations
* of a PCI I/O Slot.
* Return Codes:
* 0 Success
* -ENODEV Not a valid drc_name
* -EIO Internal PCI Error
*/
int dlpar_remove_pci_slot(struct slot *slot, char *drc_name)
{
struct pci_dev *bridge_dev;
bridge_dev = slot->bridge;
if (!bridge_dev) {
printk(KERN_ERR "%s: unexpected null bridge device\n",
__FUNCTION__);
return -EIO;
}
/* Remove hotplug slot */
if (rpaphp_remove_slot(slot)) {
printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
__FUNCTION__, drc_name);
return -EIO;
}
/* Remove pci bus */
if (dlpar_pci_remove_bus(bridge_dev)) {
printk(KERN_ERR "%s: unable to remove pci bus %s\n",
__FUNCTION__, drc_name);
return -EIO;
}
return 0;
}
/**
* dlpar_remove_slot - DLPAR remove an I/O Slot
* @drc_name: drc-name of newly added slot
*
* Remove the kernel and hotplug representations
* of an I/O Slot.
* Return Codes:
* 0 Success
* -ENODEV Not a valid drc_name
* -EINVAL Slot already removed
* -ERESTARTSYS Signalled before obtaining lock
* -EIO Internal Error
*/
int dlpar_remove_slot(char *drc_name)
{
struct slot *slot;
int rc = 0;
if (down_interruptible(&rpadlpar_sem))
return -ERESTARTSYS;
if (!find_php_slot_vio_node(drc_name) &&
!find_php_slot_pci_node(drc_name, "SLOT") &&
!find_php_slot_pci_node(drc_name, "PHB")) {
rc = -ENODEV;
goto exit;
}
slot = find_slot(drc_name);
if (!slot) {
rc = -EINVAL;
goto exit;
}
if (slot->type == PHB) {
rc = dlpar_remove_phb(slot);
} else {
switch (slot->dev_type) {
case PCI_DEV:
rc = dlpar_remove_pci_slot(slot, drc_name);
break;
case VIO_DEV:
rc = dlpar_remove_vio_slot(slot, drc_name);
break;
}
}
exit:
up(&rpadlpar_sem);
return rc;
}
static inline int is_dlpar_capable(void)
{
int rc = rtas_token("ibm,configure-connector");
return (int) (rc != RTAS_UNKNOWN_SERVICE);
}
int __init rpadlpar_io_init(void)
{
int rc = 0;
if (!is_dlpar_capable()) {
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
__FUNCTION__);
return -EPERM;
}
rc = dlpar_sysfs_init();
return rc;
}
void rpadlpar_io_exit(void)
{
dlpar_sysfs_exit();
return;
}
module_init(rpadlpar_io_init);
module_exit(rpadlpar_io_exit);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,151 @@
/*
* Interface for Dynamic Logical Partitioning of I/O Slots on
* RPA-compliant PPC64 platform.
*
* John Rose <johnrose@austin.ibm.com>
* October 2003
*
* Copyright (C) 2003 IBM.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kobject.h>
#include <linux/string.h>
#include "pci_hotplug.h"
#include "rpadlpar.h"
#define DLPAR_KOBJ_NAME "control"
#define ADD_SLOT_ATTR_NAME "add_slot"
#define REMOVE_SLOT_ATTR_NAME "remove_slot"
#define MAX_DRC_NAME_LEN 64
/* Store return code of dlpar operation in attribute struct */
struct dlpar_io_attr {
int rc;
struct attribute attr;
ssize_t (*store)(struct dlpar_io_attr *dlpar_attr, const char *buf,
size_t nbytes);
};
/* Common show callback for all attrs, display the return code
* of the dlpar op */
static ssize_t
dlpar_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
struct dlpar_io_attr *dlpar_attr = container_of(attr,
struct dlpar_io_attr, attr);
return sprintf(buf, "%d\n", dlpar_attr->rc);
}
static ssize_t
dlpar_attr_store(struct kobject * kobj, struct attribute * attr,
const char *buf, size_t nbytes)
{
struct dlpar_io_attr *dlpar_attr = container_of(attr,
struct dlpar_io_attr, attr);
return dlpar_attr->store ?
dlpar_attr->store(dlpar_attr, buf, nbytes) : 0;
}
static struct sysfs_ops dlpar_attr_sysfs_ops = {
.show = dlpar_attr_show,
.store = dlpar_attr_store,
};
static ssize_t add_slot_store(struct dlpar_io_attr *dlpar_attr,
const char *buf, size_t nbytes)
{
char drc_name[MAX_DRC_NAME_LEN];
char *end;
if (nbytes > MAX_DRC_NAME_LEN)
return 0;
memcpy(drc_name, buf, nbytes);
end = strchr(drc_name, '\n');
if (!end)
end = &drc_name[nbytes];
*end = '\0';
dlpar_attr->rc = dlpar_add_slot(drc_name);
return nbytes;
}
static ssize_t remove_slot_store(struct dlpar_io_attr *dlpar_attr,
const char *buf, size_t nbytes)
{
char drc_name[MAX_DRC_NAME_LEN];
char *end;
if (nbytes > MAX_DRC_NAME_LEN)
return 0;
memcpy(drc_name, buf, nbytes);
end = strchr(drc_name, '\n');
if (!end)
end = &drc_name[nbytes];
*end = '\0';
dlpar_attr->rc = dlpar_remove_slot(drc_name);
return nbytes;
}
static struct dlpar_io_attr add_slot_attr = {
.rc = 0,
.attr = { .name = ADD_SLOT_ATTR_NAME, .mode = 0644, },
.store = add_slot_store,
};
static struct dlpar_io_attr remove_slot_attr = {
.rc = 0,
.attr = { .name = REMOVE_SLOT_ATTR_NAME, .mode = 0644},
.store = remove_slot_store,
};
static struct attribute *default_attrs[] = {
&add_slot_attr.attr,
&remove_slot_attr.attr,
NULL,
};
static void dlpar_io_release(struct kobject *kobj)
{
/* noop */
return;
}
struct kobj_type ktype_dlpar_io = {
.release = dlpar_io_release,
.sysfs_ops = &dlpar_attr_sysfs_ops,
.default_attrs = default_attrs,
};
struct kset dlpar_io_kset = {
.subsys = &pci_hotplug_slots_subsys,
.kobj = {.name = DLPAR_KOBJ_NAME, .ktype=&ktype_dlpar_io,},
.ktype = &ktype_dlpar_io,
};
int dlpar_sysfs_init(void)
{
if (kset_register(&dlpar_io_kset)) {
printk(KERN_ERR "rpadlpar_io: cannot register kset for %s\n",
dlpar_io_kset.kobj.name);
return -EINVAL;
}
return 0;
}
void dlpar_sysfs_exit(void)
{
kset_unregister(&dlpar_io_kset);
}

View File

@@ -0,0 +1,138 @@
/*
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
*
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <lxie@us.ibm.com>,
*
*/
#ifndef _PPC64PHP_H
#define _PPC64PHP_H
#include <linux/pci.h>
#include "pci_hotplug.h"
#define PHB 2
#define HOTPLUG 1
#define EMBEDDED 0
#define DR_INDICATOR 9002
#define DR_ENTITY_SENSE 9003
#define POWER_ON 100
#define POWER_OFF 0
#define LED_OFF 0
#define LED_ON 1 /* continuous on */
#define LED_ID 2 /* slow blinking */
#define LED_ACTION 3 /* fast blinking */
/* Sensor values from rtas_get-sensor */
#define EMPTY 0 /* No card in slot */
#define PRESENT 1 /* Card in slot */
#define MY_NAME "rpaphp"
extern int debug;
#define dbg(format, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: " format, \
MY_NAME , ## arg); \
} while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
/* slot types */
#define VIO_DEV 1
#define PCI_DEV 2
/* slot states */
#define NOT_VALID 3
#define NOT_CONFIGURED 2
#define CONFIGURED 1
#define EMPTY 0
struct rpaphp_pci_func {
struct pci_dev *pci_dev;
struct list_head sibling;
};
/*
* struct slot - slot information for each *physical* slot
*/
struct slot {
struct list_head rpaphp_slot_list;
int state;
u32 index;
u32 type;
u32 power_domain;
char *name;
char *location;
u8 removable;
u8 dev_type; /* VIO or PCI */
struct device_node *dn; /* slot's device_node in OFDT */
/* dn has phb info */
struct pci_dev *bridge; /* slot's pci_dev in pci_devices */
union {
struct list_head *pci_devs; /* pci_devs in PCI slot */
struct vio_dev *vio_dev; /* vio_dev in VIO slot */
} dev;
struct hotplug_slot *hotplug_slot;
};
extern struct hotplug_slot_ops rpaphp_hotplug_slot_ops;
extern struct list_head rpaphp_slot_head;
extern int num_slots;
/* function prototypes */
/* rpaphp_pci.c */
extern struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn);
extern int rpaphp_claim_resource(struct pci_dev *dev, int resource);
extern int rpaphp_enable_pci_slot(struct slot *slot);
extern int register_pci_slot(struct slot *slot);
extern int rpaphp_unconfig_pci_adapter(struct slot *slot);
extern int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value);
extern struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev);
/* rpaphp_core.c */
extern int rpaphp_add_slot(struct device_node *dn);
extern int rpaphp_remove_slot(struct slot *slot);
extern int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
char **drc_name, char **drc_type, int *drc_power_domain);
/* rpaphp_vio.c */
extern int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 * value);
extern int rpaphp_unconfig_vio_adapter(struct slot *slot);
extern int register_vio_slot(struct device_node *dn);
extern int rpaphp_enable_vio_slot(struct slot *slot);
/* rpaphp_slot.c */
extern void dealloc_slot_struct(struct slot *slot);
extern struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name, int power_domain);
extern int register_slot(struct slot *slot);
extern int deregister_slot(struct slot *slot);
extern int rpaphp_get_power_status(struct slot *slot, u8 * value);
extern int rpaphp_set_attention_status(struct slot *slot, u8 status);
#endif /* _PPC64PHP_H */

View File

@@ -0,0 +1,536 @@
/*
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <lxie@us.ibm.com>
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <asm/eeh.h> /* for eeh_add_device() */
#include <asm/rtas.h> /* rtas_call */
#include <asm/pci-bridge.h> /* for pci_controller */
#include "../pci.h" /* for pci_add_new_bus */
/* and pci_do_scan_bus */
#include "rpaphp.h"
#include "pci_hotplug.h"
int debug;
static struct semaphore rpaphp_sem;
LIST_HEAD(rpaphp_slot_head);
int num_slots;
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "Linda Xie <lxie@us.ibm.com>"
#define DRIVER_DESC "RPA HOT Plug PCI Controller Driver"
#define MAX_LOC_CODE 128
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
static int enable_slot(struct hotplug_slot *slot);
static int disable_slot(struct hotplug_slot *slot);
static int set_attention_status(struct hotplug_slot *slot, u8 value);
static int get_power_status(struct hotplug_slot *slot, u8 * value);
static int get_attention_status(struct hotplug_slot *slot, u8 * value);
static int get_adapter_status(struct hotplug_slot *slot, u8 * value);
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value);
struct hotplug_slot_ops rpaphp_hotplug_slot_ops = {
.owner = THIS_MODULE,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.set_attention_status = set_attention_status,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_adapter_status = get_adapter_status,
.get_max_bus_speed = get_max_bus_speed,
};
static int rpaphp_get_attention_status(struct slot *slot)
{
return slot->hotplug_slot->info->attention_status;
}
/**
* set_attention_status - set attention LED
* echo 0 > attention -- set LED OFF
* echo 1 > attention -- set LED ON
* echo 2 > attention -- set LED ID(identify, light is blinking)
*
*/
static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 value)
{
int retval = 0;
struct slot *slot = (struct slot *)hotplug_slot->private;
down(&rpaphp_sem);
switch (value) {
case 0:
retval = rpaphp_set_attention_status(slot, LED_OFF);
hotplug_slot->info->attention_status = 0;
break;
case 1:
default:
retval = rpaphp_set_attention_status(slot, LED_ON);
hotplug_slot->info->attention_status = 1;
break;
case 2:
retval = rpaphp_set_attention_status(slot, LED_ID);
hotplug_slot->info->attention_status = 2;
break;
}
up(&rpaphp_sem);
return retval;
}
/**
* get_power_status - get power status of a slot
* @hotplug_slot: slot to get status
* @value: pointer to store status
*
*
*/
static int get_power_status(struct hotplug_slot *hotplug_slot, u8 * value)
{
int retval;
struct slot *slot = (struct slot *)hotplug_slot->private;
down(&rpaphp_sem);
retval = rpaphp_get_power_status(slot, value);
up(&rpaphp_sem);
return retval;
}
/**
* get_attention_status - get attention LED status
*
*
*/
static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 * value)
{
int retval = 0;
struct slot *slot = (struct slot *)hotplug_slot->private;
down(&rpaphp_sem);
*value = rpaphp_get_attention_status(slot);
up(&rpaphp_sem);
return retval;
}
static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 * value)
{
struct slot *slot = (struct slot *)hotplug_slot->private;
int retval = 0;
down(&rpaphp_sem);
/* have to go through this */
switch (slot->dev_type) {
case PCI_DEV:
retval = rpaphp_get_pci_adapter_status(slot, 0, value);
break;
case VIO_DEV:
retval = rpaphp_get_vio_adapter_status(slot, 0, value);
break;
default:
retval = -EINVAL;
}
up(&rpaphp_sem);
return retval;
}
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = (struct slot *)hotplug_slot->private;
down(&rpaphp_sem);
switch (slot->type) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
*value = PCI_SPEED_33MHz; /* speed for case 1-6 */
break;
case 7:
case 8:
*value = PCI_SPEED_66MHz;
break;
case 11:
case 14:
*value = PCI_SPEED_66MHz_PCIX;
break;
case 12:
case 15:
*value = PCI_SPEED_100MHz_PCIX;
break;
case 13:
case 16:
*value = PCI_SPEED_133MHz_PCIX;
break;
default:
*value = PCI_SPEED_UNKNOWN;
break;
}
up(&rpaphp_sem);
return 0;
}
int rpaphp_remove_slot(struct slot *slot)
{
return deregister_slot(slot);
}
static int get_children_props(struct device_node *dn, int **drc_indexes,
int **drc_names, int **drc_types, int **drc_power_domains)
{
int *indexes, *names;
int *types, *domains;
indexes = (int *) get_property(dn, "ibm,drc-indexes", NULL);
names = (int *) get_property(dn, "ibm,drc-names", NULL);
types = (int *) get_property(dn, "ibm,drc-types", NULL);
domains = (int *) get_property(dn, "ibm,drc-power-domains", NULL);
if (!indexes || !names || !types || !domains) {
/* Slot does not have dynamically-removable children */
return -EINVAL;
}
if (drc_indexes)
*drc_indexes = indexes;
if (drc_names)
/* &drc_names[1] contains NULL terminated slot names */
*drc_names = names;
if (drc_types)
/* &drc_types[1] contains NULL terminated slot types */
*drc_types = types;
if (drc_power_domains)
*drc_power_domains = domains;
return 0;
}
/* To get the DRC props describing the current node, first obtain it's
* my-drc-index property. Next obtain the DRC list from it's parent. Use
* the my-drc-index for correlation, and obtain the requested properties.
*/
int rpaphp_get_drc_props(struct device_node *dn, int *drc_index,
char **drc_name, char **drc_type, int *drc_power_domain)
{
int *indexes, *names;
int *types, *domains;
unsigned int *my_index;
char *name_tmp, *type_tmp;
int i, rc;
my_index = (int *) get_property(dn, "ibm,my-drc-index", NULL);
if (!my_index) {
/* Node isn't DLPAR/hotplug capable */
return -EINVAL;
}
rc = get_children_props(dn->parent, &indexes, &names, &types, &domains);
if (rc < 0) {
return -EINVAL;
}
name_tmp = (char *) &names[1];
type_tmp = (char *) &types[1];
/* Iterate through parent properties, looking for my-drc-index */
for (i = 0; i < indexes[0]; i++) {
if ((unsigned int) indexes[i + 1] == *my_index) {
if (drc_name)
*drc_name = name_tmp;
if (drc_type)
*drc_type = type_tmp;
if (drc_index)
*drc_index = *my_index;
if (drc_power_domain)
*drc_power_domain = domains[i+1];
return 0;
}
name_tmp += (strlen(name_tmp) + 1);
type_tmp += (strlen(type_tmp) + 1);
}
return -EINVAL;
}
static int is_php_type(char *drc_type)
{
unsigned long value;
char *endptr;
/* PCI Hotplug nodes have an integer for drc_type */
value = simple_strtoul(drc_type, &endptr, 10);
if (endptr == drc_type)
return 0;
return 1;
}
static int is_php_dn(struct device_node *dn, int **indexes, int **names,
int **types, int **power_domains)
{
int *drc_types;
int rc;
rc = get_children_props(dn, indexes, names, &drc_types, power_domains);
if (rc >= 0) {
if (is_php_type((char *) &drc_types[1])) {
*types = drc_types;
return 1;
}
}
return 0;
}
static int is_dr_dn(struct device_node *dn, int **indexes, int **names,
int **types, int **power_domains, int **my_drc_index)
{
int rc;
*my_drc_index = (int *) get_property(dn, "ibm,my-drc-index", NULL);
if(!*my_drc_index)
return (0);
if (!dn->parent)
return (0);
rc = get_children_props(dn->parent, indexes, names, types,
power_domains);
return (rc >= 0);
}
static inline int is_vdevice_root(struct device_node *dn)
{
return !strcmp(dn->name, "vdevice");
}
int is_dlpar_type(const char *type_str)
{
/* Only register DLPAR-capable nodes of drc-type PHB or SLOT */
return (!strcmp(type_str, "PHB") || !strcmp(type_str, "SLOT"));
}
/****************************************************************
* rpaphp not only registers PCI hotplug slots(HOTPLUG),
* but also logical DR slots(EMBEDDED).
* HOTPLUG slot: An adapter can be physically added/removed.
* EMBEDDED slot: An adapter can be logically removed/added
* from/to a partition with the slot.
***************************************************************/
int rpaphp_add_slot(struct device_node *dn)
{
struct slot *slot;
int retval = 0;
int i, *my_drc_index, slot_type;
int *indexes, *names, *types, *power_domains;
char *name, *type;
dbg("Entry %s: dn->full_name=%s\n", __FUNCTION__, dn->full_name);
if (dn->parent && is_vdevice_root(dn->parent)) {
/* register a VIO device */
retval = register_vio_slot(dn);
goto exit;
}
/* register PCI devices */
if (dn->name != 0 && strcmp(dn->name, "pci") == 0) {
if (is_php_dn(dn, &indexes, &names, &types, &power_domains))
slot_type = HOTPLUG;
else if (is_dr_dn(dn, &indexes, &names, &types, &power_domains, &my_drc_index))
slot_type = EMBEDDED;
else goto exit;
name = (char *) &names[1];
type = (char *) &types[1];
for (i = 0; i < indexes[0]; i++,
name += (strlen(name) + 1), type += (strlen(type) + 1)) {
if (slot_type == HOTPLUG ||
(slot_type == EMBEDDED &&
indexes[i + 1] == my_drc_index[0] &&
is_dlpar_type(type))) {
if (!(slot = alloc_slot_struct(dn, indexes[i + 1], name,
power_domains[i + 1]))) {
retval = -ENOMEM;
goto exit;
}
if (!strcmp(type, "PHB"))
slot->type = PHB;
else if (slot_type == EMBEDDED)
slot->type = EMBEDDED;
else
slot->type = simple_strtoul(type, NULL, 10);
dbg(" Found drc-index:0x%x drc-name:%s drc-type:%s\n",
indexes[i + 1], name, type);
retval = register_pci_slot(slot);
if (slot_type == EMBEDDED)
goto exit;
}
}
}
exit:
dbg("%s - Exit: num_slots=%d rc[%d]\n",
__FUNCTION__, num_slots, retval);
return retval;
}
/*
* init_slots - initialize 'struct slot' structures for each slot
*
*/
static void init_slots(void)
{
struct device_node *dn;
for (dn = find_all_nodes(); dn; dn = dn->next)
rpaphp_add_slot(dn);
}
static int __init init_rpa(void)
{
init_MUTEX(&rpaphp_sem);
/* initialize internal data structure etc. */
init_slots();
if (!num_slots)
return -ENODEV;
return 0;
}
static void __exit cleanup_slots(void)
{
struct list_head *tmp, *n;
struct slot *slot;
/*
* Unregister all of our slots with the pci_hotplug subsystem,
* and free up all memory that we had allocated.
* memory will be freed in release_slot callback.
*/
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
list_del(&slot->rpaphp_slot_list);
pci_hp_deregister(slot->hotplug_slot);
}
return;
}
static int __init rpaphp_init(void)
{
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/* read all the PRA info from the system */
return init_rpa();
}
static void __exit rpaphp_exit(void)
{
cleanup_slots();
}
static int enable_slot(struct hotplug_slot *hotplug_slot)
{
int retval = 0;
struct slot *slot = (struct slot *)hotplug_slot->private;
if (slot->state == CONFIGURED) {
dbg("%s: %s is already enabled\n", __FUNCTION__, slot->name);
goto exit;
}
dbg("ENABLING SLOT %s\n", slot->name);
down(&rpaphp_sem);
switch (slot->dev_type) {
case PCI_DEV:
retval = rpaphp_enable_pci_slot(slot);
break;
case VIO_DEV:
retval = rpaphp_enable_vio_slot(slot);
break;
default:
retval = -EINVAL;
}
up(&rpaphp_sem);
exit:
dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
return retval;
}
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
int retval = -EINVAL;
struct slot *slot = (struct slot *)hotplug_slot->private;
dbg("%s - Entry: slot[%s]\n", __FUNCTION__, slot->name);
if (slot->state == NOT_CONFIGURED) {
dbg("%s: %s is already disabled\n", __FUNCTION__, slot->name);
goto exit;
}
dbg("DISABLING SLOT %s\n", slot->name);
down(&rpaphp_sem);
switch (slot->dev_type) {
case PCI_DEV:
retval = rpaphp_unconfig_pci_adapter(slot);
break;
case VIO_DEV:
retval = rpaphp_unconfig_vio_adapter(slot);
break;
default:
retval = -ENODEV;
}
up(&rpaphp_sem);
exit:
dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
return retval;
}
module_init(rpaphp_init);
module_exit(rpaphp_exit);
EXPORT_SYMBOL_GPL(rpaphp_add_slot);
EXPORT_SYMBOL_GPL(rpaphp_remove_slot);
EXPORT_SYMBOL_GPL(rpaphp_slot_head);
EXPORT_SYMBOL_GPL(rpaphp_get_drc_props);

View File

@@ -0,0 +1,538 @@
/*
* PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform.
* Copyright (C) 2003 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <lxie@us.ibm.com>
*
*/
#include <linux/pci.h>
#include <asm/pci-bridge.h>
#include <asm/rtas.h>
#include <asm/machdep.h>
#include "../pci.h" /* for pci_add_new_bus */
#include "rpaphp.h"
struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn)
{
struct pci_dev *dev = NULL;
char bus_id[BUS_ID_SIZE];
sprintf(bus_id, "%04x:%02x:%02x.%d", dn->phb->global_number,
dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn));
for_each_pci_dev(dev) {
if (!strcmp(pci_name(dev), bus_id)) {
break;
}
}
return dev;
}
EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev);
int rpaphp_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
struct resource *root = pci_find_parent_resource(dev, res);
char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
int err = -EINVAL;
if (root != NULL) {
err = request_resource(root, res);
}
if (err) {
err("PCI: %s region %d of %s %s [%lx:%lx]\n",
root ? "Address space collision on" :
"No parent found for",
resource, dtype, pci_name(dev), res->start, res->end);
}
return err;
}
EXPORT_SYMBOL_GPL(rpaphp_claim_resource);
static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot)
{
return rpaphp_find_pci_dev(slot->dn);
}
static int rpaphp_get_sensor_state(struct slot *slot, int *state)
{
int rc;
int setlevel;
rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state);
if (rc < 0) {
if (rc == -EFAULT || rc == -EEXIST) {
dbg("%s: slot must be power up to get sensor-state\n",
__FUNCTION__);
/* some slots have to be powered up
* before get-sensor will succeed.
*/
rc = rtas_set_power_level(slot->power_domain, POWER_ON,
&setlevel);
if (rc < 0) {
dbg("%s: power on slot[%s] failed rc=%d.\n",
__FUNCTION__, slot->name, rc);
} else {
rc = rtas_get_sensor(DR_ENTITY_SENSE,
slot->index, state);
}
} else if (rc == -ENODEV)
info("%s: slot is unusable\n", __FUNCTION__);
else
err("%s failed to get sensor state\n", __FUNCTION__);
}
return rc;
}
/**
* get_pci_adapter_status - get the status of a slot
*
* 0-- slot is empty
* 1-- adapter is configured
* 2-- adapter is not configured
* 3-- not valid
*/
int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value)
{
int state, rc;
struct device_node *child_dn;
struct pci_dev *child_dev = NULL;
*value = NOT_VALID;
rc = rpaphp_get_sensor_state(slot, &state);
if (rc)
goto exit;
if ((state == EMPTY) || (slot->type == PHB)) {
dbg("slot is empty\n");
*value = EMPTY;
}
else if (state == PRESENT) {
if (!is_init) {
/* at run-time slot->state can be changed by */
/* config/unconfig adapter */
*value = slot->state;
} else {
child_dn = slot->dn->child;
if (child_dn)
child_dev = rpaphp_find_pci_dev(child_dn);
if (child_dev)
*value = CONFIGURED;
else if (!child_dn)
dbg("%s: %s is not valid OFDT node\n",
__FUNCTION__, slot->dn->full_name);
else {
err("%s: can't find pdev of adapter in slot[%s]\n",
__FUNCTION__, slot->dn->full_name);
*value = NOT_CONFIGURED;
}
}
}
exit:
return rc;
}
/* Must be called before pci_bus_add_devices */
static void
rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
* Skip already-present devices (which are on the
* global device list.)
*/
if (list_empty(&dev->global_list)) {
int i;
/* Need to setup IOMMU tables */
ppc_md.iommu_dev_setup(dev);
if(fix_bus)
pcibios_fixup_device_resources(dev, bus);
pci_read_irq_line(dev);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
if (r->parent || !r->start || !r->flags)
continue;
rpaphp_claim_resource(dev, i);
}
}
}
}
static int rpaphp_pci_config_bridge(struct pci_dev *dev);
/*****************************************************************************
rpaphp_pci_config_slot() will configure all devices under the
given slot->dn and return the the first pci_dev.
*****************************************************************************/
static struct pci_dev *
rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus)
{
struct device_node *eads_first_child = dn->child;
struct pci_dev *dev = NULL;
int num;
dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name);
if (eads_first_child) {
/* pci_scan_slot should find all children of EADs */
num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0));
if (num) {
rpaphp_fixup_new_pci_devices(bus, 1);
pci_bus_add_devices(bus);
}
dev = rpaphp_find_pci_dev(eads_first_child);
if (!dev) {
err("No new device found\n");
return NULL;
}
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
rpaphp_pci_config_bridge(dev);
}
return dev;
}
static int rpaphp_pci_config_bridge(struct pci_dev *dev)
{
u8 sec_busno;
struct pci_bus *child_bus;
struct pci_dev *child_dev;
dbg("Enter %s: BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev));
/* get busno of downstream bus */
pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno);
/* add to children of PCI bridge dev->bus */
child_bus = pci_add_new_bus(dev->bus, dev, sec_busno);
if (!child_bus) {
err("%s: could not add second bus\n", __FUNCTION__);
return -EIO;
}
sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number);
/* do pci_scan_child_bus */
pci_scan_child_bus(child_bus);
list_for_each_entry(child_dev, &child_bus->devices, bus_list) {
eeh_add_device_late(child_dev);
}
/* fixup new pci devices without touching bus struct */
rpaphp_fixup_new_pci_devices(child_bus, 0);
/* Make the discovered devices available */
pci_bus_add_devices(child_bus);
return 0;
}
static void enable_eeh(struct device_node *dn)
{
struct device_node *sib;
for (sib = dn->child; sib; sib = sib->sibling)
enable_eeh(sib);
eeh_add_device_early(dn);
return;
}
static void print_slot_pci_funcs(struct slot *slot)
{
struct pci_dev *dev;
if (slot->dev_type == PCI_DEV) {
dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->name);
list_for_each_entry (dev, slot->dev.pci_devs, bus_list)
dbg("\t%s\n", pci_name(dev));
}
return;
}
static int rpaphp_config_pci_adapter(struct slot *slot)
{
struct pci_bus *pci_bus;
struct pci_dev *dev;
int rc = -ENODEV;
dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
if (slot->bridge) {
pci_bus = slot->bridge->subordinate;
if (!pci_bus) {
err("%s: can't find bus structure\n", __FUNCTION__);
goto exit;
}
enable_eeh(slot->dn);
dev = rpaphp_pci_config_slot(slot->dn, pci_bus);
if (!dev) {
err("%s: can't find any devices.\n", __FUNCTION__);
goto exit;
}
print_slot_pci_funcs(slot);
rc = 0;
} else {
/* slot is not enabled */
err("slot doesn't have pci_dev structure\n");
}
exit:
dbg("Exit %s: rc=%d\n", __FUNCTION__, rc);
return rc;
}
static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev)
{
eeh_remove_device(dev);
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
struct pci_bus *bus = dev->subordinate;
struct list_head *ln;
if (!bus)
return;
for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
struct pci_dev *pdev = pci_dev_b(ln);
if (pdev)
rpaphp_eeh_remove_bus_device(pdev);
}
}
return;
}
int rpaphp_unconfig_pci_adapter(struct slot *slot)
{
struct pci_dev *dev;
int retval = 0;
list_for_each_entry(dev, slot->dev.pci_devs, bus_list)
rpaphp_eeh_remove_bus_device(dev);
pci_remove_behind_bridge(slot->bridge);
slot->state = NOT_CONFIGURED;
info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__,
slot->name);
return retval;
}
static int setup_pci_hotplug_slot_info(struct slot *slot)
{
dbg("%s Initilize the PCI slot's hotplug->info structure ...\n",
__FUNCTION__);
rpaphp_get_power_status(slot, &slot->hotplug_slot->info->power_status);
rpaphp_get_pci_adapter_status(slot, 1,
&slot->hotplug_slot->info->
adapter_status);
if (slot->hotplug_slot->info->adapter_status == NOT_VALID) {
err("%s: NOT_VALID: skip dn->full_name=%s\n",
__FUNCTION__, slot->dn->full_name);
return -EINVAL;
}
return 0;
}
static int set_phb_slot_name(struct slot *slot)
{
struct device_node *dn;
struct pci_controller *phb;
struct pci_bus *bus;
dn = slot->dn;
if (!dn) {
return -EINVAL;
}
phb = dn->phb;
if (!phb) {
return -EINVAL;
}
bus = phb->bus;
if (!bus) {
return -EINVAL;
}
sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus),
bus->number, 0, 0);
return 0;
}
static int setup_pci_slot(struct slot *slot)
{
struct pci_bus *bus;
int rc;
if (slot->type == PHB) {
rc = set_phb_slot_name(slot);
if (rc < 0) {
err("%s: failed to set phb slot name\n", __FUNCTION__);
goto exit_rc;
}
} else {
slot->bridge = rpaphp_find_bridge_pdev(slot);
if (!slot->bridge) {
/* slot being added doesn't have pci_dev yet */
err("%s: no pci_dev for bridge dn %s\n",
__FUNCTION__, slot->name);
goto exit_rc;
}
bus = slot->bridge->subordinate;
if (!bus)
goto exit_rc;
slot->dev.pci_devs = &bus->devices;
dbg("%s set slot->name to %s\n", __FUNCTION__,
pci_name(slot->bridge));
strcpy(slot->name, pci_name(slot->bridge));
}
/* find slot's pci_dev if it's not empty */
if (slot->hotplug_slot->info->adapter_status == EMPTY) {
slot->state = EMPTY; /* slot is empty */
} else {
/* slot is occupied */
if (!(slot->dn->child)) {
/* non-empty slot has to have child */
err("%s: slot[%s]'s device_node doesn't have child for adapter\n",
__FUNCTION__, slot->name);
goto exit_rc;
}
if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) {
dbg("%s CONFIGURING pci adapter in slot[%s]\n",
__FUNCTION__, slot->name);
if (rpaphp_config_pci_adapter(slot)) {
err("%s: CONFIG pci adapter failed\n", __FUNCTION__);
goto exit_rc;
}
} else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) {
err("%s: slot[%s]'s adapter_status is NOT_VALID.\n",
__FUNCTION__, slot->name);
goto exit_rc;
}
print_slot_pci_funcs(slot);
if (!list_empty(slot->dev.pci_devs)) {
slot->state = CONFIGURED;
} else {
/* DLPAR add as opposed to
* boot time */
slot->state = NOT_CONFIGURED;
}
}
return 0;
exit_rc:
dealloc_slot_struct(slot);
return -EINVAL;
}
int register_pci_slot(struct slot *slot)
{
int rc = -EINVAL;
slot->dev_type = PCI_DEV;
if ((slot->type == EMBEDDED) || (slot->type == PHB))
slot->removable = 0;
else
slot->removable = 1;
if (setup_pci_hotplug_slot_info(slot))
goto exit_rc;
if (setup_pci_slot(slot))
goto exit_rc;
rc = register_slot(slot);
exit_rc:
return rc;
}
int rpaphp_enable_pci_slot(struct slot *slot)
{
int retval = 0, state;
retval = rpaphp_get_sensor_state(slot, &state);
if (retval)
goto exit;
dbg("%s: sensor state[%d]\n", __FUNCTION__, state);
/* if slot is not empty, enable the adapter */
if (state == PRESENT) {
dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name);
retval = rpaphp_config_pci_adapter(slot);
if (!retval) {
slot->state = CONFIGURED;
dbg("%s: PCI devices in slot[%s] has been configured\n",
__FUNCTION__, slot->name);
} else {
slot->state = NOT_CONFIGURED;
dbg("%s: no pci_dev struct for adapter in slot[%s]\n",
__FUNCTION__, slot->name);
}
} else if (state == EMPTY) {
dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name);
slot->state = EMPTY;
} else {
err("%s: slot[%s] is in invalid state\n", __FUNCTION__,
slot->name);
slot->state = NOT_VALID;
retval = -EINVAL;
}
exit:
dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
return retval;
}
struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev)
{
struct list_head *tmp, *n;
struct slot *slot;
list_for_each_safe(tmp, n, &rpaphp_slot_head) {
struct pci_bus *bus;
struct list_head *ln;
slot = list_entry(tmp, struct slot, rpaphp_slot_list);
if (slot->bridge == NULL) {
if (slot->dev_type == PCI_DEV) {
printk(KERN_WARNING "PCI slot missing bridge %s %s \n",
slot->name, slot->location);
}
continue;
}
bus = slot->bridge->subordinate;
if (!bus) {
continue; /* should never happen? */
}
for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) {
struct pci_dev *pdev = pci_dev_b(ln);
if (pdev == dev)
return slot->hotplug_slot;
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(rpaphp_find_hotplug_slot);

View File

@@ -0,0 +1,267 @@
/*
* RPA Virtual I/O device functions
* Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <lxie@us.ibm.com>
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/pci.h>
#include <asm/rtas.h>
#include "rpaphp.h"
static ssize_t removable_read_file (struct hotplug_slot *php_slot, char *buf)
{
u8 value;
int retval = -ENOENT;
struct slot *slot = (struct slot *)php_slot->private;
if (!slot)
return retval;
value = slot->removable;
retval = sprintf (buf, "%d\n", value);
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_removable = {
.attr = {.name = "phy_removable", .mode = S_IFREG | S_IRUGO},
.show = removable_read_file,
};
static void rpaphp_sysfs_add_attr_removable (struct hotplug_slot *slot)
{
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_removable.attr);
}
static void rpaphp_sysfs_remove_attr_removable (struct hotplug_slot *slot)
{
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_removable.attr);
}
static ssize_t location_read_file (struct hotplug_slot *php_slot, char *buf)
{
char *value;
int retval = -ENOENT;
struct slot *slot = (struct slot *)php_slot->private;
if (!slot)
return retval;
value = slot->location;
retval = sprintf (buf, "%s\n", value);
return retval;
}
static struct hotplug_slot_attribute hotplug_slot_attr_location = {
.attr = {.name = "phy_location", .mode = S_IFREG | S_IRUGO},
.show = location_read_file,
};
static void rpaphp_sysfs_add_attr_location (struct hotplug_slot *slot)
{
sysfs_create_file(&slot->kobj, &hotplug_slot_attr_location.attr);
}
static void rpaphp_sysfs_remove_attr_location (struct hotplug_slot *slot)
{
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_location.attr);
}
/* free up the memory used by a slot */
static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = (struct slot *) hotplug_slot->private;
dealloc_slot_struct(slot);
}
void dealloc_slot_struct(struct slot *slot)
{
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
return;
}
struct slot *alloc_slot_struct(struct device_node *dn, int drc_index, char *drc_name,
int power_domain)
{
struct slot *slot;
slot = kmalloc(sizeof (struct slot), GFP_KERNEL);
if (!slot)
goto error_nomem;
memset(slot, 0, sizeof (struct slot));
slot->hotplug_slot = kmalloc(sizeof (struct hotplug_slot), GFP_KERNEL);
if (!slot->hotplug_slot)
goto error_slot;
memset(slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
slot->hotplug_slot->info = kmalloc(sizeof (struct hotplug_slot_info),
GFP_KERNEL);
if (!slot->hotplug_slot->info)
goto error_hpslot;
memset(slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
slot->hotplug_slot->name = kmalloc(BUS_ID_SIZE + 1, GFP_KERNEL);
if (!slot->hotplug_slot->name)
goto error_info;
slot->location = kmalloc(strlen(drc_name) + 1, GFP_KERNEL);
if (!slot->location)
goto error_name;
slot->name = slot->hotplug_slot->name;
slot->dn = dn;
slot->index = drc_index;
strcpy(slot->location, drc_name);
slot->power_domain = power_domain;
slot->hotplug_slot->private = slot;
slot->hotplug_slot->ops = &rpaphp_hotplug_slot_ops;
slot->hotplug_slot->release = &rpaphp_release_slot;
return (slot);
error_name:
kfree(slot->hotplug_slot->name);
error_info:
kfree(slot->hotplug_slot->info);
error_hpslot:
kfree(slot->hotplug_slot);
error_slot:
kfree(slot);
error_nomem:
return NULL;
}
static int is_registered(struct slot *slot)
{
struct slot *tmp_slot;
list_for_each_entry(tmp_slot, &rpaphp_slot_head, rpaphp_slot_list) {
if (!strcmp(tmp_slot->name, slot->name))
return 1;
}
return 0;
}
int deregister_slot(struct slot *slot)
{
int retval = 0;
struct hotplug_slot *php_slot = slot->hotplug_slot;
dbg("%s - Entry: deregistering slot=%s\n",
__FUNCTION__, slot->name);
list_del(&slot->rpaphp_slot_list);
/* remove "phy_location" file */
rpaphp_sysfs_remove_attr_location(php_slot);
/* remove "phy_removable" file */
rpaphp_sysfs_remove_attr_removable(php_slot);
retval = pci_hp_deregister(php_slot);
if (retval)
err("Problem unregistering a slot %s\n", slot->name);
else
num_slots--;
dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval);
return retval;
}
int register_slot(struct slot *slot)
{
int retval;
dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
__FUNCTION__, slot->dn->full_name, slot->index, slot->name,
slot->power_domain, slot->type);
/* should not try to register the same slot twice */
if (is_registered(slot)) { /* should't be here */
err("register_slot: slot[%s] is already registered\n", slot->name);
rpaphp_release_slot(slot->hotplug_slot);
return -EAGAIN;
}
retval = pci_hp_register(slot->hotplug_slot);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
rpaphp_release_slot(slot->hotplug_slot);
return retval;
}
/* create "phy_locatoin" file */
rpaphp_sysfs_add_attr_location(slot->hotplug_slot);
/* create "phy_removable" file */
rpaphp_sysfs_add_attr_removable(slot->hotplug_slot);
/* add slot to our internal list */
dbg("%s adding slot[%s] to rpaphp_slot_list\n",
__FUNCTION__, slot->name);
list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
if (slot->dev_type == VIO_DEV)
info("Slot [%s](VIO location=%s) registered\n",
slot->name, slot->location);
else
info("Slot [%s](PCI location=%s) registered\n",
slot->name, slot->location);
num_slots++;
return 0;
}
int rpaphp_get_power_status(struct slot *slot, u8 * value)
{
int rc = 0, level;
if (slot->type == HOTPLUG) {
rc = rtas_get_power_level(slot->power_domain, &level);
if (!rc) {
dbg("%s the power level of slot %s(pwd-domain:0x%x) is %d\n",
__FUNCTION__, slot->name, slot->power_domain, level);
*value = level;
} else
err("failed to get power-level for slot(%s), rc=0x%x\n",
slot->location, rc);
} else {
dbg("%s report POWER_ON for EMBEDDED or PHB slot %s\n",
__FUNCTION__, slot->location);
*value = (u8) POWER_ON;
}
return rc;
}
int rpaphp_set_attention_status(struct slot *slot, u8 status)
{
int rc;
/* status: LED_OFF or LED_ON */
rc = rtas_set_indicator(DR_INDICATOR, slot->index, status);
if (rc < 0)
err("slot(name=%s location=%s index=0x%x) set attention-status(%d) failed! rc=0x%x\n",
slot->name, slot->location, slot->index, status, rc);
return rc;
}

View File

@@ -0,0 +1,129 @@
/*
* RPA Hot Plug Virtual I/O device functions
* Copyright (C) 2004 Linda Xie <lxie@us.ibm.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <lxie@us.ibm.com>
*
*/
#include <asm/vio.h>
#include "rpaphp.h"
/*
* get_vio_adapter_status - get the status of a slot
*
* status:
*
* 1-- adapter is configured
* 2-- adapter is not configured
* 3-- not valid
*/
inline int rpaphp_get_vio_adapter_status(struct slot *slot, int is_init, u8 *value)
{
*value = slot->state;
return 0;
}
int rpaphp_unconfig_vio_adapter(struct slot *slot)
{
int retval = 0;
dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name);
if (!slot->dev.vio_dev) {
info("%s: no VIOA in slot[%s]\n", __FUNCTION__, slot->name);
retval = -EINVAL;
goto exit;
}
/* remove the device from the vio core */
vio_unregister_device(slot->dev.vio_dev);
slot->state = NOT_CONFIGURED;
info("%s: adapter in slot[%s] unconfigured.\n", __FUNCTION__, slot->name);
exit:
dbg("Exit %s, rc=0x%x\n", __FUNCTION__, retval);
return retval;
}
static int setup_vio_hotplug_slot_info(struct slot *slot)
{
slot->hotplug_slot->info->power_status = 1;
rpaphp_get_vio_adapter_status(slot, 1,
&slot->hotplug_slot->info->adapter_status);
return 0;
}
int register_vio_slot(struct device_node *dn)
{
u32 *index;
char *name;
int rc = -EINVAL;
struct slot *slot = NULL;
rc = rpaphp_get_drc_props(dn, NULL, &name, NULL, NULL);
if (rc < 0)
goto exit_rc;
index = (u32 *) get_property(dn, "ibm,my-drc-index", NULL);
if (!index)
goto exit_rc;
if (!(slot = alloc_slot_struct(dn, *index, name, 0))) {
rc = -ENOMEM;
goto exit_rc;
}
slot->dev_type = VIO_DEV;
slot->dev.vio_dev = vio_find_node(dn);
if (slot->dev.vio_dev) {
/*
* rpaphp is the only owner of vio devices and
* does not need extra reference taken by
* vio_find_node
*/
put_device(&slot->dev.vio_dev->dev);
} else
slot->dev.vio_dev = vio_register_device_node(dn);
if (slot->dev.vio_dev)
slot->state = CONFIGURED;
else
slot->state = NOT_CONFIGURED;
if (setup_vio_hotplug_slot_info(slot))
goto exit_rc;
strcpy(slot->name, slot->dev.vio_dev->dev.bus_id);
info("%s: registered VIO device[name=%s vio_dev=%p]\n",
__FUNCTION__, slot->name, slot->dev.vio_dev);
rc = register_slot(slot);
exit_rc:
if (rc && slot)
dealloc_slot_struct(slot);
return (rc);
}
int rpaphp_enable_vio_slot(struct slot *slot)
{
int retval = 0;
if ((slot->dev.vio_dev = vio_register_device_node(slot->dn))) {
info("%s: VIO adapter %s in slot[%s] has been configured\n",
__FUNCTION__, slot->dn->name, slot->name);
slot->state = CONFIGURED;
} else {
info("%s: no vio_dev struct for adapter in slot[%s]\n",
__FUNCTION__, slot->name);
slot->state = NOT_CONFIGURED;
}
return retval;
}

View File

@@ -0,0 +1,463 @@
/*
* Standard Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHP_H
#define _SHPCHP_H
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include "pci_hotplug.h"
#if !defined(MODULE)
#define MY_NAME "shpchp"
#else
#define MY_NAME THIS_MODULE->name
#endif
extern int shpchp_poll_mode;
extern int shpchp_poll_time;
extern int shpchp_debug;
/*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
#define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct pci_func {
struct pci_func *next;
u8 bus;
u8 device;
u8 function;
u8 is_a_board;
u16 status;
u8 configured;
u8 switch_save;
u8 presence_save;
u8 pwr_save;
u32 base_length[0x06];
u8 base_type[0x06];
u16 reserved2;
u32 config_space[0x20];
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev* pci_dev;
};
#define SLOT_MAGIC 0x67267321
struct slot {
u32 magic;
struct slot *next;
u8 bus;
u8 device;
u32 number;
u8 is_a_board;
u8 configured;
u8 state;
u8 switch_save;
u8 presence_save;
u32 capabilities;
u16 reserved2;
struct timer_list task_event;
u8 hp_slot;
struct controller *ctrl;
struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
struct pci_resource {
struct pci_resource * next;
u32 base;
u32 length;
};
struct event_info {
u32 event_type;
u8 hp_slot;
};
struct controller {
struct controller *next;
struct semaphore crit_sect; /* critical section semaphore */
void * hpc_ctlr_handle; /* HPC controller handle */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev *pci_dev;
struct pci_bus *pci_bus;
struct event_info event_queue[10];
struct slot *slot;
struct hpc_ops *hpc_ops;
wait_queue_head_t queue; /* sleep & wake process */
u8 next_event;
u8 seg;
u8 bus;
u8 device;
u8 function;
u8 rev;
u8 slot_device_offset;
u8 add_support;
enum pci_bus_speed speed;
u32 first_slot; /* First physical slot number */
u8 slot_bus; /* Bus where the slots handled by this controller sit */
u8 push_flag;
u16 ctlrcap;
u16 vendor_id;
};
struct irq_mapping {
u8 barber_pole;
u8 valid_INT;
u8 interrupt[4];
};
struct resource_lists {
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct irq_mapping *irqs;
};
/* Define AMD SHPC ID */
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
#define INT_BUTTON_IGNORE 0
#define INT_PRESENCE_ON 1
#define INT_PRESENCE_OFF 2
#define INT_SWITCH_CLOSE 3
#define INT_SWITCH_OPEN 4
#define INT_POWER_FAULT 5
#define INT_POWER_FAULT_CLEAR 6
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2
#define POWERON_STATE 3
#define POWEROFF_STATE 4
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
/* Error messages */
#define INTERLOCK_OPEN 0x00000002
#define ADD_NOT_SUPPORTED 0x00000003
#define CARD_FUNCTIONING 0x00000005
#define ADAPTER_NOT_SAME 0x00000006
#define NO_ADAPTER_PRESENT 0x00000009
#define NOT_ENOUGH_RESOURCES 0x0000000B
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
#define WRONG_BUS_FREQUENCY 0x0000000D
#define POWER_FAILURE 0x0000000E
#define REMOVE_NOT_SUPPORTED 0x00000003
#define DISABLE_CARD 1
/*
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
#define msg_HPC_non_shpc "The PCI hot plug controller is not supported by this driver.\n"
#define msg_HPC_not_supported "This system is not supported by this version of shpcphd mdoule. Upgrade to a newer version of shpchpd\n"
#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
/* sysfs functions for the hotplug controller info */
extern void shpchp_create_ctrl_files (struct controller *ctrl);
/* controller functions */
extern int shpchprm_find_available_resources(struct controller *ctrl);
extern int shpchp_event_start_thread(void);
extern void shpchp_event_stop_thread(void);
extern struct pci_func *shpchp_slot_create(unsigned char busnumber);
extern struct pci_func *shpchp_slot_find(unsigned char bus, unsigned char device, unsigned char index);
extern int shpchp_enable_slot(struct slot *slot);
extern int shpchp_disable_slot(struct slot *slot);
extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id);
/* resource functions */
extern int shpchp_resource_sort_and_combine(struct pci_resource **head);
/* pci functions */
extern int shpchp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
/*extern int shpchp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
extern int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
extern int shpchp_save_used_resources(struct controller *ctrl, struct pci_func * func, int flag);
extern int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot);
extern void shpchp_destroy_board_resources(struct pci_func * func);
extern int shpchp_return_board_resources(struct pci_func * func, struct resource_lists * resources);
extern void shpchp_destroy_resource_list(struct resource_lists * resources);
extern int shpchp_configure_device(struct controller* ctrl, struct pci_func* func);
extern int shpchp_unconfigure_device(struct pci_func* func);
/* Global variables */
extern struct controller *shpchp_ctrl_list;
extern struct pci_func *shpchp_slot_list[256];
/* These are added to support AMD shpc */
extern u8 shpchp_nic_irq;
extern u8 shpchp_disk_irq;
struct ctrl_reg {
volatile u32 base_offset;
volatile u32 slot_avail1;
volatile u32 slot_avail2;
volatile u32 slot_config;
volatile u16 sec_bus_config;
volatile u8 msi_ctrl;
volatile u8 prog_interface;
volatile u16 cmd;
volatile u16 cmd_status;
volatile u32 intr_loc;
volatile u32 serr_loc;
volatile u32 serr_intr_enable;
volatile u32 slot1;
volatile u32 slot2;
volatile u32 slot3;
volatile u32 slot4;
volatile u32 slot5;
volatile u32 slot6;
volatile u32 slot7;
volatile u32 slot8;
volatile u32 slot9;
volatile u32 slot10;
volatile u32 slot11;
volatile u32 slot12;
} __attribute__ ((packed));
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2),
SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config),
MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl),
PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
CMD = offsetof(struct ctrl_reg, cmd),
CMD_STATUS = offsetof(struct ctrl_reg, cmd_status),
INTR_LOC = offsetof(struct ctrl_reg, intr_loc),
SERR_LOC = offsetof(struct ctrl_reg, serr_loc),
SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable),
SLOT1 = offsetof(struct ctrl_reg, slot1),
SLOT2 = offsetof(struct ctrl_reg, slot2),
SLOT3 = offsetof(struct ctrl_reg, slot3),
SLOT4 = offsetof(struct ctrl_reg, slot4),
SLOT5 = offsetof(struct ctrl_reg, slot5),
SLOT6 = offsetof(struct ctrl_reg, slot6),
SLOT7 = offsetof(struct ctrl_reg, slot7),
SLOT8 = offsetof(struct ctrl_reg, slot8),
SLOT9 = offsetof(struct ctrl_reg, slot9),
SLOT10 = offsetof(struct ctrl_reg, slot10),
SLOT11 = offsetof(struct ctrl_reg, slot11),
SLOT12 = offsetof(struct ctrl_reg, slot12),
};
typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
struct php_ctlr_state_s {
struct php_ctlr_state_s *pnext;
struct pci_dev *pci_dev;
unsigned int irq;
unsigned long flags; /* spinlock's */
u32 slot_device_offset;
u32 num_slots;
struct timer_list int_poll_timer; /* Added for poll event */
php_intr_callback_t attention_button_callback;
php_intr_callback_t switch_change_callback;
php_intr_callback_t presence_change_callback;
php_intr_callback_t power_fault_callback;
void *callback_instance_id;
void __iomem *creg; /* Ptr to controller register space */
};
/* Inline functions */
/* Inline functions to check the sanity of a pointer that is passed to us */
static inline int slot_paranoia_check (struct slot *slot, const char *function)
{
if (!slot) {
dbg("%s - slot == NULL", function);
return -1;
}
if (slot->magic != SLOT_MAGIC) {
dbg("%s - bad magic number for slot", function);
return -1;
}
if (!slot->hotplug_slot) {
dbg("%s - slot->hotplug_slot == NULL!", function);
return -1;
}
return 0;
}
static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
{
struct slot *slot;
if (!hotplug_slot) {
dbg("%s - hotplug_slot == NULL\n", function);
return NULL;
}
slot = (struct slot *)hotplug_slot->private;
if (slot_paranoia_check (slot, function))
return NULL;
return slot;
}
static inline struct slot *shpchp_find_slot (struct controller *ctrl, u8 device)
{
struct slot *p_slot, *tmp_slot = NULL;
if (!ctrl)
return NULL;
p_slot = ctrl->slot;
dbg("p_slot = %p\n", p_slot);
while (p_slot && (p_slot->device != device)) {
tmp_slot = p_slot;
p_slot = p_slot->next;
dbg("In while loop, p_slot = %p\n", p_slot);
}
if (p_slot == NULL) {
err("ERROR: shpchp_find_slot device=0x%x\n", device);
p_slot = tmp_slot;
}
return (p_slot);
}
static inline int wait_for_ctrl_irq (struct controller *ctrl)
{
DECLARE_WAITQUEUE(wait, current);
int retval = 0;
dbg("%s : start\n",__FUNCTION__);
add_wait_queue(&ctrl->queue, &wait);
if (!shpchp_poll_mode) {
/* Sleep for up to 1 second */
msleep_interruptible(1000);
} else {
/* Sleep for up to 2 seconds */
msleep_interruptible(2000);
}
remove_wait_queue(&ctrl->queue, &wait);
if (signal_pending(current))
retval = -EINTR;
dbg("%s : end\n", __FUNCTION__);
return retval;
}
/* Puts node back in the resource list pointed to by head */
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
{
if (!node || !head)
return;
node->next = *head;
*head = node;
}
#define SLOT_NAME_SIZE 10
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
{
snprintf(buffer, buffer_size, "%d", slot->number);
}
enum php_ctlr_type {
PCI,
ISA,
ACPI
};
int shpc_init( struct controller *ctrl, struct pci_dev *pdev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
php_intr_callback_t power_fault_callback);
int shpc_get_ctlr_slot_config( struct controller *ctrl,
int *num_ctlr_slots,
int *first_device_num,
int *physical_slot_num,
int *updown,
int *flags);
struct hpc_ops {
int (*power_on_slot ) (struct slot *slot);
int (*slot_enable ) (struct slot *slot);
int (*slot_disable ) (struct slot *slot);
int (*enable_all_slots) (struct slot *slot);
int (*pwr_on_all_slots) (struct slot *slot);
int (*set_bus_speed_mode) (struct slot *slot, enum pci_bus_speed speed);
int (*get_power_status) (struct slot *slot, u8 *status);
int (*get_attention_status) (struct slot *slot, u8 *status);
int (*set_attention_status) (struct slot *slot, u8 status);
int (*get_latch_status) (struct slot *slot, u8 *status);
int (*get_adapter_status) (struct slot *slot, u8 *status);
int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_adapter_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_mode1_ECC_cap) (struct slot *slot, u8 *mode);
int (*get_prog_int) (struct slot *slot, u8 *prog_int);
int (*query_power_fault) (struct slot *slot);
void (*green_led_on) (struct slot *slot);
void (*green_led_off) (struct slot *slot);
void (*green_led_blink) (struct slot *slot);
void (*release_ctlr) (struct controller *ctrl);
int (*check_cmd_status) (struct controller *ctrl);
};
#endif /* _SHPCHP_H */

View File

@@ -0,0 +1,630 @@
/*
* Standard Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "shpchp.h"
#include "shpchprm.h"
/* Global variables */
int shpchp_debug;
int shpchp_poll_mode;
int shpchp_poll_time;
struct controller *shpchp_ctrl_list; /* = NULL */
struct pci_func *shpchp_slot_list[256];
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
#define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(shpchp_debug, bool, 0644);
module_param(shpchp_poll_mode, bool, 0644);
module_param(shpchp_poll_time, int, 0644);
MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
#define SHPC_MODULE_NAME "shpchp"
static int shpc_start_thread (void);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
/**
* release_slot - free up the memory used by a slot
* @hotplug_slot: slot to free
*/
static void release_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = (struct slot *)hotplug_slot->private;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
kfree(slot->hotplug_slot->info);
kfree(slot->hotplug_slot->name);
kfree(slot->hotplug_slot);
kfree(slot);
}
static int init_slots(struct controller *ctrl)
{
struct slot *new_slot;
u8 number_of_slots;
u8 slot_device;
u32 slot_number, sun;
int result = -ENOMEM;
dbg("%s\n",__FUNCTION__);
number_of_slots = ctrl->num_slots;
slot_device = ctrl->slot_device_offset;
slot_number = ctrl->first_slot;
while (number_of_slots) {
new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL);
if (!new_slot)
goto error;
memset(new_slot, 0, sizeof(struct slot));
new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
if (!new_slot->hotplug_slot)
goto error_slot;
memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!new_slot->hotplug_slot->info)
goto error_hpslot;
memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
if (!new_slot->hotplug_slot->name)
goto error_info;
new_slot->magic = SLOT_MAGIC;
new_slot->ctrl = ctrl;
new_slot->bus = ctrl->slot_bus;
new_slot->device = slot_device;
new_slot->hpc_ops = ctrl->hpc_ops;
if (shpchprm_get_physical_slot_number(ctrl, &sun,
new_slot->bus, new_slot->device))
goto error_name;
new_slot->number = sun;
new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
/* register this slot with the hotplug pci core */
new_slot->hotplug_slot->private = new_slot;
new_slot->hotplug_slot->release = &release_slot;
make_slot_name(new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
new_slot->hotplug_slot->ops = &shpchp_hotplug_slot_ops;
new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", new_slot->bus,
new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
result = pci_hp_register (new_slot->hotplug_slot);
if (result) {
err ("pci_hp_register failed with error %d\n", result);
goto error_name;
}
new_slot->next = ctrl->slot;
ctrl->slot = new_slot;
number_of_slots--;
slot_device++;
slot_number += ctrl->slot_num_inc;
}
return 0;
error_name:
kfree(new_slot->hotplug_slot->name);
error_info:
kfree(new_slot->hotplug_slot->info);
error_hpslot:
kfree(new_slot->hotplug_slot);
error_slot:
kfree(new_slot);
error:
return result;
}
static void cleanup_slots(struct controller *ctrl)
{
struct slot *old_slot, *next_slot;
old_slot = ctrl->slot;
ctrl->slot = NULL;
while (old_slot) {
next_slot = old_slot->next;
pci_hp_deregister(old_slot->hotplug_slot);
old_slot = next_slot;
}
}
static int get_ctlr_slot_config(struct controller *ctrl)
{
int num_ctlr_slots;
int first_device_num;
int physical_slot_num;
int updown;
int rc;
int flags;
rc = shpc_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags);
if (rc) {
err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
return -1;
}
ctrl->num_slots = num_ctlr_slots;
ctrl->slot_device_offset = first_device_num;
ctrl->first_slot = physical_slot_num;
ctrl->slot_num_inc = updown; /* either -1 or 1 */
dbg("%s: num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n",
__FUNCTION__, num_ctlr_slots, first_device_num, physical_slot_num, updown, ctrl->bus, ctrl->device);
return 0;
}
/*
* set_attention_status - Turns the Amber LED for a slot on, off or blink
*/
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
hotplug_slot->info->attention_status = status;
slot->hpc_ops->set_attention_status(slot, status);
return 0;
}
static int enable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return shpchp_enable_slot(slot);
}
static int disable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return shpchp_disable_slot(slot);
}
static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_power_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->power_status;
return 0;
}
static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_attention_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->attention_status;
return 0;
}
static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_latch_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->latch_status;
return 0;
}
static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_adapter_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->adapter_status;
return 0;
}
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
struct controller *ctrl;
struct slot *t_slot;
int first_device_num; /* first PCI device number supported by this SHPC */
int num_ctlr_slots; /* number of slots supported by this SHPC */
ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __FUNCTION__);
goto err_out_none;
}
memset(ctrl, 0, sizeof(struct controller));
dbg("DRV_thread pid = %d\n", current->pid);
rc = shpc_init(ctrl, pdev,
(php_intr_callback_t) shpchp_handle_attention_button,
(php_intr_callback_t) shpchp_handle_switch_change,
(php_intr_callback_t) shpchp_handle_presence_change,
(php_intr_callback_t) shpchp_handle_power_fault);
if (rc) {
dbg("%s: controller initialization failed\n", SHPC_MODULE_NAME);
goto err_out_free_ctrl;
}
dbg("%s: controller initialization success\n", __FUNCTION__);
ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */
pci_set_drvdata(pdev, ctrl);
ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("out of memory\n");
rc = -ENOMEM;
goto err_out_unmap_mmio_region;
}
memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
ctrl->bus = pdev->bus->number;
ctrl->slot_bus = pdev->subordinate->number;
ctrl->device = PCI_SLOT(pdev->devfn);
ctrl->function = PCI_FUNC(pdev->devfn);
dbg("ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
/*
* Save configuration headers for this and subordinate PCI buses
*/
rc = get_ctlr_slot_config(ctrl);
if (rc) {
err(msg_initialization_err, rc);
goto err_out_free_ctrl_bus;
}
first_device_num = ctrl->slot_device_offset;
num_ctlr_slots = ctrl->num_slots;
/* Store PCI Config Space for all devices on this bus */
rc = shpchp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
goto err_out_free_ctrl_bus;
}
/* Get IO, memory, and IRQ resources for new devices */
rc = shpchprm_find_available_resources(ctrl);
ctrl->add_support = !rc;
if (rc) {
dbg("shpchprm_find_available_resources = %#x\n", rc);
err("unable to locate PCI configuration resources for hot plug add.\n");
goto err_out_free_ctrl_bus;
}
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
err(msg_initialization_err, 6);
goto err_out_free_ctrl_slot;
}
/* Now hpc_functions (slot->hpc_ops->functions) are ready */
t_slot = shpchp_find_slot(ctrl, first_device_num);
/* Check for operation bus speed */
rc = t_slot->hpc_ops->get_cur_bus_speed(t_slot, &ctrl->speed);
dbg("%s: t_slot->hp_slot %x\n", __FUNCTION__,t_slot->hp_slot);
if (rc || ctrl->speed == PCI_SPEED_UNKNOWN) {
err(SHPC_MODULE_NAME ": Can't get current bus speed. Set to 33MHz PCI.\n");
ctrl->speed = PCI_SPEED_33MHz;
}
/* Finish setting up the hot plug ctrl device */
ctrl->next_event = 0;
if (!shpchp_ctrl_list) {
shpchp_ctrl_list = ctrl;
ctrl->next = NULL;
} else {
ctrl->next = shpchp_ctrl_list;
shpchp_ctrl_list = ctrl;
}
shpchp_create_ctrl_files(ctrl);
return 0;
err_out_free_ctrl_slot:
cleanup_slots(ctrl);
err_out_free_ctrl_bus:
kfree(ctrl->pci_bus);
err_out_unmap_mmio_region:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
kfree(ctrl);
err_out_none:
return -ENODEV;
}
static int shpc_start_thread(void)
{
int loop;
int retval = 0;
dbg("Initialize + Start the notification/polling mechanism \n");
retval = shpchp_event_start_thread();
if (retval) {
dbg("shpchp_event_start_thread() failed\n");
return retval;
}
dbg("Initialize slot lists\n");
/* One slot list for each bus in the system */
for (loop = 0; loop < 256; loop++) {
shpchp_slot_list[loop] = NULL;
}
return retval;
}
static inline void __exit
free_shpchp_res(struct pci_resource *res)
{
struct pci_resource *tres;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
}
static void __exit unload_shpchpd(void)
{
struct pci_func *next;
struct pci_func *TempSlot;
int loop;
struct controller *ctrl;
struct controller *tctrl;
ctrl = shpchp_ctrl_list;
while (ctrl) {
cleanup_slots(ctrl);
free_shpchp_res(ctrl->io_head);
free_shpchp_res(ctrl->mem_head);
free_shpchp_res(ctrl->p_mem_head);
free_shpchp_res(ctrl->bus_head);
kfree (ctrl->pci_bus);
dbg("%s: calling release_ctlr\n", __FUNCTION__);
ctrl->hpc_ops->release_ctlr(ctrl);
tctrl = ctrl;
ctrl = ctrl->next;
kfree(tctrl);
}
for (loop = 0; loop < 256; loop++) {
next = shpchp_slot_list[loop];
while (next != NULL) {
free_shpchp_res(next->io_head);
free_shpchp_res(next->mem_head);
free_shpchp_res(next->p_mem_head);
free_shpchp_res(next->bus_head);
TempSlot = next;
next = next->next;
kfree(TempSlot);
}
}
/* Stop the notification mechanism */
shpchp_event_stop_thread();
}
static struct pci_device_id shpcd_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, shpcd_pci_tbl);
static struct pci_driver shpc_driver = {
.name = SHPC_MODULE_NAME,
.id_table = shpcd_pci_tbl,
.probe = shpc_probe,
/* remove: shpc_remove_one, */
};
static int __init shpcd_init(void)
{
int retval = 0;
#ifdef CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
shpchp_poll_mode = 1;
#endif
retval = shpc_start_thread();
if (retval)
goto error_hpc_init;
retval = shpchprm_init(PCI);
if (!retval) {
retval = pci_register_driver(&shpc_driver);
dbg("%s: pci_register_driver = %d\n", __FUNCTION__, retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
}
error_hpc_init:
if (retval) {
shpchprm_cleanup();
shpchp_event_stop_thread();
} else
shpchprm_print_pirt();
return retval;
}
static void __exit shpcd_cleanup(void)
{
dbg("unload_shpchpd()\n");
unload_shpchpd();
shpchprm_cleanup();
dbg("pci_unregister_driver\n");
pci_unregister_driver(&shpc_driver);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
module_init(shpcd_init);
module_exit(shpcd_cleanup);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,810 @@
/*
* Standard Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
#include "../pci.h"
#include "shpchp.h"
#ifndef CONFIG_IA64
#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
#endif
int shpchp_configure_device (struct controller* ctrl, struct pci_func* func)
{
unsigned char bus;
struct pci_bus *child;
int num;
if (func->pci_dev == NULL)
func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
/* Still NULL ? Well then scan for it ! */
if (func->pci_dev == NULL) {
num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
if (num) {
dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate,
ctrl->pci_dev->subordinate->number);
pci_bus_add_devices(ctrl->pci_dev->subordinate);
}
func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
if (func->pci_dev == NULL) {
dbg("ERROR: pci_dev still null\n");
return 0;
}
}
if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
pci_do_scan_bus(child);
}
return 0;
}
int shpchp_unconfigure_device(struct pci_func* func)
{
int rc = 0;
int j;
dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus,
func->device, func->function);
for (j=0; j<8 ; j++) {
struct pci_dev* temp = pci_find_slot(func->bus,
(func->device << 3) | j);
if (temp) {
pci_remove_bus_device(temp);
}
}
return rc;
}
/*
* shpchp_set_irq
*
* @bus_num: bus number of PCI device
* @dev_num: device number of PCI device
* @slot: pointer to u8 where slot number will be returned
*/
int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
{
#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
int rc;
u16 temp_word;
struct pci_dev fakedev;
struct pci_bus fakebus;
fakedev.devfn = dev_num << 3;
fakedev.bus = &fakebus;
fakebus.number = bus_num;
dbg("%s: dev %d, bus %d, pin %d, num %d\n",
__FUNCTION__, dev_num, bus_num, int_pin, irq_num);
rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
dbg("%s: rc %d\n", __FUNCTION__, rc);
if (!rc)
return !rc;
/* set the Edge Level Control Register (ELCR) */
temp_word = inb(0x4d0);
temp_word |= inb(0x4d1) << 8;
temp_word |= 0x01 << irq_num;
/* This should only be for x86 as it sets the Edge Level Control Register */
outb((u8) (temp_word & 0xFF), 0x4d0);
outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
#endif
return 0;
}
/* More PCI configuration routines; this time centered around hotplug controller */
/*
* shpchp_save_config
*
* Reads configuration for all slots in a PCI bus and saves info.
*
* Note: For non-hot plug busses, the slot # saved is the device #
*
* returns 0 if success
*/
int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
{
int rc;
u8 class_code;
u8 header_type;
u32 ID;
u8 secondary_bus;
struct pci_func *new_slot;
int sub_bus;
int FirstSupported;
int LastSupported;
int max_functions;
int function;
u8 DevError;
int device = 0;
int cloop = 0;
int stop_it;
int index;
int is_hot_plug = num_ctlr_slots || first_device_num;
struct pci_bus lpci_bus, *pci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
num_ctlr_slots, first_device_num);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__,
num_ctlr_slots, first_device_num);
/* Decide which slots are supported */
if (is_hot_plug) {
/*********************************
* is_hot_plug is the slot mask
*********************************/
FirstSupported = first_device_num;
LastSupported = FirstSupported + num_ctlr_slots - 1;
} else {
FirstSupported = 0;
LastSupported = 0x1F;
}
dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported,
LastSupported);
/* Save PCI configuration space for all devices in supported slots */
pci_bus->number = busnumber;
for (device = FirstSupported; device <= LastSupported; device++) {
ID = 0xFFFFFFFF;
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0),
PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0),
PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
/* If multi-function device, set max_functions to 8 */
if (header_type & 0x80)
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
DevError = 0;
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */
/* Recurse the subordinate bus
* get the subordinate bus number
*/
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
PCI_SECONDARY_BUS, &secondary_bus);
if (rc) {
return rc;
} else {
sub_bus = (int) secondary_bus;
/* Save secondary bus cfg spc with this recursive call. */
rc = shpchp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
}
}
index = 0;
new_slot = shpchp_slot_find(busnumber, device, index++);
dbg("new_slot = %p\n", new_slot);
while (new_slot && (new_slot->function != (u8) function)) {
new_slot = shpchp_slot_find(busnumber, device, index++);
dbg("new_slot = %p\n", new_slot);
}
if (!new_slot) {
/* Setup slot structure. */
new_slot = shpchp_slot_create(busnumber);
dbg("new_slot = %p\n", new_slot);
if (new_slot == NULL)
return(1);
}
new_slot->bus = (u8) busnumber;
new_slot->device = (u8) device;
new_slot->function = (u8) function;
new_slot->is_a_board = 1;
new_slot->switch_save = 0x10;
new_slot->pwr_save = 1;
/* In case of unsupported board */
new_slot->status = DevError;
new_slot->pci_dev = pci_find_slot(new_slot->bus,
(new_slot->device << 3) | new_slot->function);
dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
for (cloop = 0; cloop < 0x20; cloop++) {
rc = pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(device, function),
cloop << 2,
(u32 *) &(new_slot->config_space [cloop]));
/* dbg("new_slot->config_space[%x] = %x\n",
cloop, new_slot->config_space[cloop]); */
if (rc)
return rc;
}
function++;
stop_it = 0;
/* this loop skips to the next present function
* reading in Class Code and Header type.
*/
while ((function < max_functions)&&(!stop_it)) {
rc = pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(device, function),
PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
dbg("Nothing there\n");
} else { /* Something there */
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(device, function),
PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n",
class_code, header_type);
stop_it++;
}
}
} while (function < max_functions);
/* End of IF (device in slot?) */
} else if (is_hot_plug) {
/* Setup slot structure with entry for empty slot */
new_slot = shpchp_slot_create(busnumber);
if (new_slot == NULL) {
return(1);
}
dbg("new_slot = %p\n", new_slot);
new_slot->bus = (u8) busnumber;
new_slot->device = (u8) device;
new_slot->function = 0;
new_slot->is_a_board = 0;
new_slot->presence_save = 0;
new_slot->switch_save = 0;
}
} /* End of FOR loop */
return(0);
}
/*
* shpchp_save_slot_config
*
* Saves configuration info for all PCI devices in a given slot
* including subordinate busses.
*
* returns 0 if success
*/
int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot)
{
int rc;
u8 class_code;
u8 header_type;
u32 ID;
u8 secondary_bus;
int sub_bus;
int max_functions;
int function;
int cloop = 0;
int stop_it;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = new_slot->bus;
ID = 0xFFFFFFFF;
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0),
PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
0x0B, &class_code);
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0),
PCI_HEADER_TYPE, &header_type);
if (header_type & 0x80) /* Multi-function device */
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
/* Recurse the subordinate bus */
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_SECONDARY_BUS, &secondary_bus);
sub_bus = (int) secondary_bus;
/* Save the config headers for the secondary bus. */
rc = shpchp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
} /* End of IF */
new_slot->status = 0;
for (cloop = 0; cloop < 0x20; cloop++) {
pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(new_slot->device, function),
cloop << 2,
(u32 *) &(new_slot->config_space [cloop]));
}
function++;
stop_it = 0;
/* this loop skips to the next present function
* reading in the Class Code and the Header type.
*/
while ((function < max_functions) && (!stop_it)) {
pci_bus_read_config_dword(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
} else { /* Something there */
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
0x0B, &class_code);
pci_bus_read_config_byte(pci_bus,
PCI_DEVFN(new_slot->device, function),
PCI_HEADER_TYPE, &header_type);
stop_it++;
}
}
} while (function < max_functions);
} /* End of IF (device in slot?) */
else {
return 2;
}
return 0;
}
/*
* shpchp_save_used_resources
*
* Stores used resource information for existing boards. this is
* for boards that were in the system when this driver was loaded.
* this function is for hot plug ADD
*
* returns 0 if success
* if disable == 1(DISABLE_CARD),
* it loops for all functions of the slot and disables them.
* else, it just get resources of the function and return.
*/
int shpchp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable)
{
u8 cloop;
u8 header_type;
u8 secondary_bus;
u8 temp_byte;
u16 command;
u16 save_command;
u16 w_base, w_length;
u32 temp_register;
u32 save_base;
u32 base, length;
u64 base64 = 0;
int index = 0;
unsigned int devfn;
struct pci_resource *mem_node = NULL;
struct pci_resource *p_mem_node = NULL;
struct pci_resource *t_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
if (disable)
func = shpchp_slot_find(func->bus, func->device, index++);
while ((func != NULL) && func->is_a_board) {
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Save the command register */
pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
if (disable) {
/* disable card */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
}
/* Check for Bridge */
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n",
func->bus, func->device, save_command);
if (disable) {
/* Clear Bridge Control Register */
command = 0x00;
pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
}
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
bus_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(temp_byte - secondary_bus + 1);
bus_node->next = func->bus_head;
func->bus_head = bus_node;
/* Save IO base and Limit registers */
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte);
base = temp_byte;
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
length = temp_byte;
if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
io_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
io_node->length = (ulong)(length - base + 0x10) << 8;
io_node->next = func->io_head;
func->io_head = io_node;
}
/* Save memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
mem_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)w_base << 16;
mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
/* Save prefetchable memory base and Limit registers */
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
p_mem_node = kmalloc(sizeof(struct pci_resource),
GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)w_base << 16;
p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
} else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n",
func->bus, func->device, save_command);
/* Figure out IO and memory base lengths */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
temp_register = 0xFFFFFFFF;
pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
if (!disable)
pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base);
if (!temp_register)
continue;
base = temp_register;
if ((base & PCI_BASE_ADDRESS_SPACE_IO) &&
(!disable || (save_command & PCI_COMMAND_IO))) {
/* IO base */
/* set temp_register = amount of IO space requested */
base = base & 0xFFFFFFFCL;
base = (~base) + 1;
io_node = kmalloc(sizeof (struct pci_resource),
GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
io_node->length = (ulong)base;
dbg("sur adapter: IO bar=0x%x(length=0x%x)\n",
io_node->base, io_node->length);
io_node->next = func->io_head;
func->io_head = io_node;
} else { /* map Memory */
int prefetchable = 1;
/* struct pci_resources **res_node; */
char *res_type_str = "PMEM";
u32 temp_register2;
t_mem_node = kmalloc(sizeof (struct pci_resource),
GFP_KERNEL);
if (!t_mem_node)
return -ENOMEM;
if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) &&
(!disable || (save_command & PCI_COMMAND_MEMORY))) {
prefetchable = 0;
mem_node = t_mem_node;
res_type_str++;
} else
p_mem_node = t_mem_node;
base = base & 0xFFFFFFF0L;
base = (~base) + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
if (prefetchable) {
p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
res_type_str,
p_mem_node->base,
p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n",
res_type_str,
mem_node->base,
mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
base64 = temp_register2;
base64 = (base64 << 32) | save_base;
if (temp_register2) {
dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
res_type_str, temp_register2, (u32)base64);
base64 &= 0x00000000FFFFFFFFL;
}
if (prefetchable) {
p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
res_type_str,
p_mem_node->base,
p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n",
res_type_str,
mem_node->base,
mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
cloop += 4;
break;
default:
dbg("asur: reserved BAR type=0x%x\n",
temp_register);
break;
}
}
} /* End of base register loop */
} else { /* Some other unknown header type */
dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n",
func->bus, func->device);
}
/* find the next device in this slot */
if (!disable)
break;
func = shpchp_slot_find(func->bus, func->device, index++);
}
return 0;
}
/**
* kfree_resource_list: release memory of all list members
* @res: resource list to free
*/
static inline void
return_resource_list(struct pci_resource **func, struct pci_resource **res)
{
struct pci_resource *node;
struct pci_resource *t_node;
node = *func;
*func = NULL;
while (node) {
t_node = node->next;
return_resource(res, node);
node = t_node;
}
}
/*
* shpchp_return_board_resources
*
* this routine returns all resources allocated to a board to
* the available pool.
*
* returns 0 if success
*/
int shpchp_return_board_resources(struct pci_func * func,
struct resource_lists * resources)
{
int rc;
dbg("%s\n", __FUNCTION__);
if (!func)
return 1;
return_resource_list(&(func->io_head),&(resources->io_head));
return_resource_list(&(func->mem_head),&(resources->mem_head));
return_resource_list(&(func->p_mem_head),&(resources->p_mem_head));
return_resource_list(&(func->bus_head),&(resources->bus_head));
rc = shpchp_resource_sort_and_combine(&(resources->mem_head));
rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head));
rc |= shpchp_resource_sort_and_combine(&(resources->io_head));
rc |= shpchp_resource_sort_and_combine(&(resources->bus_head));
return rc;
}
/**
* kfree_resource_list: release memory of all list members
* @res: resource list to free
*/
static inline void
kfree_resource_list(struct pci_resource **r)
{
struct pci_resource *res, *tres;
res = *r;
*r = NULL;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
}
/**
* shpchp_destroy_resource_list: put node back in the resource list
* @resources: list to put nodes back
*/
void shpchp_destroy_resource_list(struct resource_lists *resources)
{
kfree_resource_list(&(resources->io_head));
kfree_resource_list(&(resources->mem_head));
kfree_resource_list(&(resources->p_mem_head));
kfree_resource_list(&(resources->bus_head));
}
/**
* shpchp_destroy_board_resources: put node back in the resource list
* @resources: list to put nodes back
*/
void shpchp_destroy_board_resources(struct pci_func * func)
{
kfree_resource_list(&(func->io_head));
kfree_resource_list(&(func->mem_head));
kfree_resource_list(&(func->p_mem_head));
kfree_resource_list(&(func->bus_head));
}

View File

@@ -0,0 +1,143 @@
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (c) 1995,2001 Compaq Computer Corporation
* Copyright (c) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001 IBM Corp.
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include "shpchp.h"
/* A few routines that create sysfs entries for the hot plug controller */
static ssize_t show_ctrl (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
out += sprintf(buf, "Free resources: memory\n");
index = 11;
res = ctrl->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: prefetchable memory\n");
index = 11;
res = ctrl->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: IO\n");
index = 11;
res = ctrl->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: bus numbers\n");
index = 11;
res = ctrl->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
return out - buf;
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
static ssize_t show_dev (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
struct pci_func *new_slot;
struct slot *slot;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
slot=ctrl->slot;
while (slot) {
new_slot = shpchp_slot_find(slot->bus, slot->device, 0);
if (!new_slot)
break;
out += sprintf(out, "assigned resources: memory\n");
index = 11;
res = new_slot->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: prefetchable memory\n");
index = 11;
res = new_slot->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: IO\n");
index = 11;
res = new_slot->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: bus numbers\n");
index = 11;
res = new_slot->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
slot=slot->next;
}
return out - buf;
}
static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
void shpchp_create_ctrl_files (struct controller *ctrl)
{
device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
}

View File

@@ -0,0 +1,55 @@
/*
* SHPCHPRM : SHPCHP Resource Manager for ACPI/non-ACPI platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_H_
#define _SHPCHPRM_H_
#ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
#include "shpchprm_legacy.h"
#else
#include "shpchprm_nonacpi.h"
#endif
int shpchprm_init(enum php_ctlr_type ct);
void shpchprm_cleanup(void);
int shpchprm_print_pirt(void);
int shpchprm_find_available_resources(struct controller *ctrl);
int shpchprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
void shpchprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum);
#ifdef DEBUG
#define RES_CHECK(this, bits) \
{ if (((this) & (bits - 1))) \
printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
#else
#define RES_CHECK(this, bits)
#endif
#endif /* _SHPCHPRM_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,439 @@
/*
* SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>,<dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "shpchp.h"
#include "shpchprm.h"
#include "shpchprm_legacy.h"
static void __iomem *shpchp_rom_start;
static u16 unused_IRQ;
void shpchprm_cleanup(void)
{
if (shpchp_rom_start)
iounmap(shpchp_rom_start);
}
int shpchprm_print_pirt(void)
{
return 0;
}
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
int offset = devnum - ctrl->slot_device_offset;
*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
return 0;
}
/* Find the Hot Plug Resource Table in the specified region of memory */
static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end)
{
void __iomem *fp;
void __iomem *endp;
u8 temp1, temp2, temp3, temp4;
int status = 0;
endp = (end - sizeof(struct hrt) + 1);
for (fp = begin; fp <= endp; fp += 16) {
temp1 = readb(fp + SIG0);
temp2 = readb(fp + SIG1);
temp3 = readb(fp + SIG2);
temp4 = readb(fp + SIG3);
if (temp1 == '$' && temp2 == 'H' && temp3 == 'R' && temp4 == 'T') {
status = 1;
break;
}
}
if (!status)
fp = NULL;
dbg("Discovered Hotplug Resource Table at %p\n", fp);
return fp;
}
/*
* shpchprm_find_available_resources
*
* Finds available memory, IO, and IRQ resources for programming
* devices which may be added to the system
* this function is for hot plug ADD!
*
* returns 0 if success
*/
int shpchprm_find_available_resources(struct controller *ctrl)
{
u8 populated_slot;
u8 bridged_slot;
void __iomem *one_slot;
struct pci_func *func = NULL;
int i = 10, index = 0;
u32 temp_dword, rc;
ulong temp_ulong;
struct pci_resource *mem_node;
struct pci_resource *p_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
void __iomem *rom_resource_table;
struct pci_bus lpci_bus, *pci_bus;
u8 cfgspc_irq, temp;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
rom_resource_table = detect_HRT_floating_pointer(shpchp_rom_start, shpchp_rom_start + 0xffff);
dbg("rom_resource_table = %p\n", rom_resource_table);
if (rom_resource_table == NULL)
return -ENODEV;
/* Sum all resources and setup resource maps */
unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
dbg("unused_IRQ = %x\n", unused_IRQ);
temp = 0;
while (unused_IRQ) {
if (unused_IRQ & 1) {
shpchp_disk_irq = temp;
break;
}
unused_IRQ = unused_IRQ >> 1;
temp++;
}
dbg("shpchp_disk_irq= %d\n", shpchp_disk_irq);
unused_IRQ = unused_IRQ >> 1;
temp++;
while (unused_IRQ) {
if (unused_IRQ & 1) {
shpchp_nic_irq = temp;
break;
}
unused_IRQ = unused_IRQ >> 1;
temp++;
}
dbg("shpchp_nic_irq= %d\n", shpchp_nic_irq);
unused_IRQ = readl(rom_resource_table + PCIIRQ);
temp = 0;
pci_read_config_byte(ctrl->pci_dev, PCI_INTERRUPT_LINE, &cfgspc_irq);
if (!shpchp_nic_irq) {
shpchp_nic_irq = cfgspc_irq;
}
if (!shpchp_disk_irq) {
shpchp_disk_irq = cfgspc_irq;
}
dbg("shpchp_disk_irq, shpchp_nic_irq= %d, %d\n", shpchp_disk_irq, shpchp_nic_irq);
one_slot = rom_resource_table + sizeof(struct hrt);
i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
dbg("number_of_entries = %d\n", i);
if (!readb(one_slot + SECONDARY_BUS))
return (1);
dbg("dev|IO base|length|MEMbase|length|PM base|length|PB SB MB\n");
while (i && readb(one_slot + SECONDARY_BUS)) {
u8 dev_func = readb(one_slot + DEV_FUNC);
u8 primary_bus = readb(one_slot + PRIMARY_BUS);
u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
u8 max_bus = readb(one_slot + MAX_BUS);
u16 io_base = readw(one_slot + IO_BASE);
u16 io_length = readw(one_slot + IO_LENGTH);
u16 mem_base = readw(one_slot + MEM_BASE);
u16 mem_length = readw(one_slot + MEM_LENGTH);
u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n",
dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
primary_bus, secondary_bus, max_bus);
/* If this entry isn't for our controller's bus, ignore it */
if (primary_bus != ctrl->slot_bus) {
i--;
one_slot += sizeof(struct slot_rt);
continue;
}
/* find out if this entry is for an occupied slot */
temp_dword = 0xFFFFFFFF;
pci_bus->number = primary_bus;
pci_bus_read_config_dword(pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
dbg("temp_D_word = %x\n", temp_dword);
if (temp_dword != 0xFFFFFFFF) {
index = 0;
func = shpchp_slot_find(primary_bus, dev_func >> 3, 0);
while (func && (func->function != (dev_func & 0x07))) {
dbg("func = %p b:d:f(%x:%x:%x)\n", func, primary_bus, dev_func >> 3, index);
func = shpchp_slot_find(primary_bus, dev_func >> 3, index++);
}
/* If we can't find a match, skip this table entry */
if (!func) {
i--;
one_slot += sizeof(struct slot_rt);
continue;
}
/* this may not work and shouldn't be used */
if (secondary_bus != primary_bus)
bridged_slot = 1;
else
bridged_slot = 0;
populated_slot = 1;
} else {
populated_slot = 0;
bridged_slot = 0;
}
dbg("slot populated =%s \n", populated_slot?"yes":"no");
/* If we've got a valid IO base, use it */
temp_ulong = io_base + io_length;
if ((io_base) && (temp_ulong <= 0x10000)) {
io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)io_base;
io_node->length = (ulong)io_length;
dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length);
if (!populated_slot) {
io_node->next = ctrl->io_head;
ctrl->io_head = io_node;
} else {
io_node->next = func->io_head;
func->io_head = io_node;
}
}
/* If we've got a valid memory base, use it */
temp_ulong = mem_base + mem_length;
if ((mem_base) && (temp_ulong <= 0x10000)) {
mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)mem_base << 16;
mem_node->length = (ulong)(mem_length << 16);
dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length);
if (!populated_slot) {
mem_node->next = ctrl->mem_head;
ctrl->mem_head = mem_node;
} else {
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
}
/*
* If we've got a valid prefetchable memory base, and
* the base + length isn't greater than 0xFFFF
*/
temp_ulong = pre_mem_base + pre_mem_length;
if ((pre_mem_base) && (temp_ulong <= 0x10000)) {
p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)pre_mem_base << 16;
p_mem_node->length = (ulong)pre_mem_length << 16;
dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length);
if (!populated_slot) {
p_mem_node->next = ctrl->p_mem_head;
ctrl->p_mem_head = p_mem_node;
} else {
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
}
/*
* If we've got a valid bus number, use it
* The second condition is to ignore bus numbers on
* populated slots that don't have PCI-PCI bridges
*/
if (secondary_bus && (secondary_bus != primary_bus)) {
bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(max_bus - secondary_bus + 1);
dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length);
if (!populated_slot) {
bus_node->next = ctrl->bus_head;
ctrl->bus_head = bus_node;
} else {
bus_node->next = func->bus_head;
func->bus_head = bus_node;
}
}
i--;
one_slot += sizeof(struct slot_rt);
}
/* If all of the following fail, we don't have any resources for hot plug add */
rc = 1;
rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
return (rc);
}
int shpchprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void shpchprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_shpchprm_init_pci(void)
{
shpchp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
if (!shpchp_rom_start) {
err("Could not ioremap memory region for ROM\n");
return -EIO;
}
return 0;
}
int shpchprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_shpchprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}

View File

@@ -0,0 +1,113 @@
/*
* SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform using HRT
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_LEGACY_H_
#define _SHPCHPRM_LEGACY_H_
#define ROM_PHY_ADDR 0x0F0000
#define ROM_PHY_LEN 0x00FFFF
struct slot_rt {
u8 dev_func;
u8 primary_bus;
u8 secondary_bus;
u8 max_bus;
u16 io_base;
u16 io_length;
u16 mem_base;
u16 mem_length;
u16 pre_mem_base;
u16 pre_mem_length;
} __attribute__ ((packed));
/* offsets to the hotplug slot resource table registers based on the above structure layout */
enum slot_rt_offsets {
DEV_FUNC = offsetof(struct slot_rt, dev_func),
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
MAX_BUS = offsetof(struct slot_rt, max_bus),
IO_BASE = offsetof(struct slot_rt, io_base),
IO_LENGTH = offsetof(struct slot_rt, io_length),
MEM_BASE = offsetof(struct slot_rt, mem_base),
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
};
struct hrt {
char sig0;
char sig1;
char sig2;
char sig3;
u16 unused_IRQ;
u16 PCIIRQ;
u8 number_of_entries;
u8 revision;
u16 reserved1;
u32 reserved2;
} __attribute__ ((packed));
/* offsets to the hotplug resource table registers based on the above structure layout */
enum hrt_offsets {
SIG0 = offsetof(struct hrt, sig0),
SIG1 = offsetof(struct hrt, sig1),
SIG2 = offsetof(struct hrt, sig2),
SIG3 = offsetof(struct hrt, sig3),
UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
PCIIRQ = offsetof(struct hrt, PCIIRQ),
NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
REVISION = offsetof(struct hrt, revision),
HRT_RESERVED1 = offsetof(struct hrt, reserved1),
HRT_RESERVED2 = offsetof(struct hrt, reserved2),
};
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _SHPCHPRM_LEGACY_H_ */

View File

@@ -0,0 +1,434 @@
/*
* SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "shpchp.h"
#include "shpchprm.h"
#include "shpchprm_nonacpi.h"
void shpchprm_cleanup(void)
{
return;
}
int shpchprm_print_pirt(void)
{
return 0;
}
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
int offset = devnum - ctrl->slot_device_offset;
dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
return 0;
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void phprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static int phprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int phprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
shpchp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int phprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
phprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = phprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func, new_func;
int busn = ctrl->slot_bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
/*
if (devn == ctrl->device && funn == ctrl->function)
continue;
*/
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
func = shpchp_slot_find(busn, devn, funn);
if (!func) {
memset(&new_func, 0, sizeof(struct pci_func));
new_func.bus = busn;
new_func.device = devn;
new_func.function = funn;
new_func.is_a_board = 1;
configure_existing_function(ctrl, &new_func);
phprm_dump_func_res(&new_func);
} else {
configure_existing_function(ctrl, func);
phprm_dump_func_res(func);
}
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
}
}
}
return 0;
}
static void phprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
/*
* phprm_find_available_resources
*
* Finds available memory, IO, and IRQ resources for programming
* devices which may be added to the system
* this function is for hot plug ADD!
*
* returns 0 if success
*/
int shpchprm_find_available_resources(struct controller *ctrl)
{
struct pci_func func;
u32 rc;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ctrl->bus;
func.device = ctrl->device;
func.function = ctrl->function;
func.is_a_board = 1;
/* Get resources for this PCI bridge */
rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc);
if (func.mem_head)
func.mem_head->next = ctrl->mem_head;
ctrl->mem_head = func.mem_head;
if (func.p_mem_head)
func.p_mem_head->next = ctrl->p_mem_head;
ctrl->p_mem_head = func.p_mem_head;
if (func.io_head)
func.io_head->next = ctrl->io_head;
ctrl->io_head = func.io_head;
if(func.bus_head)
func.bus_head->next = ctrl->bus_head;
ctrl->bus_head = func.bus_head;
if (ctrl->bus_head)
phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
bind_pci_resources_to_slots (ctrl);
dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
/* If all of the following fail, we don't have any resources for hot plug add */
rc = 1;
rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
return (rc);
}
int shpchprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void shpchprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_shpchprm_init_pci(void)
{
return 0;
}
int shpchprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_shpchprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}

View File

@@ -0,0 +1,56 @@
/*
* SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_NONACPI_H_
#define _SHPCHPRM_NONACPI_H_
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _SHPCHPRM_NONACPI_H_ */

1151
drivers/pci/msi.c Normal file

File diff suppressed because it is too large Load Diff

159
drivers/pci/msi.h Normal file
View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2003-2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#ifndef MSI_H
#define MSI_H
#include <asm/msi.h>
/*
* Assume the maximum number of hot plug slots supported by the system is about
* ten. The worstcase is that each of these slots is hot-added with a device,
* which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which
* attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined
* as below to ensure at least one message is assigned to each detected MSI/
* MSI-X device function.
*/
#define NR_HP_RESERVED_VECTORS 20
extern int vector_irq[NR_VECTORS];
extern cpumask_t pending_irq_balance_cpumask[NR_IRQS];
extern void (*interrupt[NR_IRQS])(void);
extern int pci_vector_resources(int last, int nr_released);
#ifdef CONFIG_SMP
#define set_msi_irq_affinity set_msi_affinity
#else
#define set_msi_irq_affinity NULL
#endif
#ifndef CONFIG_IRQBALANCE
static inline void move_msi(int vector) {}
#endif
/*
* MSI-X Address Register
*/
#define PCI_MSIX_FLAGS_QSIZE 0x7FF
#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
#define PCI_MSIX_FLAGS_BITMASK (1 << 0)
#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0
#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4
#define PCI_MSIX_ENTRY_DATA_OFFSET 8
#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12
#define PCI_MSIX_ENTRY_SIZE 16
#define msi_control_reg(base) (base + PCI_MSI_FLAGS)
#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO)
#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI)
#define msi_data_reg(base, is64bit) \
( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 )
#define msi_mask_bits_reg(base, is64bit) \
( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4)
#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE
#define multi_msi_capable(control) \
(1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1))
#define multi_msi_enable(control, num) \
control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE);
#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT)
#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT)
#define msi_enable(control, num) multi_msi_enable(control, num); \
control |= PCI_MSI_FLAGS_ENABLE
#define msix_control_reg msi_control_reg
#define msix_table_offset_reg(base) (base + 0x04)
#define msix_pba_offset_reg(base) (base + 0x08)
#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE
#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE
#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1)
#define multi_msix_capable msix_table_size
#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK)
#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK)
#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK)
/*
* MSI Defined Data Structures
*/
#define MSI_ADDRESS_HEADER 0xfee
#define MSI_ADDRESS_HEADER_SHIFT 12
#define MSI_ADDRESS_HEADER_MASK 0xfff000
#define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f
#define MSI_TARGET_CPU_MASK 0xff
#define MSI_DELIVERY_MODE 0
#define MSI_LEVEL_MODE 1 /* Edge always assert */
#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */
#define MSI_PHYSICAL_MODE 0
#define MSI_LOGICAL_MODE 1
#define MSI_REDIRECTION_HINT_MODE 0
struct msg_data {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u32 vector : 8;
__u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
__u32 reserved_1 : 3;
__u32 level : 1; /* 0: deassert | 1: assert */
__u32 trigger : 1; /* 0: edge | 1: level */
__u32 reserved_2 : 16;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u32 reserved_2 : 16;
__u32 trigger : 1; /* 0: edge | 1: level */
__u32 level : 1; /* 0: deassert | 1: assert */
__u32 reserved_1 : 3;
__u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */
__u32 vector : 8;
#else
#error "Bitfield endianness not defined! Check your byteorder.h"
#endif
} __attribute__ ((packed));
struct msg_address {
union {
struct {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u32 reserved_1 : 2;
__u32 dest_mode : 1; /*0:physic | 1:logic */
__u32 redirection_hint: 1; /*0: dedicated CPU
1: lowest priority */
__u32 reserved_2 : 4;
__u32 dest_id : 24; /* Destination ID */
#elif defined(__BIG_ENDIAN_BITFIELD)
__u32 dest_id : 24; /* Destination ID */
__u32 reserved_2 : 4;
__u32 redirection_hint: 1; /*0: dedicated CPU
1: lowest priority */
__u32 dest_mode : 1; /*0:physic | 1:logic */
__u32 reserved_1 : 2;
#else
#error "Bitfield endianness not defined! Check your byteorder.h"
#endif
}u;
__u32 value;
}lo_address;
__u32 hi_address;
} __attribute__ ((packed));
struct msi_desc {
struct {
__u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
__u8 maskbit : 1; /* mask-pending bit supported ? */
__u8 state : 1; /* {0: free, 1: busy} */
__u8 reserved: 1; /* reserved */
__u8 entry_nr; /* specific enabled entry */
__u8 default_vector; /* default pre-assigned vector */
__u8 current_cpu; /* current destination cpu */
}msi_attrib;
struct {
__u16 head;
__u16 tail;
}link;
void __iomem *mask_base;
struct pci_dev *dev;
};
#endif /* MSI_H */

137
drivers/pci/names.c Normal file
View File

@@ -0,0 +1,137 @@
/*
* PCI Class and Device Name Tables
*
* Copyright 1993--1999 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang, Martin Mares
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#ifdef CONFIG_PCI_NAMES
struct pci_device_info {
unsigned short device;
unsigned short seen;
const char *name;
};
struct pci_vendor_info {
unsigned short vendor;
unsigned short nr;
const char *name;
struct pci_device_info *devices;
};
/*
* This is ridiculous, but we want the strings in
* the .init section so that they don't take up
* real memory.. Parse the same file multiple times
* to get all the info.
*/
#define VENDOR( vendor, name ) static char __vendorstr_##vendor[] __devinitdata = name;
#define ENDVENDOR()
#define DEVICE( vendor, device, name ) static char __devicestr_##vendor##device[] __devinitdata = name;
#include "devlist.h"
#define VENDOR( vendor, name ) static struct pci_device_info __devices_##vendor[] __devinitdata = {
#define ENDVENDOR() };
#define DEVICE( vendor, device, name ) { 0x##device, 0, __devicestr_##vendor##device },
#include "devlist.h"
static struct pci_vendor_info __devinitdata pci_vendor_list[] = {
#define VENDOR( vendor, name ) { 0x##vendor, sizeof(__devices_##vendor) / sizeof(struct pci_device_info), __vendorstr_##vendor, __devices_##vendor },
#define ENDVENDOR()
#define DEVICE( vendor, device, name )
#include "devlist.h"
};
#define VENDORS (sizeof(pci_vendor_list)/sizeof(struct pci_vendor_info))
void __devinit pci_name_device(struct pci_dev *dev)
{
const struct pci_vendor_info *vendor_p = pci_vendor_list;
int i = VENDORS;
char *name = dev->pretty_name;
do {
if (vendor_p->vendor == dev->vendor)
goto match_vendor;
vendor_p++;
} while (--i);
/* Couldn't find either the vendor nor the device */
sprintf(name, "PCI device %04x:%04x", dev->vendor, dev->device);
return;
match_vendor: {
struct pci_device_info *device_p = vendor_p->devices;
int i = vendor_p->nr;
while (i > 0) {
if (device_p->device == dev->device)
goto match_device;
device_p++;
i--;
}
/* Ok, found the vendor, but unknown device */
sprintf(name, "PCI device %04x:%04x (%." PCI_NAME_HALF "s)",
dev->vendor, dev->device, vendor_p->name);
return;
/* Full match */
match_device: {
char *n = name + sprintf(name, "%s %s",
vendor_p->name, device_p->name);
int nr = device_p->seen + 1;
device_p->seen = nr;
if (nr > 1)
sprintf(n, " (#%d)", nr);
}
}
}
/*
* Class names. Not in .init section as they are needed in runtime.
*/
static u16 pci_class_numbers[] = {
#define CLASS(x,y) 0x##x,
#include "classlist.h"
};
static char *pci_class_names[] = {
#define CLASS(x,y) y,
#include "classlist.h"
};
char *
pci_class_name(u32 class)
{
int i;
for(i=0; i<sizeof(pci_class_numbers)/sizeof(pci_class_numbers[0]); i++)
if (pci_class_numbers[i] == class)
return pci_class_names[i];
return NULL;
}
#else
void __devinit pci_name_device(struct pci_dev *dev)
{
}
char *
pci_class_name(u32 class)
{
return NULL;
}
#endif /* CONFIG_PCI_NAMES */

209
drivers/pci/pci-acpi.c Normal file
View File

@@ -0,0 +1,209 @@
/*
* File: pci-acpi.c
* Purpose: Provide PCI supports in ACPI
*
* Copyright (C) 2004 Intel
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <acpi/acpi.h>
#include <acpi/acnamesp.h>
#include <acpi/acresrc.h>
#include <acpi/acpi_bus.h>
#include <linux/pci-acpi.h>
static u32 ctrlset_buf[3] = {0, 0, 0};
static u32 global_ctrlsets = 0;
u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
static acpi_status
acpi_query_osc (
acpi_handle handle,
u32 level,
void *context,
void **retval )
{
acpi_status status;
struct acpi_object_list input;
union acpi_object in_params[4];
struct acpi_buffer output;
union acpi_object out_obj;
u32 osc_dw0;
/* Setting up output buffer */
output.length = sizeof(out_obj) + 3*sizeof(u32);
output.pointer = &out_obj;
/* Setting up input parameters */
input.count = 4;
input.pointer = in_params;
in_params[0].type = ACPI_TYPE_BUFFER;
in_params[0].buffer.length = 16;
in_params[0].buffer.pointer = OSC_UUID;
in_params[1].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = 1;
in_params[2].type = ACPI_TYPE_INTEGER;
in_params[2].integer.value = 3;
in_params[3].type = ACPI_TYPE_BUFFER;
in_params[3].buffer.length = 12;
in_params[3].buffer.pointer = (u8 *)context;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE (status)) {
printk(KERN_DEBUG
"Evaluate _OSC Set fails. Status = 0x%04x\n", status);
return status;
}
if (out_obj.type != ACPI_TYPE_BUFFER) {
printk(KERN_DEBUG
"Evaluate _OSC returns wrong type\n");
return AE_TYPE;
}
osc_dw0 = *((u32 *) out_obj.buffer.pointer);
if (osc_dw0) {
if (osc_dw0 & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request fails\n");
if (osc_dw0 & OSC_INVALID_UUID_ERROR)
printk(KERN_DEBUG "_OSC invalid UUID\n");
if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
/* Update Global Control Set */
global_ctrlsets = *((u32 *)(out_obj.buffer.pointer+8));
return AE_OK;
}
return AE_ERROR;
}
/* Update Global Control Set */
global_ctrlsets = *((u32 *)(out_obj.buffer.pointer + 8));
return AE_OK;
}
static acpi_status
acpi_run_osc (
acpi_handle handle,
u32 level,
void *context,
void **retval )
{
acpi_status status;
struct acpi_object_list input;
union acpi_object in_params[4];
struct acpi_buffer output;
union acpi_object out_obj;
u32 osc_dw0;
/* Setting up output buffer */
output.length = sizeof(out_obj) + 3*sizeof(u32);
output.pointer = &out_obj;
/* Setting up input parameters */
input.count = 4;
input.pointer = in_params;
in_params[0].type = ACPI_TYPE_BUFFER;
in_params[0].buffer.length = 16;
in_params[0].buffer.pointer = OSC_UUID;
in_params[1].type = ACPI_TYPE_INTEGER;
in_params[1].integer.value = 1;
in_params[2].type = ACPI_TYPE_INTEGER;
in_params[2].integer.value = 3;
in_params[3].type = ACPI_TYPE_BUFFER;
in_params[3].buffer.length = 12;
in_params[3].buffer.pointer = (u8 *)context;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE (status)) {
printk(KERN_DEBUG
"Evaluate _OSC Set fails. Status = 0x%04x\n", status);
return status;
}
if (out_obj.type != ACPI_TYPE_BUFFER) {
printk(KERN_DEBUG
"Evaluate _OSC returns wrong type\n");
return AE_TYPE;
}
osc_dw0 = *((u32 *) out_obj.buffer.pointer);
if (osc_dw0) {
if (osc_dw0 & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request fails\n");
if (osc_dw0 & OSC_INVALID_UUID_ERROR)
printk(KERN_DEBUG "_OSC invalid UUID\n");
if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
printk(KERN_DEBUG "_OSC FW not grant req. control\n");
return AE_SUPPORT;
}
return AE_ERROR;
}
return AE_OK;
}
/**
* pci_osc_support_set - register OS support to Firmware
* @flags: OS support bits
*
* Update OS support fields and doing a _OSC Query to obtain an update
* from Firmware on supported control bits.
**/
acpi_status pci_osc_support_set(u32 flags)
{
u32 temp;
if (!(flags & OSC_SUPPORT_MASKS)) {
return AE_TYPE;
}
ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
/* do _OSC query for all possible controls */
temp = ctrlset_buf[OSC_CONTROL_TYPE];
ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
acpi_get_devices ( PCI_ROOT_HID_STRING,
acpi_query_osc,
ctrlset_buf,
NULL );
ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
ctrlset_buf[OSC_CONTROL_TYPE] = temp;
return AE_OK;
}
EXPORT_SYMBOL(pci_osc_support_set);
/**
* pci_osc_control_set - commit requested control to Firmware
* @flags: driver's requested control bits
*
* Attempt to take control from Firmware on requested control bits.
**/
acpi_status pci_osc_control_set(u32 flags)
{
acpi_status status;
u32 ctrlset;
ctrlset = (flags & OSC_CONTROL_MASKS);
if (!ctrlset) {
return AE_TYPE;
}
if (ctrlset_buf[OSC_SUPPORT_TYPE] &&
((global_ctrlsets & ctrlset) != ctrlset)) {
return AE_SUPPORT;
}
ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
status = acpi_get_devices ( PCI_ROOT_HID_STRING,
acpi_run_osc,
ctrlset_buf,
NULL );
if (ACPI_FAILURE (status)) {
ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
}
return status;
}
EXPORT_SYMBOL(pci_osc_control_set);

531
drivers/pci/pci-driver.c Normal file
View File

@@ -0,0 +1,531 @@
/*
* drivers/pci/pci-driver.c
*
*/
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/pci-dynids.h>
#include "pci.h"
/*
* Registration of PCI drivers and handling of hot-pluggable devices.
*/
/*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
#ifdef CONFIG_HOTPLUG
/**
* pci_device_probe_dynamic()
*
* Walk the dynamic ID list looking for a match.
* returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
*/
static int
pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
{
int error = -ENODEV;
struct list_head *pos;
struct dynid *dynid;
spin_lock(&drv->dynids.lock);
list_for_each(pos, &drv->dynids.list) {
dynid = list_entry(pos, struct dynid, node);
if (pci_match_one_device(&dynid->id, pci_dev)) {
spin_unlock(&drv->dynids.lock);
error = drv->probe(pci_dev, &dynid->id);
if (error >= 0) {
pci_dev->driver = drv;
return 0;
}
return error;
}
}
spin_unlock(&drv->dynids.lock);
return error;
}
/**
* store_new_id
*
* Adds a new dynamic pci device ID to this driver,
* and causes the driver to probe for all devices again.
*/
static inline ssize_t
store_new_id(struct device_driver *driver, const char *buf, size_t count)
{
struct dynid *dynid;
struct bus_type * bus;
struct pci_driver *pdrv = to_pci_driver(driver);
__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0;
int fields=0;
fields = sscanf(buf, "%x %x %x %x %x %x %lux",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask, &driver_data);
if (fields < 0)
return -EINVAL;
dynid = kmalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid)
return -ENOMEM;
memset(dynid, 0, sizeof(*dynid));
INIT_LIST_HEAD(&dynid->node);
dynid->id.vendor = vendor;
dynid->id.device = device;
dynid->id.subvendor = subvendor;
dynid->id.subdevice = subdevice;
dynid->id.class = class;
dynid->id.class_mask = class_mask;
dynid->id.driver_data = pdrv->dynids.use_driver_data ?
driver_data : 0UL;
spin_lock(&pdrv->dynids.lock);
list_add_tail(&pdrv->dynids.list, &dynid->node);
spin_unlock(&pdrv->dynids.lock);
bus = get_bus(pdrv->driver.bus);
if (bus) {
if (get_driver(&pdrv->driver)) {
down_write(&bus->subsys.rwsem);
driver_attach(&pdrv->driver);
up_write(&bus->subsys.rwsem);
put_driver(&pdrv->driver);
}
put_bus(bus);
}
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
static inline void
pci_init_dynids(struct pci_dynids *dynids)
{
spin_lock_init(&dynids->lock);
INIT_LIST_HEAD(&dynids->list);
}
static void
pci_free_dynids(struct pci_driver *drv)
{
struct list_head *pos, *n;
struct dynid *dynid;
spin_lock(&drv->dynids.lock);
list_for_each_safe(pos, n, &drv->dynids.list) {
dynid = list_entry(pos, struct dynid, node);
list_del(&dynid->node);
kfree(dynid);
}
spin_unlock(&drv->dynids.lock);
}
static int
pci_create_newid_file(struct pci_driver *drv)
{
int error = 0;
if (drv->probe != NULL)
error = sysfs_create_file(&drv->driver.kobj,
&driver_attr_new_id.attr);
return error;
}
static int
pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv)
{
struct list_head *pos;
struct dynid *dynid;
spin_lock(&pci_drv->dynids.lock);
list_for_each(pos, &pci_drv->dynids.list) {
dynid = list_entry(pos, struct dynid, node);
if (pci_match_one_device(&dynid->id, pci_dev)) {
spin_unlock(&pci_drv->dynids.lock);
return 1;
}
}
spin_unlock(&pci_drv->dynids.lock);
return 0;
}
#else /* !CONFIG_HOTPLUG */
static inline int pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
{
return -ENODEV;
}
static inline void pci_init_dynids(struct pci_dynids *dynids) {}
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv)
{
return 0;
}
static inline int pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv)
{
return 0;
}
#endif
/**
* pci_match_device - Tell if a PCI device structure has a matching
* PCI device id structure
* @ids: array of PCI device id structures to search in
* @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices.Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
const struct pci_device_id *
pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
{
while (ids->vendor || ids->subvendor || ids->class_mask) {
if (pci_match_one_device(ids, dev))
return ids;
ids++;
}
return NULL;
}
/**
* pci_device_probe_static()
*
* returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
*/
static int
pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev)
{
int error = -ENODEV;
const struct pci_device_id *id;
if (!drv->id_table)
return error;
id = pci_match_device(drv->id_table, pci_dev);
if (id)
error = drv->probe(pci_dev, id);
if (error >= 0) {
pci_dev->driver = drv;
error = 0;
}
return error;
}
/**
* __pci_device_probe()
*
* returns 0 on success, else error.
* side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
*/
static int
__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
{
int error = 0;
if (!pci_dev->driver && drv->probe) {
error = pci_device_probe_static(drv, pci_dev);
if (error == -ENODEV)
error = pci_device_probe_dynamic(drv, pci_dev);
}
return error;
}
static int pci_device_probe(struct device * dev)
{
int error = 0;
struct pci_driver *drv;
struct pci_dev *pci_dev;
drv = to_pci_driver(dev->driver);
pci_dev = to_pci_dev(dev);
pci_dev_get(pci_dev);
error = __pci_device_probe(drv, pci_dev);
if (error)
pci_dev_put(pci_dev);
return error;
}
static int pci_device_remove(struct device * dev)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv) {
if (drv->remove)
drv->remove(pci_dev);
pci_dev->driver = NULL;
}
/*
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
* unfortunate fact is there are too many odd BIOS and bridge setups
* that don't like drivers doing that all of the time.
* Oh well, we can dream of sane hardware when we sleep, no matter how
* horrible the crap we have to deal with is when we are awake...
*/
pci_dev_put(pci_dev);
return 0;
}
static int pci_device_suspend(struct device * dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
int i = 0;
if (drv && drv->suspend)
i = drv->suspend(pci_dev, state);
else
pci_save_state(pci_dev);
return i;
}
/*
* Default resume method for devices that have no driver provided resume,
* or not even a driver at all.
*/
static void pci_default_resume(struct pci_dev *pci_dev)
{
/* restore the PCI config space */
pci_restore_state(pci_dev);
/* if the device was enabled before suspend, reenable */
if (pci_dev->is_enabled)
pci_enable_device(pci_dev);
/* if the device was busmaster before the suspend, make it busmaster again */
if (pci_dev->is_busmaster)
pci_set_master(pci_dev);
}
static int pci_device_resume(struct device * dev)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv && drv->resume)
drv->resume(pci_dev);
else
pci_default_resume(pci_dev);
return 0;
}
#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
static ssize_t
pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
{
struct device_driver *driver = kobj_to_pci_driver(kobj);
struct driver_attribute *dattr = attr_to_driver_attribute(attr);
ssize_t ret = 0;
if (get_driver(driver)) {
if (dattr->show)
ret = dattr->show(driver, buf);
put_driver(driver);
}
return ret;
}
static ssize_t
pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct device_driver *driver = kobj_to_pci_driver(kobj);
struct driver_attribute *dattr = attr_to_driver_attribute(attr);
ssize_t ret = 0;
if (get_driver(driver)) {
if (dattr->store)
ret = dattr->store(driver, buf, count);
put_driver(driver);
}
return ret;
}
static struct sysfs_ops pci_driver_sysfs_ops = {
.show = pci_driver_attr_show,
.store = pci_driver_attr_store,
};
static struct kobj_type pci_driver_kobj_type = {
.sysfs_ops = &pci_driver_sysfs_ops,
};
static int
pci_populate_driver_dir(struct pci_driver *drv)
{
return pci_create_newid_file(drv);
}
/**
* pci_register_driver - register a new pci driver
* @drv: the driver structure to register
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occured, the driver remains registered even if
* no device was claimed during registration.
*/
int pci_register_driver(struct pci_driver *drv)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
drv->driver.probe = pci_device_probe;
drv->driver.remove = pci_device_remove;
drv->driver.owner = drv->owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
pci_init_dynids(&drv->dynids);
/* register with core */
error = driver_register(&drv->driver);
if (!error)
pci_populate_driver_dir(drv);
return error;
}
/**
* pci_unregister_driver - unregister a pci driver
* @drv: the driver structure to unregister
*
* Deletes the driver structure from the list of registered PCI drivers,
* gives it a chance to clean up by calling its remove() function for
* each device it was responsible for, and marks those devices as
* driverless.
*/
void
pci_unregister_driver(struct pci_driver *drv)
{
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
static struct pci_driver pci_compat_driver = {
.name = "compat"
};
/**
* pci_dev_driver - get the pci_driver of a device
* @dev: the device to query
*
* Returns the appropriate pci_driver structure or %NULL if there is no
* registered driver for the device.
*/
struct pci_driver *
pci_dev_driver(const struct pci_dev *dev)
{
if (dev->driver)
return dev->driver;
else {
int i;
for(i=0; i<=PCI_ROM_RESOURCE; i++)
if (dev->resource[i].flags & IORESOURCE_BUSY)
return &pci_compat_driver;
}
return NULL;
}
/**
* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
* @ids: array of PCI device id structures to search in
* @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices.Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
static int pci_bus_match(struct device * dev, struct device_driver * drv)
{
const struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * pci_drv = to_pci_driver(drv);
const struct pci_device_id * ids = pci_drv->id_table;
const struct pci_device_id *found_id;
if (!ids)
return 0;
found_id = pci_match_device(ids, pci_dev);
if (found_id)
return 1;
return pci_bus_match_dynids(pci_dev, pci_drv);
}
/**
* pci_dev_get - increments the reference count of the pci device structure
* @dev: the device being referenced
*
* Each live reference to a device should be refcounted.
*
* Drivers for PCI devices should normally record such references in
* their probe() methods, when they bind to a device, and release
* them by calling pci_dev_put(), in their disconnect() methods.
*
* A pointer to the device with the incremented reference counter is returned.
*/
struct pci_dev *pci_dev_get(struct pci_dev *dev)
{
if (dev)
get_device(&dev->dev);
return dev;
}
/**
* pci_dev_put - release a use of the pci device structure
* @dev: device that's been disconnected
*
* Must be called when a user of a device is finished with it. When the last
* user of the device calls this function, the memory of the device is freed.
*/
void pci_dev_put(struct pci_dev *dev)
{
if (dev)
put_device(&dev->dev);
}
#ifndef CONFIG_HOTPLUG
int pci_hotplug (struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.hotplug = pci_hotplug,
.suspend = pci_device_suspend,
.resume = pci_device_resume,
.dev_attrs = pci_dev_attrs,
};
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);
}
postcore_initcall(pci_driver_init);
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
EXPORT_SYMBOL(pci_dev_driver);
EXPORT_SYMBOL(pci_bus_type);
EXPORT_SYMBOL(pci_dev_get);
EXPORT_SYMBOL(pci_dev_put);

490
drivers/pci/pci-sysfs.c Normal file
View File

@@ -0,0 +1,490 @@
/*
* drivers/pci/pci-sysfs.c
*
* (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
* (C) Copyright 2002-2004 IBM Corp.
* (C) Copyright 2003 Matthew Wilcox
* (C) Copyright 2003 Hewlett-Packard
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
*
* File attributes for PCI devices
*
* Modeled after usb's driverfs.c
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/stat.h>
#include <linux/topology.h>
#include <linux/mm.h>
#include "pci.h"
static int sysfs_initialized; /* = 0 */
/* show configuration fields */
#define pci_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, char *buf) \
{ \
struct pci_dev *pdev; \
\
pdev = to_pci_dev (dev); \
return sprintf (buf, format_string, pdev->field); \
}
pci_config_attr(vendor, "0x%04x\n");
pci_config_attr(device, "0x%04x\n");
pci_config_attr(subsystem_vendor, "0x%04x\n");
pci_config_attr(subsystem_device, "0x%04x\n");
pci_config_attr(class, "0x%06x\n");
pci_config_attr(irq, "%u\n");
static ssize_t local_cpus_show(struct device *dev, char *buf)
{
cpumask_t mask = pcibus_to_cpumask(to_pci_dev(dev)->bus);
int len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
strcat(buf,"\n");
return 1+len;
}
/* show resources */
static ssize_t
resource_show(struct device * dev, char * buf)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
char * str = buf;
int i;
int max = 7;
if (pci_dev->subordinate)
max = DEVICE_COUNT_RESOURCE;
for (i = 0; i < max; i++) {
str += sprintf(str,"0x%016lx 0x%016lx 0x%016lx\n",
pci_resource_start(pci_dev,i),
pci_resource_end(pci_dev,i),
pci_resource_flags(pci_dev,i));
}
return (str - buf);
}
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
__ATTR_RO(device),
__ATTR_RO(subsystem_vendor),
__ATTR_RO(subsystem_device),
__ATTR_RO(class),
__ATTR_RO(irq),
__ATTR_RO(local_cpus),
__ATTR_NULL,
};
static ssize_t
pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64;
loff_t init_off = off;
/* Several chips lock up trying to read undefined config space */
if (capable(CAP_SYS_ADMIN)) {
size = dev->cfg_size;
} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
size = 128;
}
if (off > size)
return 0;
if (off + count > size) {
size -= off;
count = size;
} else {
size = count;
}
while (off & 3) {
unsigned char val;
pci_read_config_byte(dev, off, &val);
buf[off - init_off] = val;
off++;
if (--size == 0)
break;
}
while (size > 3) {
unsigned int val;
pci_read_config_dword(dev, off, &val);
buf[off - init_off] = val & 0xff;
buf[off - init_off + 1] = (val >> 8) & 0xff;
buf[off - init_off + 2] = (val >> 16) & 0xff;
buf[off - init_off + 3] = (val >> 24) & 0xff;
off += 4;
size -= 4;
}
while (size > 0) {
unsigned char val;
pci_read_config_byte(dev, off, &val);
buf[off - init_off] = val;
off++;
--size;
}
return count;
}
static ssize_t
pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count;
loff_t init_off = off;
if (off > dev->cfg_size)
return 0;
if (off + count > dev->cfg_size) {
size = dev->cfg_size - off;
count = size;
}
while (off & 3) {
pci_write_config_byte(dev, off, buf[off - init_off]);
off++;
if (--size == 0)
break;
}
while (size > 3) {
unsigned int val = buf[off - init_off];
val |= (unsigned int) buf[off - init_off + 1] << 8;
val |= (unsigned int) buf[off - init_off + 2] << 16;
val |= (unsigned int) buf[off - init_off + 3] << 24;
pci_write_config_dword(dev, off, val);
off += 4;
size -= 4;
}
while (size > 0) {
pci_write_config_byte(dev, off, buf[off - init_off]);
off++;
--size;
}
return count;
}
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
* @kobj: kobject corresponding to file to read from
* @buf: buffer to store results
* @off: offset into legacy I/O port space
* @count: number of bytes to read
*
* Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
* callback routine (pci_legacy_read).
*/
ssize_t
pci_read_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(container_of(kobj,
struct class_device,
kobj));
/* Only support 1, 2 or 4 byte accesses */
if (count != 1 && count != 2 && count != 4)
return -EINVAL;
return pci_legacy_read(bus, off, (u32 *)buf, count);
}
/**
* pci_write_legacy_io - write byte(s) to legacy I/O port space
* @kobj: kobject corresponding to file to read from
* @buf: buffer containing value to be written
* @off: offset into legacy I/O port space
* @count: number of bytes to write
*
* Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
* callback routine (pci_legacy_write).
*/
ssize_t
pci_write_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(container_of(kobj,
struct class_device,
kobj));
/* Only support 1, 2 or 4 byte accesses */
if (count != 1 && count != 2 && count != 4)
return -EINVAL;
return pci_legacy_write(bus, off, *(u32 *)buf, count);
}
/**
* pci_mmap_legacy_mem - map legacy PCI memory into user memory space
* @kobj: kobject corresponding to device to be mapped
* @attr: struct bin_attribute for this file
* @vma: struct vm_area_struct passed to mmap
*
* Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap
* legacy memory space (first meg of bus space) into application virtual
* memory space.
*/
int
pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
struct vm_area_struct *vma)
{
struct pci_bus *bus = to_pci_bus(container_of(kobj,
struct class_device,
kobj));
return pci_mmap_legacy_page_range(bus, vma);
}
#endif /* HAVE_PCI_LEGACY */
#ifdef HAVE_PCI_MMAP
/**
* pci_mmap_resource - map a PCI resource into user memory space
* @kobj: kobject for mapping
* @attr: struct bin_attribute for the file being mapped
* @vma: struct vm_area_struct passed into the mmap
*
* Use the regular PCI mapping routines to map a PCI resource into userspace.
* FIXME: write combining? maybe automatic for prefetchable regions?
*/
static int
pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
struct vm_area_struct *vma)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
struct resource *res = (struct resource *)attr->private;
enum pci_mmap_state mmap_type;
vma->vm_pgoff += res->start >> PAGE_SHIFT;
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
return pci_mmap_page_range(pdev, vma, mmap_type, 0);
}
/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
*
* Walk the resources in @dev creating files for each resource available.
*/
static void
pci_create_resource_files(struct pci_dev *pdev)
{
int i;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
struct bin_attribute *res_attr;
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
res_attr = kmalloc(sizeof(*res_attr) + 10, GFP_ATOMIC);
if (res_attr) {
memset(res_attr, 0, sizeof(*res_attr) + 10);
pdev->res_attr[i] = res_attr;
/* Allocated above after the res_attr struct */
res_attr->attr.name = (char *)(res_attr + 1);
sprintf(res_attr->attr.name, "resource%d", i);
res_attr->size = pci_resource_len(pdev, i);
res_attr->attr.mode = S_IRUSR | S_IWUSR;
res_attr->attr.owner = THIS_MODULE;
res_attr->mmap = pci_mmap_resource;
res_attr->private = &pdev->resource[i];
sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
}
}
}
/**
* pci_remove_resource_files - cleanup resource files
* @dev: dev to cleanup
*
* If we created resource files for @dev, remove them from sysfs and
* free their resources.
*/
static void
pci_remove_resource_files(struct pci_dev *pdev)
{
int i;
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
struct bin_attribute *res_attr;
res_attr = pdev->res_attr[i];
if (res_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
}
}
#else /* !HAVE_PCI_MMAP */
static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
/**
* pci_write_rom - used to enable access to the PCI ROM display
* @kobj: kernel object handle
* @buf: user input
* @off: file offset
* @count: number of byte in input
*
* writing anything except 0 enables it
*/
static ssize_t
pci_write_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
if ((off == 0) && (*buf == '0') && (count == 2))
pdev->rom_attr_enabled = 0;
else
pdev->rom_attr_enabled = 1;
return count;
}
/**
* pci_read_rom - read a PCI ROM
* @kobj: kernel object handle
* @buf: where to put the data we read from the ROM
* @off: file offset
* @count: number of bytes to read
*
* Put @count bytes starting at @off into @buf from the ROM in the PCI
* device corresponding to @kobj.
*/
static ssize_t
pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
void __iomem *rom;
size_t size;
if (!pdev->rom_attr_enabled)
return -EINVAL;
rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
if (!rom)
return 0;
if (off >= size)
count = 0;
else {
if (off + count > size)
count = size - off;
memcpy_fromio(buf, rom + off, count);
}
pci_unmap_rom(pdev, rom);
return count;
}
static struct bin_attribute pci_config_attr = {
.attr = {
.name = "config",
.mode = S_IRUGO | S_IWUSR,
.owner = THIS_MODULE,
},
.size = 256,
.read = pci_read_config,
.write = pci_write_config,
};
static struct bin_attribute pcie_config_attr = {
.attr = {
.name = "config",
.mode = S_IRUGO | S_IWUSR,
.owner = THIS_MODULE,
},
.size = 4096,
.read = pci_read_config,
.write = pci_write_config,
};
int pci_create_sysfs_dev_files (struct pci_dev *pdev)
{
if (!sysfs_initialized)
return -EACCES;
if (pdev->cfg_size < 4096)
sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
pci_create_resource_files(pdev);
/* If the device has a ROM, try to expose it in sysfs. */
if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
struct bin_attribute *rom_attr;
rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC);
if (rom_attr) {
memset(rom_attr, 0x00, sizeof(*rom_attr));
pdev->rom_attr = rom_attr;
rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
rom_attr->attr.name = "rom";
rom_attr->attr.mode = S_IRUSR;
rom_attr->attr.owner = THIS_MODULE;
rom_attr->read = pci_read_rom;
rom_attr->write = pci_write_rom;
sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
}
}
/* add platform-specific attributes */
pcibios_add_platform_entries(pdev);
return 0;
}
/**
* pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
* @pdev: device whose entries we should free
*
* Cleanup when @pdev is removed from sysfs.
*/
void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
{
if (pdev->cfg_size < 4096)
sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
pci_remove_resource_files(pdev);
if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
if (pdev->rom_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
kfree(pdev->rom_attr);
}
}
}
static int __init pci_sysfs_init(void)
{
struct pci_dev *pdev = NULL;
sysfs_initialized = 1;
for_each_pci_dev(pdev)
pci_create_sysfs_dev_files(pdev);
return 0;
}
__initcall(pci_sysfs_init);

837
drivers/pci/pci.c Normal file
View File

@@ -0,0 +1,837 @@
/*
* $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
*
* PCI Bus Services, see include/linux/pci.h for further explanation.
*
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
*
* Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/dma.h> /* isa_dma_bridge_buggy */
/**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search
*
* Given a PCI bus, returns the highest PCI bus number present in the set
* including the given PCI bus and its list of child PCI buses.
*/
unsigned char __devinit
pci_bus_max_busnr(struct pci_bus* bus)
{
struct list_head *tmp;
unsigned char max, n;
max = bus->number;
list_for_each(tmp, &bus->children) {
n = pci_bus_max_busnr(pci_bus_b(tmp));
if(n > max)
max = n;
}
return max;
}
/**
* pci_max_busnr - returns maximum PCI bus number
*
* Returns the highest PCI bus number present in the system global list of
* PCI buses.
*/
unsigned char __devinit
pci_max_busnr(void)
{
struct pci_bus *bus = NULL;
unsigned char max, n;
max = 0;
while ((bus = pci_find_next_bus(bus)) != NULL) {
n = pci_bus_max_busnr(bus);
if(n > max)
max = n;
}
return max;
}
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;
pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
pci_bus_read_config_byte(bus, devfn, PCI_CAPABILITY_LIST, &pos);
break;
case PCI_HEADER_TYPE_CARDBUS:
pci_bus_read_config_byte(bus, devfn, PCI_CB_CAPABILITY_LIST, &pos);
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;
}
/**
* pci_find_capability - query for devices' capabilities
* @dev: PCI device to query
* @cap: capability code
*
* Tell if a device supports a given PCI capability.
* Returns the address of the requested capability structure within the
* device's PCI configuration space or 0 in case the device does not
* support it. Possible values for @cap:
*
* %PCI_CAP_ID_PM Power Management
* %PCI_CAP_ID_AGP Accelerated Graphics Port
* %PCI_CAP_ID_VPD Vital Product Data
* %PCI_CAP_ID_SLOTID Slot Identification
* %PCI_CAP_ID_MSI Message Signalled Interrupts
* %PCI_CAP_ID_CHSWP CompactPCI HotSwap
* %PCI_CAP_ID_PCIX PCI-X
* %PCI_CAP_ID_EXP PCI Express
*/
int pci_find_capability(struct pci_dev *dev, int cap)
{
return __pci_bus_find_cap(dev->bus, dev->devfn, dev->hdr_type, cap);
}
/**
* pci_bus_find_capability - query for devices' capabilities
* @bus: the PCI bus to query
* @devfn: PCI device to query
* @cap: capability code
*
* Like pci_find_capability() but works for pci devices that do not have a
* pci_dev structure set up yet.
*
* Returns the address of the requested capability structure within the
* device's PCI configuration space or 0 in case the device does not
* support it.
*/
int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
{
u8 hdr_type;
pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);
return __pci_bus_find_cap(bus, devfn, hdr_type & 0x7f, cap);
}
/**
* pci_find_ext_capability - Find an extended capability
* @dev: PCI device to query
* @cap: capability code
*
* Returns the address of the requested extended capability structure
* within the device's PCI configuration space or 0 if the device does
* not support it. Possible values for @cap:
*
* %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
* %PCI_EXT_CAP_ID_VC Virtual Channel
* %PCI_EXT_CAP_ID_DSN Device Serial Number
* %PCI_EXT_CAP_ID_PWR Power Budgeting
*/
int pci_find_ext_capability(struct pci_dev *dev, int cap)
{
u32 header;
int ttl = 480; /* 3840 bytes, minimum 8 bytes per capability */
int pos = 0x100;
if (dev->cfg_size <= 256)
return 0;
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
/*
* If we have no capabilities, this is indicated by cap ID,
* cap version and next pointer all being 0.
*/
if (header == 0)
return 0;
while (ttl-- > 0) {
if (PCI_EXT_CAP_ID(header) == cap)
return pos;
pos = PCI_EXT_CAP_NEXT(header);
if (pos < 0x100)
break;
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
break;
}
return 0;
}
/**
* pci_find_parent_resource - return resource region of parent bus of given region
* @dev: PCI device structure contains resources to be searched
* @res: child resource record for which parent is sought
*
* For given resource region of given device, return the resource
* region of parent bus the given region is contained in or where
* it should be allocated from.
*/
struct resource *
pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
{
const struct pci_bus *bus = dev->bus;
int i;
struct resource *best = NULL;
for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
struct resource *r = bus->resource[i];
if (!r)
continue;
if (res->start && !(res->start >= r->start && res->end <= r->end))
continue; /* Not contained */
if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
continue; /* Wrong type */
if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
return r; /* Exact match */
if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH))
best = r; /* Approximating prefetchable by non-prefetchable */
}
return best;
}
/**
* pci_set_power_state - Set the power state of a PCI device
* @dev: PCI device to be suspended
* @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.
*
* RETURN VALUE:
* -EINVAL if trying to enter a lower state than we're already in.
* 0 if we're already in the requested state.
* -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, pci_power_t state)
{
int pm;
u16 pmcsr, pmc;
/* bound the state we're entering */
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 != PCI_D0 && dev->current_state > state)
return -EINVAL;
else if (dev->current_state == state)
return 0; /* we're already there */
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
/* abort if the device doesn't support PM capabilities */
if (!pm)
return -EIO;
pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);
if ((pmc & PCI_PM_CAP_VER_MASK) > 2) {
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 == PCI_D1 || state == PCI_D2) {
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.
* This doesn't affect PME_Status, disables PME_En, and
* sets PowerState to 0.
*/
if (dev->current_state >= PCI_D3hot)
pmcsr = 0;
else {
pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
pmcsr |= state;
}
/* enter specified state */
pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr);
/* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */
if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
msleep(10);
else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200);
dev->current_state = state;
return 0;
}
/**
* 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)
{
if (!pci_find_capability(dev, PCI_CAP_ID_PM))
return PCI_D0;
switch (state) {
case 0: return PCI_D0;
case 3: return PCI_D3hot;
default:
printk("They asked me for state %d\n", state);
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)
{
int 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)
{
int i;
for (i = 0; i < 16; i++)
pci_write_config_dword(dev,i * 4, dev->saved_config_space[i]);
return 0;
}
/**
* pci_enable_device_bars - Initialize some of a device for use
* @dev: PCI device to be initialized
* @bars: bitmask of BAR's that must be configured
*
* Initialize device before it's used by a driver. Ask low-level code
* to enable selected I/O and memory resources. Wake up the device if it
* was suspended. Beware, this function can fail.
*/
int
pci_enable_device_bars(struct pci_dev *dev, int bars)
{
int err;
pci_set_power_state(dev, PCI_D0);
if ((err = pcibios_enable_device(dev, bars)) < 0)
return err;
return 0;
}
/**
* pci_enable_device - Initialize device before it's used by a driver.
* @dev: PCI device to be initialized
*
* Initialize device before it's used by a driver. Ask low-level code
* to enable I/O and memory. Wake up the device if it was suspended.
* Beware, this function can fail.
*/
int
pci_enable_device(struct pci_dev *dev)
{
int err;
dev->is_enabled = 1;
if ((err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1)))
return err;
pci_fixup_device(pci_fixup_enable, dev);
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
*
* Signal to the system that the PCI device is not in use by the system
* anymore. This only involves disabling PCI bus-mastering, if active.
*/
void
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);
}
pcibios_disable_device(dev);
}
/**
* pci_enable_wake - enable device to generate PME# when suspended
* @dev: - PCI device to operate on
* @state: - Current state of device.
* @enable: - Flag to enable or disable generation
*
* Set the bits in the device's PM Capabilities to generate PME# when
* the system is suspended.
*
* -EIO is returned if device doesn't have PM Capabilities.
* -EINVAL is returned if device supports it, but can't generate wake events.
* 0 if operation is successful.
*
*/
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
{
int pm;
u16 value;
/* find PCI PM capability in list */
pm = pci_find_capability(dev, PCI_CAP_ID_PM);
/* If device doesn't support PM Capabilities, but request is to disable
* wake events, it's a nop; otherwise fail */
if (!pm)
return enable ? -EIO : 0;
/* Check device's ability to generate PME# */
pci_read_config_word(dev,pm+PCI_PM_PMC,&value);
value &= PCI_PM_CAP_PME_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)))
return enable ? -EINVAL : 0;
pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
/* Clear PME_Status by writing 1 to it and enable PME# */
value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE;
if (!enable)
value &= ~PCI_PM_CTRL_PME_ENABLE;
pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
return 0;
}
int
pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge)
{
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin)
return -1;
pin--;
while (dev->bus->self) {
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
dev = dev->bus->self;
}
*bridge = dev;
return pin;
}
/**
* pci_release_region - Release a PCI bar
* @pdev: PCI device whose resources were previously reserved by pci_request_region
* @bar: BAR to release
*
* Releases the PCI I/O and memory resources previously reserved by a
* successful call to pci_request_region. Call this function only
* after all use of the PCI regions has ceased.
*/
void pci_release_region(struct pci_dev *pdev, int bar)
{
if (pci_resource_len(pdev, bar) == 0)
return;
if (pci_resource_flags(pdev, bar) & IORESOURCE_IO)
release_region(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar));
else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM)
release_mem_region(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar));
}
/**
* pci_request_region - Reserved PCI I/O and memory resource
* @pdev: PCI device whose resources are to be reserved
* @bar: BAR to be reserved
* @res_name: Name to be associated with resource.
*
* Mark the PCI region associated with PCI device @pdev BR @bar as
* being reserved by owner @res_name. Do not access any
* address inside the PCI regions unless this call returns
* successfully.
*
* Returns 0 on success, or %EBUSY on error. A warning
* message is also printed on failure.
*/
int pci_request_region(struct pci_dev *pdev, int bar, char *res_name)
{
if (pci_resource_len(pdev, bar) == 0)
return 0;
if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) {
if (!request_region(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar), res_name))
goto err_out;
}
else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
if (!request_mem_region(pci_resource_start(pdev, bar),
pci_resource_len(pdev, bar), res_name))
goto err_out;
}
return 0;
err_out:
printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%lx@%lx for device %s\n",
pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
bar + 1, /* PCI BAR # */
pci_resource_len(pdev, bar), pci_resource_start(pdev, bar),
pci_name(pdev));
return -EBUSY;
}
/**
* pci_release_regions - Release reserved PCI I/O and memory resources
* @pdev: PCI device whose resources were previously reserved by pci_request_regions
*
* Releases all PCI I/O and memory resources previously reserved by a
* successful call to pci_request_regions. Call this function only
* after all use of the PCI regions has ceased.
*/
void pci_release_regions(struct pci_dev *pdev)
{
int i;
for (i = 0; i < 6; i++)
pci_release_region(pdev, i);
}
/**
* pci_request_regions - Reserved PCI I/O and memory resources
* @pdev: PCI device whose resources are to be reserved
* @res_name: Name to be associated with resource.
*
* Mark all PCI regions associated with PCI device @pdev as
* being reserved by owner @res_name. Do not access any
* address inside the PCI regions unless this call returns
* successfully.
*
* Returns 0 on success, or %EBUSY on error. A warning
* message is also printed on failure.
*/
int pci_request_regions(struct pci_dev *pdev, char *res_name)
{
int i;
for (i = 0; i < 6; i++)
if(pci_request_region(pdev, i, res_name))
goto err_out;
return 0;
err_out:
while(--i >= 0)
pci_release_region(pdev, i);
return -EBUSY;
}
/**
* pci_set_master - enables bus-mastering for device dev
* @dev: the PCI device to enable
*
* Enables bus-mastering on the device and calls pcibios_set_master()
* to do the needed arch specific settings.
*/
void
pci_set_master(struct pci_dev *dev)
{
u16 cmd;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
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);
}
dev->is_busmaster = 1;
pcibios_set_master(dev);
}
#ifndef HAVE_ARCH_PCI_MWI
/* This can be overridden by arch code. */
u8 pci_cache_line_size = L1_CACHE_BYTES >> 2;
/**
* pci_generic_prep_mwi - helper function for pci_set_mwi
* @dev: the PCI device for which MWI is enabled
*
* Helper function for generic implementation of pcibios_prep_mwi
* function. Originally copied from drivers/net/acenic.c.
* Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>.
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
static int
pci_generic_prep_mwi(struct pci_dev *dev)
{
u8 cacheline_size;
if (!pci_cache_line_size)
return -EINVAL; /* The system doesn't support MWI. */
/* Validate current setting: the PCI_CACHE_LINE_SIZE must be
equal to or multiple of the right value. */
pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cacheline_size);
if (cacheline_size >= pci_cache_line_size &&
(cacheline_size % pci_cache_line_size) == 0)
return 0;
/* Write the correct value. */
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cache_line_size);
/* Read it back. */
pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cacheline_size);
if (cacheline_size == pci_cache_line_size)
return 0;
printk(KERN_DEBUG "PCI: cache line size of %d is not supported "
"by device %s\n", pci_cache_line_size << 2, pci_name(dev));
return -EINVAL;
}
#endif /* !HAVE_ARCH_PCI_MWI */
/**
* pci_set_mwi - enables memory-write-invalidate PCI transaction
* @dev: the PCI device for which MWI is enabled
*
* Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND,
* and then calls @pcibios_set_mwi to do the needed arch specific
* operations or a generic mwi-prep function.
*
* RETURNS: An appropriate -ERRNO error value on error, or zero for success.
*/
int
pci_set_mwi(struct pci_dev *dev)
{
int rc;
u16 cmd;
#ifdef HAVE_ARCH_PCI_MWI
rc = pcibios_prep_mwi(dev);
#else
rc = pci_generic_prep_mwi(dev);
#endif
if (rc)
return rc;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_INVALIDATE)) {
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);
}
return 0;
}
/**
* pci_clear_mwi - disables Memory-Write-Invalidate for device dev
* @dev: the PCI device to disable
*
* Disables PCI Memory-Write-Invalidate transaction on the device
*/
void
pci_clear_mwi(struct pci_dev *dev)
{
u16 cmd;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (cmd & PCI_COMMAND_INVALIDATE) {
cmd &= ~PCI_COMMAND_INVALIDATE;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
}
#ifndef HAVE_ARCH_PCI_SET_DMA_MASK
/*
* These can be overridden by arch-specific implementations
*/
int
pci_set_dma_mask(struct pci_dev *dev, u64 mask)
{
if (!pci_dma_supported(dev, mask))
return -EIO;
dev->dma_mask = 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)
{
if (!pci_dma_supported(dev, mask))
return -EIO;
dev->dev.coherent_dma_mask = mask;
return 0;
}
#endif
static int __devinit pci_init(void)
{
struct pci_dev *dev = NULL;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
pci_fixup_device(pci_fixup_final, dev);
}
return 0;
}
static int __devinit pci_setup(char *str)
{
while (str) {
char *k = strchr(str, ',');
if (k)
*k++ = 0;
if (*str && (str = pcibios_setup(str)) && *str) {
/* PCI layer options should be handled here */
printk(KERN_ERR "PCI: Unknown option `%s'\n", str);
}
str = k;
}
return 1;
}
device_initcall(pci_init);
__setup("pci=", pci_setup);
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
/* FIXME: Some boxes have multiple ISA bridges! */
struct pci_dev *isa_bridge;
EXPORT_SYMBOL(isa_bridge);
#endif
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);
EXPORT_SYMBOL(pci_request_regions);
EXPORT_SYMBOL(pci_release_region);
EXPORT_SYMBOL(pci_request_region);
EXPORT_SYMBOL(pci_set_master);
EXPORT_SYMBOL(pci_set_mwi);
EXPORT_SYMBOL(pci_clear_mwi);
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);
EXPORT_SYMBOL(pci_set_power_state);
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
EXPORT_SYMBOL(pci_enable_wake);
/* Quirk info */
EXPORT_SYMBOL(isa_dma_bridge_buggy);
EXPORT_SYMBOL(pci_pci_problems);

96
drivers/pci/pci.h Normal file
View File

@@ -0,0 +1,96 @@
/* Functions internal to the PCI core code */
extern int pci_hotplug (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,
unsigned long size, unsigned long align,
unsigned long min, unsigned int type_mask,
void (*alignf)(void *, struct resource *,
unsigned long, unsigned long),
void *alignf_data);
/* 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_attach_bus(struct pci_bus *bus);
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_attach_bus(struct pci_bus *bus) { return 0; }
static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; }
#endif
/* Functions for PCI Hotplug drivers to use */
extern struct pci_bus * pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr);
extern unsigned int pci_do_scan_bus(struct pci_bus *bus);
extern int pci_remove_device_safe(struct pci_dev *dev);
extern unsigned char pci_max_busnr(void);
extern unsigned char pci_bus_max_busnr(struct pci_bus *bus);
extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int cap);
struct pci_dev_wrapped {
struct pci_dev *dev;
void *data;
};
struct pci_bus_wrapped {
struct pci_bus *bus;
void *data;
};
struct pci_visit {
int (* pre_visit_pci_bus) (struct pci_bus_wrapped *,
struct pci_dev_wrapped *);
int (* post_visit_pci_bus) (struct pci_bus_wrapped *,
struct pci_dev_wrapped *);
int (* pre_visit_pci_dev) (struct pci_dev_wrapped *,
struct pci_bus_wrapped *);
int (* visit_pci_dev) (struct pci_dev_wrapped *,
struct pci_bus_wrapped *);
int (* post_visit_pci_dev) (struct pci_dev_wrapped *,
struct pci_bus_wrapped *);
};
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;
#ifdef CONFIG_X86_IO_APIC
extern int pci_msi_quirk;
#else
#define pci_msi_quirk 0
#endif
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;
}

10179
drivers/pci/pci.ids Normal file

File diff suppressed because it is too large Load Diff

36
drivers/pci/pcie/Kconfig Normal file
View File

@@ -0,0 +1,36 @@
#
# PCI Express Port Bus Configuration
#
config PCIEPORTBUS
bool "PCI Express support"
depends on PCI
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.

View File

@@ -0,0 +1,7 @@
#
# Makefile for PCI-Express PORT Driver
#
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o

View File

@@ -0,0 +1,41 @@
/*
* 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 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 pci_dev *dev, u32 state);
extern int pcie_port_device_resume(struct pci_dev *dev);
#endif
extern void pcie_port_device_remove(struct pci_dev *dev);
extern void pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
#endif /* _PORTDRV_H_ */

View File

@@ -0,0 +1,77 @@
/*
* 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 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,
};
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)
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;
}

View File

@@ -0,0 +1,434 @@
/*
* File: portdrv_core.c
* Purpose: PCI Express Port Bus Driver's Core 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>
#include "portdrv.h"
extern int pcie_mch_quirk; /* MSI-quirk Indicator */
static int pcie_port_probe_service(struct device *dev)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
int status = -ENODEV;
if (!dev || !dev->driver)
return status;
driver = to_service_driver(dev->driver);
if (!driver || !driver->probe)
return status;
pciedev = to_pcie_device(dev);
status = driver->probe(pciedev, driver->id_table);
if (!status) {
printk(KERN_DEBUG "Load service driver %s on pcie device %s\n",
driver->name, dev->bus_id);
get_device(dev);
}
return status;
}
static int pcie_port_remove_service(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->remove) {
printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n",
driver->name, dev->bus_id);
driver->remove(pciedev);
put_device(dev);
}
return 0;
}
static void pcie_port_shutdown_service(struct device *dev) {}
static int pcie_port_suspend_service(struct device *dev, u32 state, u32 level)
{
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_resume_service(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->resume)
driver->resume(pciedev);
return 0;
}
/*
* release_pcie_device
*
* Being invoked automatically when device is being removed
* in response to device_unregister(dev) call.
* Release all resources being claimed.
*/
static void release_pcie_device(struct device *dev)
{
printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
kfree(to_pcie_device(dev));
}
static int is_msi_quirked(struct pci_dev *dev)
{
int port_type, quirk = 0;
u16 reg16;
pci_read_config_word(dev,
pci_find_capability(dev, PCI_CAP_ID_EXP) +
PCIE_CAPABILITIES_REG, &reg16);
port_type = (reg16 >> 4) & PORT_TYPE_MASK;
switch(port_type) {
case PCIE_RC_PORT:
if (pcie_mch_quirk == 1)
quirk = 1;
break;
case PCIE_SW_UPSTREAM_PORT:
case PCIE_SW_DOWNSTREAM_PORT:
default:
break;
}
return quirk;
}
static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
{
int i, pos, nvec, status = -EINVAL;
int interrupt_mode = PCIE_PORT_INTx_MODE;
/* Set INTx as default */
for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
if (mask & (1 << i))
nvec++;
vectors[i] = dev->irq;
}
/* Check MSI quirk */
if (is_msi_quirked(dev))
return interrupt_mode;
/* Select MSI-X over MSI if supported */
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
if (pos) {
struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] =
{{0, 0}, {0, 1}, {0, 2}, {0, 3}};
printk("%s Found MSIX capability\n", __FUNCTION__);
status = pci_enable_msix(dev, msix_entries, nvec);
if (!status) {
int j = 0;
interrupt_mode = PCIE_PORT_MSIX_MODE;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
if (mask & (1 << i))
vectors[i] = msix_entries[j++].vector;
}
}
}
if (status) {
pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
printk("%s Found MSI capability\n", __FUNCTION__);
status = pci_enable_msi(dev);
if (!status) {
interrupt_mode = PCIE_PORT_MSI_MODE;
for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
vectors[i] = dev->irq;
}
}
}
return interrupt_mode;
}
static int get_port_device_capability(struct pci_dev *dev)
{
int services = 0, pos;
u16 reg16;
u32 reg32;
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
/* Hot-Plug Capable */
if (reg16 & PORT_TO_SLOT_MASK) {
pci_read_config_dword(dev,
pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
if (reg32 & SLOT_HP_CAPABLE_MASK)
services |= PCIE_PORT_SERVICE_HP;
}
/* PME Capable */
pos = pci_find_capability(dev, PCI_CAP_ID_PME);
if (pos)
services |= PCIE_PORT_SERVICE_PME;
pos = PCI_CFG_SPACE_SIZE;
while (pos) {
pci_read_config_dword(dev, pos, &reg32);
switch (reg32 & 0xffff) {
case PCI_EXT_CAP_ID_ERR:
services |= PCIE_PORT_SERVICE_AER;
pos = reg32 >> 20;
break;
case PCI_EXT_CAP_ID_VC:
services |= PCIE_PORT_SERVICE_VC;
pos = reg32 >> 20;
break;
default:
pos = 0;
break;
}
}
return services;
}
static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
int port_type, int service_type, int irq, int irq_mode)
{
struct device *device;
dev->port = parent;
dev->interrupt_mode = irq_mode;
dev->irq = irq;
dev->id.vendor = parent->vendor;
dev->id.device = parent->device;
dev->id.port_type = port_type;
dev->id.service_type = (1 << service_type);
/* Initialize generic device interface */
device = &dev->device;
memset(device, 0, sizeof(struct device));
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->bus_list);
device->bus = &pcie_port_bus_type;
device->driver = NULL;
device->driver_data = NULL;
device->release = release_pcie_device; /* callback to free pcie dev */
sprintf(&device->bus_id[0], "pcie%02x",
get_descriptor_id(port_type, service_type));
device->parent = &parent->dev;
}
static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
int port_type, int service_type, int irq, int irq_mode)
{
struct pcie_device *device;
device = kmalloc(sizeof(struct pcie_device), GFP_KERNEL);
if (!device)
return NULL;
memset(device, 0, sizeof(struct pcie_device));
pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id);
return device;
}
int pcie_port_device_probe(struct pci_dev *dev)
{
int pos, type;
u16 reg;
if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
return -ENODEV;
pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
type = (reg >> 4) & PORT_TYPE_MASK;
if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
type == PCIE_SW_DOWNSTREAM_PORT )
return 0;
return -ENODEV;
}
int pcie_port_device_register(struct pci_dev *dev)
{
int status, type, capabilities, irq_mode, i;
int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
u16 reg16;
/* Get port type */
pci_read_config_word(dev,
pci_find_capability(dev, PCI_CAP_ID_EXP) +
PCIE_CAPABILITIES_REG, &reg16);
type = (reg16 >> 4) & PORT_TYPE_MASK;
/* Now get port services */
capabilities = get_port_device_capability(dev);
irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
/* Allocate child services if any */
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
struct pcie_device *child;
if (capabilities & (1 << i)) {
child = alloc_pcie_device(
dev, /* parent */
type, /* port type */
i, /* service type */
vectors[i], /* irq */
irq_mode /* interrupt mode */);
if (child) {
status = device_register(&child->device);
if (status) {
kfree(child);
continue;
}
get_device(&child->device);
}
}
}
return 0;
}
#ifdef CONFIG_PM
int pcie_port_device_suspend(struct pci_dev *dev, u32 state)
{
struct list_head *head, *tmp;
struct device *parent, *child;
struct device_driver *driver;
struct pcie_port_service_driver *service_driver;
parent = &dev->dev;
head = &parent->children;
tmp = head->next;
while (head != tmp) {
child = container_of(tmp, struct device, node);
tmp = tmp->next;
if (child->bus != &pcie_port_bus_type)
continue;
driver = child->driver;
if (!driver)
continue;
service_driver = to_service_driver(driver);
if (service_driver->suspend)
service_driver->suspend(to_pcie_device(child), state);
}
return 0;
}
int pcie_port_device_resume(struct pci_dev *dev)
{
struct list_head *head, *tmp;
struct device *parent, *child;
struct device_driver *driver;
struct pcie_port_service_driver *service_driver;
parent = &dev->dev;
head = &parent->children;
tmp = head->next;
while (head != tmp) {
child = container_of(tmp, struct device, node);
tmp = tmp->next;
if (child->bus != &pcie_port_bus_type)
continue;
driver = child->driver;
if (!driver)
continue;
service_driver = to_service_driver(driver);
if (service_driver->resume)
service_driver->resume(to_pcie_device(child));
}
return 0;
}
#endif
void pcie_port_device_remove(struct pci_dev *dev)
{
struct list_head *head, *tmp;
struct device *parent, *child;
struct device_driver *driver;
struct pcie_port_service_driver *service_driver;
int interrupt_mode = PCIE_PORT_INTx_MODE;
parent = &dev->dev;
head = &parent->children;
tmp = head->next;
while (head != tmp) {
child = container_of(tmp, struct device, node);
tmp = tmp->next;
if (child->bus != &pcie_port_bus_type)
continue;
driver = child->driver;
if (driver) {
service_driver = to_service_driver(driver);
if (service_driver->remove)
service_driver->remove(to_pcie_device(child));
}
interrupt_mode = (to_pcie_device(child))->interrupt_mode;
put_device(child);
device_unregister(child);
}
/* Switch to INTx by default if MSI enabled */
if (interrupt_mode == PCIE_PORT_MSIX_MODE)
pci_disable_msix(dev);
else if (interrupt_mode == PCIE_PORT_MSI_MODE)
pci_disable_msi(dev);
}
void pcie_port_bus_register(void)
{
bus_register(&pcie_port_bus_type);
}
void pcie_port_bus_unregister(void)
{
bus_unregister(&pcie_port_bus_type);
}
int pcie_port_service_register(struct pcie_port_service_driver *new)
{
new->driver.name = (char *)new->name;
new->driver.bus = &pcie_port_bus_type;
new->driver.probe = pcie_port_probe_service;
new->driver.remove = pcie_port_remove_service;
new->driver.shutdown = pcie_port_shutdown_service;
new->driver.suspend = pcie_port_suspend_service;
new->driver.resume = pcie_port_resume_service;
return driver_register(&new->driver);
}
void pcie_port_service_unregister(struct pcie_port_service_driver *new)
{
driver_unregister(&new->driver);
}
EXPORT_SYMBOL(pcie_port_service_register);
EXPORT_SYMBOL(pcie_port_service_unregister);

View File

@@ -0,0 +1,122 @@
/*
* 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)
{
pcie_port_device_remove(dev);
}
#ifdef CONFIG_PM
static int pcie_portdrv_suspend (struct pci_dev *dev, u32 state)
{
return pcie_port_device_suspend(dev, state);
}
static int pcie_portdrv_resume (struct pci_dev *dev)
{
return pcie_port_device_resume(dev);
}
#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_register_driver(&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);

939
drivers/pci/probe.c Normal file
View File

@@ -0,0 +1,939 @@
/*
* probe.c - PCI detection and setup code
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/cpumask.h>
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
#define CARDBUS_RESERVE_BUSNR 3
#define PCI_CFG_SPACE_SIZE 256
#define PCI_CFG_SPACE_EXP_SIZE 4096
/* Ugh. Need to stop exporting this to modules. */
LIST_HEAD(pci_root_buses);
EXPORT_SYMBOL(pci_root_buses);
LIST_HEAD(pci_devices);
#ifdef HAVE_PCI_LEGACY
/**
* pci_create_legacy_files - create legacy I/O port and memory files
* @b: bus to create files under
*
* Some platforms allow access to legacy I/O port and ISA memory space on
* a per-bus basis. This routine creates the files and ties them into
* their associated read, write and mmap files from pci-sysfs.c
*/
static void pci_create_legacy_files(struct pci_bus *b)
{
b->legacy_io = kmalloc(sizeof(struct bin_attribute) * 2,
GFP_ATOMIC);
if (b->legacy_io) {
memset(b->legacy_io, 0, sizeof(struct bin_attribute) * 2);
b->legacy_io->attr.name = "legacy_io";
b->legacy_io->size = 0xffff;
b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
b->legacy_io->attr.owner = THIS_MODULE;
b->legacy_io->read = pci_read_legacy_io;
b->legacy_io->write = pci_write_legacy_io;
class_device_create_bin_file(&b->class_dev, b->legacy_io);
/* Allocated above after the legacy_io struct */
b->legacy_mem = b->legacy_io + 1;
b->legacy_mem->attr.name = "legacy_mem";
b->legacy_mem->size = 1024*1024;
b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
b->legacy_mem->attr.owner = THIS_MODULE;
b->legacy_mem->mmap = pci_mmap_legacy_mem;
class_device_create_bin_file(&b->class_dev, b->legacy_mem);
}
}
void pci_remove_legacy_files(struct pci_bus *b)
{
if (b->legacy_io) {
class_device_remove_bin_file(&b->class_dev, b->legacy_io);
class_device_remove_bin_file(&b->class_dev, b->legacy_mem);
kfree(b->legacy_io); /* both are allocated here */
}
}
#else /* !HAVE_PCI_LEGACY */
static inline void pci_create_legacy_files(struct pci_bus *bus) { return; }
void pci_remove_legacy_files(struct pci_bus *bus) { return; }
#endif /* HAVE_PCI_LEGACY */
/*
* PCI Bus Class Devices
*/
static ssize_t pci_bus_show_cpuaffinity(struct class_device *class_dev, char *buf)
{
cpumask_t cpumask = pcibus_to_cpumask(to_pci_bus(class_dev));
int ret;
ret = cpumask_scnprintf(buf, PAGE_SIZE, cpumask);
if (ret < PAGE_SIZE)
buf[ret++] = '\n';
return ret;
}
CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL);
/*
* PCI Bus Class
*/
static void release_pcibus_dev(struct class_device *class_dev)
{
struct pci_bus *pci_bus = to_pci_bus(class_dev);
if (pci_bus->bridge)
put_device(pci_bus->bridge);
kfree(pci_bus);
}
static struct class pcibus_class = {
.name = "pci_bus",
.release = &release_pcibus_dev,
};
static int __init pcibus_class_init(void)
{
return class_register(&pcibus_class);
}
postcore_initcall(pcibus_class_init);
/*
* Translate the low bits of the PCI base
* to the resource type
*/
static inline unsigned int pci_calc_resource_flags(unsigned int flags)
{
if (flags & PCI_BASE_ADDRESS_SPACE_IO)
return IORESOURCE_IO;
if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
return IORESOURCE_MEM | IORESOURCE_PREFETCH;
return IORESOURCE_MEM;
}
/*
* Find the extent of a PCI decode..
*/
static u32 pci_size(u32 base, u32 maxbase, unsigned long mask)
{
u32 size = mask & maxbase; /* Find the significant bits */
if (!size)
return 0;
/* Get the lowest of them to find the decode size, and
from that the extent. */
size = (size & ~(size-1)) - 1;
/* base == maxbase can be valid only if the BAR has
already been programmed with all 1s. */
if (base == maxbase && ((base | size) & mask) != mask)
return 0;
return size;
}
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
unsigned int pos, reg, next;
u32 l, sz;
struct resource *res;
for(pos=0; pos<howmany; pos = next) {
next = pos+1;
res = &dev->resource[pos];
res->name = pci_name(dev);
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
pci_read_config_dword(dev, reg, &l);
pci_write_config_dword(dev, reg, ~0);
pci_read_config_dword(dev, reg, &sz);
pci_write_config_dword(dev, reg, l);
if (!sz || sz == 0xffffffff)
continue;
if (l == 0xffffffff)
l = 0;
if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK);
if (!sz)
continue;
res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
} else {
sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff);
if (!sz)
continue;
res->start = l & PCI_BASE_ADDRESS_IO_MASK;
res->flags |= l & ~PCI_BASE_ADDRESS_IO_MASK;
}
res->end = res->start + (unsigned long) sz;
res->flags |= pci_calc_resource_flags(l);
if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
== (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
pci_read_config_dword(dev, reg+4, &l);
next++;
#if BITS_PER_LONG == 64
res->start |= ((unsigned long) l) << 32;
res->end = res->start + sz;
pci_write_config_dword(dev, reg+4, ~0);
pci_read_config_dword(dev, reg+4, &sz);
pci_write_config_dword(dev, reg+4, l);
sz = pci_size(l, sz, 0xffffffff);
if (sz) {
/* This BAR needs > 4GB? Wow. */
res->end |= (unsigned long)sz<<32;
}
#else
if (l) {
printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", pci_name(dev));
res->start = 0;
res->flags = 0;
continue;
}
#endif
}
}
if (rom) {
dev->rom_base_reg = rom;
res = &dev->resource[PCI_ROM_RESOURCE];
res->name = pci_name(dev);
pci_read_config_dword(dev, rom, &l);
pci_write_config_dword(dev, rom, ~PCI_ROM_ADDRESS_ENABLE);
pci_read_config_dword(dev, rom, &sz);
pci_write_config_dword(dev, rom, l);
if (l == 0xffffffff)
l = 0;
if (sz && sz != 0xffffffff) {
sz = pci_size(l, sz, PCI_ROM_ADDRESS_MASK);
if (sz) {
res->flags = (l & IORESOURCE_ROM_ENABLE) |
IORESOURCE_MEM | IORESOURCE_PREFETCH |
IORESOURCE_READONLY | IORESOURCE_CACHEABLE;
res->start = l & PCI_ROM_ADDRESS_MASK;
res->end = res->start + (unsigned long) sz;
}
}
}
}
void __devinit pci_read_bridge_bases(struct pci_bus *child)
{
struct pci_dev *dev = child->self;
u8 io_base_lo, io_limit_lo;
u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
struct resource *res;
int i;
if (!dev) /* It's a host bus, nothing to read */
return;
if (dev->transparent) {
printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev));
for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++)
child->resource[i] = child->parent->resource[i];
return;
}
for(i=0; i<3; i++)
child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo);
base = (io_base_lo & PCI_IO_RANGE_MASK) << 8;
limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8;
if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
u16 io_base_hi, io_limit_hi;
pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi);
pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi);
base |= (io_base_hi << 16);
limit |= (io_limit_hi << 16);
}
if (base <= limit) {
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
res->start = base;
res->end = limit + 0xfff;
}
res = child->resource[1];
pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo);
base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16;
limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
if (base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
res->start = base;
res->end = limit + 0xfffff;
}
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi);
pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi);
/*
* Some bridges set the base > limit by default, and some
* (broken) BIOSes do not initialize them. If we find
* this, just assume they are not being used.
*/
if (mem_base_hi <= mem_limit_hi) {
#if BITS_PER_LONG == 64
base |= ((long) mem_base_hi) << 32;
limit |= ((long) mem_limit_hi) << 32;
#else
if (mem_base_hi || mem_limit_hi) {
printk(KERN_ERR "PCI: Unable to handle 64-bit address space for bridge %s\n", pci_name(dev));
return;
}
#endif
}
}
if (base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
res->start = base;
res->end = limit + 0xfffff;
}
}
static struct pci_bus * __devinit pci_alloc_bus(void)
{
struct pci_bus *b;
b = kmalloc(sizeof(*b), GFP_KERNEL);
if (b) {
memset(b, 0, sizeof(*b));
INIT_LIST_HEAD(&b->node);
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
}
return b;
}
static struct pci_bus * __devinit
pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
{
struct pci_bus *child;
int i;
/*
* Allocate a new bus, and inherit stuff from the parent..
*/
child = pci_alloc_bus();
if (!child)
return NULL;
child->self = bridge;
child->parent = parent;
child->ops = parent->ops;
child->sysdata = parent->sysdata;
child->bridge = get_device(&bridge->dev);
child->class_dev.class = &pcibus_class;
sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr);
class_device_register(&child->class_dev);
class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity);
/*
* Set up the primary, secondary and subordinate
* bus numbers.
*/
child->number = child->secondary = busnr;
child->primary = parent->secondary;
child->subordinate = 0xff;
/* Set up default resource pointers and names.. */
for (i = 0; i < 4; i++) {
child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i];
child->resource[i]->name = child->name;
}
bridge->subordinate = child;
return child;
}
struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
{
struct pci_bus *child;
child = pci_alloc_child_bus(parent, dev, busnr);
if (child)
list_add_tail(&child->node, &parent->children);
return child;
}
static void pci_enable_crs(struct pci_dev *dev)
{
u16 cap, rpctl;
int rpcap = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!rpcap)
return;
pci_read_config_word(dev, rpcap + PCI_CAP_FLAGS, &cap);
if (((cap & PCI_EXP_FLAGS_TYPE) >> 4) != PCI_EXP_TYPE_ROOT_PORT)
return;
pci_read_config_word(dev, rpcap + PCI_EXP_RTCTL, &rpctl);
rpctl |= PCI_EXP_RTCTL_CRSSVE;
pci_write_config_word(dev, rpcap + PCI_EXP_RTCTL, rpctl);
}
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus);
/*
* If it's a bridge, configure it and scan the bus behind it.
* For CardBus bridges, we don't scan behind as the devices will
* be handled by the bridge driver itself.
*
* We need to process bridges in two passes -- first we scan those
* already configured by the BIOS and after we are done with all of
* them, we proceed to assigning numbers to the remaining buses in
* order to avoid overlaps between old and new bus numbers.
*/
int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass)
{
struct pci_bus *child;
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
u32 buses;
u16 bctl;
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
pr_debug("PCI: Scanning behind PCI bridge %s, config %06x, pass %d\n",
pci_name(dev), buses & 0xffffff, pass);
/* Disable MasterAbortMode during probing to avoid reporting
of bus errors (in some architectures) */
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
pci_enable_crs(dev);
if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) {
unsigned int cmax, busnr;
/*
* Bus already configured by firmware, process it in the first
* pass and just note the configuration.
*/
if (pass)
return max;
busnr = (buses >> 8) & 0xFF;
/*
* If we already got to this bus through a different bridge,
* ignore it. This can happen with the i450NX chipset.
*/
if (pci_find_bus(pci_domain_nr(bus), busnr)) {
printk(KERN_INFO "PCI: Bus %04x:%02x already known\n",
pci_domain_nr(bus), busnr);
return max;
}
child = pci_alloc_child_bus(bus, dev, busnr);
if (!child)
return max;
child->primary = buses & 0xFF;
child->subordinate = (buses >> 16) & 0xFF;
child->bridge_ctl = bctl;
cmax = pci_scan_child_bus(child);
if (cmax > max)
max = cmax;
if (child->subordinate > max)
max = child->subordinate;
} else {
/*
* We need to assign a number to this bus which we always
* do in the second pass.
*/
if (!pass)
return max;
/* Clear errors */
pci_write_config_word(dev, PCI_STATUS, 0xffff);
child = pci_alloc_child_bus(bus, dev, ++max);
buses = (buses & 0xff000000)
| ((unsigned int)(child->primary) << 0)
| ((unsigned int)(child->secondary) << 8)
| ((unsigned int)(child->subordinate) << 16);
/*
* yenta.c forces a secondary latency timer of 176.
* Copy that behaviour here.
*/
if (is_cardbus) {
buses &= ~0xff000000;
buses |= CARDBUS_LATENCY_TIMER << 24;
}
/*
* We need to blast all three values with a single write.
*/
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
if (!is_cardbus) {
child->bridge_ctl = PCI_BRIDGE_CTL_NO_ISA;
/* Now we can scan all subordinate buses... */
max = pci_scan_child_bus(child);
} else {
/*
* For CardBus bridges, we leave 4 bus numbers
* as cards with a PCI-to-PCI bridge can be
* inserted later.
*/
max += CARDBUS_RESERVE_BUSNR;
}
/*
* Set the subordinate bus number to its real value.
*/
child->subordinate = max;
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
}
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);
sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number);
return max;
}
/*
* Read interrupt line and base address registers.
* The architecture-dependent code can tweak these, of course.
*/
static void pci_read_irq(struct pci_dev *dev)
{
unsigned char irq;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
if (irq)
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
dev->irq = irq;
}
/**
* pci_setup_device - fill in class and map information of a device
* @dev: the device structure to fill
*
* Initialize the device structure with information about the device's
* vendor,class,memory and IO-space addresses,IRQ lines etc.
* Called at initialisation of the PCI subsystem and by CardBus services.
* Returns 0 on success and -1 if unknown type of device (not normal, bridge
* or CardBus).
*/
static int pci_setup_device(struct pci_dev * dev)
{
u32 class;
sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
class >>= 8; /* upper 3 bytes */
dev->class = class;
class >>= 8;
pr_debug("PCI: Found %s [%04x/%04x] %06x %02x\n", pci_name(dev),
dev->vendor, dev->device, class, dev->hdr_type);
/* "Unknown power state" */
dev->current_state = 4;
/* Early fixups, before probing the BARs */
pci_fixup_device(pci_fixup_early, dev);
class = dev->class >> 8;
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
break;
case PCI_HEADER_TYPE_BRIDGE: /* bridge header */
if (class != PCI_CLASS_BRIDGE_PCI)
goto bad;
/* The PCI-to-PCI bridge spec requires that subtractive
decoding (i.e. transparent) bridge must have programming
interface code of 0x01. */
dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
if (class != PCI_CLASS_BRIDGE_CARDBUS)
goto bad;
pci_read_irq(dev);
pci_read_bases(dev, 1, 0);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_CB_SUBSYSTEM_ID, &dev->subsystem_device);
break;
default: /* unknown header */
printk(KERN_ERR "PCI: device %s has unknown header type %02x, ignoring.\n",
pci_name(dev), dev->hdr_type);
return -1;
bad:
printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n",
pci_name(dev), class, dev->hdr_type);
dev->class = PCI_CLASS_NOT_DEFINED;
}
/* We found a fine healthy device, go go go... */
return 0;
}
/**
* pci_release_dev - free a pci device structure when all users of it are finished.
* @dev: device that's been disconnected
*
* Will be called only by the device core when all users of this pci device are
* done.
*/
static void pci_release_dev(struct device *dev)
{
struct pci_dev *pci_dev;
pci_dev = to_pci_dev(dev);
kfree(pci_dev);
}
/**
* pci_cfg_space_size - get the configuration space size of the PCI device.
*
* Regular PCI devices have 256 bytes, but PCI-X 2 and PCI Express devices
* have 4096 bytes. Even if the device is capable, that doesn't mean we can
* access it. Maybe we don't have a way to generate extended config space
* accesses, or the device is behind a reverse Express bridge. So we try
* reading the dword at 0x100 which must either be 0 or a valid extended
* capability header.
*/
static int pci_cfg_space_size(struct pci_dev *dev)
{
int pos;
u32 status;
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!pos) {
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
goto fail;
pci_read_config_dword(dev, pos + PCI_X_STATUS, &status);
if (!(status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ)))
goto fail;
}
if (pci_read_config_dword(dev, 256, &status) != PCIBIOS_SUCCESSFUL)
goto fail;
if (status == 0xffffffff)
goto fail;
return PCI_CFG_SPACE_EXP_SIZE;
fail:
return PCI_CFG_SPACE_SIZE;
}
static void pci_release_bus_bridge_dev(struct device *dev)
{
kfree(dev);
}
/*
* Read the config data for a PCI device, sanity-check it
* and fill in the dev structure...
*/
static struct pci_dev * __devinit
pci_scan_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
u32 l;
u8 hdr_type;
int delay = 1;
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
/* some broken boards return 0 or ~0 if a slot is empty: */
if (l == 0xffffffff || l == 0x00000000 ||
l == 0x0000ffff || l == 0xffff0000)
return NULL;
/* Configuration request Retry Status */
while (l == 0xffff0001) {
msleep(delay);
delay *= 2;
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
/* Card hasn't responded in 60 seconds? Must be stuck. */
if (delay > 60 * 1000) {
printk(KERN_WARNING "Device %04x:%02x:%02x.%d not "
"responding\n", pci_domain_nr(bus),
bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn));
return NULL;
}
}
if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type))
return NULL;
dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, sizeof(struct pci_dev));
dev->bus = bus;
dev->sysdata = bus->sysdata;
dev->dev.parent = bus->bridge;
dev->dev.bus = &pci_bus_type;
dev->devfn = devfn;
dev->hdr_type = hdr_type & 0x7f;
dev->multifunction = !!(hdr_type & 0x80);
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
dev->cfg_size = pci_cfg_space_size(dev);
/* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
set this higher, assuming the system even supports it. */
dev->dma_mask = 0xffffffff;
if (pci_setup_device(dev) < 0) {
kfree(dev);
return NULL;
}
device_initialize(&dev->dev);
dev->dev.release = pci_release_dev;
pci_dev_get(dev);
pci_name_device(dev);
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.coherent_dma_mask = 0xffffffffull;
return dev;
}
struct pci_dev * __devinit
pci_scan_single_device(struct pci_bus *bus, int devfn)
{
struct pci_dev *dev;
dev = pci_scan_device(bus, devfn);
pci_scan_msi_device(dev);
if (!dev)
return NULL;
/* Fix up broken headers */
pci_fixup_device(pci_fixup_header, dev);
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
*/
INIT_LIST_HEAD(&dev->global_list);
list_add_tail(&dev->bus_list, &bus->devices);
return dev;
}
/**
* pci_scan_slot - scan a PCI slot on a bus for devices.
* @bus: PCI bus to scan
* @devfn: slot number to scan (must have zero function.)
*
* Scan a PCI slot on the specified PCI bus for devices, adding
* discovered devices to the @bus->devices list. New devices
* will have an empty dev->global_list head.
*/
int __devinit pci_scan_slot(struct pci_bus *bus, int devfn)
{
int func, nr = 0;
int scan_all_fns;
scan_all_fns = pcibios_scan_all_fns(bus, devfn);
for (func = 0; func < 8; func++, devfn++) {
struct pci_dev *dev;
dev = pci_scan_single_device(bus, devfn);
if (dev) {
nr++;
/*
* If this is a single function device,
* don't scan past the first function.
*/
if (!dev->multifunction) {
if (func > 0) {
dev->multifunction = 1;
} else {
break;
}
}
} else {
if (func == 0 && !scan_all_fns)
break;
}
}
return nr;
}
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
unsigned int devfn, pass, max = bus->secondary;
struct pci_dev *dev;
pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
/* Go find them, Rover! */
for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
/*
* After performing arch-dependent fixup of the bus, look behind
* all PCI-to-PCI bridges on this bus.
*/
pr_debug("PCI: Fixups for bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
pcibios_fixup_bus(bus);
for (pass=0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
max = pci_scan_bridge(bus, dev, max, pass);
}
/*
* We've scanned the bus and so we know all about what's on
* the other side of any bridges that may be on this bus plus
* any devices.
*
* Return how far we've got finding sub-buses.
*/
pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
pci_domain_nr(bus), bus->number, max);
return max;
}
unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
{
unsigned int max;
max = pci_scan_child_bus(bus);
/*
* Make the discovered devices available.
*/
pci_bus_add_devices(bus);
return max;
}
struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata)
{
int error;
struct pci_bus *b;
struct device *dev;
b = pci_alloc_bus();
if (!b)
return NULL;
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev){
kfree(b);
return NULL;
}
b->sysdata = sysdata;
b->ops = ops;
if (pci_find_bus(pci_domain_nr(b), bus)) {
/* If we already got to this bus through a different bridge, ignore it */
pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus);
goto err_out;
}
list_add_tail(&b->node, &pci_root_buses);
memset(dev, 0, sizeof(*dev));
dev->parent = parent;
dev->release = pci_release_bus_bridge_dev;
sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus);
error = device_register(dev);
if (error)
goto dev_reg_err;
b->bridge = get_device(dev);
b->class_dev.class = &pcibus_class;
sprintf(b->class_dev.class_id, "%04x:%02x", pci_domain_nr(b), bus);
error = class_device_register(&b->class_dev);
if (error)
goto class_dev_reg_err;
error = class_device_create_file(&b->class_dev, &class_device_attr_cpuaffinity);
if (error)
goto class_dev_create_file_err;
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(b);
error = sysfs_create_link(&b->class_dev.kobj, &b->bridge->kobj, "bridge");
if (error)
goto sys_create_link_err;
b->number = b->secondary = bus;
b->resource[0] = &ioport_resource;
b->resource[1] = &iomem_resource;
b->subordinate = pci_scan_child_bus(b);
pci_bus_add_devices(b);
return b;
sys_create_link_err:
class_device_remove_file(&b->class_dev, &class_device_attr_cpuaffinity);
class_dev_create_file_err:
class_device_unregister(&b->class_dev);
class_dev_reg_err:
device_unregister(dev);
dev_reg_err:
list_del(&b->node);
err_out:
kfree(dev);
kfree(b);
return NULL;
}
EXPORT_SYMBOL(pci_scan_bus_parented);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_add_new_bus);
EXPORT_SYMBOL(pci_do_scan_bus);
EXPORT_SYMBOL(pci_scan_slot);
EXPORT_SYMBOL(pci_scan_bridge);
EXPORT_SYMBOL(pci_scan_single_device);
EXPORT_SYMBOL_GPL(pci_scan_child_bus);
#endif

619
drivers/pci/proc.c Normal file
View File

@@ -0,0 +1,619 @@
/*
* $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
*
* Procfs interface for the PCI bus.
*
* Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
static int proc_initialized; /* = 0 */
static loff_t
proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
loff_t new = -1;
struct inode *inode = file->f_dentry->d_inode;
down(&inode->i_sem);
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
case 2:
new = inode->i_size + off;
break;
}
if (new < 0 || new > inode->i_size)
new = -EINVAL;
else
file->f_pos = new;
up(&inode->i_sem);
return new;
}
static ssize_t
proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
const struct inode *ino = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = PDE(ino);
struct pci_dev *dev = dp->data;
unsigned int pos = *ppos;
unsigned int cnt, size;
/*
* Normal users can read only the standardized portion of the
* configuration space as several chips lock up when trying to read
* undefined locations (think of Intel PIIX4 as a typical example).
*/
if (capable(CAP_SYS_ADMIN))
size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
size = 64;
if (pos >= size)
return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
nbytes = size - pos;
cnt = nbytes;
if (!access_ok(VERIFY_WRITE, buf, cnt))
return -EINVAL;
if ((pos & 1) && cnt) {
unsigned char val;
pci_read_config_byte(dev, pos, &val);
__put_user(val, buf);
buf++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
unsigned short val;
pci_read_config_word(dev, pos, &val);
__put_user(cpu_to_le16(val), (unsigned short __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
unsigned int val;
pci_read_config_dword(dev, pos, &val);
__put_user(cpu_to_le32(val), (unsigned int __user *) buf);
buf += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
unsigned short val;
pci_read_config_word(dev, pos, &val);
__put_user(cpu_to_le16(val), (unsigned short __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
unsigned char val;
pci_read_config_byte(dev, pos, &val);
__put_user(val, buf);
buf++;
pos++;
cnt--;
}
*ppos = pos;
return nbytes;
}
static ssize_t
proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos)
{
const struct inode *ino = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = PDE(ino);
struct pci_dev *dev = dp->data;
int pos = *ppos;
int size = dev->cfg_size;
int cnt;
if (pos >= size)
return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
nbytes = size - pos;
cnt = nbytes;
if (!access_ok(VERIFY_READ, buf, cnt))
return -EINVAL;
if ((pos & 1) && cnt) {
unsigned char val;
__get_user(val, buf);
pci_write_config_byte(dev, pos, val);
buf++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
unsigned short val;
__get_user(val, (unsigned short __user *) buf);
pci_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
unsigned int val;
__get_user(val, (unsigned int __user *) buf);
pci_write_config_dword(dev, pos, le32_to_cpu(val));
buf += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
unsigned short val;
__get_user(val, (unsigned short __user *) buf);
pci_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
unsigned char val;
__get_user(val, buf);
pci_write_config_byte(dev, pos, val);
buf++;
pos++;
cnt--;
}
*ppos = pos;
return nbytes;
}
struct pci_filp_private {
enum pci_mmap_state mmap_state;
int write_combine;
};
static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
const struct proc_dir_entry *dp = PDE(inode);
struct pci_dev *dev = dp->data;
#ifdef HAVE_PCI_MMAP
struct pci_filp_private *fpriv = file->private_data;
#endif /* HAVE_PCI_MMAP */
int ret = 0;
switch (cmd) {
case PCIIOC_CONTROLLER:
ret = pci_domain_nr(dev->bus);
break;
#ifdef HAVE_PCI_MMAP
case PCIIOC_MMAP_IS_IO:
fpriv->mmap_state = pci_mmap_io;
break;
case PCIIOC_MMAP_IS_MEM:
fpriv->mmap_state = pci_mmap_mem;
break;
case PCIIOC_WRITE_COMBINE:
if (arg)
fpriv->write_combine = 1;
else
fpriv->write_combine = 0;
break;
#endif /* HAVE_PCI_MMAP */
default:
ret = -EINVAL;
break;
};
return ret;
}
#ifdef HAVE_PCI_MMAP
static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file->f_dentry->d_inode;
const struct proc_dir_entry *dp = PDE(inode);
struct pci_dev *dev = dp->data;
struct pci_filp_private *fpriv = file->private_data;
int ret;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
ret = pci_mmap_page_range(dev, vma,
fpriv->mmap_state,
fpriv->write_combine);
if (ret < 0)
return ret;
return 0;
}
static int proc_bus_pci_open(struct inode *inode, struct file *file)
{
struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL);
if (!fpriv)
return -ENOMEM;
fpriv->mmap_state = pci_mmap_io;
fpriv->write_combine = 0;
file->private_data = fpriv;
return 0;
}
static int proc_bus_pci_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
#endif /* HAVE_PCI_MMAP */
static struct file_operations proc_bus_pci_operations = {
.llseek = proc_bus_pci_lseek,
.read = proc_bus_pci_read,
.write = proc_bus_pci_write,
.ioctl = proc_bus_pci_ioctl,
#ifdef HAVE_PCI_MMAP
.open = proc_bus_pci_open,
.release = proc_bus_pci_release,
.mmap = proc_bus_pci_mmap,
#ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA
.get_unmapped_area = get_pci_unmapped_area,
#endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */
#endif /* HAVE_PCI_MMAP */
};
#if BITS_PER_LONG == 32
#define LONG_FORMAT "\t%08lx"
#else
#define LONG_FORMAT "\t%16lx"
#endif
/* iterator */
static void *pci_seq_start(struct seq_file *m, loff_t *pos)
{
struct pci_dev *dev = NULL;
loff_t n = *pos;
for_each_pci_dev(dev) {
if (!n--)
break;
}
return dev;
}
static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
struct pci_dev *dev = v;
(*pos)++;
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
return dev;
}
static void pci_seq_stop(struct seq_file *m, void *v)
{
if (v) {
struct pci_dev *dev = v;
pci_dev_put(dev);
}
}
static int show_device(struct seq_file *m, void *v)
{
const struct pci_dev *dev = v;
const struct pci_driver *drv;
int i;
if (dev == NULL)
return 0;
drv = pci_dev_driver(dev);
seq_printf(m, "%02x%02x\t%04x%04x\t%x",
dev->bus->number,
dev->devfn,
dev->vendor,
dev->device,
dev->irq);
/* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */
for(i=0; i<7; i++)
seq_printf(m, LONG_FORMAT,
dev->resource[i].start |
(dev->resource[i].flags & PCI_REGION_FLAG_MASK));
for(i=0; i<7; i++)
seq_printf(m, LONG_FORMAT,
dev->resource[i].start < dev->resource[i].end ?
dev->resource[i].end - dev->resource[i].start + 1 : 0);
seq_putc(m, '\t');
if (drv)
seq_printf(m, "%s", drv->name);
seq_putc(m, '\n');
return 0;
}
static struct seq_operations proc_bus_pci_devices_op = {
.start = pci_seq_start,
.next = pci_seq_next,
.stop = pci_seq_stop,
.show = show_device
};
static struct proc_dir_entry *proc_bus_pci_dir;
int pci_proc_attach_device(struct pci_dev *dev)
{
struct pci_bus *bus = dev->bus;
struct proc_dir_entry *e;
char name[16];
if (!proc_initialized)
return -EACCES;
if (!bus->procdir) {
if (pci_proc_domain(bus)) {
sprintf(name, "%04x:%02x", pci_domain_nr(bus),
bus->number);
} else {
sprintf(name, "%02x", bus->number);
}
bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
if (!bus->procdir)
return -ENOMEM;
}
sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
e = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir);
if (!e)
return -ENOMEM;
e->proc_fops = &proc_bus_pci_operations;
e->data = dev;
e->size = dev->cfg_size;
dev->procent = e;
return 0;
}
int pci_proc_detach_device(struct pci_dev *dev)
{
struct proc_dir_entry *e;
if ((e = dev->procent)) {
if (atomic_read(&e->count))
return -EBUSY;
remove_proc_entry(e->name, dev->bus->procdir);
dev->procent = NULL;
}
return 0;
}
int pci_proc_attach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
if (!proc_initialized)
return -EACCES;
if (!de) {
char name[16];
sprintf(name, "%02x", bus->number);
de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
if (!de)
return -ENOMEM;
}
return 0;
}
int pci_proc_detach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
if (de)
remove_proc_entry(de->name, proc_bus_pci_dir);
return 0;
}
#ifdef CONFIG_PCI_LEGACY_PROC
/*
* Backward compatible /proc/pci interface.
*/
/*
* Convert some of the configuration space registers of the device at
* address (bus,devfn) into a string (possibly several lines each).
* The configuration string is stored starting at buf[len]. If the
* string would exceed the size of the buffer (SIZE), 0 is returned.
*/
static int show_dev_config(struct seq_file *m, void *v)
{
struct pci_dev *dev = v;
struct pci_dev *first_dev;
struct pci_driver *drv;
u32 class_rev;
unsigned char latency, min_gnt, max_lat, *class;
int reg;
first_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
if (dev == first_dev)
seq_puts(m, "PCI devices found:\n");
pci_dev_put(first_dev);
drv = pci_dev_driver(dev);
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
pci_read_config_byte (dev, PCI_MIN_GNT, &min_gnt);
pci_read_config_byte (dev, PCI_MAX_LAT, &max_lat);
seq_printf(m, " Bus %2d, device %3d, function %2d:\n",
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
class = pci_class_name(class_rev >> 16);
if (class)
seq_printf(m, " %s", class);
else
seq_printf(m, " Class %04x", class_rev >> 16);
#ifdef CONFIG_PCI_NAMES
seq_printf(m, ": %s", dev->pretty_name);
#else
seq_printf(m, ": PCI device %04x:%04x", dev->vendor, dev->device);
#endif
seq_printf(m, " (rev %d).\n", class_rev & 0xff);
if (dev->irq)
seq_printf(m, " IRQ %d.\n", dev->irq);
if (latency || min_gnt || max_lat) {
seq_printf(m, " Master Capable. ");
if (latency)
seq_printf(m, "Latency=%d. ", latency);
else
seq_puts(m, "No bursts. ");
if (min_gnt)
seq_printf(m, "Min Gnt=%d.", min_gnt);
if (max_lat)
seq_printf(m, "Max Lat=%d.", max_lat);
seq_putc(m, '\n');
}
for (reg = 0; reg < 6; reg++) {
struct resource *res = dev->resource + reg;
unsigned long base, end, flags;
base = res->start;
end = res->end;
flags = res->flags;
if (!end)
continue;
if (flags & PCI_BASE_ADDRESS_SPACE_IO) {
seq_printf(m, " I/O at 0x%lx [0x%lx].\n",
base, end);
} else {
const char *pref, *type = "unknown";
if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
pref = "P";
else
pref = "Non-p";
switch (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
type = "32 bit"; break;
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
type = "20 bit"; break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
type = "64 bit"; break;
}
seq_printf(m, " %srefetchable %s memory at "
"0x%lx [0x%lx].\n", pref, type,
base,
end);
}
}
return 0;
}
static struct seq_operations proc_pci_op = {
.start = pci_seq_start,
.next = pci_seq_next,
.stop = pci_seq_stop,
.show = show_dev_config
};
static int proc_pci_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_pci_op);
}
static struct file_operations proc_pci_operations = {
.open = proc_pci_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static void legacy_proc_init(void)
{
struct proc_dir_entry * entry = create_proc_entry("pci", 0, NULL);
if (entry)
entry->proc_fops = &proc_pci_operations;
}
#else
static void legacy_proc_init(void)
{
}
#endif /* CONFIG_PCI_LEGACY_PROC */
static int proc_bus_pci_dev_open(struct inode *inode, struct file *file)
{
return seq_open(file, &proc_bus_pci_devices_op);
}
static struct file_operations proc_bus_pci_dev_operations = {
.open = proc_bus_pci_dev_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int __init pci_proc_init(void)
{
struct proc_dir_entry *entry;
struct pci_dev *dev = NULL;
proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
if (entry)
entry->proc_fops = &proc_bus_pci_dev_operations;
proc_initialized = 1;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
pci_proc_attach_device(dev);
}
legacy_proc_init();
return 0;
}
__initcall(pci_proc_init);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_proc_attach_device);
EXPORT_SYMBOL(pci_proc_attach_bus);
EXPORT_SYMBOL(pci_proc_detach_bus);
#endif

1352
drivers/pci/quirks.c Normal file

File diff suppressed because it is too large Load Diff

118
drivers/pci/remove.c Normal file
View File

@@ -0,0 +1,118 @@
#include <linux/pci.h>
#include <linux/module.h>
#include "pci.h"
static void pci_free_resources(struct pci_dev *dev)
{
int i;
msi_remove_pci_irq_vectors(dev);
pci_cleanup_rom(dev);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *res = dev->resource + i;
if (res->parent)
release_resource(res);
}
}
static void pci_destroy_dev(struct pci_dev *dev)
{
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
device_unregister(&dev->dev);
/* Remove the device from the device lists, and prevent any further
* list accesses from this device */
spin_lock(&pci_bus_lock);
list_del(&dev->bus_list);
list_del(&dev->global_list);
dev->bus_list.next = dev->bus_list.prev = NULL;
dev->global_list.next = dev->global_list.prev = NULL;
spin_unlock(&pci_bus_lock);
pci_free_resources(dev);
pci_dev_put(dev);
}
/**
* pci_remove_device_safe - remove an unused hotplug device
* @dev: the device to remove
*
* Delete the device structure from the device lists and
* notify userspace (/sbin/hotplug), but only if the device
* in question is not being used by a driver.
* Returns 0 on success.
*/
int pci_remove_device_safe(struct pci_dev *dev)
{
if (pci_dev_driver(dev))
return -EBUSY;
pci_destroy_dev(dev);
return 0;
}
EXPORT_SYMBOL(pci_remove_device_safe);
void pci_remove_bus(struct pci_bus *pci_bus)
{
pci_proc_detach_bus(pci_bus);
spin_lock(&pci_bus_lock);
list_del(&pci_bus->node);
spin_unlock(&pci_bus_lock);
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);
/**
* pci_remove_bus_device - remove a PCI device and any children
* @dev: the device to remove
*
* Remove a PCI device from the device lists, informing the drivers
* that the device has been removed. We also remove any subordinate
* buses and children in a depth-first manner.
*
* For each device we remove, delete the device structure from the
* device lists, remove the /proc entry, and notify userspace
* (/sbin/hotplug).
*/
void pci_remove_bus_device(struct pci_dev *dev)
{
if (dev->subordinate) {
struct pci_bus *b = dev->subordinate;
pci_remove_behind_bridge(dev);
pci_remove_bus(b);
dev->subordinate = NULL;
}
pci_destroy_dev(dev);
}
/**
* pci_remove_behind_bridge - remove all devices behind a PCI bridge
* @dev: PCI bridge device
*
* Remove all devices on the bus, except for the parent bridge.
* This also removes any child buses, and any devices they may
* contain in a depth-first manner.
*/
void pci_remove_behind_bridge(struct pci_dev *dev)
{
struct list_head *l, *n;
if (dev->subordinate) {
list_for_each_safe(l, n, &dev->subordinate->devices) {
struct pci_dev *dev = pci_dev_b(l);
pci_remove_bus_device(dev);
}
}
}
EXPORT_SYMBOL(pci_remove_bus_device);
EXPORT_SYMBOL(pci_remove_behind_bridge);

227
drivers/pci/rom.c Normal file
View File

@@ -0,0 +1,227 @@
/*
* drivers/pci/rom.c
*
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (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>
#include "pci.h"
/**
* pci_enable_rom - enable ROM decoding for a PCI device
* @dev: PCI device to enable
*
* Enable ROM decoding on @dev. This involves simply turning on the last
* bit of the PCI ROM BAR. Note that some cards may share address decoders
* 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)
{
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);
}
/**
* pci_disable_rom - disable ROM decoding for a PCI device
* @dev: PCI device to disable
*
* 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)
{
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);
}
/**
* pci_map_rom - map a PCI ROM to kernel space
* @dev: pointer to pci device struct
* @size: pointer to receive size of pci window over ROM
* @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
* actual ROM.
*/
void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
loff_t start;
void __iomem *rom;
void __iomem *image;
int last_image;
/* 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);
return (void __iomem *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
} else {
/* 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)))
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.
*/
image = rom;
do {
void __iomem *pds;
/* Standard PCI ROMs start out with these bytes 55 AA */
if (readb(image) != 0x55)
break;
if (readb(image + 1) != 0xAA)
break;
/* get the PCI data structure and check its signature */
pds = image + readw(image + 24);
if (readb(pds) != 'P')
break;
if (readb(pds + 1) != 'C')
break;
if (readb(pds + 2) != 'I')
break;
if (readb(pds + 3) != 'R')
break;
last_image = readb(pds + 21) & 0x80;
/* this length is reliable */
image += readw(pds + 16) * 512;
} while (!last_image);
*size = image - rom;
return rom;
}
/**
* pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
* @dev: pointer to pci device struct
* @size: pointer to receive size of pci window over ROM
* @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
* 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)
return rom;
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;
}
/**
* pci_unmap_rom - unmap the ROM from kernel space
* @dev: pointer to pci device struct
* @rom: virtual address of the previous mapping
*
* Remove a mapping of a previously mapped 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);
}
/**
* 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)
{
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)))
pci_disable_rom(pdev);
}
/**
* 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)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (res->flags & IORESOURCE_ROM_COPY) {
kfree((void*)res->start);
res->flags &= ~IORESOURCE_ROM_COPY;
res->start = 0;
res->end = 0;
}
}
EXPORT_SYMBOL(pci_map_rom);
EXPORT_SYMBOL(pci_map_rom_copy);
EXPORT_SYMBOL(pci_unmap_rom);
EXPORT_SYMBOL(pci_remove_rom);

388
drivers/pci/search.c Normal file
View File

@@ -0,0 +1,388 @@
/*
* PCI searching functions.
*
* Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang
* Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
* Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include "pci.h"
DEFINE_SPINLOCK(pci_bus_lock);
static struct pci_bus * __devinit
pci_do_find_bus(struct pci_bus* bus, unsigned char busnr)
{
struct pci_bus* child;
struct list_head *tmp;
if(bus->number == busnr)
return bus;
list_for_each(tmp, &bus->children) {
child = pci_do_find_bus(pci_bus_b(tmp), busnr);
if(child)
return child;
}
return NULL;
}
/**
* pci_find_bus - locate PCI bus from a given domain and bus number
* @domain: number of PCI domain to search
* @busnr: number of desired PCI bus
*
* Given a PCI bus number and domain number, the desired PCI bus is located
* in the global list of PCI buses. If the bus is found, a pointer to its
* data structure is returned. If no bus is found, %NULL is returned.
*/
struct pci_bus * __devinit pci_find_bus(int domain, int busnr)
{
struct pci_bus *bus = NULL;
struct pci_bus *tmp_bus;
while ((bus = pci_find_next_bus(bus)) != NULL) {
if (pci_domain_nr(bus) != domain)
continue;
tmp_bus = pci_do_find_bus(bus, busnr);
if (tmp_bus)
return tmp_bus;
}
return NULL;
}
/**
* pci_find_next_bus - begin or continue searching for a PCI bus
* @from: Previous PCI bus found, or %NULL for new search.
*
* Iterates through the list of known PCI busses. A new search is
* initiated by passing %NULL to the @from argument. Otherwise if
* @from is not %NULL, searches continue from next device on the
* global list.
*/
struct pci_bus *
pci_find_next_bus(const struct pci_bus *from)
{
struct list_head *n;
struct pci_bus *b = NULL;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
n = from ? from->node.next : pci_root_buses.next;
if (n != &pci_root_buses)
b = pci_bus_b(n);
spin_unlock(&pci_bus_lock);
return b;
}
/**
* pci_find_slot - locate PCI device from a given PCI slot
* @bus: number of PCI bus on which desired PCI device resides
* @devfn: encodes number of PCI slot in which the desired PCI
* device resides and the logical device number within that slot
* in case of multi-function devices.
*
* Given a PCI bus and slot/function number, the desired PCI device
* is located in system global list of PCI devices. If the device
* is found, a pointer to its data structure is returned. If no
* device is found, %NULL is returned.
*/
struct pci_dev *
pci_find_slot(unsigned int bus, unsigned int devfn)
{
struct pci_dev *dev = NULL;
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
if (dev->bus->number == bus && dev->devfn == devfn)
return dev;
}
return NULL;
}
/**
* pci_get_slot - locate PCI device for a given PCI slot
* @bus: PCI bus on which desired PCI device resides
* @devfn: encodes number of PCI slot in which the desired PCI
* device resides and the logical device number within that slot
* in case of multi-function devices.
*
* Given a PCI bus and slot/function number, the desired PCI device
* is located in the list of PCI devices.
* If the device is found, its reference count is increased and this
* function returns a pointer to its data structure. The caller must
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
struct list_head *tmp;
struct pci_dev *dev;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
list_for_each(tmp, &bus->devices) {
dev = pci_dev_b(tmp);
if (dev->devfn == devfn)
goto out;
}
dev = NULL;
out:
pci_dev_get(dev);
spin_unlock(&pci_bus_lock);
return dev;
}
/**
* pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its
* device structure is returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*
* NOTE: Do not use this function anymore, use pci_get_subsys() instead, as
* the pci device returned by this function can disappear at any moment in
* time.
*/
static struct pci_dev * pci_find_subsys(unsigned int vendor,
unsigned int device,
unsigned int ss_vendor,
unsigned int ss_device,
const struct pci_dev *from)
{
struct list_head *n;
struct pci_dev *dev;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
dev = pci_dev_g(n);
if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
(device == PCI_ANY_ID || dev->device == device) &&
(ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
(ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
goto exit;
n = n->next;
}
dev = NULL;
exit:
spin_unlock(&pci_bus_lock);
return dev;
}
/**
* pci_find_device - begin or continue searching for a PCI device by vendor/device id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor and @device, a pointer to its device structure is
* returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the global list.
*
* NOTE: Do not use this function anymore, use pci_get_device() instead, as
* the pci device returned by this function can disappear at any moment in
* time.
*/
struct pci_dev *
pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from)
{
return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
/**
* pci_get_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its
* device structure is returned, and the reference count to the device is
* incremented. Otherwise, %NULL is returned. A new search is initiated by
* passing %NULL to the @from argument. Otherwise if @from is not %NULL,
* searches continue from next device on the global list.
* The reference count for @from is always decremented if it is not %NULL.
*/
struct pci_dev *
pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
struct pci_dev *from)
{
struct list_head *n;
struct pci_dev *dev;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
dev = pci_dev_g(n);
if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
(device == PCI_ANY_ID || dev->device == device) &&
(ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) &&
(ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device))
goto exit;
n = n->next;
}
dev = NULL;
exit:
pci_dev_put(from);
dev = pci_dev_get(dev);
spin_unlock(&pci_bus_lock);
return dev;
}
/**
* pci_get_device - begin or continue searching for a PCI device by vendor/device id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @vendor and @device, the reference count to the
* device is incremented and a pointer to its device structure is returned.
* Otherwise, %NULL is returned. A new search is initiated by passing %NULL
* to the @from argument. Otherwise if @from is not %NULL, searches continue
* from next device on the global list. The reference count for @from is
* always decremented if it is not %NULL.
*/
struct pci_dev *
pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
{
return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
/**
* pci_find_device_reverse - begin or continue searching for a PCI device by vendor/device id
* @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
* @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices in the reverse order of pci_find_device().
* If a PCI device is found with a matching @vendor and @device, a pointer to
* its device structure is returned. Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from previous device on the global list.
*/
struct pci_dev *
pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct pci_dev *from)
{
struct list_head *n;
struct pci_dev *dev;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
n = from ? from->global_list.prev : pci_devices.prev;
while (n && (n != &pci_devices)) {
dev = pci_dev_g(n);
if ((vendor == PCI_ANY_ID || dev->vendor == vendor) &&
(device == PCI_ANY_ID || dev->device == device))
goto exit;
n = n->prev;
}
dev = NULL;
exit:
spin_unlock(&pci_bus_lock);
return dev;
}
/**
* pci_get_class - begin or continue searching for a PCI device by class
* @class: search for a PCI device with this class designation
* @from: Previous PCI device found in search, or %NULL for new search.
*
* Iterates through the list of known PCI devices. If a PCI device is
* found with a matching @class, the reference count to the device is
* incremented and a pointer to its device structure is returned.
* Otherwise, %NULL is returned.
* A new search is initiated by passing %NULL to the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device
* on the global list. The reference count for @from is always decremented
* if it is not %NULL.
*/
struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
{
struct list_head *n;
struct pci_dev *dev;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
n = from ? from->global_list.next : pci_devices.next;
while (n && (n != &pci_devices)) {
dev = pci_dev_g(n);
if (dev->class == class)
goto exit;
n = n->next;
}
dev = NULL;
exit:
pci_dev_put(from);
dev = pci_dev_get(dev);
spin_unlock(&pci_bus_lock);
return dev;
}
/**
* pci_dev_present - Returns 1 if device matching the device list is present, 0 if not.
* @ids: A pointer to a null terminated list of struct pci_device_id structures
* that describe the type of PCI device the caller is trying to find.
*
* Obvious fact: You do not have a reference to any device that might be found
* by this function, so if that device is removed from the system right after
* this function is finished, the value will be stale. Use this function to
* find devices that are usually built into a system, or for a general hint as
* to if another device happens to be present at this specific moment in time.
*/
int pci_dev_present(const struct pci_device_id *ids)
{
struct pci_dev *dev;
int found = 0;
WARN_ON(in_interrupt());
spin_lock(&pci_bus_lock);
while (ids->vendor || ids->subvendor || ids->class_mask) {
list_for_each_entry(dev, &pci_devices, global_list) {
if (pci_match_one_device(ids, dev)) {
found = 1;
goto exit;
}
}
ids++;
}
exit:
spin_unlock(&pci_bus_lock);
return found;
}
EXPORT_SYMBOL(pci_dev_present);
EXPORT_SYMBOL(pci_find_bus);
EXPORT_SYMBOL(pci_find_device);
EXPORT_SYMBOL(pci_find_device_reverse);
EXPORT_SYMBOL(pci_find_slot);
EXPORT_SYMBOL(pci_get_device);
EXPORT_SYMBOL(pci_get_subsys);
EXPORT_SYMBOL(pci_get_slot);
EXPORT_SYMBOL(pci_get_class);

552
drivers/pci/setup-bus.c Normal file
View File

@@ -0,0 +1,552 @@
/*
* drivers/pci/setup-bus.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* PCI-PCI bridges cleanup, sorted resource allocation.
* Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Converted to allocation in 3 passes, which gives
* tighter packing. Prefetchable range support.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
#define DEBUG_CONFIG 1
#if DEBUG_CONFIG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
/*
* FIXME: IO should be max 256 bytes. However, since we may
* have a P2P bridge below a cardbus bridge, we need 4K.
*/
#define CARDBUS_IO_SIZE (4096)
#define CARDBUS_MEM_SIZE (32*1024*1024)
static void __devinit
pbus_assign_resources_sorted(struct pci_bus *bus)
{
struct pci_dev *dev;
struct resource *res;
struct resource_list head, *list, *tmp;
int idx;
bus->bridge_ctl &= ~PCI_BRIDGE_CTL_VGA;
head.next = NULL;
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 class = dev->class >> 8;
/* Don't touch classless devices and host bridges. */
if (class == PCI_CLASS_NOT_DEFINED ||
class == PCI_CLASS_BRIDGE_HOST)
continue;
if (class == PCI_CLASS_DISPLAY_VGA ||
class == PCI_CLASS_NOT_DEFINED_VGA)
bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
pdev_sort_resources(dev, &head);
}
for (list = head.next; list;) {
res = list->res;
idx = res - &list->dev->resource[0];
pci_assign_resource(list->dev, idx);
tmp = list;
list = list->next;
kfree(tmp);
}
}
static void __devinit
pci_setup_cardbus(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
printk("PCI: Bus %d, cardbus bridge: %s\n",
bus->number, pci_name(bridge));
pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
if (bus->resource[0]->flags & IORESOURCE_IO) {
/*
* The IO resource is allocated a range twice as large as it
* would normally need. This allows us to set both IO regs.
*/
printk(" IO window: %08lx-%08lx\n",
region.start, region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
region.start);
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
region.end);
}
pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
if (bus->resource[1]->flags & IORESOURCE_IO) {
printk(" IO window: %08lx-%08lx\n",
region.start, region.end);
pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
region.start);
pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
region.end);
}
pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_MEM) {
printk(" PREFETCH window: %08lx-%08lx\n",
region.start, region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
region.start);
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
region.end);
}
pcibios_resource_to_bus(bridge, &region, bus->resource[3]);
if (bus->resource[3]->flags & IORESOURCE_MEM) {
printk(" MEM window: %08lx-%08lx\n",
region.start, region.end);
pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
region.start);
pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
region.end);
}
}
/* Initialize bridges with base/limit values we have collected.
PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
requires that if there is no I/O ports or memory behind the
bridge, corresponding range must be turned off by writing base
value greater than limit to the bridge's base/limit registers.
Note: care must be taken when updating I/O base/limit registers
of bridges which support 32-bit I/O. This update requires two
config space writes, so it's quite possible that an I/O window of
the bridge will have some undesirable address (e.g. 0) after the
first write. Ditto 64-bit prefetchable MMIO. */
static void __devinit
pci_setup_bridge(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
u32 l, io_upper16;
DBG(KERN_INFO "PCI: Bridge: %s\n", pci_name(bridge));
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
if (bus->resource[0]->flags & IORESOURCE_IO) {
pci_read_config_dword(bridge, PCI_IO_BASE, &l);
l &= 0xffff0000;
l |= (region.start >> 8) & 0x00f0;
l |= region.end & 0xf000;
/* Set up upper 16 bits of I/O base/limit. */
io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
DBG(KERN_INFO " IO window: %04lx-%04lx\n",
region.start, region.end);
}
else {
/* Clear upper 16 bits of I/O base/limit. */
io_upper16 = 0;
l = 0x00f0;
DBG(KERN_INFO " IO window: disabled.\n");
}
/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
/* Update lower 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE, l);
/* Update upper 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16);
/* Set up the top and bottom of the PCI Memory segment
for this bus. */
pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
if (bus->resource[1]->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
DBG(KERN_INFO " MEM window: %08lx-%08lx\n",
region.start, region.end);
}
else {
l = 0x0000fff0;
DBG(KERN_INFO " MEM window: disabled.\n");
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
/* Clear out the upper 32 bits of PREF limit.
If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily
disables PREF range, which is ok. */
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
/* Set up PREF base/limit. */
pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
DBG(KERN_INFO " PREFETCH window: %08lx-%08lx\n",
region.start, region.end);
}
else {
l = 0x0000fff0;
DBG(KERN_INFO " PREFETCH window: disabled.\n");
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
/* Clear out the upper 32 bits of PREF base. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
/* Check whether the bridge supports optional I/O and
prefetchable memory ranges. If not, the respective
base/limit registers must be read-only and read as 0. */
static void __devinit
pci_bridge_check_ranges(struct pci_bus *bus)
{
u16 io;
u32 pmem;
struct pci_dev *bridge = bus->self;
struct resource *b_res;
b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
b_res[1].flags |= IORESOURCE_MEM;
pci_read_config_word(bridge, PCI_IO_BASE, &io);
if (!io) {
pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
pci_read_config_word(bridge, PCI_IO_BASE, &io);
pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
}
if (io)
b_res[0].flags |= IORESOURCE_IO;
/* DECchip 21050 pass 2 errata: the bridge may miss an address
disconnect boundary by one PCI data phase.
Workaround: do not use prefetching on this device. */
if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
return;
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
if (!pmem) {
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
0xfff0fff0);
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
}
if (pmem)
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
}
/* Helper function for sizing routines: find first available
bus resource of a given type. Note: we intentionally skip
the bus resources which have already been assigned (that is,
have non-NULL parent resource). */
static struct resource * __devinit
find_free_bus_resource(struct pci_bus *bus, unsigned long type)
{
int i;
struct resource *r;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
r = bus->resource[i];
if (r && (r->flags & type_mask) == type && !r->parent)
return r;
}
return NULL;
}
/* Sizing the IO windows of the PCI-PCI bridge is trivial,
since these windows have 4K granularity and the IO ranges
of non-bridge PCI devices are limited to 256 bytes.
We must be careful with the ISA aliasing though. */
static void __devinit
pbus_size_io(struct pci_bus *bus)
{
struct pci_dev *dev;
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
unsigned long size = 0, size1 = 0;
if (!b_res)
return;
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
unsigned long r_size;
if (r->parent || !(r->flags & IORESOURCE_IO))
continue;
r_size = r->end - r->start + 1;
if (r_size < 0x400)
/* Might be re-aligned for ISA */
size += r_size;
else
size1 += r_size;
}
}
/* To be fixed in 2.5: we should have sort of HAVE_ISA
flag in the struct pci_bus. */
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
size = (size & 0xff) + ((size & ~0xffUL) << 2);
#endif
size = ROUND_UP(size + size1, 4096);
if (!size) {
b_res->flags = 0;
return;
}
/* Alignment of the IO window is always 4K */
b_res->start = 4096;
b_res->end = b_res->start + size - 1;
}
/* Calculate the size of the bus and minimal alignment which
guarantees that all child resources fit in this size. */
static int __devinit
pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type)
{
struct pci_dev *dev;
unsigned long min_align, align, size;
unsigned long aligns[12]; /* Alignments from 1Mb to 2Gb */
int order, max_order;
struct resource *b_res = find_free_bus_resource(bus, type);
if (!b_res)
return 0;
memset(aligns, 0, sizeof(aligns));
max_order = 0;
size = 0;
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
unsigned long r_size;
if (r->parent || (r->flags & mask) != type)
continue;
r_size = r->end - r->start + 1;
/* For bridges size != alignment */
align = (i < PCI_BRIDGE_RESOURCES) ? r_size : r->start;
order = __ffs(align) - 20;
if (order > 11) {
printk(KERN_WARNING "PCI: region %s/%d "
"too large: %lx-%lx\n",
pci_name(dev), i, r->start, r->end);
r->flags = 0;
continue;
}
size += r_size;
if (order < 0)
order = 0;
/* Exclude ranges with size > align from
calculation of the alignment. */
if (r_size == align)
aligns[order] += align;
if (order > max_order)
max_order = order;
}
}
align = 0;
min_align = 0;
for (order = 0; order <= max_order; order++) {
unsigned long align1 = 1UL << (order + 20);
if (!align)
min_align = align1;
else if (ROUND_UP(align + min_align, min_align) < align1)
min_align = align1 >> 1;
align += aligns[order];
}
size = ROUND_UP(size, min_align);
if (!size) {
b_res->flags = 0;
return 1;
}
b_res->start = min_align;
b_res->end = size + min_align - 1;
return 1;
}
static void __devinit
pci_bus_size_cardbus(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
u16 ctrl;
/*
* Reserve some resources for CardBus. We reserve
* a fixed amount of bus space for CardBus bridges.
*/
b_res[0].start = CARDBUS_IO_SIZE;
b_res[0].end = b_res[0].start + CARDBUS_IO_SIZE - 1;
b_res[0].flags |= IORESOURCE_IO;
b_res[1].start = CARDBUS_IO_SIZE;
b_res[1].end = b_res[1].start + CARDBUS_IO_SIZE - 1;
b_res[1].flags |= IORESOURCE_IO;
/*
* Check whether prefetchable memory is supported
* by this bridge.
*/
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) {
ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
}
/*
* If we have prefetchable memory support, allocate
* two regions. Otherwise, allocate one region of
* twice the size.
*/
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
b_res[2].start = CARDBUS_MEM_SIZE;
b_res[2].end = b_res[2].start + CARDBUS_MEM_SIZE - 1;
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
b_res[3].start = CARDBUS_MEM_SIZE;
b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE - 1;
b_res[3].flags |= IORESOURCE_MEM;
} else {
b_res[3].start = CARDBUS_MEM_SIZE * 2;
b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE * 2 - 1;
b_res[3].flags |= IORESOURCE_MEM;
}
}
void __devinit
pci_bus_size_bridges(struct pci_bus *bus)
{
struct pci_dev *dev;
unsigned long mask, prefmask;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate;
if (!b)
continue;
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_CARDBUS:
pci_bus_size_cardbus(b);
break;
case PCI_CLASS_BRIDGE_PCI:
default:
pci_bus_size_bridges(b);
break;
}
}
/* The root bus? */
if (!bus->self)
return;
switch (bus->self->class >> 8) {
case PCI_CLASS_BRIDGE_CARDBUS:
/* don't size cardbuses yet. */
break;
case PCI_CLASS_BRIDGE_PCI:
pci_bridge_check_ranges(bus);
default:
pbus_size_io(bus);
/* If the bridge supports prefetchable range, size it
separately. If it doesn't, or its prefetchable window
has already been allocated by arch code, try
non-prefetchable range for both types of PCI memory
resources. */
mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (pbus_size_mem(bus, prefmask, prefmask))
mask = prefmask; /* Success, size non-prefetch only. */
pbus_size_mem(bus, mask, IORESOURCE_MEM);
break;
}
}
EXPORT_SYMBOL(pci_bus_size_bridges);
void __devinit
pci_bus_assign_resources(struct pci_bus *bus)
{
struct pci_bus *b;
struct pci_dev *dev;
pbus_assign_resources_sorted(bus);
if (bus->bridge_ctl & PCI_BRIDGE_CTL_VGA) {
/* Propagate presence of the VGA to upstream bridges */
for (b = bus; b->parent; b = b->parent) {
b->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
}
}
list_for_each_entry(dev, &bus->devices, bus_list) {
b = dev->subordinate;
if (!b)
continue;
pci_bus_assign_resources(b);
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
pci_setup_bridge(b);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
pci_setup_cardbus(b);
break;
default:
printk(KERN_INFO "PCI: not setting up bridge %s "
"for bus %d\n", pci_name(dev), b->number);
break;
}
}
}
EXPORT_SYMBOL(pci_bus_assign_resources);
void __init
pci_assign_unassigned_resources(void)
{
struct pci_bus *bus;
/* Depth first, calculate sizes and alignments of all
subordinate buses. */
list_for_each_entry(bus, &pci_root_buses, node) {
pci_bus_size_bridges(bus);
}
/* Depth last, allocate resources and update the hardware. */
list_for_each_entry(bus, &pci_root_buses, node) {
pci_bus_assign_resources(bus);
pci_enable_bridges(bus);
}
}

64
drivers/pci/setup-irq.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* drivers/pci/setup-irq.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
static void __init
pdev_fixup_irq(struct pci_dev *dev,
u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(struct pci_dev *, u8, u8))
{
u8 pin, slot;
int irq;
/* If this device is not on the primary bus, we need to figure out
which interrupt pin it will come in on. We know which slot it
will come in on 'cos that slot is where the bridge is. Each
time the interrupt line passes through a PCI-PCI bridge we must
apply the swizzle function. */
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
/* Cope with 0 and illegal. */
if (pin == 0 || pin > 4)
pin = 1;
/* Follow the chain of bridges, swizzling as we go. */
slot = (*swizzle)(dev, &pin);
irq = (*map_irq)(dev, slot, pin);
if (irq == -1)
irq = 0;
dev->irq = irq;
pr_debug("PCI: fixup irq: (%s) got %d\n",
dev->dev.kobj.name, dev->irq);
/* Always tell the device, so the driver knows what is
the real IRQ to use; the device does not use it. */
pcibios_update_irq(dev, irq);
}
void __init
pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(struct pci_dev *, u8, u8))
{
struct pci_dev *dev = NULL;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
pdev_fixup_irq(dev, swizzle, map_irq);
}
}

200
drivers/pci/setup-res.c Normal file
View File

@@ -0,0 +1,200 @@
/*
* drivers/pci/setup-res.c
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Support routines for initializing a PCI subsystem.
*/
/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
/*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Resource sorting
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
#include "pci.h"
static void
pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
{
struct pci_bus_region region;
u32 new, check, mask;
int reg;
pcibios_resource_to_bus(dev, &region, res);
pr_debug(" got res [%lx:%lx] bus [%lx:%lx] flags %lx for "
"BAR %d of %s\n", res->start, res->end,
region.start, region.end, res->flags, resno, pci_name(dev));
new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
if (res->flags & IORESOURCE_IO)
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
else
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
if (resno < 6) {
reg = PCI_BASE_ADDRESS_0 + 4 * resno;
} else if (resno == PCI_ROM_RESOURCE) {
new |= res->flags & IORESOURCE_ROM_ENABLE;
reg = dev->rom_base_reg;
} else {
/* Hmm, non-standard resource. */
return; /* kill uninitialised var warning */
}
pci_write_config_dword(dev, reg, new);
pci_read_config_dword(dev, reg, &check);
if ((new ^ check) & mask) {
printk(KERN_ERR "PCI: Error while updating region "
"%s/%d (%08x != %08x)\n", pci_name(dev), resno,
new, check);
}
if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
(PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
new = 0; /* currently everyone zeros the high address */
pci_write_config_dword(dev, reg + 4, new);
pci_read_config_dword(dev, reg + 4, &check);
if (check != new) {
printk(KERN_ERR "PCI: Error updating region "
"%s/%d (high %08x != %08x)\n",
pci_name(dev), resno, new, check);
}
}
res->flags &= ~IORESOURCE_UNSET;
pr_debug("PCI: moved device %s resource %d (%lx) to %x\n",
pci_name(dev), resno, res->flags,
new & ~PCI_REGION_FLAG_MASK);
}
int __devinit
pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
struct resource *root = NULL;
char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
int err;
if (res->flags & IORESOURCE_IO)
root = &ioport_resource;
if (res->flags & IORESOURCE_MEM)
root = &iomem_resource;
err = -EINVAL;
if (root != NULL)
err = insert_resource(root, res);
if (err) {
printk(KERN_ERR "PCI: %s region %d of %s %s [%lx:%lx]\n",
root ? "Address space collision on" :
"No parent found for",
resource, dtype, pci_name(dev), res->start, res->end);
}
return err;
}
int pci_assign_resource(struct pci_dev *dev, int resno)
{
struct pci_bus *bus = dev->bus;
struct resource *res = dev->resource + resno;
unsigned long size, min, align;
int ret;
size = res->end - res->start + 1;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
/* The bridge resources are special, as their
size != alignment. Sizing routines return
required alignment in the "start" field. */
align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
/* First, try exact prefetching match.. */
ret = pci_bus_alloc_resource(bus, res, size, align, min,
IORESOURCE_PREFETCH,
pcibios_align_resource, dev);
if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {
/*
* That failed.
*
* But a prefetching area can handle a non-prefetching
* window (it will just not perform as well).
*/
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
pcibios_align_resource, dev);
}
if (ret) {
printk(KERN_ERR "PCI: Failed to allocate %s resource #%d:%lx@%lx for %s\n",
res->flags & IORESOURCE_IO ? "I/O" : "mem",
resno, size, res->start, pci_name(dev));
} else if (resno < PCI_BRIDGE_RESOURCES) {
pci_update_resource(dev, res, resno);
}
return ret;
}
/* Sort resources by alignment */
void __devinit
pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
{
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r;
struct resource_list *list, *tmp;
unsigned long r_align;
r = &dev->resource[i];
r_align = r->end - r->start;
if (!(r->flags) || r->parent)
continue;
if (!r_align) {
printk(KERN_WARNING "PCI: Ignore bogus resource %d "
"[%lx:%lx] of %s\n",
i, r->start, r->end, pci_name(dev));
continue;
}
r_align = (i < PCI_BRIDGE_RESOURCES) ? r_align + 1 : r->start;
for (list = head; ; list = list->next) {
unsigned long align = 0;
struct resource_list *ln = list->next;
int idx;
if (ln) {
idx = ln->res - &ln->dev->resource[0];
align = (idx < PCI_BRIDGE_RESOURCES) ?
ln->res->end - ln->res->start + 1 :
ln->res->start;
}
if (r_align > align) {
tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
panic("pdev_sort_resources(): "
"kmalloc() failed!\n");
tmp->next = ln;
tmp->res = r;
tmp->dev = dev;
list->next = tmp;
break;
}
}
}
}

145
drivers/pci/syscall.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* pci_syscall.c
*
* For architectures where we want to allow direct access
* to the PCI config stuff - it would probably be preferable
* on PCs too, but there people just do it by hand with the
* magic northbridge registers..
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/smp_lock.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
asmlinkage long
sys_pciconfig_read(unsigned long bus, unsigned long dfn,
unsigned long off, unsigned long len,
void __user *buf)
{
struct pci_dev *dev;
u8 byte;
u16 word;
u32 dword;
long err, cfg_ret;
err = -EPERM;
if (!capable(CAP_SYS_ADMIN))
goto error;
err = -ENODEV;
dev = pci_find_slot(bus, dfn);
if (!dev)
goto error;
lock_kernel();
switch (len) {
case 1:
cfg_ret = pci_read_config_byte(dev, off, &byte);
break;
case 2:
cfg_ret = pci_read_config_word(dev, off, &word);
break;
case 4:
cfg_ret = pci_read_config_dword(dev, off, &dword);
break;
default:
err = -EINVAL;
unlock_kernel();
goto error;
};
unlock_kernel();
err = -EIO;
if (cfg_ret != PCIBIOS_SUCCESSFUL)
goto error;
switch (len) {
case 1:
err = put_user(byte, (unsigned char __user *)buf);
break;
case 2:
err = put_user(word, (unsigned short __user *)buf);
break;
case 4:
err = put_user(dword, (unsigned int __user *)buf);
break;
};
return err;
error:
/* ??? XFree86 doesn't even check the return value. They
just look for 0xffffffff in the output, since that's what
they get instead of a machine check on x86. */
switch (len) {
case 1:
put_user(-1, (unsigned char __user *)buf);
break;
case 2:
put_user(-1, (unsigned short __user *)buf);
break;
case 4:
put_user(-1, (unsigned int __user *)buf);
break;
};
return err;
}
asmlinkage long
sys_pciconfig_write(unsigned long bus, unsigned long dfn,
unsigned long off, unsigned long len,
void __user *buf)
{
struct pci_dev *dev;
u8 byte;
u16 word;
u32 dword;
int err = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
dev = pci_find_slot(bus, dfn);
if (!dev)
return -ENODEV;
lock_kernel();
switch(len) {
case 1:
err = get_user(byte, (u8 __user *)buf);
if (err)
break;
err = pci_write_config_byte(dev, off, byte);
if (err != PCIBIOS_SUCCESSFUL)
err = -EIO;
break;
case 2:
err = get_user(word, (u16 __user *)buf);
if (err)
break;
err = pci_write_config_word(dev, off, word);
if (err != PCIBIOS_SUCCESSFUL)
err = -EIO;
break;
case 4:
err = get_user(dword, (u32 __user *)buf);
if (err)
break;
err = pci_write_config_dword(dev, off, dword);
if (err != PCIBIOS_SUCCESSFUL)
err = -EIO;
break;
default:
err = -EINVAL;
break;
};
unlock_kernel();
return err;
}