aboutsummaryrefslogtreecommitdiff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/Config.lb1
-rw-r--r--src/devices/chip.c11
-rw-r--r--src/devices/device.c20
-rw-r--r--src/devices/device_util.c73
-rw-r--r--src/devices/hypertransport.c51
-rw-r--r--src/devices/pci_device.c124
-rw-r--r--src/devices/pnp_device.c217
7 files changed, 414 insertions, 83 deletions
diff --git a/src/devices/Config.lb b/src/devices/Config.lb
index 6da04ee2e4..50d05579e5 100644
--- a/src/devices/Config.lb
+++ b/src/devices/Config.lb
@@ -2,5 +2,6 @@ object device.o
object root_device.o
object device_util.o
object pci_device.o
+object pnp_device.o
object hypertransport.o
object chip.o
diff --git a/src/devices/chip.c b/src/devices/chip.c
index c9e1ac5643..d8a59e3062 100644
--- a/src/devices/chip.c
+++ b/src/devices/chip.c
@@ -80,6 +80,7 @@ void chip_enumerate(struct chip *chip)
link += 1;
}
if (dev) {
+ struct chip_resource *res, *res_limit;
printk_spew("path (%p) %s %s", dev, dev_path(dev), identical_paths?"identical":"");
printk_spew(" parent: (%p) %s\n",dev->bus->dev, dev_path(dev->bus->dev));
dev->chip = chip;
@@ -90,6 +91,16 @@ void chip_enumerate(struct chip *chip)
child->bus = &dev->link[link];
}
}
+ res = &chip->path[i].resource[0];
+ res_limit = &chip->path[i].resource[MAX_RESOURCES];
+ for(; res < res_limit; res++) {
+ if (res->flags) {
+ struct resource *resource;
+ resource = get_resource(dev, res->index);
+ resource->flags = res->flags | IORESOURCE_FIXED | IORESOURCE_ASSIGNED;
+ resource->base = res->base;
+ }
+ }
}
if (dev && !chip->dev) {
chip->dev = dev;
diff --git a/src/devices/device.c b/src/devices/device.c
index 4f1cbb2c5d..b54e770294 100644
--- a/src/devices/device.c
+++ b/src/devices/device.c
@@ -23,9 +23,9 @@
#include <string.h>
/* Linked list of ALL devices */
-struct device *all_devices = 0;
+struct device *all_devices = &dev_root;
/* pointer to the last device */
-static struct device **last_dev_p = &all_devices;
+static struct device **last_dev_p = &dev_root.next;
#define DEVICE_MEM_HIGH 0xFEC00000UL /* Reserve 20M for the system */
#define DEVICE_IO_START 0x1000
@@ -323,7 +323,8 @@ void compute_allocate_resource(
/* base must be aligned to size */
base = round(base, 1UL << align);
resource->base = base;
- resource->flags |= IORESOURCE_SET;
+ resource->flags |= IORESOURCE_ASSIGNED;
+ resource->flags &= ~IORESOURCE_STORED;
base += size;
printk_spew(
@@ -459,19 +460,24 @@ void dev_configure(void)
* safe.
*/
root->resource[0].base = DEVICE_IO_START;
- root->resource[0].flags |= IORESOURCE_SET;
+ root->resource[0].flags |= IORESOURCE_ASSIGNED;
+ root->resource[0].flags &= ~IORESOURCE_STORED;
/* Now reallocate the pci resources memory with the
* highest addresses I can manage.
*/
root->resource[1].base =
round_down(DEVICE_MEM_HIGH - root->resource[1].size,
1UL << root->resource[1].align);
- root->resource[1].flags |= IORESOURCE_SET;
+ root->resource[1].flags |= IORESOURCE_ASSIGNED;
+ root->resource[1].flags &= ~IORESOURCE_STORED;
+
+ /* Allocate the VGA I/O resource..
+ */
+ allocate_vga_resource();
+
// now just set things into registers ... we hope ...
root->ops->set_resources(root);
- allocate_vga_resource();
-
printk_info("done.\n");
}
diff --git a/src/devices/device_util.c b/src/devices/device_util.c
index c806726853..013501474c 100644
--- a/src/devices/device_util.c
+++ b/src/devices/device_util.c
@@ -34,7 +34,8 @@ struct device *dev_find_slot(unsigned int bus, unsigned int devfn)
result = 0;
for (dev = all_devices; dev; dev = dev->next) {
- if ((dev->bus->secondary == bus) &&
+ if ((dev->path.type == DEVICE_PATH_PCI) &&
+ (dev->bus->secondary == bus) &&
(dev->path.u.pci.devfn == devfn)) {
result = dev;
break;
@@ -57,8 +58,9 @@ struct device *dev_find_device(unsigned int vendor, unsigned int device, struct
from = all_devices;
else
from = from->next;
- while (from && (from->vendor != vendor || from->device != device))
+ while (from && (from->vendor != vendor || from->device != device)) {
from = from->next;
+ }
return from;
}
@@ -142,3 +144,70 @@ int path_eq(struct device_path *path1, struct device_path *path2)
}
return equal;
}
+
+/**
+ * See if we have unused but allocated resource structures.
+ * If so remove the allocation.
+ * @param dev The device to find the resource on
+ */
+void compact_resources(device_t dev)
+{
+ struct resource *resource;
+ int i;
+ /* Move all of the free resources to the end */
+ for(i = 0; i < dev->resources;) {
+ resource = &dev->resource[i];
+ if (!resource->flags) {
+ memmove(resource, resource + 1, dev->resources - i);
+ dev->resources -= 1;
+ memset(&dev->resource[dev->resources], 0, sizeof(*resource));
+ } else {
+ i++;
+ }
+ }
+}
+
+/**
+ * See if a resource structure already exists for a given index and if
+ * not allocate one.
+ * @param dev The device to find the resource on
+ * @param index The index of the resource on the device.
+ */
+struct resource *get_resource(device_t dev, unsigned index)
+{
+ struct resource *resource;
+ int i;
+
+ /* First move all of the free resources to the end */
+ compact_resources(dev);
+
+ /* See if there is a resource with the appropriate index */
+ resource = 0;
+ for(i = 0; i < dev->resources; i++) {
+ if (dev->resource[i].index == index) {
+ resource = &dev->resource[i];
+ break;
+ }
+ }
+ if (!resource) {
+ if (dev->resources == MAX_RESOURCES) {
+ die("MAX_RESOURCES exceeded.");
+ }
+ resource = &dev->resource[dev->resources];
+ memset(resource, 0, sizeof(*resource));
+ dev->resources++;
+ }
+ /* Initialize the resource values */
+ if (!(resource->flags & IORESOURCE_FIXED)) {
+ resource->flags = 0;
+ resource->base = 0;
+ }
+ resource->size = 0;
+ resource->limit = 0;
+ resource->index = index;
+ resource->align = 0;
+ resource->gran = 0;
+
+ return resource;
+}
+
diff --git a/src/devices/hypertransport.c b/src/devices/hypertransport.c
index b65f518d3a..e50ebf6f20 100644
--- a/src/devices/hypertransport.c
+++ b/src/devices/hypertransport.c
@@ -3,6 +3,7 @@
#include <device/device.h>
#include <device/path.h>
#include <device/pci.h>
+#include <device/pci_ids.h>
#include <device/hypertransport.h>
#include <device/chip.h>
#include <part/hard_reset.h>
@@ -25,6 +26,30 @@ static device_t ht_scan_get_devs(device_t *old_devices)
return first;
}
+static unsigned ht_read_freq_cap(device_t dev, unsigned pos)
+{
+ /* Handle bugs in valid hypertransport frequency reporting */
+ unsigned freq_cap;
+
+ freq_cap = pci_read_config16(dev, pos);
+ freq_cap &= ~(1 << HT_FREQ_VENDOR); /* Ignore Vendor HT frequencies */
+
+ /* AMD 8131 Errata 48 */
+ if ((dev->vendor == PCI_VENDOR_ID_AMD) &&
+ (dev->device == PCI_DEVICE_ID_AMD_8131_PCIX)) {
+ freq_cap &= ~(1 << HT_FREQ_800Mhz);
+ }
+ /* AMD 8151 Errata 23 */
+ if ((dev->vendor == PCI_VENDOR_ID_AMD) &&
+ (dev->device == PCI_DEVICE_ID_AMD_8151_SYSCTRL)) {
+ freq_cap &= ~(1 << HT_FREQ_800Mhz);
+ }
+ /* AMD K8 Unsupported 1Ghz? */
+ if ((dev->vendor == PCI_VENDOR_ID_AMD) && (dev->device == 0x1100)) {
+ freq_cap &= ~(1 << HT_FREQ_1000Mhz);
+ }
+ return freq_cap;
+}
struct prev_link {
struct device *dev;
@@ -48,18 +73,13 @@ static int ht_setup_link(struct prev_link *prev, device_t dev, unsigned pos)
reset_needed = 0;
/* Read the capabilities */
- present_freq_cap = pci_read_config16(dev, pos + PCI_HT_CAP_SLAVE_FREQ_CAP0);
- upstream_freq_cap = pci_read_config16(prev->dev, prev->pos + prev->freq_cap_off);
+ present_freq_cap = ht_read_freq_cap(dev, pos + PCI_HT_CAP_SLAVE_FREQ_CAP0);
+ upstream_freq_cap = ht_read_freq_cap(prev->dev, prev->pos + prev->freq_cap_off);
present_width_cap = pci_read_config8(dev, pos + PCI_HT_CAP_SLAVE_WIDTH0);
upstream_width_cap = pci_read_config8(prev->dev, prev->pos + prev->config_off);
/* Calculate the highest useable frequency */
-#if 0
freq = log2(present_freq_cap & upstream_freq_cap);
-#else
- /* Errata for 8131 - freq 5 has hardware problems don't support it */
- freq = log2(present_freq_cap & upstream_freq_cap & 0x1f);
-#endif
/* Calculate the highest width */
ln_upstream_width_in = link_width_to_pow2[upstream_width_cap & 7];
@@ -144,9 +164,7 @@ static unsigned ht_lookup_slave_capability(struct device *dev)
break;
}
}
- if(pos) {
- pos = pci_read_config8(dev, pos + PCI_CAP_LIST_NEXT);
- }
+ pos = pci_read_config8(dev, pos + PCI_CAP_LIST_NEXT);
}
return pos;
}
@@ -244,9 +262,12 @@ unsigned int hypertransport_scan_chain(struct bus *bus, unsigned int max)
else {
/* Add this device to the pci bus chain */
*chain_last = dev;
- /* Run the magice enable/disable sequence for the device */
+ /* Run the magice enable sequence for the device */
if (dev->chip && dev->chip->control && dev->chip->control->enable_dev) {
+ int enable = dev->enable;
+ dev->enable = 1;
dev->chip->control->enable_dev(dev);
+ dev->enable = enable;
}
/* Now read the vendor and device id */
id = pci_read_config32(dev, PCI_VENDOR_ID);
@@ -320,9 +341,11 @@ unsigned int hypertransport_scan_chain(struct bus *bus, unsigned int max)
#if HAVE_HARD_RESET == 1
if(reset_needed) {
printk_info("HyperT reset needed\n");
-// By LYH hard_reset();
- } else
- printk_debug("HyperT reset not needed\n");
+ hard_reset();
+ }
+ else {
+ printk_debug("HyperT reset not needed\n");
+ }
#endif
if (next_unitid > 0x1f) {
next_unitid = 0x1f;
diff --git a/src/devices/pci_device.c b/src/devices/pci_device.c
index 734b982a6d..c3f1f6ac8c 100644
--- a/src/devices/pci_device.c
+++ b/src/devices/pci_device.c
@@ -27,19 +27,14 @@
* @param resource Pointer to the resource structure
* @param index Address of the pci configuration register
*/
-static void pci_get_resource(struct device *dev, struct resource *resource, unsigned long index)
+static struct resource *pci_get_resource(struct device *dev, unsigned long index)
{
+ struct resource *resource;
uint32_t addr, size, base;
unsigned long type;
/* Initialize the resources to nothing */
- resource->base = 0;
- resource->size = 0;
- resource->align = 0;
- resource->gran = 0;
- resource->limit = 0;
- resource->flags = 0;
- resource->index = index;
+ resource = get_resource(dev, index);
addr = pci_read_config32(dev, index);
@@ -87,7 +82,7 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi
resource->size = (~((size | 0xffff0000) & PCI_BASE_ADDRESS_IO_MASK)) +1;
resource->align = log2(resource->size);
resource->gran = resource->align;
- resource->flags = IORESOURCE_IO;
+ resource->flags |= IORESOURCE_IO;
resource->limit = 0xffff;
}
else {
@@ -96,7 +91,7 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi
resource->size = (~(size &PCI_BASE_ADDRESS_MEM_MASK)) +1;
resource->align = log2(resource->size);
resource->gran = resource->align;
- resource->flags = IORESOURCE_MEM;
+ resource->flags |= IORESOURCE_MEM;
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
resource->flags |= IORESOURCE_PREFETCH;
}
@@ -145,7 +140,7 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi
}
}
/* dev->size holds the flags... */
- return;
+ return resource;
}
/** Read the base address registers for a given device.
@@ -154,75 +149,63 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi
*/
static void pci_read_bases(struct device *dev, unsigned int howmany)
{
- unsigned int reg;
unsigned long index;
- reg = dev->resources;
- for(index = PCI_BASE_ADDRESS_0;
- (reg < MAX_RESOURCES) && (index < PCI_BASE_ADDRESS_0 + (howmany << 2)); ) {
+ for(index = PCI_BASE_ADDRESS_0; (index < PCI_BASE_ADDRESS_0 + (howmany << 2)); ) {
struct resource *resource;
- resource = &dev->resource[reg];
- pci_get_resource(dev, resource, index);
- reg += (resource->flags & (IORESOURCE_IO | IORESOURCE_MEM))? 1:0;
+ resource = pci_get_resource(dev, index);
index += (resource->flags & IORESOURCE_PCI64)?8:4;
}
- dev->resources = reg;
+ compact_resources(dev);
}
static void pci_bridge_read_bases(struct device *dev)
{
- unsigned int reg = dev->resources;
+ struct resource *resource;
/* FIXME handle bridges without some of the optional resources */
/* Initialize the io space constraints on the current bus */
- dev->resource[reg].base = 0;
- dev->resource[reg].size = 0;
- dev->resource[reg].align = log2(PCI_IO_BRIDGE_ALIGN);
- dev->resource[reg].gran = log2(PCI_IO_BRIDGE_ALIGN);
- dev->resource[reg].limit = 0xffffUL;
- dev->resource[reg].flags = IORESOURCE_IO | IORESOURCE_PCI_BRIDGE;
- dev->resource[reg].index = PCI_IO_BASE;
- compute_allocate_resource(&dev->link[0], &dev->resource[reg],
+ resource = get_resource(dev, PCI_IO_BASE);
+ resource->size = 0;
+ resource->align = log2(PCI_IO_BRIDGE_ALIGN);
+ resource->gran = log2(PCI_IO_BRIDGE_ALIGN);
+ resource->limit = 0xffffUL;
+ resource->flags |= IORESOURCE_IO | IORESOURCE_PCI_BRIDGE;
+ compute_allocate_resource(&dev->link[0], resource,
IORESOURCE_IO, IORESOURCE_IO);
- reg++;
/* Initiliaze the prefetchable memory constraints on the current bus */
- dev->resource[reg].base = 0;
- dev->resource[reg].size = 0;
- dev->resource[reg].align = log2(PCI_MEM_BRIDGE_ALIGN);
- dev->resource[reg].gran = log2(PCI_MEM_BRIDGE_ALIGN);
- dev->resource[reg].limit = 0xffffffffUL;
- dev->resource[reg].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_PCI_BRIDGE;
- dev->resource[reg].index = PCI_PREF_MEMORY_BASE;
- compute_allocate_resource(&dev->link[0], &dev->resource[reg],
+ resource = get_resource(dev, PCI_PREF_MEMORY_BASE);
+ resource->size = 0;
+ resource->align = log2(PCI_MEM_BRIDGE_ALIGN);
+ resource->gran = log2(PCI_MEM_BRIDGE_ALIGN);
+ resource->limit = 0xffffffffUL;
+ resource->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_PCI_BRIDGE;
+ resource->index = PCI_PREF_MEMORY_BASE;
+ compute_allocate_resource(&dev->link[0], resource,
IORESOURCE_MEM | IORESOURCE_PREFETCH,
IORESOURCE_MEM | IORESOURCE_PREFETCH);
- reg++;
/* Initialize the memory resources on the current bus */
- dev->resource[reg].base = 0;
- dev->resource[reg].size = 0;
- dev->resource[reg].align = log2(PCI_MEM_BRIDGE_ALIGN);
- dev->resource[reg].gran = log2(PCI_MEM_BRIDGE_ALIGN);
- dev->resource[reg].limit = 0xffffffffUL;
- dev->resource[reg].flags = IORESOURCE_MEM | IORESOURCE_PCI_BRIDGE;
- dev->resource[reg].index = PCI_MEMORY_BASE;
- compute_allocate_resource(&dev->link[0], &dev->resource[reg],
- IORESOURCE_MEM | IORESOURCE_PREFETCH,
+ resource = get_resource(dev, PCI_MEMORY_BASE);
+ resource->size = 0;
+ resource->align = log2(PCI_MEM_BRIDGE_ALIGN);
+ resource->gran = log2(PCI_MEM_BRIDGE_ALIGN);
+ resource->limit = 0xffffffffUL;
+ resource->flags = IORESOURCE_MEM | IORESOURCE_PCI_BRIDGE;
+ compute_allocate_resource(&dev->link[0], resource,
+ IORESOURCE_MEM | IORESOURCE_PREFETCH,
IORESOURCE_MEM);
- reg++;
- dev->resources = reg;
+ compact_resources(dev);
}
void pci_dev_read_resources(struct device *dev)
{
uint32_t addr;
- dev->resources = 0;
- memset(&dev->resource[0], 0, sizeof(dev->resource));
pci_read_bases(dev, 6);
addr = pci_read_config32(dev, PCI_ROM_ADDRESS);
dev->rom_address = (addr == 0xffffffff)? 0 : addr;
@@ -231,8 +214,6 @@ void pci_dev_read_resources(struct device *dev)
void pci_bus_read_resources(struct device *dev)
{
uint32_t addr;
- dev->resources = 0;
- memset(&dev->resource, 0, sizeof(dev->resource));
pci_bridge_read_bases(dev);
pci_read_bases(dev, 2);
@@ -246,10 +227,10 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
{
unsigned long base, limit;
unsigned char buf[10];
- unsigned long align;
+ unsigned long gran;
/* Make certain the resource has actually been set */
- if (!(resource->flags & IORESOURCE_SET)) {
+ if (!(resource->flags & IORESOURCE_ASSIGNED)) {
#if 1
printk_err("ERROR: %s %02x not allocated\n",
dev_path(dev), resource->index);
@@ -257,6 +238,11 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
return;
}
+ /* If I have already stored this resource don't worry about it */
+ if (resource->flags & IORESOURCE_STORED) {
+ return;
+ }
+
/* Only handle PCI memory and IO resources for now */
if (!(resource->flags & (IORESOURCE_MEM |IORESOURCE_IO)))
return;
@@ -272,12 +258,20 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
}
/* Get the base address */
base = resource->base;
- /* Get the resource alignment */
- align = 1UL << resource->align;
+ /* Get the resource granularity */
+ gran = 1UL << resource->gran;
+
+ /* For a non bridge resource granularity and alignment are the same.
+ * For a bridge resource align is the largest needed alignment below
+ * the bridge. While the granularity is simply how many low bits of the
+ * address cannot be set.
+ */
/* Get the limit (rounded up) */
- limit = base + ((resource->size + align - 1UL) & ~(align - 1UL)) -1UL;
+ limit = base + ((resource->size + gran - 1UL) & ~(gran - 1UL)) -1UL;
+ /* Now store the resource */
+ resource->flags |= IORESOURCE_STORED;
if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) {
/*
* some chipsets allow us to set/clear the IO bit.
@@ -326,6 +320,8 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, 0);
}
else {
+ /* Don't let me think I stored the resource */
+ resource->flags &= ~IORESOURCE_STORED;
printk_err("ERROR: invalid resource->index %x\n",
resource->index);
}
@@ -386,6 +382,7 @@ void pci_dev_enable_resources(struct device *dev)
uint16_t command;
command = pci_read_config16(dev, PCI_COMMAND);
command |= dev->command;
+ command |= (PCI_COMMAND_PARITY + PCI_COMMAND_SERR); /* error check */
printk_debug("%s cmd <- %02x\n", dev_path(dev), command);
pci_write_config16(dev, PCI_COMMAND, command);
@@ -397,6 +394,7 @@ void pci_bus_enable_resources(struct device *dev)
uint16_t ctrl;
ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL);
ctrl |= dev->link[0].bridge_ctrl;
+ ctrl |= (PCI_BRIDGE_CTL_PARITY + PCI_BRIDGE_CTL_SERR); /* error check */
printk_debug("%s bridge ctrl <- %04x\n", dev_path(dev), ctrl);
pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl);
@@ -560,9 +558,12 @@ unsigned int pci_scan_bus(struct bus *bus,
dev = alloc_dev(bus, &dummy.path);
}
else {
- /* Run the magic enable/disable sequence for the device */
+ /* Run the magic enable sequence for the device */
if (dev->chip && dev->chip->control && dev->chip->control->enable_dev) {
+ int enable = dev->enable;
+ dev->enable = 1;
dev->chip->control->enable_dev(dev);
+ dev->enable = enable;
}
/* Now read the vendor and device id */
id = pci_read_config32(dev, PCI_VENDOR_ID);
@@ -584,7 +585,7 @@ unsigned int pci_scan_bus(struct bus *bus,
*/
set_pci_ops(dev);
/* Error if we don't have some pci operations for it */
- if (dev->enable && !dev->ops) {
+ if (!dev->ops) {
printk_err("%s No device operations\n",
dev_path(dev));
continue;
@@ -594,6 +595,9 @@ unsigned int pci_scan_bus(struct bus *bus,
if (dev->ops && dev->ops->enable) {
dev->ops->enable(dev);
}
+ else if (dev->chip && dev->chip->control && dev->chip->control->enable_dev) {
+ dev->chip->control->enable_dev(dev);
+ }
printk_debug("%s [%04x/%04x] %s\n",
dev_path(dev),
diff --git a/src/devices/pnp_device.c b/src/devices/pnp_device.c
new file mode 100644
index 0000000000..0bccd7d151
--- /dev/null
+++ b/src/devices/pnp_device.c
@@ -0,0 +1,217 @@
+/* Copyright 2004 Linux Networx */
+/* This code is distrubted wihtout warrant under the GPL v2 (see COPYING) */
+
+#include <console/console.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <bitops.h>
+#include <string.h>
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pnp.h>
+
+
+/* PNP fundamental operations */
+
+void pnp_write_config(device_t dev, uint8_t reg, uint8_t value)
+{
+ outb(reg, dev->path.u.pnp.port);
+ outb(value, dev->path.u.pnp.port + 1);
+}
+
+uint8_t pnp_read_config(device_t dev, uint8_t reg)
+{
+ outb(reg, dev->path.u.pnp.port);
+ return inb(dev->path.u.pnp.port + 1);
+}
+
+void pnp_set_logical_device(device_t dev)
+{
+ pnp_write_config(dev, 0x07, dev->path.u.pnp.device);
+}
+
+void pnp_set_enable(device_t dev, int enable)
+{
+ pnp_write_config(dev, 0x30, enable?0x1:0x0);
+}
+
+int pnp_read_enable(device_t dev)
+{
+ return !!pnp_read_config(dev, 0x30);
+}
+
+void pnp_set_iobase(device_t dev, unsigned index, unsigned iobase)
+{
+ /* Index == 0x60 or 0x62 */
+ pnp_write_config(dev, index + 0, (iobase >> 8) & 0xff);
+ pnp_write_config(dev, index + 1, iobase & 0xff);
+}
+
+void pnp_set_irq(device_t dev, unsigned index, unsigned irq)
+{
+ /* Index == 0x70 or 0x72 */
+ pnp_write_config(dev, index, irq);
+}
+
+
+void pnp_set_drq(device_t dev, unsigned drq, unsigned index)
+{
+ /* Index == 0x74 */
+ pnp_write_config(dev, index, drq & 0xff);
+}
+
+/* PNP device operations */
+
+void pnp_read_resources(device_t dev)
+{
+ return;
+}
+
+static void pnp_set_resource(device_t dev, struct resource *resource)
+{
+ if (!(resource->flags & IORESOURCE_ASSIGNED)) {
+#if 1
+ printk_err("ERROR: %s %02x not allocated\n",
+ dev_path(dev), resource->index);
+#endif
+ return;
+ }
+ /* Now store the resource */
+ resource->flags |= IORESOURCE_STORED;
+ if (resource->flags & IORESOURCE_IO) {
+ pnp_set_iobase(dev, resource->index, resource->base);
+ }
+ else if (resource->flags & IORESOURCE_DRQ) {
+ pnp_set_drq(dev, resource->index, resource->base);
+ }
+ else if (resource->flags & IORESOURCE_IRQ) {
+ pnp_set_irq(dev, resource->index, resource->base);
+ }
+ else {
+ /* Don't let me think I stored the resource */
+ resource->flags &= IORESOURCE_STORED;
+ printk_err("ERROR: %s %02x unknown resource type\n",
+ dev_path(dev), resource->index);
+ return;
+ }
+
+ printk_debug(
+ "%s %02x <- [0x%08lx - 0x%08lx] %s\n",
+ dev_path(dev),
+ resource->index,
+ resource->base, resource->base + resource->size - 1,
+ (resource->flags & IORESOURCE_IO)? "io":
+ (resource->flags & IORESOURCE_DRQ)? "drq":
+ (resource->flags & IORESOURCE_IRQ)? "irq":
+ (resource->flags & IORESOURCE_MEM)? "mem":
+ "???");
+}
+
+void pnp_set_resources(device_t dev)
+{
+ int i;
+
+ /* Select the device */
+ pnp_set_logical_device(dev);
+
+ /* Paranoia says I should disable the device here... */
+ for(i = 0; i < dev->resources; i++) {
+ pnp_set_resource(dev, &dev->resource[i]);
+ }
+}
+
+void pnp_enable_resources(device_t dev)
+{
+ pnp_set_logical_device(dev);
+ pnp_set_enable(dev, 1);
+
+}
+
+void pnp_enable(device_t dev)
+{
+ pnp_set_logical_device(dev);
+ if (!dev->enable) {
+ pnp_set_enable(dev, 0);
+ }
+}
+
+struct device_operations pnp_ops = {
+ .read_resources = pnp_read_resources,
+ .set_resources = pnp_set_resources,
+ .enable_resources = pnp_enable_resources,
+ .enable = pnp_enable,
+};
+
+/* PNP chip opertations */
+
+static void pnp_get_ioresource(device_t dev, unsigned index, struct io_info *info)
+{
+ struct resource *resource;
+ uint32_t size;
+ resource = get_resource(dev, index);
+
+ /* Initilize the resource */
+ resource->limit = 0xffff;
+ resource->flags |= IORESOURCE_IO;
+
+ /* Set the resource size and alignment */
+ size = (0xffff & info->mask);
+ resource->size = (~(size | 0xfffff800) + 1);
+ resource->align = log2(resource->size);
+ resource->gran = resource->align;
+}
+
+static void get_resources(device_t dev, struct pnp_info *info)
+{
+ struct resource *resource;
+
+ pnp_set_logical_device(dev);
+
+ if (info->flags & PNP_IO0) {
+ pnp_get_ioresource(dev, PNP_IDX_IO0, &info->io0);
+ }
+ if (info->flags & PNP_IO1) {
+ pnp_get_ioresource(dev, PNP_IDX_IO1, &info->io1);
+ }
+ if (info->flags & PNP_IRQ0) {
+ resource = get_resource(dev, PNP_IDX_IRQ0);
+ resource->size = 1;
+ resource->flags |= IORESOURCE_IRQ;
+ }
+ if (info->flags & PNP_IRQ1) {
+ resource = get_resource(dev, PNP_IDX_IRQ1);
+ resource->size = 1;
+ resource->flags |= IORESOURCE_IRQ;
+ }
+ if (info->flags & PNP_DRQ0) {
+ resource = get_resource(dev, PNP_IDX_DRQ0);
+ resource->size = 1;
+ resource->flags |= IORESOURCE_DRQ;
+ }
+ if (info->flags & PNP_DRQ1) {
+ resource = get_resource(dev, PNP_IDX_DRQ1);
+ resource->size = 1;
+ resource->flags |= IORESOURCE_DRQ;
+ }
+
+}
+
+void pnp_enumerate(struct chip *chip, unsigned functions,
+ struct device_operations *ops, struct pnp_info *info)
+{
+ struct device_path path;
+ device_t dev;
+ int i;
+
+ chip_enumerate(chip);
+ path.type = DEVICE_PATH_PNP;
+ path.u.pnp.port = chip->dev->path.u.pnp.port;
+
+ /* Setup the ops and resources on the newly allocated devices */
+ for(i = 0; i < functions; i++) {
+ path.u.pnp.device = info[i].function;
+ dev = alloc_find_dev(chip->bus, &path);
+ dev->ops = ops;
+ get_resources(dev, &info[i]);
+ }
+}