diff --git a/devices/agp_device.c b/devices/agp_device.c new file mode 100644 index 0000000000..5ef8d8b2c2 --- /dev/null +++ b/devices/agp_device.c @@ -0,0 +1,70 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* (c) 2005 Linux Networx GPL see COPYING for details */ + +#include +#include +#include +#include +#include + +static void agp_tune_dev(device_t dev) +{ + unsigned cap; + cap = pci_find_capability(dev, PCI_CAP_ID_AGP); + if (!cap) { + return; + } + /* The OS is responsible for AGP tuning so do nothing here */ +} + +unsigned int agp_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, unsigned int max) +{ + device_t child; + max = pci_scan_bus(bus, min_devfn, max_devfn, max); + for(child = bus->children; child; child = child->sibling) { + if ( (child->path.u.pci.devfn < min_devfn) || + (child->path.u.pci.devfn > max_devfn)) + { + continue; + } + agp_tune_dev(child); + } + return max; +} + +unsigned int agp_scan_bridge(device_t dev, unsigned int max) +{ + return do_pci_scan_bridge(dev, max, agp_scan_bus); +} + +/** Default device operations for AGP bridges */ +static struct pci_operations agp_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_agp_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = agp_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &agp_bus_ops_pci, +}; diff --git a/devices/cardbus_device.c b/devices/cardbus_device.c new file mode 100644 index 0000000000..7c3d53899f --- /dev/null +++ b/devices/cardbus_device.c @@ -0,0 +1,251 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* (c) 2005 Linux Networx GPL see COPYING for details */ + +#include +#include +#include +#include +#include + +/* I don't think this code is quite correct but it is close. + * Anyone with a cardbus bridge and a little time should be able + * to make it usable quickly. -- Eric Biederman 24 March 2005 + */ + +/* + * 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 cardbus_record_bridge_resource( + device_t dev, resource_t moving, resource_t min_size, + unsigned index, unsigned long type) +{ + /* Initiliaze the constraints on the current bus */ + struct resource *resource; + resource = 0; + if (moving) { + unsigned long gran; + resource_t step; + resource = new_resource(dev, index); + resource->size = 0; + gran = 0; + step = 1; + while((moving & step) == 0) { + gran += 1; + step <<= 1; + } + resource->gran = gran; + resource->align = gran; + resource->limit = moving | (step - 1); + resource->flags = type; + /* Don't let the minimum size exceed what we + * can put in the resource. + */ + if ((min_size - 1) > resource->limit) { + min_size = resource->limit + 1; + } + resource->size = min_size; + } + return; +} + +static void cardbus_size_bridge_resource(device_t dev, unsigned index) +{ + struct resource *resource; + resource_t min_size; + resource = find_resource(dev, index); + if (resource) { + min_size = resource->size; + compute_allocate_resource(&dev->link[0], resource, + resource->flags, resource->flags); + /* Allways allocate at least the miniumum size to a + * cardbus bridge in case a new card is plugged in. + */ + if (resource->size < min_size) { + resource->size = min_size; + } + } +} + +void cardbus_read_resources(device_t dev) +{ + resource_t moving_base, moving_limit, moving; + unsigned long type; + uint16_t ctl; + unsigned long index; + + /* See if needs a card control registers base address */ + + pci_get_resource(dev, PCI_BASE_ADDRESS_0); + + compact_resources(dev); + + /* See which bridge I/O resources are implemented */ + moving_base = pci_moving_config32(dev, PCI_CB_IO_BASE_0); + moving_limit = pci_moving_config32(dev, PCI_CB_IO_LIMIT_0); + moving = moving_base & moving_limit; + + /* Initialize the io space constraints on the current bus */ + cardbus_record_bridge_resource(dev, moving, CARDBUS_IO_SIZE, + PCI_CB_IO_BASE_0, IORESOURCE_IO); + cardbus_size_bridge_resource(dev, PCI_CB_IO_BASE_0); + + /* See which bridge I/O resources are implemented */ + moving_base = pci_moving_config32(dev, PCI_CB_IO_BASE_1); + moving_limit = pci_moving_config32(dev, PCI_CB_IO_LIMIT_1); + moving = moving_base & moving_limit; + + /* Initialize the io space constraints on the current bus */ + cardbus_record_bridge_resource(dev, moving, CARDBUS_IO_SIZE, + PCI_CB_IO_BASE_1, IORESOURCE_IO); + + /* If I can enable prefetch for mem0 */ + ctl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); + ctl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; + ctl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; + ctl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; + pci_write_config16(dev, PCI_CB_BRIDGE_CONTROL, ctl); + ctl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); + + /* See which bridge memory resources are implemented */ + moving_base = pci_moving_config32(dev, PCI_CB_MEMORY_BASE_0); + moving_limit = pci_moving_config32(dev, PCI_CB_MEMORY_LIMIT_0); + moving = moving_base & moving_limit; + + /* Initialize the memory space constraints on the current bus */ + type = IORESOURCE_MEM; + if (ctl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { + type |= IORESOURCE_PREFETCH; + } + cardbus_record_bridge_resource(dev, moving, CARDBUS_MEM_SIZE, + PCI_CB_MEMORY_BASE_0, type); + if (type & IORESOURCE_PREFETCH) { + cardbus_size_bridge_resource(dev, PCI_CB_MEMORY_BASE_0); + } + + /* See which bridge memory resources are implemented */ + moving_base = pci_moving_config32(dev, PCI_CB_MEMORY_BASE_1); + moving_limit = pci_moving_config32(dev, PCI_CB_MEMORY_LIMIT_1); + moving = moving_base & moving_limit; + + /* Initialize the memory space constraints on the current bus */ + cardbus_record_bridge_resource(dev, moving, CARDBUS_MEM_SIZE, + PCI_CB_MEMORY_BASE_1, IORESOURCE_MEM); + cardbus_size_bridge_resource(dev, PCI_CB_MEMORY_BASE_1); + + compact_resources(dev); +} + +void cardbus_enable_resources(device_t dev) +{ + uint16_t ctrl; + ctrl = pci_read_config16(dev, PCI_CB_BRIDGE_CONTROL); + ctrl |= (dev->link[0].bridge_ctrl & ( + PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_NO_ISA | + PCI_BRIDGE_CTL_VGA | + PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_BUS_RESET)); + ctrl |= (PCI_CB_BRIDGE_CTL_PARITY + PCI_CB_BRIDGE_CTL_SERR); /* error check */ + printk_debug("%s bridge ctrl <- %04x\n", dev_path(dev), ctrl); + pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl); + + pci_dev_enable_resources(dev); + + enable_childrens_resources(dev); +} + +unsigned int cardbus_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, + unsigned int max) +{ + return pci_scan_bus(bus, min_devfn, max_devfn, max); +} + + +unsigned int cardbus_scan_bridge(device_t dev, unsigned int max) +{ + struct bus *bus; + uint32_t buses; + uint16_t cr; + + printk_spew("%s for %s\n", __func__, dev_path(dev)); + + bus = &dev->link[0]; + bus->dev = dev; + dev->links = 1; + + /* Set up the primary, secondary and subordinate bus numbers. We have + * no idea how many buses are behind this bridge yet, so we set the + * subordinate bus number to 0xff for the moment. + */ + bus->secondary = ++max; + bus->subordinate = 0xff; + + /* Clear all status bits and turn off memory, I/O and master enables. */ + cr = pci_read_config16(dev, PCI_COMMAND); + pci_write_config16(dev, PCI_COMMAND, 0x0000); + pci_write_config16(dev, PCI_STATUS, 0xffff); + + /* + * Read the existing primary/secondary/subordinate bus + * number configuration. + */ + buses = pci_read_config32(dev, PCI_CB_PRIMARY_BUS); + + /* Configure the bus numbers for this bridge: the configuration + * transactions will not be propagated by the bridge if it is not + * correctly configured. + */ + buses &= 0xff000000; + buses |= (((unsigned int) (dev->bus->secondary) << 0) | + ((unsigned int) (bus->secondary) << 8) | + ((unsigned int) (bus->subordinate) << 16)); + pci_write_config32(dev, PCI_CB_PRIMARY_BUS, buses); + + /* Now we can scan all subordinate buses + * i.e. the bus behind the bridge. + */ + max = cardbus_scan_bus(bus, 0x00, 0xff, max); + + /* We know the number of buses behind this bridge. Set the subordinate + * bus number to its real value. + */ + bus->subordinate = max; + buses = (buses & 0xff00ffff) | + ((unsigned int) (bus->subordinate) << 16); + pci_write_config32(dev, PCI_CB_PRIMARY_BUS, buses); + pci_write_config16(dev, PCI_COMMAND, cr); + + printk_spew("%s returns max %d\n", __func__, max); + return max; +} + +struct device_operations default_cardbus_ops_bus = { + .read_resources = cardbus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = cardbus_enable_resources, + .init = 0, + .scan_bus = cardbus_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, +}; diff --git a/devices/device.c b/devices/device.c new file mode 100644 index 0000000000..1a309c1e84 --- /dev/null +++ b/devices/device.c @@ -0,0 +1,738 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* + * (c) 1999--2000 Martin Mares + * (c) 2003 Eric Biederman + * (c) 2003 Linux Networx + */ +/* lots of mods by ron minnich (rminnich@lanl.gov), with + * the final architecture guidance from Tom Merritt (tjm@codegen.com) + * In particular, we changed from the one-pass original version to + * Tom's recommended multiple-pass version. I wasn't sure about doing + * it with multiple passes, until I actually started doing it and saw + * the wisdom of Tom's recommendations ... + * + * Lots of cleanups by Eric Biederman to handle bridges, and to + * handle resource allocation for non-pci devices. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** Linked list of ALL devices */ +struct device *all_devices = &dev_root; +/** Pointer to the last device */ +extern struct device **last_dev_p; + +/** The upper limit of MEM resource of the devices. + * Reserve 20M for the system */ +#define DEVICE_MEM_HIGH 0xFEBFFFFFUL +/** The lower limit of IO resource of the devices. + * Reserve 4k for ISA/Legacy devices */ +#define DEVICE_IO_START 0x1000 + +/** + * @brief Allocate a new device structure. + * + * Allocte a new device structure and attached it to the device tree as a + * child of the parent bus. + * + * @param parent parent bus the newly created device attached to. + * @param path path to the device to be created. + * + * @return pointer to the newly created device structure. + * + * @see device_path + */ +static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; +device_t alloc_dev(struct bus *parent, struct device_path *path) +{ + device_t dev, child; + int link; + + spin_lock(&dev_lock); + + /* Find the last child of our parent */ + for(child = parent->children; child && child->sibling; ) { + child = child->sibling; + } + + dev = malloc(sizeof(*dev)); + if (dev == 0) { + die("DEV: out of memory.\n"); + } + memset(dev, 0, sizeof(*dev)); + memcpy(&dev->path, path, sizeof(*path)); + + /* Initialize the back pointers in the link fields */ + for(link = 0; link < MAX_LINKS; link++) { + dev->link[link].dev = dev; + dev->link[link].link = link; + } + + /* By default devices are enabled */ + dev->enabled = 1; + + /* Add the new device to the list of children of the bus. */ + dev->bus = parent; + if (child) { + child->sibling = dev; + } else { + parent->children = dev; + } + + /* Append a new device to the global device list. + * The list is used to find devices once everything is set up. + */ + *last_dev_p = dev; + last_dev_p = &dev->next; + + spin_unlock(&dev_lock); + return dev; +} + +/** + * @brief round a number up to an alignment. + * @param val the starting value + * @param roundup Alignment as a power of two + * @returns rounded up number + */ +static resource_t round(resource_t val, unsigned long pow) +{ + resource_t mask; + mask = (1ULL << pow) - 1ULL; + val += mask; + val &= ~mask; + return val; +} + +/** Read the resources on all devices of a given bus. + * @param bus bus to read the resources on. + */ +static void read_resources(struct bus *bus) +{ + struct device *curdev; + + printk_spew("%s read_resources bus %d link: %d\n", + dev_path(bus->dev), bus->secondary, bus->link); + + /* Walk through all of the devices and find which resources they need. */ + for(curdev = bus->children; curdev; curdev = curdev->sibling) { + unsigned links; + int i; + if (curdev->have_resources) { + continue; + } + if (!curdev->enabled) { + continue; + } + if (!curdev->ops || !curdev->ops->read_resources) { + printk_err("%s missing read_resources\n", + dev_path(curdev)); + continue; + } + curdev->ops->read_resources(curdev); + curdev->have_resources = 1; + /* Read in subtractive resources behind the current device */ + links = 0; + for(i = 0; i < curdev->resources; i++) { + struct resource *resource; + unsigned link; + resource = &curdev->resource[i]; + if (!(resource->flags & IORESOURCE_SUBTRACTIVE)) + continue; + link = IOINDEX_SUBTRACTIVE_LINK(resource->index); + if (link > MAX_LINKS) { + printk_err("%s subtractive index on link: %d\n", + dev_path(curdev), link); + continue; + } + if (!(links & (1 << link))) { + links |= (1 << link); + read_resources(&curdev->link[link]); + } + } + } + printk_spew("%s read_resources bus %d link: %d done\n", + dev_path(bus->dev), bus->secondary, bus->link); +} + +struct pick_largest_state { + struct resource *last; + struct device *result_dev; + struct resource *result; + int seen_last; +}; + +static void pick_largest_resource(void *gp, + struct device *dev, struct resource *resource) +{ + struct pick_largest_state *state = gp; + struct resource *last; + last = state->last; + /* Be certain to pick the successor to last */ + if (resource == last) { + state->seen_last = 1; + return; + } + if (resource->flags & IORESOURCE_FIXED ) return; //skip it + if (last && ( + (last->align < resource->align) || + ((last->align == resource->align) && + (last->size < resource->size)) || + ((last->align == resource->align) && + (last->size == resource->size) && + (!state->seen_last)))) { + return; + } + if (!state->result || + (state->result->align < resource->align) || + ((state->result->align == resource->align) && + (state->result->size < resource->size))) + { + state->result_dev = dev; + state->result = resource; + } +} + +static struct device *largest_resource(struct bus *bus, struct resource **result_res, + unsigned long type_mask, unsigned long type) +{ + struct pick_largest_state state; + + state.last = *result_res; + state.result_dev = 0; + state.result = 0; + state.seen_last = 0; + + search_bus_resources(bus, type_mask, type, pick_largest_resource, &state); + + *result_res = state.result; + return state.result_dev; +} + +/* Compute allocate resources is the guts of the resource allocator. + * + * The problem. + * - Allocate resources locations for every device. + * - Don't overlap, and follow the rules of bridges. + * - Don't overlap with resources in fixed locations. + * - Be efficient so we don't have ugly strategies. + * + * The strategy. + * - Devices that have fixed addresses are the minority so don't + * worry about them too much. Instead only use part of the address + * space for devices with programmable addresses. This easily handles + * everything except bridges. + * + * - PCI devices are required to have thier sizes and their alignments + * equal. In this case an optimal solution to the packing problem + * exists. Allocate all devices from highest alignment to least + * alignment or vice versa. Use this. + * + * - So we can handle more than PCI run two allocation passes on + * bridges. The first to see how large the resources are behind + * the bridge, and what their alignment requirements are. The + * second to assign a safe address to the devices behind the + * bridge. This allows me to treat a bridge as just a device with + * a couple of resources, and not need to special case it in the + * allocator. Also this allows handling of other types of bridges. + * + */ + +void compute_allocate_resource( + struct bus *bus, + struct resource *bridge, + unsigned long type_mask, + unsigned long type) +{ + struct device *dev; + struct resource *resource; + resource_t base; + unsigned long align, min_align; + min_align = 0; + base = bridge->base; + + printk_spew("%s compute_allocate_%s: base: %08Lx size: %08Lx align: %d gran: %d\n", + dev_path(bus->dev), + (bridge->flags & IORESOURCE_IO)? "io": + (bridge->flags & IORESOURCE_PREFETCH)? "prefmem" : "mem", + base, bridge->size, bridge->align, bridge->gran); + + /* We want different minimum alignments for different kinds of + * resources. These minimums are not device type specific + * but resource type specific. + */ + if (bridge->flags & IORESOURCE_IO) { + min_align = log2(DEVICE_IO_ALIGN); + } + if (bridge->flags & IORESOURCE_MEM) { + min_align = log2(DEVICE_MEM_ALIGN); + } + + /* Make certain I have read in all of the resources */ + read_resources(bus); + + /* Remember I haven't found anything yet. */ + resource = 0; + + /* Walk through all the devices on the current bus and + * compute the addresses. + */ + while((dev = largest_resource(bus, &resource, type_mask, type))) { + resource_t size; + /* Do NOT I repeat do not ignore resources which have zero size. + * If they need to be ignored dev->read_resources should not even + * return them. Some resources must be set even when they have + * no size. PCI bridge resources are a good example of this. + */ + /* Propogate the resource alignment to the bridge register */ + if (resource->align > bridge->align) { + bridge->align = resource->align; + } + + /* Make certain we are dealing with a good minimum size */ + size = resource->size; + align = resource->align; + if (align < min_align) { + align = min_align; + } + + if (resource->flags & IORESOURCE_FIXED) { + continue; + } + + /* Propogate the resource limit to the bridge register */ + if (bridge->limit > resource->limit) { + bridge->limit = resource->limit; + } + /* Artificially deny limits between DEVICE_MEM_HIGH and 0xffffffff */ + if ((bridge->limit > DEVICE_MEM_HIGH) && (bridge->limit <= 0xffffffff)) { + bridge->limit = DEVICE_MEM_HIGH; + } + if (resource->flags & IORESOURCE_IO) { + /* Don't allow potential aliases over the + * legacy pci expansion card addresses. + * The legacy pci decodes only 10 bits, + * uses 100h - 3ffh. Therefor, only 0 - ff + * can be used out of each 400h block of io + * space. + */ + if ((base & 0x300) != 0) { + base = (base & ~0x3ff) + 0x400; + } + /* Don't allow allocations in the VGA IO range. + * PCI has special cases for that. + */ + else if ((base >= 0x3b0) && (base <= 0x3df)) { + base = 0x3e0; + } + } + if (((round(base, align) + size) -1) <= resource->limit) { + /* base must be aligned to size */ + base = round(base, align); + resource->base = base; + resource->flags |= IORESOURCE_ASSIGNED; + resource->flags &= ~IORESOURCE_STORED; + base += size; + + printk_spew( + "%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_PREFETCH)? "prefmem": "mem"); + } + } + /* A pci bridge resource does not need to be a power + * of two size, but it does have a minimum granularity. + * Round the size up to that minimum granularity so we + * know not to place something else at an address postitively + * decoded by the bridge. + */ + bridge->size = round(base, bridge->gran) - bridge->base; + + printk_spew("%s compute_allocate_%s: base: %08Lx size: %08Lx align: %d gran: %d done\n", + dev_path(bus->dev), + (bridge->flags & IORESOURCE_IO)? "io": + (bridge->flags & IORESOURCE_PREFETCH)? "prefmem" : "mem", + base, bridge->size, bridge->align, bridge->gran); + + +} + +#if CONFIG_CONSOLE_VGA == 1 +device_t vga_pri = 0; +static void allocate_vga_resource(void) +{ +#warning "FIXME modify allocate_vga_resource so it is less pci centric!" +#warning "This function knows to much about PCI stuff, it should be just a ietrator/visitor." + + /* FIXME handle the VGA pallette snooping */ + struct device *dev, *vga, *vga_onboard, *vga_first, *vga_last; + struct bus *bus; + bus = 0; + vga = 0; + vga_onboard = 0; + vga_first = 0; + vga_last = 0; + for(dev = all_devices; dev; dev = dev->next) { + if (!dev->enabled) continue; + if (((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && + ((dev->class >> 8) != PCI_CLASS_DISPLAY_OTHER)) + { + if (!vga_first) { + if (dev->on_mainboard) { + vga_onboard = dev; + } else { + vga_first = dev; + } + } else { + if (dev->on_mainboard) { + vga_onboard = dev; + } else { + vga_last = dev; + } + } + + /* It isn't safe to enable other VGA cards */ + dev->command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + } + } + + vga = vga_last; + + if(!vga) { + vga = vga_first; + } + +#if CONFIG_CONSOLE_VGA_ONBOARD_AT_FIRST == 1 + if (vga_onboard) // will use on board vga as pri +#else + if (!vga) // will use last add on adapter as pri +#endif + { + vga = vga_onboard; + } + + + if (vga) { + /* vga is first add on card or the only onboard vga */ + printk_debug("Allocating VGA resource %s\n", dev_path(vga)); + /* All legacy VGA cards have MEM & I/O space registers */ + vga->command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + vga_pri = vga; + bus = vga->bus; + } + /* Now walk up the bridges setting the VGA enable */ + while(bus) { + printk_debug("Setting PCI_BRIDGE_CTL_VGA for bridge %s\n", + dev_path(bus->dev)); + bus->bridge_ctrl |= PCI_BRIDGE_CTL_VGA; + bus = (bus == bus->dev->bus)? 0 : bus->dev->bus; + } +} + +#endif + + +/** + * @brief Assign the computed resources to the devices on the bus. + * + * @param bus Pointer to the structure for this bus + * + * Use the device specific set_resources method to store the computed + * resources to hardware. For bridge devices, the set_resources() method + * has to recurse into every down stream buses. + * + * Mutual recursion: + * assign_resources() -> device_operation::set_resources() + * device_operation::set_resources() -> assign_resources() + */ +void assign_resources(struct bus *bus) +{ + struct device *curdev; + + printk_spew("%s assign_resources, bus %d link: %d\n", + dev_path(bus->dev), bus->secondary, bus->link); + + for(curdev = bus->children; curdev; curdev = curdev->sibling) { + if (!curdev->enabled || !curdev->resources) { + continue; + } + if (!curdev->ops || !curdev->ops->set_resources) { + printk_err("%s missing set_resources\n", + dev_path(curdev)); + continue; + } + curdev->ops->set_resources(curdev); + } + printk_spew("%s assign_resources, bus %d link: %d\n", + dev_path(bus->dev), bus->secondary, bus->link); +} + +/** + * @brief Enable the resources for a specific device + * + * @param dev the device whose resources are to be enabled + * + * Enable resources of the device by calling the device specific + * enable_resources() method. + * + * The parent's resources should be enabled first to avoid having enabling + * order problem. This is done by calling the parent's enable_resources() + * method and let that method to call it's children's enable_resoruces() + * method via the (global) enable_childrens_resources(). + * + * Indirect mutual recursion: + * enable_resources() -> device_operations::enable_resource() + * device_operations::enable_resource() -> enable_children_resources() + * enable_children_resources() -> enable_resources() + */ +void enable_resources(struct device *dev) +{ + if (!dev->enabled) { + return; + } + if (!dev->ops || !dev->ops->enable_resources) { + printk_err("%s missing enable_resources\n", dev_path(dev)); + return; + } + dev->ops->enable_resources(dev); +} + +/** + * @brief Reset all of the devices a bus + * + * Reset all of the devices on a bus and clear the bus's reset_needed flag. + * + * @param bus pointer to the bus structure + * + * @return 1 if the bus was successfully reset, 0 otherwise. + * + */ +int reset_bus(struct bus *bus) +{ + if (bus && bus->dev && bus->dev->ops && bus->dev->ops->reset_bus) + { + bus->dev->ops->reset_bus(bus); + bus->reset_needed = 0; + return 1; + } + return 0; +} + +/** + * @brief Scan for devices on a bus. + * + * If there are bridges on the bus, recursively scan the buses behind the bridges. + * If the setting up and tuning of the bus causes a reset to be required, + * reset the bus and scan it again. + * + * @param bus pointer to the bus device + * @param max current bus number + * + * @return The maximum bus number found, after scanning all subordinate busses + */ +unsigned int scan_bus(device_t bus, unsigned int max) +{ + unsigned int new_max; + int do_scan_bus; + if ( !bus || + !bus->enabled || + !bus->ops || + !bus->ops->scan_bus) + { + return max; + } + do_scan_bus = 1; + while(do_scan_bus) { + int link; + new_max = bus->ops->scan_bus(bus, max); + do_scan_bus = 0; + for(link = 0; link < bus->links; link++) { + if (bus->link[link].reset_needed) { + if (reset_bus(&bus->link[link])) { + do_scan_bus = 1; + } else { + bus->bus->reset_needed = 1; + } + } + } + } + return new_max; +} + + +/** + * @brief Determine the existence of devices and extend the device tree. + * + * Most of the devices in the system are listed in the mainboard Config.lb + * file. The device structures for these devices are generated at compile + * time by the config tool and are organized into the device tree. This + * function determines if the devices created at compile time actually exist + * in the physical system. + * + * For devices in the physical system but not listed in the Config.lb file, + * the device structures have to be created at run time and attached to the + * device tree. + * + * This function starts from the root device 'dev_root', scan the buses in + * the system recursively, modify the device tree according to the result of + * the probe. + * + * This function has no idea how to scan and probe buses and devices at all. + * It depends on the bus/device specific scan_bus() method to do it. The + * scan_bus() method also has to create the device structure and attach + * it to the device tree. + */ +void dev_enumerate(void) +{ + struct device *root; + unsigned subordinate; + printk_info("Enumerating buses...\n"); + root = &dev_root; + if (root->chip_ops && root->chip_ops->enable_dev) { + root->chip_ops->enable_dev(root); + } + if (!root->ops || !root->ops->scan_bus) { + printk_err("dev_root missing scan_bus operation"); + return; + } + subordinate = scan_bus(root, 0); + printk_info("done\n"); +} + +/** + * @brief Configure devices on the devices tree. + * + * Starting at the root of the device tree, travel it recursively in two + * passes. In the first pass, we compute and allocate resources (ranges) + * requried by each device. In the second pass, the resources ranges are + * relocated to their final position and stored to the hardware. + * + * I/O resources start at DEVICE_IO_START and grow upward. MEM resources start + * at DEVICE_MEM_START and grow downward. + * + * Since the assignment is hierarchical we set the values into the dev_root + * struct. + */ +void dev_configure(void) +{ + struct resource *io, *mem; + struct device *root; + + printk_info("Allocating resources...\n"); + + root = &dev_root; + if (!root->ops || !root->ops->read_resources) { + printk_err("dev_root missing read_resources\n"); + return; + } + if (!root->ops || !root->ops->set_resources) { + printk_err("dev_root missing set_resources\n"); + return; + } + + printk_info("Reading resources...\n"); + root->ops->read_resources(root); + printk_info("Done reading resources.\n"); + + /* Get the resources */ + io = &root->resource[0]; + mem = &root->resource[1]; + /* Make certain the io devices are allocated somewhere safe. */ + io->base = DEVICE_IO_START; + io->flags |= IORESOURCE_ASSIGNED; + io->flags &= ~IORESOURCE_STORED; + /* Now reallocate the pci resources memory with the + * highest addresses I can manage. + */ + mem->base = resource_max(&root->resource[1]); + mem->flags |= IORESOURCE_ASSIGNED; + mem->flags &= ~IORESOURCE_STORED; + +#if CONFIG_CONSOLE_VGA == 1 + /* Allocate the VGA I/O resource.. */ + allocate_vga_resource(); +#endif + + /* Store the computed resource allocations into device registers ... */ + printk_info("Setting resources...\n"); + root->ops->set_resources(root); + printk_info("Done setting resources.\n"); +#if 0 + mem->flags |= IORESOURCE_STORED; + report_resource_stored(root, mem, ""); +#endif + + printk_info("Done allocating resources.\n"); +} + +/** + * @brief Enable devices on the device tree. + * + * Starting at the root, walk the tree and enable all devices/bridges by + * calling the device's enable_resources() method. + */ +void dev_enable(void) +{ + printk_info("Enabling resources...\n"); + + /* now enable everything. */ + enable_resources(&dev_root); + + printk_info("done.\n"); +} + +/** + * @brief Initialize all devices in the global device list. + * + * Starting at the first device on the global device link list, + * walk the list and call the device's init() method to do deivce + * specific setup. + */ +void dev_initialize(void) +{ + struct device *dev; + + printk_info("Initializing devices...\n"); + for(dev = all_devices; dev; dev = dev->next) { + if (dev->enabled && !dev->initialized && + dev->ops && dev->ops->init) + { + if (dev->path.type == DEVICE_PATH_I2C) { + printk_debug("smbus: %s[%d]->", + dev_path(dev->bus->dev), dev->bus->link); + } + printk_debug("%s init\n", dev_path(dev)); + dev->initialized = 1; + dev->ops->init(dev); + } + } + printk_info("Devices initialized\n"); +} + diff --git a/devices/device_util.c b/devices/device_util.c new file mode 100644 index 0000000000..5c24cb3c27 --- /dev/null +++ b/devices/device_util.c @@ -0,0 +1,547 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +#include +#include +#include +#include +#include +#include + +/** + * @brief See if a device structure exists for path + * + * @param bus The bus to find the device on + * @param path The relative path from the bus to the appropriate device + * @return pointer to a device structure for the device on bus at path + * or 0/NULL if no device is found + */ +device_t find_dev_path(struct bus *parent, struct device_path *path) +{ + device_t child; + for(child = parent->children; child; child = child->sibling) { + if (path_eq(path, &child->path)) { + break; + } + } + return child; +} + +/** + * @brief See if a device structure already exists and if not allocate it + * + * @param bus The bus to find the device on + * @param path The relative path from the bus to the appropriate device + * @return pointer to a device structure for the device on bus at path + */ +device_t alloc_find_dev(struct bus *parent, struct device_path *path) +{ + device_t child; + child = find_dev_path(parent, path); + if (!child) { + child = alloc_dev(parent, path); + } + return child; +} + +/** + * @brief Given a PCI bus and a devfn number, find the device structure + * + * @param bus The bus number + * @param devfn a device/function number + * @return pointer to the device structure + */ +struct device *dev_find_slot(unsigned int bus, unsigned int devfn) +{ + struct device *dev, *result; + + result = 0; + for (dev = all_devices; dev; dev = dev->next) { + if ((dev->path.type == DEVICE_PATH_PCI) && + (dev->bus->secondary == bus) && + (dev->path.u.pci.devfn == devfn)) { + result = dev; + break; + } + } + return result; +} + +/** + * @brief Given a smbus bus and a device number, find the device structure + * + * @param bus The bus number + * @param addr a device number + * @return pointer to the device structure + */ +struct device *dev_find_slot_on_smbus(unsigned int bus, unsigned int addr) +{ + struct device *dev, *result; + + result = 0; + for (dev = all_devices; dev; dev = dev->next) { + if ((dev->path.type == DEVICE_PATH_I2C) && + (dev->bus->secondary == bus) && + (dev->path.u.i2c.device == addr)) { + result = dev; + break; + } + } + return result; +} + +/** Find a device of a given vendor and type + * @param vendor Vendor ID (e.g. 0x8086 for Intel) + * @param device Device ID + * @param from Pointer to the device structure, used as a starting point + * in the linked list of all_devices, which can be 0 to start at the + * head of the list (i.e. all_devices) + * @return Pointer to the device struct + */ +struct device *dev_find_device(unsigned int vendor, unsigned int device, struct device *from) +{ + if (!from) + from = all_devices; + else + from = from->next; + while (from && (from->vendor != vendor || from->device != device)) { + from = from->next; + } + return from; +} + +/** Find a device of a given class + * @param class Class of the device + * @param from Pointer to the device structure, used as a starting point + * in the linked list of all_devices, which can be 0 to start at the + * head of the list (i.e. all_devices) + * @return Pointer to the device struct + */ +struct device *dev_find_class(unsigned int class, struct device *from) +{ + if (!from) + from = all_devices; + else + from = from->next; + while (from && (from->class & 0xffffff00) != class) + from = from->next; + return from; +} + + +const char *dev_path(device_t dev) +{ + static char buffer[DEVICE_PATH_MAX]; + buffer[0] = '\0'; + if (!dev) { + memcpy(buffer, "", 7); + } + else { + switch(dev->path.type) { + case DEVICE_PATH_ROOT: + memcpy(buffer, "Root Device", 12); + break; + case DEVICE_PATH_PCI: +#if PCI_BUS_SEGN_BITS + sprintf(buffer, "PCI: %04x:%02x:%02x.%01x", + dev->bus->secondary>>8, dev->bus->secondary & 0xff, + PCI_SLOT(dev->path.u.pci.devfn), PCI_FUNC(dev->path.u.pci.devfn)); +#else + sprintf(buffer, "PCI: %02x:%02x.%01x", + dev->bus->secondary, + PCI_SLOT(dev->path.u.pci.devfn), PCI_FUNC(dev->path.u.pci.devfn)); +#endif + break; + case DEVICE_PATH_PNP: + sprintf(buffer, "PNP: %04x.%01x", + dev->path.u.pnp.port, dev->path.u.pnp.device); + break; + case DEVICE_PATH_I2C: + sprintf(buffer, "I2C: %02x:%02x", + dev->bus->secondary, + dev->path.u.i2c.device); + break; + case DEVICE_PATH_APIC: + sprintf(buffer, "APIC: %02x", + dev->path.u.apic.apic_id); + break; + case DEVICE_PATH_PCI_DOMAIN: + sprintf(buffer, "PCI_DOMAIN: %04x", + dev->path.u.pci_domain.domain); + break; + case DEVICE_PATH_APIC_CLUSTER: + sprintf(buffer, "APIC_CLUSTER: %01x", + dev->path.u.apic_cluster.cluster); + break; + case DEVICE_PATH_CPU: + sprintf(buffer, "CPU: %02x", dev->path.u.cpu.id); + break; + case DEVICE_PATH_CPU_BUS: + sprintf(buffer, "CPU_BUS: %02x", dev->path.u.cpu_bus.id); + break; + default: + printk_err("Unknown device path type: %d\n", dev->path.type); + break; + } + } + return buffer; +} + +const char *bus_path(struct bus *bus) +{ + static char buffer[BUS_PATH_MAX]; + sprintf(buffer, "%s,%d", + dev_path(bus->dev), bus->link); + return buffer; +} + +int path_eq(struct device_path *path1, struct device_path *path2) +{ + int equal = 0; + if (path1->type == path2->type) { + switch(path1->type) { + case DEVICE_PATH_NONE: + break; + case DEVICE_PATH_ROOT: + equal = 1; + break; + case DEVICE_PATH_PCI: + equal = (path1->u.pci.devfn == path2->u.pci.devfn); + break; + case DEVICE_PATH_PNP: + equal = (path1->u.pnp.port == path2->u.pnp.port) && + (path1->u.pnp.device == path2->u.pnp.device); + break; + case DEVICE_PATH_I2C: + equal = (path1->u.i2c.device == path2->u.i2c.device); + break; + case DEVICE_PATH_APIC: + equal = (path1->u.apic.apic_id == path2->u.apic.apic_id); + break; + case DEVICE_PATH_PCI_DOMAIN: + equal = (path1->u.pci_domain.domain == path2->u.pci_domain.domain); + break; + case DEVICE_PATH_APIC_CLUSTER: + equal = (path1->u.apic_cluster.cluster == path2->u.apic_cluster.cluster); + break; + case DEVICE_PATH_CPU: + equal = (path1->u.cpu.id == path2->u.cpu.id); + break; + case DEVICE_PATH_CPU_BUS: + equal = (path1->u.cpu_bus.id == path2->u.cpu_bus.id); + break; + default: + printk_err("Uknown device type: %d\n", path1->type); + break; + } + } + 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 + * @param dev The device to find the resource on + * @param index The index of the resource on the device. + * @return the resource if it already exists + */ +struct resource *probe_resource(device_t dev, unsigned index) +{ + struct resource *resource; + int i; + /* 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; + } + } + return resource; +} + +/** + * See if a resource structure already exists for a given index and if + * not allocate one. Then initialize the initialize the resource + * to default values. + * @param dev The device to find the resource on + * @param index The index of the resource on the device. + */ +struct resource *new_resource(device_t dev, unsigned index) +{ + struct resource *resource; + + /* First move all of the free resources to the end */ + compact_resources(dev); + + /* See if there is a resource with the appropriate index */ + resource = probe_resource(dev, index); + 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; +} + +/** + * Return an existing resource structure for a given index. + * @param dev The device to find the resource on + * @param index The index of the resource on the device. + */ +struct resource *find_resource(device_t dev, unsigned index) +{ + struct resource *resource; + + /* See if there is a resource with the appropriate index */ + resource = probe_resource(dev, index); + if (!resource) { + printk_emerg("%s missing resource: %02x\n", + dev_path(dev), index); + die(""); + } + return resource; +} + + +/** + * @brief round a number up to the next multiple of gran + * @param val the starting value + * @param gran granularity we are aligning the number to. + * @returns aligned value + */ +static resource_t align_up(resource_t val, unsigned long gran) +{ + resource_t mask; + mask = (1ULL << gran) - 1ULL; + val += mask; + val &= ~mask; + return val; +} + +/** + * @brief round a number up to the previous multiple of gran + * @param val the starting value + * @param gran granularity we are aligning the number to. + * @returns aligned value + */ +static resource_t align_down(resource_t val, unsigned long gran) +{ + resource_t mask; + mask = (1ULL << gran) - 1ULL; + val &= ~mask; + return val; +} + +/** + * @brief Compute the maximum address that is part of a resource + * @param resource the resource whose limit is desired + * @returns the end + */ +resource_t resource_end(struct resource *resource) +{ + resource_t base, end; + /* get the base address */ + base = resource->base; + + /* 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 end (rounded up) */ + end = base + align_up(resource->size, resource->gran) - 1; + + return end; +} + +/** + * @brief Compute the maximum legal value for resource->base + * @param resource the resource whose maximum is desired + * @returns the maximum + */ +resource_t resource_max(struct resource *resource) +{ + resource_t max; + + max = align_down(resource->limit - resource->size + 1, resource->align); + + return max; +} + +/** + * @brief return the resource type of a resource + * @param resource the resource type to decode. + */ +const char *resource_type(struct resource *resource) +{ + static char buffer[RESOURCE_TYPE_MAX]; + sprintf(buffer, "%s%s%s%s", + ((resource->flags & IORESOURCE_READONLY)? "ro": ""), + ((resource->flags & IORESOURCE_PREFETCH)? "pref":""), + ((resource->flags == 0)? "unused": + (resource->flags & IORESOURCE_IO)? "io": + (resource->flags & IORESOURCE_DRQ)? "drq": + (resource->flags & IORESOURCE_IRQ)? "irq": + (resource->flags & IORESOURCE_MEM)? "mem":"??????"), + ((resource->flags & IORESOURCE_PCI64)?"64":"")); + return buffer; +} + +/** + * @brief print the resource that was just stored. + * @param dev the device the stored resorce lives on + * @param resource the resource that was just stored. + */ +void report_resource_stored(device_t dev, struct resource *resource, const char *comment) +{ + if (resource->flags & IORESOURCE_STORED) { + unsigned char buf[10]; + unsigned long long base, end; + base = resource->base; + end = resource_end(resource); + buf[0] = '\0'; + if (resource->flags & IORESOURCE_PCI_BRIDGE) { +#if PCI_BUS_SEGN_BITS + sprintf(buf, "bus %04x:%02x ", dev->bus->secondary>>8, dev->link[0].secondary & 0xff); +#else + sprintf(buf, "bus %02x ", dev->link[0].secondary); +#endif + } + printk_debug( + "%s %02x <- [0x%010Lx - 0x%010Lx] %s%s%s\n", + dev_path(dev), + resource->index, + base, end, + buf, + resource_type(resource), + comment); + } +} + +void search_bus_resources(struct bus *bus, + unsigned long type_mask, unsigned long type, + resource_search_t search, void *gp) +{ + struct device *curdev; + for(curdev = bus->children; curdev; curdev = curdev->sibling) { + int i; + /* Ignore disabled devices */ + if (!curdev->have_resources) continue; + for(i = 0; i < curdev->resources; i++) { + struct resource *resource = &curdev->resource[i]; + /* If it isn't the right kind of resource ignore it */ + if ((resource->flags & type_mask) != type) { + continue; + } + /* If it is a subtractive resource recurse */ + if (resource->flags & IORESOURCE_SUBTRACTIVE) { + struct bus * subbus; + subbus = &curdev->link[IOINDEX_SUBTRACTIVE_LINK(resource->index)]; + search_bus_resources(subbus, type_mask, type, search, gp); + continue; + } + search(gp, curdev, resource); + } + } +} + +void search_global_resources( + unsigned long type_mask, unsigned long type, + resource_search_t search, void *gp) +{ + struct device *curdev; + for(curdev = all_devices; curdev; curdev = curdev->next) { + int i; + /* Ignore disabled devices */ + if (!curdev->have_resources) continue; + for(i = 0; i < curdev->resources; i++) { + struct resource *resource = &curdev->resource[i]; + /* If it isn't the right kind of resource ignore it */ + if ((resource->flags & type_mask) != type) { + continue; + } + /* If it is a subtractive resource ignore it */ + if (resource->flags & IORESOURCE_SUBTRACTIVE) { + continue; + } + search(gp, curdev, resource); + } + } +} + +void dev_set_enabled(device_t dev, int enable) +{ + if (dev->enabled == enable) { + return; + } + dev->enabled = enable; + if (dev->ops && dev->ops->enable) { + dev->ops->enable(dev); + } + else if (dev->chip_ops && dev->chip_ops->enable_dev) { + dev->chip_ops->enable_dev(dev); + } +} + +void disable_children(struct bus *bus) +{ + device_t child; + for(child = bus->children; child; child = child->sibling) { + int link; + for(link = 0; link < child->links; link++) { + disable_children(&child->link[link]); + } + dev_set_enabled(child, 0); + } +} diff --git a/devices/hypertransport.c b/devices/hypertransport.c new file mode 100644 index 0000000000..290e25b2cc --- /dev/null +++ b/devices/hypertransport.c @@ -0,0 +1,607 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* + 2005.11 yhlu add let the real sb to use small uintid + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OPT_HT_LINK 0 + +#if OPT_HT_LINK == 1 +#include +#endif + +static device_t ht_scan_get_devs(device_t *old_devices) +{ + device_t first, last; + first = *old_devices; + last = first; + /* Extract the chain of devices to (first through last) + * for the next hypertransport device. + */ + while(last && last->sibling && + (last->sibling->path.type == DEVICE_PATH_PCI) && + (last->sibling->path.u.pci.devfn > last->path.u.pci.devfn)) + { + last = last->sibling; + } + if (first) { + device_t child; + /* Unlink the chain from the list of old devices */ + *old_devices = last->sibling; + last->sibling = 0; + + /* Now add the device to the list of devices on the bus. + */ + /* Find the last child of our parent */ + for(child = first->bus->children; child && child->sibling; ) { + child = child->sibling; + } + /* Place the chain on the list of children of their parent. */ + if (child) { + child->sibling = first; + } else { + first->bus->children = first; + } + } + return first; +} + +#if OPT_HT_LINK == 1 +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)) { +#if K8_HT_FREQ_1G_SUPPORT == 1 + #if K8_REV_F_SUPPORT == 0 + if (is_cpu_pre_e0()) { // only e0 later suupport 1GHz HT + freq_cap &= ~(1 << HT_FREQ_1000Mhz); + } + #endif +#else + freq_cap &= ~(1 << HT_FREQ_1000Mhz); +#endif + + } + return freq_cap; +} +#endif + +struct ht_link { + struct device *dev; + unsigned pos; + unsigned char ctrl_off, config_off, freq_off, freq_cap_off; +}; + +static int ht_setup_link(struct ht_link *prev, device_t dev, unsigned pos) +{ + static const uint8_t link_width_to_pow2[]= { 3, 4, 0, 5, 1, 2, 0, 0 }; + static const uint8_t pow2_to_link_width[] = { 0x7, 4, 5, 0, 1, 3 }; + struct ht_link cur[1]; + unsigned present_width_cap, upstream_width_cap; + unsigned present_freq_cap, upstream_freq_cap; + unsigned ln_present_width_in, ln_upstream_width_in; + unsigned ln_present_width_out, ln_upstream_width_out; + unsigned freq, old_freq; + unsigned present_width, upstream_width, old_width; + int reset_needed; + int linkb_to_host; + + /* Set the hypertransport link width and frequency */ + reset_needed = 0; + /* See which side of the device our previous write to + * set the unitid came from. + */ + cur->dev = dev; + cur->pos = pos; + linkb_to_host = (pci_read_config16(cur->dev, cur->pos + PCI_CAP_FLAGS) >> 10) & 1; + if (!linkb_to_host) { + cur->ctrl_off = PCI_HT_CAP_SLAVE_CTRL0; + cur->config_off = PCI_HT_CAP_SLAVE_WIDTH0; + cur->freq_off = PCI_HT_CAP_SLAVE_FREQ0; + cur->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP0; + } + else { + cur->ctrl_off = PCI_HT_CAP_SLAVE_CTRL1; + cur->config_off = PCI_HT_CAP_SLAVE_WIDTH1; + cur->freq_off = PCI_HT_CAP_SLAVE_FREQ1; + cur->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP1; + } +#if OPT_HT_LINK == 1 + /* Read the capabilities */ + present_freq_cap = ht_read_freq_cap(cur->dev, cur->pos + cur->freq_cap_off); + upstream_freq_cap = ht_read_freq_cap(prev->dev, prev->pos + prev->freq_cap_off); + present_width_cap = pci_read_config8(cur->dev, cur->pos + cur->config_off); + upstream_width_cap = pci_read_config8(prev->dev, prev->pos + prev->config_off); + + /* Calculate the highest useable frequency */ + freq = log2(present_freq_cap & upstream_freq_cap); + + /* Calculate the highest width */ + ln_upstream_width_in = link_width_to_pow2[upstream_width_cap & 7]; + ln_present_width_out = link_width_to_pow2[(present_width_cap >> 4) & 7]; + if (ln_upstream_width_in > ln_present_width_out) { + ln_upstream_width_in = ln_present_width_out; + } + upstream_width = pow2_to_link_width[ln_upstream_width_in]; + present_width = pow2_to_link_width[ln_upstream_width_in] << 4; + + ln_upstream_width_out = link_width_to_pow2[(upstream_width_cap >> 4) & 7]; + ln_present_width_in = link_width_to_pow2[present_width_cap & 7]; + if (ln_upstream_width_out > ln_present_width_in) { + ln_upstream_width_out = ln_present_width_in; + } + upstream_width |= pow2_to_link_width[ln_upstream_width_out] << 4; + present_width |= pow2_to_link_width[ln_upstream_width_out]; + + /* Set the current device */ + old_freq = pci_read_config8(cur->dev, cur->pos + cur->freq_off); + old_freq &= 0x0f; + if (freq != old_freq) { + unsigned new_freq; + pci_write_config8(cur->dev, cur->pos + cur->freq_off, freq); + reset_needed = 1; + printk_spew("HyperT FreqP old %x new %x\n",old_freq,freq); + new_freq = pci_read_config8(cur->dev, cur->pos + cur->freq_off); + new_freq &= 0x0f; + if (new_freq != freq) { + printk_err("%s Hypertransport frequency would not set wanted: %x got: %x\n", + dev_path(dev), freq, new_freq); + } + } + old_width = pci_read_config8(cur->dev, cur->pos + cur->config_off + 1); + if (present_width != old_width) { + unsigned new_width; + pci_write_config8(cur->dev, cur->pos + cur->config_off + 1, + present_width); + reset_needed = 1; + printk_spew("HyperT widthP old %x new %x\n",old_width, present_width); + new_width = pci_read_config8(cur->dev, cur->pos + cur->config_off + 1); + if (new_width != present_width) { + printk_err("%s Hypertransport width would not set wanted: %x got: %x\n", + dev_path(dev), present_width, new_width); + } + } + + /* Set the upstream device */ + old_freq = pci_read_config8(prev->dev, prev->pos + prev->freq_off); + old_freq &= 0x0f; + if (freq != old_freq) { + unsigned new_freq; + pci_write_config8(prev->dev, prev->pos + prev->freq_off, freq); + reset_needed = 1; + printk_spew("HyperT freqU old %x new %x\n", old_freq, freq); + new_freq = pci_read_config8(prev->dev, prev->pos + prev->freq_off); + new_freq &= 0x0f; + if (new_freq != freq) { + printk_err("%s Hypertransport frequency would not set wanted: %x got: %x\n", + dev_path(prev->dev), freq, new_freq); + } + } + old_width = pci_read_config8(prev->dev, prev->pos + prev->config_off + 1); + if (upstream_width != old_width) { + unsigned new_width; + pci_write_config8(prev->dev, prev->pos + prev->config_off + 1, upstream_width); + reset_needed = 1; + printk_spew("HyperT widthU old %x new %x\n", old_width, upstream_width); + new_width = pci_read_config8(prev->dev, prev->pos + prev->config_off + 1); + if (new_width != upstream_width) { + printk_err("%s Hypertransport width would not set wanted: %x got: %x\n", + dev_path(prev->dev), upstream_width, new_width); + } + } +#endif + + /* Remember the current link as the previous link, + * But look at the other offsets. + */ + prev->dev = cur->dev; + prev->pos = cur->pos; + if (cur->ctrl_off == PCI_HT_CAP_SLAVE_CTRL0) { + prev->ctrl_off = PCI_HT_CAP_SLAVE_CTRL1; + prev->config_off = PCI_HT_CAP_SLAVE_WIDTH1; + prev->freq_off = PCI_HT_CAP_SLAVE_FREQ1; + prev->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP1; + } else { + prev->ctrl_off = PCI_HT_CAP_SLAVE_CTRL0; + prev->config_off = PCI_HT_CAP_SLAVE_WIDTH0; + prev->freq_off = PCI_HT_CAP_SLAVE_FREQ0; + prev->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP0; + } + + return reset_needed; + +} + +static unsigned ht_lookup_slave_capability(struct device *dev) +{ + unsigned pos; + pos = 0; + do { + pos = pci_find_next_capability(dev, PCI_CAP_ID_HT, pos); + if (pos) { + unsigned flags; + flags = pci_read_config16(dev, pos + PCI_CAP_FLAGS); + printk_spew("flags: 0x%04x\n", flags); + if ((flags >> 13) == 0) { + /* Entry is a Slave secondary, success... */ + break; + } + } + } while(pos); + return pos; +} + +static void ht_collapse_early_enumeration(struct bus *bus, unsigned offset_unitid) +{ + unsigned int devfn; + struct ht_link prev; + unsigned ctrl; + + /* Initialize the hypertransport enumeration state */ + prev.dev = bus->dev; + prev.pos = bus->cap; + prev.ctrl_off = PCI_HT_CAP_HOST_CTRL; + prev.config_off = PCI_HT_CAP_HOST_WIDTH; + prev.freq_off = PCI_HT_CAP_HOST_FREQ; + prev.freq_cap_off = PCI_HT_CAP_HOST_FREQ_CAP; + + /* Wait until the link initialization is complete */ + do { + ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off); + /* Is this the end of the hypertransport chain */ + if (ctrl & (1 << 6)) { + return; + } + /* Has the link failed? */ + if (ctrl & (1 << 4)) { + /* + * Either the link has failed, or we have + * a CRC error. + * Sometimes this can happen due to link + * retrain, so lets knock it down and see + * if its transient + */ + ctrl |= ((1 << 4) | (1 <<8)); // Link fail + Crc + pci_write_config16(prev.dev, prev.pos + prev.ctrl_off, ctrl); + ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off); + if (ctrl & ((1 << 4) | (1 << 8))) { + printk_alert("Detected error on Hypertransport Link\n"); + return; + } + } + } while((ctrl & (1 << 5)) == 0); + + //actually, only for one HT device HT chain, and unitid is 0 +#if HT_CHAIN_UNITID_BASE == 0 + if(offset_unitid) { + return; + } +#endif + + /* Check if is already collapsed */ + if((!offset_unitid)|| (offset_unitid && (!((HT_CHAIN_END_UNITID_BASE == 0) && (HT_CHAIN_END_UNITID_BASE > 16) & 0xffff; + dummy.hdr_type = pci_read_config8(&dummy, PCI_HEADER_TYPE); + pos = ht_lookup_slave_capability(&dummy); + if (!pos){ + continue; + } + + /* Clear the unitid */ + flags = pci_read_config16(&dummy, pos + PCI_CAP_FLAGS); + flags &= ~0x1f; + pci_write_config16(&dummy, pos + PCI_CAP_FLAGS, flags); + printk_spew("Collapsing %s [%04x/%04x]\n", + dev_path(&dummy), dummy.vendor, dummy.device); + } +} + +unsigned int hypertransport_scan_chain(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, unsigned int max, unsigned *ht_unitid_base, unsigned offset_unitid) +{ + //even HT_CHAIN_UNITID_BASE == 0, we still can go through this function, because of end_of_chain check, also We need it to optimize link + unsigned next_unitid, last_unitid; + device_t old_devices, dev, func; + unsigned min_unitid = (offset_unitid) ? HT_CHAIN_UNITID_BASE:1; + struct ht_link prev; + device_t last_func = 0; + int ht_dev_num = 0; + +#if HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE + //let't record the device of last ht device, So we can set the Unitid to HT_CHAIN_END_UNITID_BASE + unsigned real_last_unitid; + uint8_t real_last_pos; + device_t real_last_dev; +#endif + + /* Restore the hypertransport chain to it's unitialized state */ + ht_collapse_early_enumeration(bus, offset_unitid); + + /* See which static device nodes I have */ + old_devices = bus->children; + bus->children = 0; + + /* Initialize the hypertransport enumeration state */ + prev.dev = bus->dev; + prev.pos = bus->cap; + prev.ctrl_off = PCI_HT_CAP_HOST_CTRL; + prev.config_off = PCI_HT_CAP_HOST_WIDTH; + prev.freq_off = PCI_HT_CAP_HOST_FREQ; + prev.freq_cap_off = PCI_HT_CAP_HOST_FREQ_CAP; + + /* If present assign unitid to a hypertransport chain */ + last_unitid = min_unitid -1; + next_unitid = min_unitid; + do { + uint8_t pos; + uint16_t flags; + unsigned count, static_count; + unsigned ctrl; + + last_unitid = next_unitid; + + /* Wait until the link initialization is complete */ + do { + ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off); + + if (ctrl & (1 << 6)) + goto end_of_chain; // End of chain + + if (ctrl & ((1 << 4) | (1 << 8))) { + /* + * Either the link has failed, or we have + * a CRC error. + * Sometimes this can happen due to link + * retrain, so lets knock it down and see + * if its transient + */ + ctrl |= ((1 << 4) | (1 <<8)); // Link fail + Crc + pci_write_config16(prev.dev, prev.pos + prev.ctrl_off, ctrl); + ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off); + if (ctrl & ((1 << 4) | (1 << 8))) { + printk_alert("Detected error on Hypertransport Link\n"); + goto end_of_chain; + } + } + } while((ctrl & (1 << 5)) == 0); + + + /* Get and setup the device_structure */ + dev = ht_scan_get_devs(&old_devices); + + /* See if a device is present and setup the + * device structure. + */ + dev = pci_probe_dev(dev, bus, 0); + if (!dev || !dev->enabled) { + break; + } + + /* Find the hypertransport link capability */ + pos = ht_lookup_slave_capability(dev); + if (pos == 0) { + printk_err("%s Hypertransport link capability not found", + dev_path(dev)); + break; + } + + /* Update the Unitid of the current device */ + flags = pci_read_config16(dev, pos + PCI_CAP_FLAGS); + + /* If the devices has a unitid set and is at devfn 0 we are done. + * This can happen with shadow hypertransport devices, + * or if we have reached the bottom of a + * hypertransport device chain. + */ + if (flags & 0x1f) { + break; + } + + flags &= ~0x1f; /* mask out base Unit ID */ + flags |= next_unitid & 0x1f; + pci_write_config16(dev, pos + PCI_CAP_FLAGS, flags); + + /* Update the Unitd id in the device structure */ + static_count = 1; + for(func = dev; func; func = func->sibling) { + func->path.u.pci.devfn += (next_unitid << 3); + static_count = (func->path.u.pci.devfn >> 3) + - (dev->path.u.pci.devfn >> 3) + 1; + last_func = func; + } + + /* Compute the number of unitids consumed */ + count = (flags >> 5) & 0x1f; /* get unit count */ + printk_spew("%s count: %04x static_count: %04x\n", + dev_path(dev), count, static_count); + if (count < static_count) { + count = static_count; + } + + /* Update the Unitid of the next device */ + ht_unitid_base[ht_dev_num] = next_unitid; + ht_dev_num++; +#if HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE + if(offset_unitid) { + real_last_unitid = next_unitid; + real_last_pos = pos; + real_last_dev = dev; + } +#endif + next_unitid += count; + + /* Setup the hypetransport link */ + bus->reset_needed |= ht_setup_link(&prev, dev, pos); + + printk_debug("%s [%04x/%04x] %s next_unitid: %04x\n", + dev_path(dev), + dev->vendor, dev->device, + (dev->enabled? "enabled": "disabled"), next_unitid); + + } while((last_unitid != next_unitid) && (next_unitid <= (max_devfn >> 3))); + end_of_chain: +#if OPT_HT_LINK == 1 + if(bus->reset_needed) { + printk_info("HyperT reset needed\n"); + } + else { + printk_debug("HyperT reset not needed\n"); + } +#endif + +#if HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE + if(offset_unitid && (ht_dev_num>0)) { + uint16_t flags; + int i; + device_t last_func = 0; + flags = pci_read_config16(real_last_dev, real_last_pos + PCI_CAP_FLAGS); + flags &= ~0x1f; + flags |= HT_CHAIN_END_UNITID_BASE & 0x1f; + pci_write_config16(real_last_dev, real_last_pos + PCI_CAP_FLAGS, flags); + + for(func = real_last_dev; func; func = func->sibling) { + func->path.u.pci.devfn -= ((real_last_unitid - HT_CHAIN_END_UNITID_BASE) << 3); + last_func = func; + } + + ht_unitid_base[ht_dev_num-1] = HT_CHAIN_END_UNITID_BASE; // update last one + + next_unitid = real_last_unitid; + } +#endif + + if (next_unitid > 0x1f) { + next_unitid = 0x1f; + } + + /* Die if any leftover Static devices are are found. + * There's probably a problem in the Config.lb. + */ + if(old_devices) { + device_t left; + for(left = old_devices; left; left = left->sibling) { + printk_debug("%s\n", dev_path(left)); + } + printk_err("HT: Left over static devices. Check your Config.lb\n"); + if(last_func && !last_func->sibling) // put back the left over static device, and let pci_scan_bus disable it + last_func->sibling = old_devices; + } + + /* Now that nothing is overlapping it is safe to scan the + * children. + */ + max = pci_scan_bus(bus, 0x00, (next_unitid << 3)|7, max); + return max; +} + +/** + * @brief Scan a PCI bridge and the buses behind the bridge. + * + * Determine the existence of buses behind the bridge. Set up the bridge + * according to the result of the scan. + * + * This function is the default scan_bus() method for PCI bridge devices. + * + * @param dev pointer to the bridge device + * @param max the highest bus number assgined up to now + * + * @return The maximum bus number found, after scanning all subordinate busses + */ +unsigned int hypertransport_scan_chain_x(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, unsigned int max) +{ + unsigned ht_unitid_base[4]; + unsigned offset_unitid = 1; + return hypertransport_scan_chain(bus, min_devfn, max_devfn, max, ht_unitid_base, offset_unitid); +} + +unsigned int ht_scan_bridge(struct device *dev, unsigned int max) +{ + return do_pci_scan_bridge(dev, max, hypertransport_scan_chain_x); +} + + +/** Default device operations for hypertransport bridges */ +static struct pci_operations ht_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_ht_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = ht_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &ht_bus_ops_pci, +}; diff --git a/devices/pci_device.c b/devices/pci_device.c new file mode 100644 index 0000000000..b730e7198a --- /dev/null +++ b/devices/pci_device.c @@ -0,0 +1,1258 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* + * PCI Bus Services, see include/linux/pci.h for further explanation. + * + * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang + * + * Copyright 1997 -- 1999 Martin Mares + * + * Copyright 2003 -- Eric Biederman + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CONFIG_HYPERTRANSPORT_PLUGIN_SUPPORT == 1 +#include +#endif +#if CONFIG_PCIX_PLUGIN_SUPPORT == 1 +#include +#endif +#if CONFIG_PCIEXP_PLUGIN_SUPPORT == 1 +#include +#endif +#if CONFGI_AGP_PLUGIN_SUPPORT == 1 +#include +#endif +#if CONFIG_CARDBUS_PLUGIN_SUPPORT == 1 +#include +#endif + +uint8_t pci_moving_config8(struct device *dev, unsigned reg) +{ + uint8_t value, ones, zeroes; + value = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, 0xff); + ones = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, 0x00); + zeroes = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, value); + + return ones ^ zeroes; +} + +uint16_t pci_moving_config16(struct device *dev, unsigned reg) +{ + uint16_t value, ones, zeroes; + value = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, 0xffff); + ones = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, 0x0000); + zeroes = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, value); + + return ones ^ zeroes; +} + +uint32_t pci_moving_config32(struct device *dev, unsigned reg) +{ + uint32_t value, ones, zeroes; + value = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, 0xffffffff); + ones = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, 0x00000000); + zeroes = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, value); + + return ones ^ zeroes; +} + +unsigned pci_find_next_capability(device_t dev, unsigned cap, unsigned last) +{ + unsigned pos; + unsigned status; + unsigned reps = 48; + pos = 0; + status = pci_read_config16(dev, PCI_STATUS); + if (!(status & PCI_STATUS_CAP_LIST)) { + return 0; + } + switch(dev->hdr_type & 0x7f) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + pos = PCI_CAPABILITY_LIST; + break; + case PCI_HEADER_TYPE_CARDBUS: + pos = PCI_CB_CAPABILITY_LIST; + break; + default: + return 0; + } + pos = pci_read_config8(dev, pos); + while(reps-- && (pos >= 0x40)) { /* loop through the linked list */ + int this_cap; + pos &= ~3; + this_cap = pci_read_config8(dev, pos + PCI_CAP_LIST_ID); + printk_spew("Capability: 0x%02x @ 0x%02x\n", cap, pos); + if (this_cap == 0xff) { + break; + } + if (!last && (this_cap == cap)) { + return pos; + } + if (last == pos) { + last = 0; + } + pos = pci_read_config8(dev, pos + PCI_CAP_LIST_NEXT); + } + return 0; +} + +unsigned pci_find_capability(device_t dev, unsigned cap) +{ + return pci_find_next_capability(dev, cap, 0); + +} + +/** Given a device and register, read the size of the BAR for that register. + * @param dev Pointer to the device structure + * @param resource Pointer to the resource structure + * @param index Address of the pci configuration register + */ +struct resource *pci_get_resource(struct device *dev, unsigned long index) +{ + struct resource *resource; + unsigned long value, attr; + resource_t moving, limit; + + /* Initialize the resources to nothing */ + resource = new_resource(dev, index); + + /* Get the initial value */ + value = pci_read_config32(dev, index); + + /* See which bits move */ + moving = pci_moving_config32(dev, index); + + /* Initialize attr to the bits that do not move */ + attr = value & ~moving; + + /* If it is a 64bit resource look at the high half as well */ + if (((attr & PCI_BASE_ADDRESS_SPACE_IO) == 0) && + ((attr & PCI_BASE_ADDRESS_MEM_LIMIT_MASK) == PCI_BASE_ADDRESS_MEM_LIMIT_64)) + { + /* Find the high bits that move */ + moving |= ((resource_t)pci_moving_config32(dev, index + 4)) << 32; + } + /* Find the resource constraints. + * + * Start by finding the bits that move. From there: + * - Size is the least significant bit of the bits that move. + * - Limit is all of the bits that move plus all of the lower bits. + * See PCI Spec 6.2.5.1 ... + */ + limit = 0; + if (moving) { + resource->size = 1; + resource->align = resource->gran = 0; + while(!(moving & resource->size)) { + resource->size <<= 1; + resource->align += 1; + resource->gran += 1; + } + resource->limit = limit = moving | (resource->size - 1); + } + /* + * some broken hardware has read-only registers that do not + * really size correctly. + * Example: the acer m7229 has BARs 1-4 normally read-only. + * so BAR1 at offset 0x10 reads 0x1f1. If you size that register + * by writing 0xffffffff to it, it will read back as 0x1f1 -- a + * violation of the spec. + * We catch this case and ignore it by observing which bits move, + * This also catches the common case unimplemented registers + * that always read back as 0. + */ + if (moving == 0) { + if (value != 0) { + printk_debug( + "%s register %02x(%08x), read-only ignoring it\n", + dev_path(dev), index, value); + } + resource->flags = 0; + } + else if (attr & PCI_BASE_ADDRESS_SPACE_IO) { + /* An I/O mapped base address */ + attr &= PCI_BASE_ADDRESS_IO_ATTR_MASK; + resource->flags |= IORESOURCE_IO; + /* I don't want to deal with 32bit I/O resources */ + resource->limit = 0xffff; + } + else { + /* A Memory mapped base address */ + attr &= PCI_BASE_ADDRESS_MEM_ATTR_MASK; + resource->flags |= IORESOURCE_MEM; + if (attr & PCI_BASE_ADDRESS_MEM_PREFETCH) { + resource->flags |= IORESOURCE_PREFETCH; + } + attr &= PCI_BASE_ADDRESS_MEM_LIMIT_MASK; + if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_32) { + /* 32bit limit */ + resource->limit = 0xffffffffUL; + } + else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_1M) { + /* 1MB limit */ + resource->limit = 0x000fffffUL; + } + else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_64) { + /* 64bit limit */ + resource->limit = 0xffffffffffffffffULL; + resource->flags |= IORESOURCE_PCI64; + } + else { + /* Invalid value */ + resource->flags = 0; + } + } + /* Don't let the limit exceed which bits can move */ + if (resource->limit > limit) { + resource->limit = limit; + } +#if 0 + if (resource->flags) { + printk_debug("%s %02x ->", + dev_path(dev), resource->index); + printk_debug(" value: 0x%08Lx zeroes: 0x%08Lx ones: 0x%08Lx attr: %08lx\n", + value, zeroes, ones, attr); + printk_debug( + "%s %02x -> size: 0x%08Lx max: 0x%08Lx %s\n ", + dev_path(dev), + resource->index, + resource->size, resource->limit, + resource_type(resource)); + } +#endif + + return resource; +} + +static void pci_get_rom_resource(struct device *dev, unsigned long index) +{ + struct resource *resource; + unsigned long value; + resource_t moving, limit; + + if ((dev->on_mainboard) && (dev->rom_address == 0)) { + //skip it if rom_address is not set in MB Config.lb + return; + } + + /* Initialize the resources to nothing */ + resource = new_resource(dev, index); + + /* Get the initial value */ + value = pci_read_config32(dev, index); + + /* See which bits move */ + moving = pci_moving_config32(dev, index); + /* clear the Enable bit */ + moving = moving & ~PCI_ROM_ADDRESS_ENABLE; + + /* Find the resource constraints. + * + * Start by finding the bits that move. From there: + * - Size is the least significant bit of the bits that move. + * - Limit is all of the bits that move plus all of the lower bits. + * See PCI Spec 6.2.5.1 ... + */ + limit = 0; + + if (moving) { + resource->size = 1; + resource->align = resource->gran = 0; + while (!(moving & resource->size)) { + resource->size <<= 1; + resource->align += 1; + resource->gran += 1; + } + resource->limit = limit = moving | (resource->size - 1); + } + + if (moving == 0) { + if (value != 0) { + printk_debug("%s register %02x(%08x), read-only ignoring it\n", + dev_path(dev), index, value); + } + resource->flags = 0; + } else { + resource->flags |= IORESOURCE_MEM | IORESOURCE_READONLY; + } + + /* for on board device with embedded ROM image, the ROM image is at + * fixed address specified in the Config.lb, the dev->rom_address is + * inited by driver_pci_onboard_ops::enable_dev() */ + if ((dev->on_mainboard) && (dev->rom_address != 0)) { + resource->base = dev->rom_address; + resource->flags |= IORESOURCE_MEM | IORESOURCE_READONLY | + IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + } + + compact_resources(dev); +} + +/** Read the base address registers for a given device. + * @param dev Pointer to the dev structure + * @param howmany How many registers to read (6 for device, 2 for bridge) + */ +static void pci_read_bases(struct device *dev, unsigned int howmany) +{ + unsigned long index; + + for(index = PCI_BASE_ADDRESS_0; (index < PCI_BASE_ADDRESS_0 + (howmany << 2)); ) { + struct resource *resource; + resource = pci_get_resource(dev, index); + index += (resource->flags & IORESOURCE_PCI64)?8:4; + } + + compact_resources(dev); +} + +static void pci_set_resource(struct device *dev, struct resource *resource); + +static void pci_record_bridge_resource( + struct device *dev, resource_t moving, + unsigned index, unsigned long mask, unsigned long type) +{ + /* Initiliaze the constraints on the current bus */ + struct resource *resource; + resource = 0; + if (moving) { + unsigned long gran; + resource_t step; + resource = new_resource(dev, index); + resource->size = 0; + gran = 0; + step = 1; + while((moving & step) == 0) { + gran += 1; + step <<= 1; + } + resource->gran = gran; + resource->align = gran; + resource->limit = moving | (step - 1); + resource->flags = type | IORESOURCE_PCI_BRIDGE; + compute_allocate_resource(&dev->link[0], resource, mask, type); + /* If there is nothing behind the resource, + * clear it and forget it. + */ + if (resource->size == 0) { + resource->base = moving; + resource->flags |= IORESOURCE_ASSIGNED; + resource->flags &= ~IORESOURCE_STORED; + pci_set_resource(dev, resource); + resource->flags = 0; + } + } + return; +} + +static void pci_bridge_read_bases(struct device *dev) +{ + resource_t moving_base, moving_limit, moving; + + /* See if the bridge I/O resources are implemented */ + moving_base = ((uint32_t)pci_moving_config8(dev, PCI_IO_BASE)) << 8; + moving_base |= ((uint32_t)pci_moving_config16(dev, PCI_IO_BASE_UPPER16)) << 16; + + moving_limit = ((uint32_t)pci_moving_config8(dev, PCI_IO_LIMIT)) << 8; + moving_limit |= ((uint32_t)pci_moving_config16(dev, PCI_IO_LIMIT_UPPER16)) << 16; + + moving = moving_base & moving_limit; + + /* Initialize the io space constraints on the current bus */ + pci_record_bridge_resource( + dev, moving, PCI_IO_BASE, + IORESOURCE_IO, IORESOURCE_IO); + + + /* See if the bridge prefmem resources are implemented */ + moving_base = ((resource_t)pci_moving_config16(dev, PCI_PREF_MEMORY_BASE)) << 16; + moving_base |= ((resource_t)pci_moving_config32(dev, PCI_PREF_BASE_UPPER32)) << 32; + + moving_limit = ((resource_t)pci_moving_config16(dev, PCI_PREF_MEMORY_LIMIT)) << 16; + moving_limit |= ((resource_t)pci_moving_config32(dev, PCI_PREF_LIMIT_UPPER32)) << 32; + + moving = moving_base & moving_limit; + /* Initiliaze the prefetchable memory constraints on the current bus */ + pci_record_bridge_resource( + dev, moving, PCI_PREF_MEMORY_BASE, + IORESOURCE_MEM | IORESOURCE_PREFETCH, + IORESOURCE_MEM | IORESOURCE_PREFETCH); + + + /* See if the bridge mem resources are implemented */ + moving_base = ((uint32_t)pci_moving_config16(dev, PCI_MEMORY_BASE)) << 16; + moving_limit = ((uint32_t)pci_moving_config16(dev, PCI_MEMORY_LIMIT)) << 16; + + moving = moving_base & moving_limit; + + /* Initialize the memory resources on the current bus */ + pci_record_bridge_resource( + dev, moving, PCI_MEMORY_BASE, + IORESOURCE_MEM | IORESOURCE_PREFETCH, + IORESOURCE_MEM); + + compact_resources(dev); +} + +void pci_dev_read_resources(struct device *dev) +{ + pci_read_bases(dev, 6); + pci_get_rom_resource(dev, PCI_ROM_ADDRESS); +} + +void pci_bus_read_resources(struct device *dev) +{ + pci_bridge_read_bases(dev); + pci_read_bases(dev, 2); + pci_get_rom_resource(dev, PCI_ROM_ADDRESS1); +} + +static void pci_set_resource(struct device *dev, struct resource *resource) +{ + resource_t base, end; + + /* Make certain the resource has actually been set */ + if (!(resource->flags & IORESOURCE_ASSIGNED)) { + printk_err("ERROR: %s %02x %s size: 0x%010Lx not assigned\n", + dev_path(dev), resource->index, + resource_type(resource), + resource->size); + return; + } + + /* If I have already stored this resource don't worry about it */ + if (resource->flags & IORESOURCE_STORED) { + return; + } + + /* If the resources is substractive don't worry about it */ + if (resource->flags & IORESOURCE_SUBTRACTIVE) { + return; + } + + /* Only handle PCI memory and IO resources for now */ + if (!(resource->flags & (IORESOURCE_MEM |IORESOURCE_IO))) + return; + + /* Enable the resources in the command register */ + if (resource->size) { + if (resource->flags & IORESOURCE_MEM) { + dev->command |= PCI_COMMAND_MEMORY; + } + if (resource->flags & IORESOURCE_IO) { + dev->command |= PCI_COMMAND_IO; + } + if (resource->flags & IORESOURCE_PCI_BRIDGE) { + dev->command |= PCI_COMMAND_MASTER; + } + } + /* Get the base address */ + base = resource->base; + + /* Get the end */ + end = resource_end(resource); + + /* Now store the resource */ + resource->flags |= IORESOURCE_STORED; + if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) { + unsigned long base_lo, base_hi; + /* + * some chipsets allow us to set/clear the IO bit. + * (e.g. VIA 82c686a.) So set it to be safe) + */ + base_lo = base & 0xffffffff; + base_hi = (base >> 32) & 0xffffffff; + if (resource->flags & IORESOURCE_IO) { + base_lo |= PCI_BASE_ADDRESS_SPACE_IO; + } + pci_write_config32(dev, resource->index, base_lo); + if (resource->flags & IORESOURCE_PCI64) { + pci_write_config32(dev, resource->index + 4, base_hi); + } + } + else if (resource->index == PCI_IO_BASE) { + /* set the IO ranges */ + compute_allocate_resource(&dev->link[0], resource, + IORESOURCE_IO, IORESOURCE_IO); + pci_write_config8(dev, PCI_IO_BASE, base >> 8); + pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16); + pci_write_config8(dev, PCI_IO_LIMIT, end >> 8); + pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16); + } + else if (resource->index == PCI_MEMORY_BASE) { + /* set the memory range */ + compute_allocate_resource(&dev->link[0], resource, + IORESOURCE_MEM | IORESOURCE_PREFETCH, + IORESOURCE_MEM); + pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16); + pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16); + } + else if (resource->index == PCI_PREF_MEMORY_BASE) { + /* set the prefetchable memory range */ + compute_allocate_resource(&dev->link[0], resource, + IORESOURCE_MEM | IORESOURCE_PREFETCH, + IORESOURCE_MEM | IORESOURCE_PREFETCH); + pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16); + pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32); + pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16); + pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32); + } + else { + /* Don't let me think I stored the resource */ + resource->flags &= ~IORESOURCE_STORED; + printk_err("ERROR: invalid resource->index %x\n", + resource->index); + } + report_resource_stored(dev, resource, ""); + return; +} + +void pci_dev_set_resources(struct device *dev) +{ + struct resource *resource, *last; + unsigned link; + uint8_t line; + + last = &dev->resource[dev->resources]; + + for(resource = &dev->resource[0]; resource < last; resource++) { + pci_set_resource(dev, resource); + } + for(link = 0; link < dev->links; link++) { + struct bus *bus; + bus = &dev->link[link]; + if (bus->children) { + assign_resources(bus); + } + } + + /* set a default latency timer */ + pci_write_config8(dev, PCI_LATENCY_TIMER, 0x40); + + /* set a default secondary latency timer */ + if ((dev->hdr_type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) { + pci_write_config8(dev, PCI_SEC_LATENCY_TIMER, 0x40); + } + + /* zero the irq settings */ + line = pci_read_config8(dev, PCI_INTERRUPT_PIN); + if (line) { + pci_write_config8(dev, PCI_INTERRUPT_LINE, 0); + } + /* set the cache line size, so far 64 bytes is good for everyone */ + pci_write_config8(dev, PCI_CACHE_LINE_SIZE, 64 >> 2); +} + +void pci_dev_enable_resources(struct device *dev) +{ + const struct pci_operations *ops; + uint16_t command; + + /* Set the subsystem vendor and device id for mainboard devices */ + ops = ops_pci(dev); + if (dev->on_mainboard && ops && ops->set_subsystem) { + printk_debug("%s subsystem <- %02x/%02x\n", + dev_path(dev), + MAINBOARD_PCI_SUBSYSTEM_VENDOR_ID, + MAINBOARD_PCI_SUBSYSTEM_DEVICE_ID); + ops->set_subsystem(dev, + MAINBOARD_PCI_SUBSYSTEM_VENDOR_ID, + MAINBOARD_PCI_SUBSYSTEM_DEVICE_ID); + } + 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); +} + +void pci_bus_enable_resources(struct device *dev) +{ + uint16_t ctrl; + /* enable IO in command register if there is VGA card + * connected with (even it does not claim IO resource) */ + if (dev->link[0].bridge_ctrl & PCI_BRIDGE_CTL_VGA) + dev->command |= PCI_COMMAND_IO; + 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); + + pci_dev_enable_resources(dev); + + enable_childrens_resources(dev); +} + +void pci_bus_reset(struct bus *bus) +{ + unsigned ctl; + ctl = pci_read_config16(bus->dev, PCI_BRIDGE_CONTROL); + ctl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config16(bus->dev, PCI_BRIDGE_CONTROL, ctl); + mdelay(10); + ctl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config16(bus->dev, PCI_BRIDGE_CONTROL, ctl); + delay(1); +} + +void pci_dev_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); +} + +void pci_dev_init(struct device *dev) +{ +#if CONFIG_PCI_ROM_RUN == 1 + struct rom_header *rom, *ram; + + rom = pci_rom_probe(dev); + if (rom == NULL) + return; + ram = pci_rom_load(dev, rom); + if (ram == NULL) + return; + + run_bios(dev, ram); +#endif +} + +/** Default device operation for PCI devices */ +static struct pci_operations pci_dev_ops_pci = { + .set_subsystem = pci_dev_set_subsystem, +}; + +struct device_operations default_pci_ops_dev = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = pci_dev_init, + .scan_bus = 0, + .enable = 0, + .ops_pci = &pci_dev_ops_pci, +}; + +/** Default device operations for PCI bridges */ +static struct pci_operations pci_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_pci_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = pci_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &pci_bus_ops_pci, +}; + +/** + * @brief Detect the type of downstream bridge + * + * This function is a heuristic to detect which type + * of bus is downstream of a pci to pci bridge. This + * functions by looking for various capability blocks + * to figure out the type of downstream bridge. PCI-X + * PCI-E, and Hypertransport all seem to have appropriate + * capabilities. + * + * When only a PCI-Express capability is found the type + * is examined to see which type of bridge we have. + * + * @param dev + * + * @return appropriate bridge operations + */ +static struct device_operations *get_pci_bridge_ops(device_t dev) +{ + unsigned pos; + +#if CONFIG_PCIX_PLUGIN_SUPPORT == 1 + pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (pos) { + printk_debug("%s subbordinate bus PCI-X\n", dev_path(dev)); + return &default_pcix_ops_bus; + } +#endif +#if CONFIG_AGP_PLUGIN_SUPPORT == 1 + /* How do I detect an PCI to AGP bridge? */ +#endif +#if CONFIG_HYPERTRANSPORT_PLUGIN_SUPPORT == 1 + pos = 0; + while((pos = pci_find_next_capability(dev, PCI_CAP_ID_HT, pos))) { + unsigned flags; + flags = pci_read_config16(dev, pos + PCI_CAP_FLAGS); + if ((flags >> 13) == 1) { + /* Host or Secondary Interface */ + printk_debug("%s subbordinate bus Hypertransport\n", + dev_path(dev)); + return &default_ht_ops_bus; + } + } +#endif +#if CONFIG_PCIEXP_PLUGIN_SUPPORT == 1 + pos = pci_find_capability(dev, PCI_CAP_ID_PCIE); + if (pos) { + unsigned flags; + flags = pci_read_config16(dev, pos + PCI_EXP_FLAGS); + switch((flags & PCI_EXP_FLAGS_TYPE) >> 4) { + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + printk_debug("%s subbordinate bus PCI Express\n", + dev_path(dev)); + return &default_pciexp_ops_bus; + case PCI_EXP_TYPE_PCI_BRIDGE: + printk_debug("%s subbordinate PCI\n", + dev_path(dev)); + return &default_pci_ops_bus; + default: + break; + } + } +#endif + return &default_pci_ops_bus; +} + +/** + * @brief Set up PCI device operation + * + * + * @param dev + * + * @see pci_drivers + */ +static void set_pci_ops(struct device *dev) +{ + struct pci_driver *driver; + if (dev->ops) { + return; + } + + /* Look through the list of setup drivers and find one for + * this pci device + */ + for(driver = &pci_drivers[0]; driver != &epci_drivers[0]; driver++) { + if ((driver->vendor == dev->vendor) && + (driver->device == dev->device)) + { + dev->ops = driver->ops; + printk_spew("%s [%04x/%04x] %sops\n", + dev_path(dev), + driver->vendor, driver->device, + (driver->ops->scan_bus?"bus ":"")); + return; + } + } + + /* If I don't have a specific driver use the default operations */ + switch(dev->hdr_type & 0x7f) { /* header type */ + case PCI_HEADER_TYPE_NORMAL: /* standard header */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) + goto bad; + dev->ops = &default_pci_ops_dev; + break; + case PCI_HEADER_TYPE_BRIDGE: + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + goto bad; + dev->ops = get_pci_bridge_ops(dev); + break; +#if CONFIG_CARDBUS_PLUGIN_SUPPORT == 1 + case PCI_HEADER_TYPE_CARDBUS: + dev->ops = &default_cardbus_ops_bus; + break; +#endif + default: + bad: + if (dev->enabled) { + printk_err("%s [%04x/%04x/%06x] has unknown header " + "type %02x, ignoring.\n", + dev_path(dev), + dev->vendor, dev->device, + dev->class >> 8, dev->hdr_type); + } + } + return; +} + + + +/** + * @brief See if we have already allocated a device structure for a given devfn. + * + * Given a linked list of PCI device structures and a devfn number, find the + * device structure correspond to the devfn, if present. This function also + * removes the device structure from the linked list. + * + * @param list the device structure list + * @param devfn a device/function number + * + * @return pointer to the device structure found or null of we have not + * allocated a device for this devfn yet. + */ +static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) +{ + struct device *dev; + dev = 0; + for(; *list; list = &(*list)->sibling) { + if ((*list)->path.type != DEVICE_PATH_PCI) { + printk_err("child %s not a pci device\n", + dev_path(*list)); + continue; + } + if ((*list)->path.u.pci.devfn == devfn) { + /* Unlink from the list */ + dev = *list; + *list = (*list)->sibling; + dev->sibling = 0; + break; + } + } + /* Just like alloc_dev add the device to the list of device on the bus. + * When the list of devices was formed we removed all of the parents + * children, and now we are interleaving static and dynamic devices in + * order on the bus. + */ + if (dev) { + device_t child; + /* Find the last child of our parent */ + for(child = dev->bus->children; child && child->sibling; ) { + child = child->sibling; + } + /* Place the device on the list of children of it's parent. */ + if (child) { + child->sibling = dev; + } else { + dev->bus->children = dev; + } + } + + return dev; +} + +/** + * @brief Scan a PCI bus. + * + * Determine the existence of a given PCI device. + * + * @param bus pointer to the bus structure + * @param devfn to look at + * + * @return The device structure for hte device (if found) + * or the NULL if no device is found. + */ +device_t pci_probe_dev(device_t dev, struct bus *bus, unsigned devfn) +{ + uint32_t id, class; + uint8_t hdr_type; + + /* Detect if a device is present */ + if (!dev) { + struct device dummy; + dummy.bus = bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.u.pci.devfn = devfn; + id = pci_read_config32(&dummy, PCI_VENDOR_ID); + /* Have we found somthing? + * Some broken boards return 0 if a slot is empty. + */ + if ( (id == 0xffffffff) || (id == 0x00000000) || + (id == 0x0000ffff) || (id == 0xffff0000)) + { + printk_spew("PCI: devfn 0x%x, bad id 0x%x\n", devfn, id); + return NULL; + } + dev = alloc_dev(bus, &dummy.path); + } + else { + /* Enable/disable the device. Once we have + * found the device specific operations this + * operations we will disable the device with + * those as well. + * + * This is geared toward devices that have subfunctions + * that do not show up by default. + * + * If a device is a stuff option on the motherboard + * it may be absent and enable_dev must cope. + * + */ + /* Run the magice enable sequence for the device */ + if (dev->chip_ops && dev->chip_ops->enable_dev) { + dev->chip_ops->enable_dev(dev); + } + /* Now read the vendor and device id */ + id = pci_read_config32(dev, PCI_VENDOR_ID); + + + /* If the device does not have a pci id disable it. + * Possibly this is because we have already disabled + * the device. But this also handles optional devices + * that may not always show up. + */ + /* If the chain is fully enumerated quit */ + if ( (id == 0xffffffff) || (id == 0x00000000) || + (id == 0x0000ffff) || (id == 0xffff0000)) + { + if (dev->enabled) { + printk_info("Disabling static device: %s\n", + dev_path(dev)); + dev->enabled = 0; + } + return dev; + } + } + /* Read the rest of the pci configuration information */ + hdr_type = pci_read_config8(dev, PCI_HEADER_TYPE); + class = pci_read_config32(dev, PCI_CLASS_REVISION); + + /* Store the interesting information in the device structure */ + dev->vendor = id & 0xffff; + dev->device = (id >> 16) & 0xffff; + dev->hdr_type = hdr_type; + /* class code, the upper 3 bytes of PCI_CLASS_REVISION */ + dev->class = class >> 8; + + + /* Architectural/System devices always need to + * be bus masters. + */ + if ((dev->class >> 16) == PCI_BASE_CLASS_SYSTEM) { + dev->command |= PCI_COMMAND_MASTER; + } + /* Look at the vendor and device id, or at least the + * header type and class and figure out which set of + * configuration methods to use. Unless we already + * have some pci ops. + */ + set_pci_ops(dev); + + /* Now run the magic enable/disable sequence for the device */ + if (dev->ops && dev->ops->enable) { + dev->ops->enable(dev); + } + + + /* Display the device and error if we don't have some pci operations + * for it. + */ + printk_debug("%s [%04x/%04x] %s%s\n", + dev_path(dev), + dev->vendor, dev->device, + dev->enabled?"enabled": "disabled", + dev->ops?"" : " No operations" + ); + + return dev; +} + +/** + * @brief Scan a PCI bus. + * + * Determine the existence of devices and bridges on a PCI bus. If there are + * bridges on the bus, recursively scan the buses behind the bridges. + * + * This function is the default scan_bus() method for the root device + * 'dev_root'. + * + * @param bus pointer to the bus structure + * @param min_devfn minimum devfn to look at in the scan usually 0x00 + * @param max_devfn maximum devfn to look at in the scan usually 0xff + * @param max current bus number + * + * @return The maximum bus number found, after scanning all subordinate busses + */ +unsigned int pci_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, + unsigned int max) +{ + unsigned int devfn; + device_t old_devices; + device_t child; + +#if PCI_BUS_SEGN_BITS + printk_debug("PCI: pci_scan_bus for bus %04x:%02x\n", bus->secondary >> 8, bus->secondary & 0xff); +#else + printk_debug("PCI: pci_scan_bus for bus %02x\n", bus->secondary); +#endif + + old_devices = bus->children; + bus->children = 0; + + post_code(0x24); + /* probe all devices/functions on this bus with some optimization for + * non-existence and single funcion devices + */ + for (devfn = min_devfn; devfn <= max_devfn; devfn++) { + device_t dev; + + /* First thing setup the device structure */ + dev = pci_scan_get_dev(&old_devices, devfn); + + /* See if a device is present and setup the device + * structure. + */ + dev = pci_probe_dev(dev, bus, devfn); + + /* if this is not a multi function device, + * or the device is not present don't waste + * time probing another function. + * Skip to next device. + */ + if ((PCI_FUNC(devfn) == 0x00) && + (!dev || (dev->enabled && ((dev->hdr_type & 0x80) != 0x80)))) + { + devfn += 0x07; + } + } + post_code(0x25); + + /* Die if any leftover Static devices are are found. + * There's probably a problem in the Config.lb. + */ + if(old_devices) { + device_t left; + for(left = old_devices; left; left = left->sibling) { + printk_err("%s\n", dev_path(left)); + } + die("PCI: Left over static devices. Check your Config.lb\n"); + } + + /* For all children that implement scan_bus (i.e. bridges) + * scan the bus behind that child. + */ + for(child = bus->children; child; child = child->sibling) { + max = scan_bus(child, max); + } + + /* + * 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. + */ + printk_debug("PCI: pci_scan_bus returning with max=%03x\n", max); + post_code(0x55); + return max; +} + + +/** + * @brief Scan a PCI bridge and the buses behind the bridge. + * + * Determine the existence of buses behind the bridge. Set up the bridge + * according to the result of the scan. + * + * This function is the default scan_bus() method for PCI bridge devices. + * + * @param dev pointer to the bridge device + * @param max the highest bus number assgined up to now + * + * @return The maximum bus number found, after scanning all subordinate busses + */ +unsigned int do_pci_scan_bridge(struct device *dev, unsigned int max, + unsigned int (*do_scan_bus)(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, unsigned int max)) +{ + struct bus *bus; + uint32_t buses; + uint16_t cr; + + printk_spew("%s for %s\n", __func__, dev_path(dev)); + + bus = &dev->link[0]; + bus->dev = dev; + dev->links = 1; + + /* Set up the primary, secondary and subordinate bus numbers. We have + * no idea how many buses are behind this bridge yet, so we set the + * subordinate bus number to 0xff for the moment. + */ + bus->secondary = ++max; + bus->subordinate = 0xff; + + /* Clear all status bits and turn off memory, I/O and master enables. */ + cr = pci_read_config16(dev, PCI_COMMAND); + pci_write_config16(dev, PCI_COMMAND, 0x0000); + pci_write_config16(dev, PCI_STATUS, 0xffff); + + /* + * Read the existing primary/secondary/subordinate bus + * number configuration. + */ + buses = pci_read_config32(dev, PCI_PRIMARY_BUS); + + /* Configure the bus numbers for this bridge: the configuration + * transactions will not be propagated by the bridge if it is not + * correctly configured. + */ + buses &= 0xff000000; + buses |= (((unsigned int) (dev->bus->secondary) << 0) | + ((unsigned int) (bus->secondary) << 8) | + ((unsigned int) (bus->subordinate) << 16)); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); + + /* Now we can scan all subordinate buses + * i.e. the bus behind the bridge. + */ + max = do_scan_bus(bus, 0x00, 0xff, max); + + /* We know the number of buses behind this bridge. Set the subordinate + * bus number to its real value. + */ + bus->subordinate = max; + buses = (buses & 0xff00ffff) | + ((unsigned int) (bus->subordinate) << 16); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); + pci_write_config16(dev, PCI_COMMAND, cr); + + printk_spew("%s returns max %d\n", __func__, max); + return max; +} + +/** + * @brief Scan a PCI bridge and the buses behind the bridge. + * + * Determine the existence of buses behind the bridge. Set up the bridge + * according to the result of the scan. + * + * This function is the default scan_bus() method for PCI bridge devices. + * + * @param dev pointer to the bridge device + * @param max the highest bus number assgined up to now + * + * @return The maximum bus number found, after scanning all subordinate busses + */ +unsigned int pci_scan_bridge(struct device *dev, unsigned int max) +{ + return do_pci_scan_bridge(dev, max, pci_scan_bus); +} + +/* + Tell the EISA int controller this int must be level triggered + THIS IS A KLUDGE -- sorry, this needs to get cleaned up. +*/ +void pci_level_irq(unsigned char intNum) +{ + unsigned short intBits = inb(0x4d0) | (((unsigned) inb(0x4d1)) << 8); + + printk_spew("%s: current ints are 0x%x\n", __func__, intBits); + intBits |= (1 << intNum); + + printk_spew("%s: try to set ints 0x%x\n", __func__, intBits); + + // Write new values + outb((unsigned char) intBits, 0x4d0); + outb((unsigned char) (intBits >> 8), 0x4d1); + + /* this seems like an error but is not ... */ +#if 1 + if (inb(0x4d0) != (intBits & 0xff)) { + printk_err("%s: lower order bits are wrong: want 0x%x, got 0x%x\n", + __func__, intBits &0xff, inb(0x4d0)); + } + if (inb(0x4d1) != ((intBits >> 8) & 0xff)) { + printk_err("%s: lower order bits are wrong: want 0x%x, got 0x%x\n", + __func__, (intBits>>8) &0xff, inb(0x4d1)); + } +#endif +} + +/* + This function assigns IRQs for all functions contained within + the indicated device address. If the device does not exist or does + not require interrupts then this function has no effect. + + This function should be called for each PCI slot in your system. + + pIntAtoD is an array of IRQ #s that are assigned to PINTA through PINTD of + this slot. + The particular irq #s that are passed in depend on the routing inside + your southbridge and on your motherboard. + + -kevinh@ispiri.com +*/ +void pci_assign_irqs(unsigned bus, unsigned slot, + const unsigned char pIntAtoD[4]) +{ + unsigned functNum; + device_t pdev; + unsigned char line; + unsigned char irq; + unsigned char readback; + + /* Each slot may contain up to eight functions */ + for (functNum = 0; functNum < 8; functNum++) { + pdev = dev_find_slot(bus, (slot << 3) + functNum); + + if (pdev) { + line = pci_read_config8(pdev, PCI_INTERRUPT_PIN); + + // PCI spec says all other values are reserved + if ((line >= 1) && (line <= 4)) { + irq = pIntAtoD[line - 1]; + + printk_debug("Assigning IRQ %d to %d:%x.%d\n", \ + irq, bus, slot, functNum); + + pci_write_config8(pdev, PCI_INTERRUPT_LINE,\ + pIntAtoD[line - 1]); + + readback = pci_read_config8(pdev, PCI_INTERRUPT_LINE); + printk_debug(" Readback = %d\n", readback); + + // Change to level triggered + pci_level_irq(pIntAtoD[line - 1]); + } + } + } +} diff --git a/devices/pci_ops.c b/devices/pci_ops.c new file mode 100644 index 0000000000..f02283adf0 --- /dev/null +++ b/devices/pci_ops.c @@ -0,0 +1,71 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +#include +#include +#include +#include +#include + +static struct bus *get_pbus(device_t dev) +{ + struct bus *pbus = dev->bus; + while(pbus && pbus->dev && !ops_pci_bus(pbus)) { + pbus = pbus->dev->bus; + } + if (!pbus || !pbus->dev || !pbus->dev->ops || !pbus->dev->ops->ops_pci_bus) { + printk_alert("%s Cannot find pci bus operations", dev_path(dev)); + die(""); + for(;;); + } + return pbus; +} + +uint8_t pci_read_config8(device_t dev, unsigned where) +{ + struct bus *pbus = get_pbus(dev); + return ops_pci_bus(pbus)->read8(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where); +} + +uint16_t pci_read_config16(device_t dev, unsigned where) +{ + struct bus *pbus = get_pbus(dev); + return ops_pci_bus(pbus)->read16(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where); +} + +uint32_t pci_read_config32(device_t dev, unsigned where) +{ + struct bus *pbus = get_pbus(dev); + return ops_pci_bus(pbus)->read32(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where); +} + +void pci_write_config8(device_t dev, unsigned where, uint8_t val) +{ + struct bus *pbus = get_pbus(dev); + ops_pci_bus(pbus)->write8(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where, val); +} + +void pci_write_config16(device_t dev, unsigned where, uint16_t val) +{ + struct bus *pbus = get_pbus(dev); + ops_pci_bus(pbus)->write16(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where, val); +} + +void pci_write_config32(device_t dev, unsigned where, uint32_t val) +{ + struct bus *pbus = get_pbus(dev); + ops_pci_bus(pbus)->write32(pbus, dev->bus->secondary, dev->path.u.pci.devfn, where, val); +} diff --git a/devices/pci_rom.c b/devices/pci_rom.c new file mode 100644 index 0000000000..8b14300ee1 --- /dev/null +++ b/devices/pci_rom.c @@ -0,0 +1,129 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +#include +#include +#include +#include +#include + +struct rom_header * pci_rom_probe(struct device *dev) +{ + unsigned long rom_address; + struct rom_header *rom_header; + struct pci_data *rom_data; + + if (dev->on_mainboard) { + // in case some device PCI_ROM_ADDRESS can not be set or readonly + rom_address = dev->rom_address; + } else { + rom_address = pci_read_config32(dev, PCI_ROM_ADDRESS); + } + + if (rom_address == 0x00000000 || rom_address == 0xffffffff) { + return NULL; + } + + printk_debug("rom address for %s = %x\n", dev_path(dev), rom_address); + + if(!dev->on_mainboard) { + /* enable expansion ROM address decoding */ + pci_write_config32(dev, PCI_ROM_ADDRESS, + rom_address|PCI_ROM_ADDRESS_ENABLE); + } + + rom_header = (struct rom_header *)rom_address; + printk_spew("PCI Expansion ROM, signature 0x%04x, INIT size 0x%04x, data ptr 0x%04x\n", + le32_to_cpu(rom_header->signature), + rom_header->size * 512, le32_to_cpu(rom_header->data)); + if (le32_to_cpu(rom_header->signature) != PCI_ROM_HDR) { + printk_err("Incorrect Expansion ROM Header Signature %04x\n", + le32_to_cpu(rom_header->signature)); + return NULL; + } + + rom_data = (unsigned char *) rom_header + le32_to_cpu(rom_header->data); + printk_spew("PCI ROM Image, Vendor %04x, Device %04x,\n", + rom_data->vendor, rom_data->device); + if (dev->vendor != rom_data->vendor || dev->device != rom_data->device) { + printk_err("Device or Vendor ID mismatch Vendor %04x, Device %04x\n", + rom_data->vendor, rom_data->device); + return NULL; + } + + printk_spew("PCI ROM Image, Class Code %04x%02x, Code Type %02x\n", + rom_data->class_hi, rom_data->class_lo, + rom_data->type); + if (dev->class != ((rom_data->class_hi << 8) | rom_data->class_lo)) { + printk_debug("Class Code mismatch ROM %08x, dev %08x\n", + (rom_data->class_hi << 8) | rom_data->class_lo, dev->class); + //return NULL; + } + + return rom_header; +} + +static void *pci_ram_image_start = (void *)PCI_RAM_IMAGE_START; + +#if CONFIG_CONSOLE_VGA == 1 +extern int vga_inited; // defined in vga_console.c +#if CONFIG_CONSOLE_VGA_MULTI == 0 +extern device_t vga_pri; // the primary vga device, defined in device.c +#endif +#endif + +struct rom_header *pci_rom_load(struct device *dev, struct rom_header *rom_header) +{ + struct pci_data * rom_data; + unsigned long rom_address; + unsigned int rom_size; + unsigned int image_size=0; + + rom_address = pci_read_config32(dev, PCI_ROM_ADDRESS); + + do { + rom_header = (unsigned char *) rom_header + image_size; // get next image + rom_data = (unsigned char *) rom_header + le32_to_cpu(rom_header->data); + image_size = le32_to_cpu(rom_data->ilen) * 512; + } while ((rom_data->type!=0) && (rom_data->indicator!=0)); // make sure we got x86 version + + if(rom_data->type!=0) return NULL; + + rom_size = rom_header->size * 512; + + if (PCI_CLASS_DISPLAY_VGA == rom_data->class_hi) { +#if CONFIG_CONSOLE_VGA == 1 + #if CONFIG_CONSOLE_VGA_MULTI == 0 + if (dev != vga_pri) return NULL; // only one VGA supported + #endif + printk_debug("copying VGA ROM Image from 0x%x to 0x%x, 0x%x bytes\n", + rom_header, PCI_VGA_RAM_IMAGE_START, rom_size); + memcpy(PCI_VGA_RAM_IMAGE_START, rom_header, rom_size); + vga_inited = 1; + return (struct rom_header *) (PCI_VGA_RAM_IMAGE_START); +#endif + } else { + printk_debug("copying non-VGA ROM Image from 0x%x to 0x%x, 0x%x bytes\n", + rom_header, pci_ram_image_start, rom_size); + memcpy(pci_ram_image_start, rom_header, rom_size); + pci_ram_image_start += rom_size; + return (struct rom_header *) (pci_ram_image_start-rom_size); + } + /* disable expansion ROM address decoding */ + pci_write_config32(dev, PCI_ROM_ADDRESS, rom_address & ~PCI_ROM_ADDRESS_ENABLE); + + return NULL; +} diff --git a/devices/pciexp_device.c b/devices/pciexp_device.c new file mode 100644 index 0000000000..2f43121e35 --- /dev/null +++ b/devices/pciexp_device.c @@ -0,0 +1,76 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* (c) 2005 Linux Networx GPL see COPYING for details */ + +#include +#include +#include +#include +#include + + +static void pciexp_tune_dev(device_t dev) +{ + unsigned cap; + + cap = pci_find_capability(dev, PCI_CAP_ID_PCIE); + if (!cap) { + /* error... */ + return; + } + printk_debug("PCIEXP: tunning %s\n", dev_path(dev)); +#warning "IMPLEMENT PCI EXPRESS TUNING" +} + +unsigned int pciexp_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, + unsigned int max) +{ + device_t child; + max = pci_scan_bus(bus, min_devfn, max_devfn, max); + for(child = bus->children; child; child = child->sibling) { + if ( (child->path.u.pci.devfn < min_devfn) || + (child->path.u.pci.devfn > max_devfn)) + { + continue; + } + pciexp_tune_dev(child); + } + return max; +} + + +unsigned int pciexp_scan_bridge(device_t dev, unsigned int max) +{ + return do_pci_scan_bridge(dev, max, pciexp_scan_bus); +} + +/** Default device operations for PCI Express bridges */ +static struct pci_operations pciexp_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_pciexp_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = pciexp_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &pciexp_bus_ops_pci, +}; diff --git a/devices/pcix_device.c b/devices/pcix_device.c new file mode 100644 index 0000000000..ef3b6b3e0c --- /dev/null +++ b/devices/pcix_device.c @@ -0,0 +1,156 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* (c) 2005 Linux Networx GPL see COPYING for details */ + +#include +#include +#include +#include +#include + + +static void pcix_tune_dev(device_t dev) +{ + unsigned cap; + unsigned status, orig_cmd, cmd; + unsigned max_read, max_tran; + + if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL) { + return; + } + cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (!cap) { + return; + } + printk_debug("%s PCI-X tuning\n", dev_path(dev)); + status = pci_read_config32(dev, cap + PCI_X_STATUS); + orig_cmd = cmd = pci_read_config16(dev,cap + PCI_X_CMD); + + max_read = (status & PCI_X_STATUS_MAX_READ) >> 21; + max_tran = (status & PCI_X_STATUS_MAX_SPLIT) >> 23; + if (max_read != ((cmd & PCI_X_CMD_MAX_READ) >> 2)) { + cmd &= ~PCI_X_CMD_MAX_READ; + cmd |= max_read << 2; + } + if (max_tran != ((cmd & PCI_X_CMD_MAX_SPLIT) >> 4)) { + cmd &= ~PCI_X_CMD_MAX_SPLIT; + cmd |= max_tran << 4; + } + /* Don't attempt to handle PCI-X errors */ + cmd &= ~PCI_X_CMD_DPERR_E; + /* Enable Relaxed Ordering */ + cmd |= PCI_X_CMD_ERO; + if (orig_cmd != cmd) { + pci_write_config16(dev, cap + PCI_X_CMD, cmd); + } +} + +unsigned int pcix_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, unsigned int max) +{ + device_t child; + max = pci_scan_bus(bus, min_devfn, max_devfn, max); + for(child = bus->children; child; child = child->sibling) { + if ( (child->path.u.pci.devfn < min_devfn) || + (child->path.u.pci.devfn > max_devfn)) + { + continue; + } + pcix_tune_dev(child); + } + return max; +} + +const char *pcix_speed(unsigned sstatus) +{ + static const char conventional[] = "Conventional PCI"; + static const char pcix_66mhz[] = "66MHz PCI-X"; + static const char pcix_100mhz[] = "100MHz PCI-X"; + static const char pcix_133mhz[] = "133MHz PCI-X"; + static const char pcix_266mhz[] = "266MHz PCI-X"; + static const char pcix_533mhz[] = "533MHZ PCI-X"; + static const char unknown[] = "Unknown"; + + const char *result; + result = unknown; + switch(PCI_X_SSTATUS_MFREQ(sstatus)) { + case PCI_X_SSTATUS_CONVENTIONAL_PCI: + result = conventional; + break; + case PCI_X_SSTATUS_MODE1_66MHZ: + result = pcix_66mhz; + break; + case PCI_X_SSTATUS_MODE1_100MHZ: + result = pcix_100mhz; + break; + + case PCI_X_SSTATUS_MODE1_133MHZ: + result = pcix_133mhz; + break; + + case PCI_X_SSTATUS_MODE2_266MHZ_REF_66MHZ: + case PCI_X_SSTATUS_MODE2_266MHZ_REF_100MHZ: + case PCI_X_SSTATUS_MODE2_266MHZ_REF_133MHZ: + result = pcix_266mhz; + break; + + case PCI_X_SSTATUS_MODE2_533MHZ_REF_66MHZ: + case PCI_X_SSTATUS_MODE2_533MHZ_REF_100MHZ: + case PCI_X_SSTATUS_MODE2_533MHZ_REF_133MHZ: + result = pcix_533mhz; + break; + } + return result; +} + +unsigned int pcix_scan_bridge(device_t dev, unsigned int max) +{ + unsigned pos; + unsigned sstatus; + + /* Find the PCI-X capability */ + pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + sstatus = pci_read_config16(dev, pos + PCI_X_SEC_STATUS); + + if (PCI_X_SSTATUS_MFREQ(sstatus) == PCI_X_SSTATUS_CONVENTIONAL_PCI) { + max = do_pci_scan_bridge(dev, max, pci_scan_bus); + } else { + max = do_pci_scan_bridge(dev, max, pcix_scan_bus); + } + + /* Print the PCI-X bus speed */ + printk_debug("PCI: %02x: %s\n", dev->link[0].secondary, pcix_speed(sstatus)); + + return max; +} + + +/** Default device operations for PCI-X bridges */ +static struct pci_operations pcix_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_pcix_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = pcix_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &pcix_bus_ops_pci, +}; diff --git a/devices/pnp_device.c b/devices/pnp_device.c new file mode 100644 index 0000000000..d595c253dc --- /dev/null +++ b/devices/pnp_device.c @@ -0,0 +1,252 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +/* Copyright 2004 Linux Networx */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 index, unsigned drq) +{ + /* 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)) { + printk_err("ERROR: %s %02x %s size: 0x%010Lx not assigned\n", + dev_path(dev), resource->index, + resource_type(resource), + resource->size); + return; + } + + /* Now store the resource */ + 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 { + printk_err("ERROR: %s %02x unknown resource type\n", + dev_path(dev), resource->index); + return; + } + resource->flags |= IORESOURCE_STORED; + + report_resource_stored(dev, resource, ""); +} + +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) +{ + if (!dev->enabled) { + pnp_set_logical_device(dev); + 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; + unsigned moving, gran, step; + + resource = new_resource(dev, index); + + /* Initilize the resource */ + resource->limit = 0xffff; + resource->flags |= IORESOURCE_IO; + + /* Get the resource size */ + moving = info->mask; + gran = 15; + step = 1 << gran; + /* Find the first bit that moves */ + while((moving & step) == 0) { + gran--; + step >>= 1; + } + /* Now find the first bit that does not move */ + while((moving & step) != 0) { + gran--; + step >>= 1; + } + /* Of the moving bits the last bit in the first group, + * tells us the size of this resource. + */ + if ((moving & step) == 0) { + gran++; + step <<= 1; + } + /* Set the resource size and alignment */ + resource->gran = gran; + resource->align = gran; + resource->limit = info->mask | (step - 1); + resource->size = 1 << gran; +} + +static void get_resources(device_t dev, struct pnp_info *info) +{ + struct resource *resource; + + 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_IO2) { + pnp_get_ioresource(dev, PNP_IDX_IO2, &info->io2); + } + if (info->flags & PNP_IO3) { + pnp_get_ioresource(dev, PNP_IDX_IO3, &info->io3); + } + if (info->flags & PNP_IRQ0) { + resource = new_resource(dev, PNP_IDX_IRQ0); + resource->size = 1; + resource->flags |= IORESOURCE_IRQ; + } + if (info->flags & PNP_IRQ1) { + resource = new_resource(dev, PNP_IDX_IRQ1); + resource->size = 1; + resource->flags |= IORESOURCE_IRQ; + } + if (info->flags & PNP_DRQ0) { + resource = new_resource(dev, PNP_IDX_DRQ0); + resource->size = 1; + resource->flags |= IORESOURCE_DRQ; + } + if (info->flags & PNP_DRQ1) { + resource = new_resource(dev, PNP_IDX_DRQ1); + resource->size = 1; + resource->flags |= IORESOURCE_DRQ; + } +} + +void pnp_enable_devices(device_t base_dev, struct device_operations *ops, + unsigned functions, struct pnp_info *info) +{ + struct device_path path; + device_t dev; + int i; + + path.type = DEVICE_PATH_PNP; + path.u.pnp.port = base_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(base_dev->bus, &path); + + /* Don't initialize a device multiple times */ + if (dev->ops) + continue; + + if (info[i].ops == 0) { + dev->ops = ops; + } else { + dev->ops = info[i].ops; + } + get_resources(dev, &info[i]); + } +} diff --git a/devices/root_device.c b/devices/root_device.c new file mode 100644 index 0000000000..599f5efa0c --- /dev/null +++ b/devices/root_device.c @@ -0,0 +1,213 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +#include +#include +#include +#include + +/** + * Read the resources for the root device, + * that encompass the resources for the entire system. + * @param root Pointer to the device structure for the system root device + */ +void root_dev_read_resources(device_t root) +{ + struct resource *resource; + + /* Initialize the system wide io space constraints */ + resource = new_resource(root, 0); + resource->base = 0x400; + resource->size = 0; + resource->align = 0; + resource->gran = 0; + resource->limit = 0xffffUL; + resource->flags = IORESOURCE_IO; + compute_allocate_resource(&root->link[0], resource, + IORESOURCE_IO, IORESOURCE_IO); + + /* Initialize the system wide memory resources constraints */ + resource = new_resource(root, 1); + resource->base = 0; + resource->size = 0; + resource->align = 0; + resource->gran = 0; + resource->limit = 0xffffffffUL; + resource->flags = IORESOURCE_MEM; + compute_allocate_resource(&root->link[0], resource, + IORESOURCE_MEM, IORESOURCE_MEM); +} + +/** + * @brief Write the resources for every device + * + * Write the resources for the root device, + * and every device under it which are all of the devices. + * @param root Pointer to the device structure for the system root device + */ +void root_dev_set_resources(device_t root) +{ + struct bus *bus; + + bus = &root->link[0]; + compute_allocate_resource(bus, + &root->resource[0], IORESOURCE_IO, IORESOURCE_IO); + compute_allocate_resource(bus, + &root->resource[1], IORESOURCE_MEM, IORESOURCE_MEM); + assign_resources(bus); +} + +/** + * @brief Scan devices on static buses. + * + * The enumeration of certain buses is purely static. The existence of + * devices on those buses can be completely determined at compile time + * and is specified in the config file. Typical examples are the 'PNP' + * devices on a legacy ISA/LPC bus. There is no need of probing of any kind, + * the only thing we have to do is to walk through the bus and + * enable or disable devices as indicated in the config file. + * + * On the other hand, some devices are virtual and their existence is + * artificial. They can not be probed at run time. One example is the + * debug device. Those virtual devices have to be listed in the config + * file under some static bus in order to be enumerated at run time. + * + * This function is the default scan_bus() method for the root device and + * LPC bridges. + * + * @param bus Pointer to the device structure which the static buses are attached + * @param max Maximum bus number currently used before scanning. + * @return Largest bus number used. + */ +static int smbus_max = 0; +unsigned int scan_static_bus(device_t bus, unsigned int max) +{ + device_t child; + unsigned link; + + printk_spew("%s for %s\n", __func__, dev_path(bus)); + + for(link = 0; link < bus->links; link++) { + /* for smbus bus enumerate */ + child = bus->link[link].children; + if(child && child->path.type == DEVICE_PATH_I2C) { + bus->link[link].secondary = ++smbus_max; + } + for(child = bus->link[link].children; child; child = child->sibling) { + if (child->chip_ops && child->chip_ops->enable_dev) { + child->chip_ops->enable_dev(child); + } + if (child->ops && child->ops->enable) { + child->ops->enable(child); + } + if (child->path.type == DEVICE_PATH_I2C) { + printk_debug("smbus: %s[%d]->", + dev_path(child->bus->dev), child->bus->link ); + } + printk_debug("%s %s\n", + dev_path(child), + child->enabled?"enabled": "disabled"); + } + } + for(link = 0; link < bus->links; link++) { + for(child = bus->link[link].children; child; child = child->sibling) { + if (!child->ops || !child->ops->scan_bus) + continue; + printk_spew("%s scanning...\n", dev_path(child)); + max = scan_bus(child, max); + } + } + + printk_spew("%s for %s done\n", __func__, dev_path(bus)); + + return max; +} + +/** + * @brief Enable resources for children devices + * + * @param dev the device whos children's resources are to be enabled + * + * This function is called by the global enable_resource() indirectly via the + * device_operation::enable_resources() method of devices. + * + * Indirect mutual recursion: + * enable_childrens_resources() -> enable_resources() + * enable_resources() -> device_operation::enable_resources() + * device_operation::enable_resources() -> enable_children_resources() + */ +void enable_childrens_resources(device_t dev) +{ + unsigned link; + for(link = 0; link < dev->links; link++) { + device_t child; + for(child = dev->link[link].children; child; child = child->sibling) { + enable_resources(child); + } + } +} + +void root_dev_enable_resources(device_t dev) +{ + enable_childrens_resources(dev); +} + +/** + * @brief Scan root bus for generic systems + * + * @param root The root device structure + * @param max The current bus number scanned so far, usually 0x00 + * + * This function is the default scan_bus() method of the root device. + */ +unsigned int root_dev_scan_bus(device_t root, unsigned int max) +{ + return scan_static_bus(root, max); +} + +void root_dev_init(device_t root) +{ +} + +void root_dev_reset(struct bus *bus) +{ + printk_info("Reseting board...\n"); + hard_reset(); +} + +/** + * @brief Default device operation for root device + * + * This is the default device operation for root devices. These operations + * should be fully usable as is. However the chip_operations::enable_dev() + * of a motherboard can override this if you want non-default behavior. + */ +struct device_operations default_dev_ops_root = { + .read_resources = root_dev_read_resources, + .set_resources = root_dev_set_resources, + .enable_resources = root_dev_enable_resources, + .init = root_dev_init, + .scan_bus = root_dev_scan_bus, + .reset_bus = root_dev_reset, +}; + +/** + * @brief The root of device tree. + * + * This is the root of the device tree. The device tree is defined in the + * static.c file and is generated by config tool during compile time. + */ +extern struct device dev_root; diff --git a/devices/smbus_ops.c b/devices/smbus_ops.c new file mode 100644 index 0000000000..f5e441e664 --- /dev/null +++ b/devices/smbus_ops.c @@ -0,0 +1,110 @@ +/* + 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. 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ +#include +#include +#include +#include +#include + +struct bus *get_pbus_smbus(device_t dev) +{ + struct bus *pbus = dev->bus; + while (pbus && pbus->dev && !ops_smbus_bus(pbus)) { + pbus = pbus->dev->bus; + } + if (!pbus || !pbus->dev || !pbus->dev->ops || !pbus->dev->ops->ops_smbus_bus) { + printk_alert("%s Cannot find smbus bus operations", dev_path(dev)); + die(""); + for(;;); + } + return pbus; +} + +/*multi level i2c MUX??? may need to find the first i2c device and then set link + * down to current dev + 1 store get_pbus_smbus list link + 2 reverse the link and call set link */ + +int smbus_set_link(device_t dev) +{ + struct bus *pbus_a[4]; // 4 level mux only. Enough? + struct bus *pbus = dev->bus; + int pbus_num=0; + int i; + while(pbus && pbus->dev && (pbus->dev->path.type==DEVICE_PATH_I2C)) { + pbus_a[pbus_num++] = pbus; + pbus = pbus->dev->bus; + } +// printk_info("smbus_set_link: "); + for (i=pbus_num-1; i>=0; i--) { +// printk_info(" %s[%d] -> ", dev_path(pbus_a[i]->dev), pbus_a[i]->link); + if (ops_smbus_bus(get_pbus_smbus(pbus_a[i]->dev))) { + if (pbus_a[i]->dev->ops && pbus_a[i]->dev->ops->set_link) + pbus_a[i]->dev->ops->set_link(pbus_a[i]->dev, pbus_a[i]->link); + } + } +// printk_info(" %s\n", dev_path(dev)); + + return pbus_num; +} + +int smbus_quick_read(device_t dev) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->quick_read(dev); +} +int smbus_quick_write(device_t dev) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->quick_write(dev); +} +int smbus_recv_byte(device_t dev) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->recv_byte(dev); +} +int smbus_send_byte(device_t dev, uint8_t byte) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->send_byte(dev, byte); +} +int smbus_read_byte(device_t dev, uint8_t addr) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->read_byte(dev, addr); +} +int smbus_write_byte(device_t dev, uint8_t addr, uint8_t val) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->write_byte(dev, addr, val); +} +int smbus_read_word(device_t dev, uint8_t addr) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->read_word(dev, addr); +} +int smbus_write_word(device_t dev, uint8_t addr, uint16_t val) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->write_word(dev, addr, val); +} +int smbus_process_call(device_t dev, uint8_t cmd, uint16_t data) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->process_call(dev, cmd, data); +} +int smbus_block_read(device_t dev, uint8_t cmd, uint8_t bytes, uint8_t *buffer) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->block_read(dev, cmd, bytes, buffer); +} +int smbus_block_write(device_t dev, uint8_t cmd, uint8_t bytes, const uint8_t *buffer) +{ + return ops_smbus_bus(get_pbus_smbus(dev))->block_write(dev, cmd, bytes, buffer); +} + +