aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/device/device.c75
-rw-r--r--src/include/device/device.h5
-rw-r--r--src/lib/hardwaremain.c3
3 files changed, 83 insertions, 0 deletions
diff --git a/src/device/device.c b/src/device/device.c
index 3837928743..a2a64b2cae 100644
--- a/src/device/device.c
+++ b/src/device/device.c
@@ -72,6 +72,26 @@ void dev_initialize_chips(void)
}
}
+/**
+ * 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)
+{
+ 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)
#if CONFIG_GFXUMA
@@ -1167,3 +1187,58 @@ void dev_initialize(void)
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 childrens'
+ * 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;
+ struct bus *c_link;
+
+ for (dev = link->children; dev; dev = dev->sibling)
+ final_dev(dev);
+
+ for (dev = link->children; dev; dev = dev->sibling) {
+ for (c_link = dev->link_list; c_link; c_link = c_link->next)
+ final_link(c_link);
+ }
+}
+/**
+ * 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)
+{
+ struct bus *link;
+
+ printk(BIOS_INFO, "Finalize devices...\n");
+
+ /* First call the mainboard finalize. */
+ final_dev(&dev_root);
+
+ /* Now finalize everything. */
+ for (link = dev_root.link_list; link; link = link->next)
+ final_link(link);
+
+ printk(BIOS_INFO, "Devices finalized\n");
+}
diff --git a/src/include/device/device.h b/src/include/device/device.h
index c0e6e0f6c6..fec0497508 100644
--- a/src/include/device/device.h
+++ b/src/include/device/device.h
@@ -22,7 +22,9 @@ struct pnp_mode_ops;
struct chip_operations {
void (*enable_dev)(struct device *dev);
void (*init)(void *chip_info);
+ void (*final)(void *chip_info);
unsigned int initialized : 1;
+ unsigned int finalized : 1;
const char *name;
};
@@ -35,6 +37,7 @@ struct device_operations {
void (*set_resources)(device_t dev);
void (*enable_resources)(device_t dev);
void (*init)(device_t dev);
+ void (*final)(device_t dev);
unsigned int (*scan_bus)(device_t bus, unsigned int max);
void (*enable)(device_t dev);
void (*disable)(device_t dev);
@@ -140,6 +143,8 @@ void dev_configure(void);
void dev_enable(void);
void dev_initialize(void);
void dev_optimize(void);
+void dev_finalize(void);
+void dev_finalize_chips(void);
/* Generic device helper functions */
int reset_bus(struct bus *bus);
diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c
index a5993c41dc..501591f114 100644
--- a/src/lib/hardwaremain.c
+++ b/src/lib/hardwaremain.c
@@ -175,6 +175,7 @@ static boot_state_t bs_dev_init(void *arg)
static boot_state_t bs_post_device(void *arg)
{
+ dev_finalize();
timestamp_add_now(TS_DEVICE_DONE);
timestamp_reinit();
@@ -217,6 +218,8 @@ static boot_state_t bs_write_tables(void *arg)
*/
write_tables();
+ dev_finalize_chips();
+
return BS_PAYLOAD_LOAD;
}