| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* |
| * Originally based on the Linux kernel (arch/i386/kernel/pci-pc.c). |
| */ |
| |
| #include <console/console.h> |
| #include <device/device.h> |
| #include <device/pci_def.h> |
| #include <device/pci_ids.h> |
| #include <post.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <smp/spinlock.h> |
| #include <timer.h> |
| |
| /** Pointer to the last device */ |
| extern struct device *last_dev; |
| /** Linked list of free resources */ |
| struct resource *free_resources = NULL; |
| /* Disable a PCI device based on bus, device and function. */ |
| void devfn_disable(const struct bus *bus, unsigned int devfn) |
| { |
| struct device *dev = pcidev_path_behind(bus, devfn); |
| if (dev) |
| dev->enabled = 0; |
| } |
| |
| /** |
| * Initialize all chips of statically known devices. |
| * |
| * Will be called before bus enumeration to initialize chips stated in the |
| * device tree. |
| */ |
| void dev_initialize_chips(void) |
| { |
| const struct device *dev; |
| |
| for (dev = all_devices; dev; dev = dev->next) { |
| /* Initialize chip if we haven't yet. */ |
| if (dev->chip_ops && dev->chip_ops->init && |
| !dev->chip_ops->initialized) { |
| post_log_path(dev); |
| dev->chip_ops->init(dev->chip_info); |
| dev->chip_ops->initialized = 1; |
| } |
| } |
| post_log_clear(); |
| } |
| |
| /** |
| * Finalize all chips of statically known devices. |
| * |
| * This is the last call before calling the payload. This is a good place |
| * to lock registers or other final cleanup. |
| */ |
| void dev_finalize_chips(void) |
| { |
| const struct device *dev; |
| |
| for (dev = all_devices; dev; dev = dev->next) { |
| /* Initialize chip if we haven't yet. */ |
| if (dev->chip_ops && dev->chip_ops->final && |
| !dev->chip_ops->finalized) { |
| dev->chip_ops->final(dev->chip_info); |
| dev->chip_ops->finalized = 1; |
| } |
| } |
| } |
| |
| DECLARE_SPIN_LOCK(dev_lock) |
| |
| /** |
| * Allocate a new device structure. |
| * |
| * Allocate a new device structure and attach it to the device tree as a |
| * child of the parent bus. |
| * |
| * @param parent Parent bus the newly created device should be attached to. |
| * @param path Path to the device to be created. |
| * @return Pointer to the newly created device structure. |
| * |
| * @see device_path |
| */ |
| static struct device *__alloc_dev(struct bus *parent, struct device_path *path) |
| { |
| struct device *dev, *child; |
| |
| /* 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("alloc_dev(): out of memory.\n"); |
| |
| memset(dev, 0, sizeof(*dev)); |
| memcpy(&dev->path, path, sizeof(*path)); |
| |
| /* By default devices are enabled. */ |
| dev->enabled = 1; |
| |
| /* Add the new device to the list of children of the bus. */ |
| dev->upstream = 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->next = dev; |
| last_dev = dev; |
| |
| return dev; |
| } |
| |
| struct device *alloc_dev(struct bus *parent, struct device_path *path) |
| { |
| struct device *dev; |
| spin_lock(&dev_lock); |
| dev = __alloc_dev(parent, path); |
| spin_unlock(&dev_lock); |
| return dev; |
| } |
| |
| DECLARE_SPIN_LOCK(bus_lock) |
| |
| /** |
| * Allocate a new bus structure |
| * |
| * Allocate a new downstream bus structure below a device and attach it |
| * to the device tree if the device doesn't already have a downstream bus. |
| * |
| * @param parent Parent device the to-be-created bus should be attached to. |
| * @return Pointer to the newly created bus structure or the existing bus. |
| * |
| */ |
| static struct bus *__alloc_bus(struct device *parent) |
| { |
| if (parent->downstream) |
| return parent->downstream; |
| |
| struct bus *bus = calloc(1, sizeof(struct bus)); |
| if (!bus) |
| die("Couldn't allocate downstream bus!\n"); |
| parent->downstream = bus; |
| bus->dev = parent; |
| |
| return bus; |
| } |
| |
| struct bus *alloc_bus(struct device *parent) |
| { |
| struct bus *bus; |
| spin_lock(&bus_lock); |
| bus = __alloc_bus(parent); |
| spin_unlock(&bus_lock); |
| return bus; |
| } |
| |
| /** |
| * See if a device structure already exists and if not allocate it. |
| * |
| * @param parent 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. |
| */ |
| struct device *alloc_find_dev(struct bus *parent, struct device_path *path) |
| { |
| struct device *child; |
| spin_lock(&dev_lock); |
| child = find_dev_path(parent, path); |
| if (!child) |
| child = __alloc_dev(parent, path); |
| spin_unlock(&dev_lock); |
| return child; |
| } |
| |
| /** |
| * 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(BIOS_SPEW, "%s %s segment group %d bus %d\n", dev_path(bus->dev), |
| __func__, bus->segment_group, bus->secondary); |
| |
| /* Walk through all devices and find which resources they need. */ |
| for (curdev = bus->children; curdev; curdev = curdev->sibling) { |
| if (!curdev->enabled) |
| continue; |
| |
| if (!curdev->ops || !curdev->ops->read_resources) { |
| if (curdev->path.type != DEVICE_PATH_APIC) |
| printk(BIOS_ERR, "%s missing %s\n", |
| dev_path(curdev), __func__); |
| continue; |
| } |
| post_log_path(curdev); |
| curdev->ops->read_resources(curdev); |
| |
| /* Read in the resources behind the current device's links. */ |
| if (curdev->downstream) |
| read_resources(curdev->downstream); |
| } |
| post_log_clear(); |
| printk(BIOS_SPEW, "%s %s segment group %d bus %d done\n", |
| dev_path(bus->dev), __func__, bus->segment_group, bus->secondary); |
| } |
| |
| struct device *vga_pri = NULL; |
| static void set_vga_bridge_bits(void) |
| { |
| /* |
| * FIXME: Modify set_vga_bridge() so it is less PCI-centric! |
| * This function knows too much about PCI stuff, it should be just |
| * an iterator/visitor. |
| */ |
| |
| /* FIXME: Handle the VGA palette snooping. */ |
| struct device *dev, *vga, *vga_onboard; |
| struct bus *bus; |
| |
| bus = 0; |
| vga = 0; |
| vga_onboard = 0; |
| |
| dev = NULL; |
| while ((dev = dev_find_class(PCI_CLASS_DISPLAY_VGA << 8, dev))) { |
| if (!dev->enabled) |
| continue; |
| |
| printk(BIOS_DEBUG, "found VGA at %s\n", dev_path(dev)); |
| if (dev->upstream->no_vga16) { |
| printk(BIOS_WARNING, |
| "A bridge on the path doesn't support 16-bit VGA decoding!"); |
| } |
| |
| if (dev->on_mainboard) |
| vga_onboard = dev; |
| else |
| vga = dev; |
| |
| /* It isn't safe to enable all VGA cards. */ |
| dev->command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_IO); |
| } |
| |
| if (!vga) |
| vga = vga_onboard; |
| |
| if (CONFIG(ONBOARD_VGA_IS_PRIMARY) && vga_onboard) |
| vga = vga_onboard; |
| |
| /* If we prefer plugin VGA over chipset VGA, the chipset might |
| want to know. */ |
| if (!CONFIG(ONBOARD_VGA_IS_PRIMARY) && (vga != vga_onboard) && |
| vga_onboard && vga_onboard->ops && vga_onboard->ops->vga_disable) { |
| printk(BIOS_DEBUG, "Use plugin graphics over integrated.\n"); |
| vga_onboard->ops->vga_disable(vga_onboard); |
| } |
| |
| if (vga) { |
| /* VGA is first add-on card or the only onboard VGA. */ |
| printk(BIOS_DEBUG, "Setting up VGA for %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->upstream; |
| } |
| |
| /* Now walk up the bridges setting the VGA enable. */ |
| while (bus) { |
| printk(BIOS_DEBUG, "Setting PCI_BRIDGE_CTL_VGA for bridge %s\n", |
| dev_path(bus->dev)); |
| bus->bridge_ctrl |= PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_VGA16; |
| bus = (bus == bus->dev->upstream) ? 0 : bus->dev->upstream; |
| } |
| } |
| |
| /** |
| * Assign the computed resources to the devices on the 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() |
| * |
| * @param bus Pointer to the structure for this bus. |
| */ |
| void assign_resources(struct bus *bus) |
| { |
| struct device *curdev; |
| |
| printk(BIOS_SPEW, "%s %s, segment group %d bus %d\n", |
| dev_path(bus->dev), __func__, bus->segment_group, bus->secondary); |
| |
| for (curdev = bus->children; curdev; curdev = curdev->sibling) { |
| if (!curdev->enabled || !curdev->resource_list) |
| continue; |
| |
| if (!curdev->ops || !curdev->ops->set_resources) { |
| printk(BIOS_ERR, "%s missing set_resources\n", |
| dev_path(curdev)); |
| continue; |
| } |
| post_log_path(curdev); |
| curdev->ops->set_resources(curdev); |
| } |
| post_log_clear(); |
| printk(BIOS_SPEW, "%s %s, segment group %d bus %d done\n", |
| dev_path(bus->dev), __func__, bus->segment_group, bus->secondary); |
| } |
| |
| /** |
| * Enable the resources for devices on a link. |
| * |
| * 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 before its children's enable_resources() methods. |
| * |
| * @param link The link whose devices' resources are to be enabled. |
| */ |
| static void enable_resources(struct bus *link) |
| { |
| struct device *dev; |
| |
| for (dev = link->children; dev; dev = dev->sibling) { |
| if (dev->enabled && dev->ops && dev->ops->enable_resources) { |
| post_log_path(dev); |
| dev->ops->enable_resources(dev); |
| } |
| } |
| |
| for (dev = link->children; dev; dev = dev->sibling) { |
| if (dev->downstream) |
| enable_resources(dev->downstream); |
| } |
| post_log_clear(); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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 busdev Pointer to the bus device. |
| */ |
| static void scan_bus(struct device *busdev) |
| { |
| int do_scan_bus; |
| struct stopwatch sw; |
| long scan_time; |
| |
| if (!busdev->enabled) |
| return; |
| |
| printk(BIOS_DEBUG, "%s scanning...\n", dev_path(busdev)); |
| |
| post_log_path(busdev); |
| |
| stopwatch_init(&sw); |
| |
| do_scan_bus = 1; |
| while (do_scan_bus) { |
| struct bus *link = busdev->downstream; |
| busdev->ops->scan_bus(busdev); |
| do_scan_bus = 0; |
| if (!link || !link->reset_needed) |
| continue; |
| if (reset_bus(link)) |
| do_scan_bus = 1; |
| else |
| busdev->upstream->reset_needed = 1; |
| } |
| |
| scan_time = stopwatch_duration_msecs(&sw); |
| printk(BIOS_DEBUG, "%s: bus %s finished in %ld msecs\n", __func__, |
| dev_path(busdev), scan_time); |
| } |
| |
| void scan_bridges(struct bus *bus) |
| { |
| struct device *child; |
| |
| for (child = bus->children; child; child = child->sibling) { |
| if (!child->ops || !child->ops->scan_bus) |
| continue; |
| scan_bus(child); |
| } |
| } |
| |
| /** |
| * Determine the existence of devices and extend the device tree. |
| * |
| * Most of the devices in the system are listed in the mainboard devicetree.cb |
| * 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 devicetree.cb, |
| * 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', scans the buses in |
| * the system recursively, and modifies 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; |
| |
| printk(BIOS_INFO, "Enumerating buses...\n"); |
| |
| root = &dev_root; |
| |
| show_all_devs(BIOS_SPEW, "Before device enumeration."); |
| printk(BIOS_SPEW, "Compare with tree...\n"); |
| show_devs_tree(root, BIOS_SPEW, 0); |
| |
| if (root->chip_ops && root->chip_ops->enable_dev) |
| root->chip_ops->enable_dev(root); |
| |
| if (!root->ops || !root->ops->scan_bus) { |
| printk(BIOS_ERR, "dev_root missing scan_bus operation"); |
| return; |
| } |
| scan_bus(root); |
| post_log_clear(); |
| printk(BIOS_INFO, "done\n"); |
| } |
| |
| /** |
| * 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) |
| * required by each device. In the second pass, the resources ranges are |
| * relocated to their final position and stored to the hardware. |
| * |
| * I/O resources grow upward. MEM resources grow downward. |
| * |
| * Since the assignment is hierarchical we set the values into the dev_root |
| * struct. |
| */ |
| void dev_configure(void) |
| { |
| const struct device *root; |
| |
| set_vga_bridge_bits(); |
| |
| printk(BIOS_INFO, "Allocating resources...\n"); |
| |
| root = &dev_root; |
| |
| /* |
| * Each domain should create resources which contain the entire address |
| * space for IO, MEM, and PREFMEM resources in the domain. The |
| * allocation of device resources will be done from this address space. |
| */ |
| |
| /* Read the resources for the entire tree. */ |
| |
| printk(BIOS_INFO, "Reading resources...\n"); |
| read_resources(root->downstream); |
| printk(BIOS_INFO, "Done reading resources.\n"); |
| |
| print_resource_tree(root, BIOS_SPEW, "After reading."); |
| |
| allocate_resources(root); |
| |
| assign_resources(root->downstream); |
| printk(BIOS_INFO, "Done setting resources.\n"); |
| print_resource_tree(root, BIOS_SPEW, "After assigning values."); |
| |
| printk(BIOS_INFO, "Done allocating resources.\n"); |
| } |
| |
| /** |
| * 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(BIOS_INFO, "Enabling resources...\n"); |
| |
| /* Now enable everything. */ |
| if (dev_root.downstream) |
| enable_resources(dev_root.downstream); |
| |
| printk(BIOS_INFO, "done.\n"); |
| } |
| |
| /** |
| * Initialize a specific device. |
| * |
| * The parent should be initialized first to avoid having an ordering problem. |
| * This is done by calling the parent's init() method before its children's |
| * init() methods. |
| * |
| * @param dev The device to be initialized. |
| */ |
| static void init_dev(struct device *dev) |
| { |
| if (!dev->enabled) |
| return; |
| |
| if (!dev->initialized && dev->ops && dev->ops->init) { |
| struct stopwatch sw; |
| long init_time; |
| |
| if (dev->path.type == DEVICE_PATH_I2C) { |
| printk(BIOS_DEBUG, "smbus: %s->", dev_path(dev->upstream->dev)); |
| } |
| |
| printk(BIOS_DEBUG, "%s init\n", dev_path(dev)); |
| |
| stopwatch_init(&sw); |
| dev->initialized = 1; |
| dev->ops->init(dev); |
| |
| init_time = stopwatch_duration_msecs(&sw); |
| printk(BIOS_DEBUG, "%s init finished in %ld msecs\n", dev_path(dev), |
| init_time); |
| } |
| } |
| |
| static void init_link(struct bus *link) |
| { |
| struct device *dev; |
| |
| for (dev = link->children; dev; dev = dev->sibling) { |
| post_code(POSTCODE_BS_DEV_INIT); |
| post_log_path(dev); |
| init_dev(dev); |
| } |
| |
| for (dev = link->children; dev; dev = dev->sibling) |
| if (dev->downstream) |
| init_link(dev->downstream); |
| } |
| |
| /** |
| * Initialize all devices in the global device tree. |
| * |
| * Starting at the root device, call the device's init() method to do |
| * device-specific setup, then call each child's init() method. |
| */ |
| void dev_initialize(void) |
| { |
| printk(BIOS_INFO, "Initializing devices...\n"); |
| |
| /* First call the mainboard init. */ |
| init_dev(&dev_root); |
| |
| /* Now initialize everything. */ |
| if (dev_root.downstream) |
| init_link(dev_root.downstream); |
| post_log_clear(); |
| |
| printk(BIOS_INFO, "Devices initialized\n"); |
| show_all_devs(BIOS_SPEW, "After init."); |
| } |
| |
| /** |
| * Finalize a specific device. |
| * |
| * The parent should be finalized first to avoid having an ordering problem. |
| * This is done by calling the parent's final() method before its children's |
| * final() methods. |
| * |
| * @param dev The device to be initialized. |
| */ |
| static void final_dev(struct device *dev) |
| { |
| if (!dev->enabled) |
| return; |
| |
| if (dev->ops && dev->ops->final) { |
| printk(BIOS_DEBUG, "%s final\n", dev_path(dev)); |
| dev->ops->final(dev); |
| } |
| } |
| |
| static void final_link(struct bus *link) |
| { |
| struct device *dev; |
| |
| for (dev = link->children; dev; dev = dev->sibling) |
| final_dev(dev); |
| |
| for (dev = link->children; dev; dev = dev->sibling) |
| if (dev->downstream) |
| final_link(dev->downstream); |
| } |
| /** |
| * Finalize all devices in the global device tree. |
| * |
| * Starting at the root device, call the device's final() method to do |
| * device-specific cleanup, then call each child's final() method. |
| */ |
| void dev_finalize(void) |
| { |
| printk(BIOS_INFO, "Finalize devices...\n"); |
| |
| /* First call the mainboard finalize. */ |
| final_dev(&dev_root); |
| |
| /* Now finalize everything. */ |
| final_link(dev_root.downstream); |
| |
| printk(BIOS_INFO, "Devices finalized\n"); |
| } |