mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
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:
commit
1da177e4c3
17291 changed files with 6718755 additions and 0 deletions
59
drivers/pci/Kconfig
Normal file
59
drivers/pci/Kconfig
Normal 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
65
drivers/pci/Makefile
Normal 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
62
drivers/pci/access.c
Normal 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
151
drivers/pci/bus.c
Normal 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
132
drivers/pci/gen-devlist.c
Normal 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
163
drivers/pci/hotplug.c
Normal 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
197
drivers/pci/hotplug/Kconfig
Normal 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
|
||||
|
||||
74
drivers/pci/hotplug/Makefile
Normal file
74
drivers/pci/hotplug/Makefile
Normal 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
|
||||
268
drivers/pci/hotplug/acpiphp.h
Normal file
268
drivers/pci/hotplug/acpiphp.h
Normal 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 */
|
||||
453
drivers/pci/hotplug/acpiphp_core.c
Normal file
453
drivers/pci/hotplug/acpiphp_core.c
Normal 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 <[email protected]>, Takayoshi Kochi <[email protected]>"
|
||||
#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);
|
||||
1344
drivers/pci/hotplug/acpiphp_glue.c
Normal file
1344
drivers/pci/hotplug/acpiphp_glue.c
Normal file
File diff suppressed because it is too large
Load diff
499
drivers/pci/hotplug/acpiphp_ibm.c
Normal file
499
drivers/pci/hotplug/acpiphp_ibm.c
Normal 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 <[email protected]>, Vernon Mauery <[email protected]>"
|
||||
#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", ¶ms, &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);
|
||||
449
drivers/pci/hotplug/acpiphp_pci.c
Normal file
449
drivers/pci/hotplug/acpiphp_pci.c
Normal 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);
|
||||
}
|
||||
700
drivers/pci/hotplug/acpiphp_res.c
Normal file
700
drivers/pci/hotplug/acpiphp_res.c
Normal 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);
|
||||
}
|
||||
96
drivers/pci/hotplug/cpci_hotplug.h
Normal file
96
drivers/pci/hotplug/cpci_hotplug.h
Normal 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 */
|
||||
792
drivers/pci/hotplug/cpci_hotplug_core.c
Normal file
792
drivers/pci/hotplug/cpci_hotplug_core.c
Normal 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 <[email protected]>"
|
||||
#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);
|
||||
661
drivers/pci/hotplug/cpci_hotplug_pci.c
Normal file
661
drivers/pci/hotplug/cpci_hotplug_pci.c
Normal 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;
|
||||
}
|
||||
223
drivers/pci/hotplug/cpcihp_generic.c
Normal file
223
drivers/pci/hotplug/cpcihp_generic.c
Normal 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 <[email protected]>"
|
||||
#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)");
|
||||
305
drivers/pci/hotplug/cpcihp_zt5550.c
Normal file
305
drivers/pci/hotplug/cpcihp_zt5550.c
Normal 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 <[email protected]>"
|
||||
#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");
|
||||
79
drivers/pci/hotplug/cpcihp_zt5550.h
Normal file
79
drivers/pci/hotplug/cpcihp_zt5550.h
Normal 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 */
|
||||
721
drivers/pci/hotplug/cpqphp.h
Normal file
721
drivers/pci/hotplug/cpqphp.h
Normal 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
|
||||
|
||||
1509
drivers/pci/hotplug/cpqphp_core.c
Normal file
1509
drivers/pci/hotplug/cpqphp_core.c
Normal file
File diff suppressed because it is too large
Load diff
3096
drivers/pci/hotplug/cpqphp_ctrl.c
Normal file
3096
drivers/pci/hotplug/cpqphp_ctrl.c
Normal file
File diff suppressed because it is too large
Load diff
666
drivers/pci/hotplug/cpqphp_nvram.c
Normal file
666
drivers/pci/hotplug/cpqphp_nvram.c
Normal 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;
|
||||
}
|
||||
|
||||
57
drivers/pci/hotplug/cpqphp_nvram.h
Normal file
57
drivers/pci/hotplug/cpqphp_nvram.h
Normal 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
|
||||
|
||||
1569
drivers/pci/hotplug/cpqphp_pci.c
Normal file
1569
drivers/pci/hotplug/cpqphp_pci.c
Normal file
File diff suppressed because it is too large
Load diff
143
drivers/pci/hotplug/cpqphp_sysfs.c
Normal file
143
drivers/pci/hotplug/cpqphp_sysfs.c
Normal 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);
|
||||
}
|
||||
358
drivers/pci/hotplug/fakephp.c
Normal file
358
drivers/pci/hotplug/fakephp.c
Normal 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 <[email protected]>"
|
||||
#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");
|
||||
|
||||
763
drivers/pci/hotplug/ibmphp.h
Normal file
763
drivers/pci/hotplug/ibmphp.h
Normal 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
|
||||
|
||||
1422
drivers/pci/hotplug/ibmphp_core.c
Normal file
1422
drivers/pci/hotplug/ibmphp_core.c
Normal file
File diff suppressed because it is too large
Load diff
1275
drivers/pci/hotplug/ibmphp_ebda.c
Normal file
1275
drivers/pci/hotplug/ibmphp_ebda.c
Normal file
File diff suppressed because it is too large
Load diff
1161
drivers/pci/hotplug/ibmphp_hpc.c
Normal file
1161
drivers/pci/hotplug/ibmphp_hpc.c
Normal file
File diff suppressed because it is too large
Load diff
1747
drivers/pci/hotplug/ibmphp_pci.c
Normal file
1747
drivers/pci/hotplug/ibmphp_pci.c
Normal file
File diff suppressed because it is too large
Load diff
2156
drivers/pci/hotplug/ibmphp_res.c
Normal file
2156
drivers/pci/hotplug/ibmphp_res.c
Normal file
File diff suppressed because it is too large
Load diff
180
drivers/pci/hotplug/pci_hotplug.h
Normal file
180
drivers/pci/hotplug/pci_hotplug.h
Normal 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
|
||||
|
||||
715
drivers/pci/hotplug/pci_hotplug_core.c
Normal file
715
drivers/pci/hotplug/pci_hotplug_core.c
Normal 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 <[email protected]>, Scott Murray <[email protected]>"
|
||||
#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);
|
||||
352
drivers/pci/hotplug/pciehp.h
Normal file
352
drivers/pci/hotplug/pciehp.h
Normal 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 */
|
||||
662
drivers/pci/hotplug/pciehp_core.c
Normal file
662
drivers/pci/hotplug/pciehp_core.c
Normal 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 <[email protected]>, Greg Kroah-Hartman <[email protected]>, Dely Sy <[email protected]>"
|
||||
#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);
|
||||
2706
drivers/pci/hotplug/pciehp_ctrl.c
Normal file
2706
drivers/pci/hotplug/pciehp_ctrl.c
Normal file
File diff suppressed because it is too large
Load diff
1501
drivers/pci/hotplug/pciehp_hpc.c
Normal file
1501
drivers/pci/hotplug/pciehp_hpc.c
Normal file
File diff suppressed because it is too large
Load diff
827
drivers/pci/hotplug/pciehp_pci.c
Normal file
827
drivers/pci/hotplug/pciehp_pci.c
Normal 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));
|
||||
}
|
||||
52
drivers/pci/hotplug/pciehprm.h
Normal file
52
drivers/pci/hotplug/pciehprm.h
Normal 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_ */
|
||||
1737
drivers/pci/hotplug/pciehprm_acpi.c
Normal file
1737
drivers/pci/hotplug/pciehprm_acpi.c
Normal file
File diff suppressed because it is too large
Load diff
501
drivers/pci/hotplug/pciehprm_nonacpi.c
Normal file
501
drivers/pci/hotplug/pciehprm_nonacpi.c
Normal 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;
|
||||
}
|
||||
56
drivers/pci/hotplug/pciehprm_nonacpi.h
Normal file
56
drivers/pci/hotplug/pciehprm_nonacpi.h
Normal 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_ */
|
||||
375
drivers/pci/hotplug/pcihp_skeleton.c
Normal file
375
drivers/pci/hotplug/pcihp_skeleton.c
Normal 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 <[email protected]>"
|
||||
#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);
|
||||
24
drivers/pci/hotplug/rpadlpar.h
Normal file
24
drivers/pci/hotplug/rpadlpar.h
Normal 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
|
||||
503
drivers/pci/hotplug/rpadlpar_core.c
Normal file
503
drivers/pci/hotplug/rpadlpar_core.c
Normal 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");
|
||||
151
drivers/pci/hotplug/rpadlpar_sysfs.c
Normal file
151
drivers/pci/hotplug/rpadlpar_sysfs.c
Normal 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);
|
||||
}
|
||||
138
drivers/pci/hotplug/rpaphp.h
Normal file
138
drivers/pci/hotplug/rpaphp.h
Normal 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 */
|
||||
536
drivers/pci/hotplug/rpaphp_core.c
Normal file
536
drivers/pci/hotplug/rpaphp_core.c
Normal 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 <[email protected]>"
|
||||
#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);
|
||||
538
drivers/pci/hotplug/rpaphp_pci.c
Normal file
538
drivers/pci/hotplug/rpaphp_pci.c
Normal 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);
|
||||
267
drivers/pci/hotplug/rpaphp_slot.c
Normal file
267
drivers/pci/hotplug/rpaphp_slot.c
Normal 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;
|
||||
}
|
||||
129
drivers/pci/hotplug/rpaphp_vio.c
Normal file
129
drivers/pci/hotplug/rpaphp_vio.c
Normal 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;
|
||||
}
|
||||
463
drivers/pci/hotplug/shpchp.h
Normal file
463
drivers/pci/hotplug/shpchp.h
Normal 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 */
|
||||
630
drivers/pci/hotplug/shpchp_core.c
Normal file
630
drivers/pci/hotplug/shpchp_core.c
Normal 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 <[email protected]>, Greg Kroah-Hartman <[email protected]>, Dely Sy <[email protected]>"
|
||||
#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);
|
||||
2848
drivers/pci/hotplug/shpchp_ctrl.c
Normal file
2848
drivers/pci/hotplug/shpchp_ctrl.c
Normal file
File diff suppressed because it is too large
Load diff
1620
drivers/pci/hotplug/shpchp_hpc.c
Normal file
1620
drivers/pci/hotplug/shpchp_hpc.c
Normal file
File diff suppressed because it is too large
Load diff
810
drivers/pci/hotplug/shpchp_pci.c
Normal file
810
drivers/pci/hotplug/shpchp_pci.c
Normal 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));
|
||||
}
|
||||
143
drivers/pci/hotplug/shpchp_sysfs.c
Normal file
143
drivers/pci/hotplug/shpchp_sysfs.c
Normal 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);
|
||||
}
|
||||
55
drivers/pci/hotplug/shpchprm.h
Normal file
55
drivers/pci/hotplug/shpchprm.h
Normal 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_ */
|
||||
1713
drivers/pci/hotplug/shpchprm_acpi.c
Normal file
1713
drivers/pci/hotplug/shpchprm_acpi.c
Normal file
File diff suppressed because it is too large
Load diff
439
drivers/pci/hotplug/shpchprm_legacy.c
Normal file
439
drivers/pci/hotplug/shpchprm_legacy.c
Normal 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;
|
||||
}
|
||||
113
drivers/pci/hotplug/shpchprm_legacy.h
Normal file
113
drivers/pci/hotplug/shpchprm_legacy.h
Normal 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_ */
|
||||
434
drivers/pci/hotplug/shpchprm_nonacpi.c
Normal file
434
drivers/pci/hotplug/shpchprm_nonacpi.c
Normal 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;
|
||||
}
|
||||
56
drivers/pci/hotplug/shpchprm_nonacpi.h
Normal file
56
drivers/pci/hotplug/shpchprm_nonacpi.h
Normal 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
1151
drivers/pci/msi.c
Normal file
File diff suppressed because it is too large
Load diff
159
drivers/pci/msi.h
Normal file
159
drivers/pci/msi.h
Normal 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
137
drivers/pci/names.c
Normal 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
209
drivers/pci/pci-acpi.c
Normal 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
531
drivers/pci/pci-driver.c
Normal 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
490
drivers/pci/pci-sysfs.c
Normal 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
837
drivers/pci/pci.c
Normal 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
96
drivers/pci/pci.h
Normal 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
10179
drivers/pci/pci.ids
Normal file
File diff suppressed because it is too large
Load diff
36
drivers/pci/pcie/Kconfig
Normal file
36
drivers/pci/pcie/Kconfig
Normal 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.
|
||||
|
||||
7
drivers/pci/pcie/Makefile
Normal file
7
drivers/pci/pcie/Makefile
Normal 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
|
||||
41
drivers/pci/pcie/portdrv.h
Normal file
41
drivers/pci/pcie/portdrv.h
Normal 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_ */
|
||||
77
drivers/pci/pcie/portdrv_bus.c
Normal file
77
drivers/pci/pcie/portdrv_bus.c
Normal 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;
|
||||
}
|
||||
434
drivers/pci/pcie/portdrv_core.c
Normal file
434
drivers/pci/pcie/portdrv_core.c
Normal 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, ®16);
|
||||
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, ®16);
|
||||
/* Hot-Plug Capable */
|
||||
if (reg16 & PORT_TO_SLOT_MASK) {
|
||||
pci_read_config_dword(dev,
|
||||
pos + PCIE_SLOT_CAPABILITIES_REG, ®32);
|
||||
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, ®32);
|
||||
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, ®);
|
||||
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, ®16);
|
||||
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);
|
||||
122
drivers/pci/pcie/portdrv_pci.c
Normal file
122
drivers/pci/pcie/portdrv_pci.c
Normal 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 "[email protected]"
|
||||
#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
939
drivers/pci/probe.c
Normal 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
619
drivers/pci/proc.c
Normal 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
1352
drivers/pci/quirks.c
Normal file
File diff suppressed because it is too large
Load diff
118
drivers/pci/remove.c
Normal file
118
drivers/pci/remove.c
Normal 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
227
drivers/pci/rom.c
Normal 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
388
drivers/pci/search.c
Normal 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
552
drivers/pci/setup-bus.c
Normal 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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, ®ion, 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
64
drivers/pci/setup-irq.c
Normal 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
200
drivers/pci/setup-res.c
Normal 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 <[email protected]> */
|
||||
|
||||
/*
|
||||
* 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, ®ion, 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
145
drivers/pci/syscall.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue