This simplifies creating device drivers for hardware or information
described in the coreboot table. It also avoids needing to search
through the table every time a driver is loaded.

Signed-off-by: Samuel Holland <sam...@sholland.org>
---
 drivers/firmware/google/coreboot_table-acpi.c |   2 +-
 drivers/firmware/google/coreboot_table-of.c   |   2 +-
 drivers/firmware/google/coreboot_table.c      | 121 ++++++++++++++++++++++++--
 drivers/firmware/google/coreboot_table.h      |  49 +++++++++--
 4 files changed, 156 insertions(+), 18 deletions(-)

diff --git a/drivers/firmware/google/coreboot_table-acpi.c 
b/drivers/firmware/google/coreboot_table-acpi.c
index fb98db2d20e2..77197fe3d42f 100644
--- a/drivers/firmware/google/coreboot_table-acpi.c
+++ b/drivers/firmware/google/coreboot_table-acpi.c
@@ -53,7 +53,7 @@ static int coreboot_table_acpi_probe(struct platform_device 
*pdev)
        if (!ptr)
                return -ENOMEM;
 
-       return coreboot_table_init(ptr);
+       return coreboot_table_init(&pdev->dev, ptr);
 }
 
 static int coreboot_table_acpi_remove(struct platform_device *pdev)
diff --git a/drivers/firmware/google/coreboot_table-of.c 
b/drivers/firmware/google/coreboot_table-of.c
index 727acdc83e83..f15bf404c579 100644
--- a/drivers/firmware/google/coreboot_table-of.c
+++ b/drivers/firmware/google/coreboot_table-of.c
@@ -34,7 +34,7 @@ static int coreboot_table_of_probe(struct platform_device 
*pdev)
        if (!ptr)
                return -ENOMEM;
 
-       return coreboot_table_init(ptr);
+       return coreboot_table_init(&pdev->dev, ptr);
 }
 
 static int coreboot_table_of_remove(struct platform_device *pdev)
diff --git a/drivers/firmware/google/coreboot_table.c 
b/drivers/firmware/google/coreboot_table.c
index 0019d3ec18dd..04fc08e81744 100644
--- a/drivers/firmware/google/coreboot_table.c
+++ b/drivers/firmware/google/coreboot_table.c
@@ -4,6 +4,7 @@
  * Module providing coreboot table access.
  *
  * Copyright 2017 Google Inc.
+ * Copyright 2017 Samuel Holland <sam...@sholland.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License v2.0 as published by
@@ -15,21 +16,87 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/device.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 #include "coreboot_table.h"
 
-struct coreboot_table_entry {
-       u32 tag;
-       u32 size;
-};
+#define CB_DEV(d) container_of(d, struct coreboot_device, dev)
+#define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
 
 static struct coreboot_table_header __iomem *ptr_header;
 
+static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct coreboot_device *device = CB_DEV(dev);
+       struct coreboot_driver *driver = CB_DRV(drv);
+
+       return device->entry.tag == driver->tag;
+}
+
+static int coreboot_bus_probe(struct device *dev)
+{
+       int ret = -ENODEV;
+       struct coreboot_device *device = CB_DEV(dev);
+       struct coreboot_driver *driver = CB_DRV(dev->driver);
+
+       if (driver->probe)
+               ret = driver->probe(device);
+
+       return ret;
+}
+
+static int coreboot_bus_remove(struct device *dev)
+{
+       int ret = 0;
+       struct coreboot_device *device = CB_DEV(dev);
+       struct coreboot_driver *driver = CB_DRV(dev->driver);
+
+       if (driver->remove)
+               ret = driver->remove(device);
+
+       return ret;
+}
+
+static struct bus_type coreboot_bus_type = {
+       .name           = "coreboot",
+       .match          = coreboot_bus_match,
+       .probe          = coreboot_bus_probe,
+       .remove         = coreboot_bus_remove,
+};
+
+static int __init coreboot_bus_init(void)
+{
+       return bus_register(&coreboot_bus_type);
+}
+module_init(coreboot_bus_init);
+
+static void coreboot_device_release(struct device *dev)
+{
+       struct coreboot_device *device = CB_DEV(dev);
+
+       kfree(device);
+}
+
+int coreboot_driver_register(struct coreboot_driver *driver)
+{
+       driver->drv.bus = &coreboot_bus_type;
+
+       return driver_register(&driver->drv);
+}
+EXPORT_SYMBOL(coreboot_driver_register);
+
+void coreboot_driver_unregister(struct coreboot_driver *driver)
+{
+       driver_unregister(&driver->drv);
+}
+EXPORT_SYMBOL(coreboot_driver_unregister);
+
 /*
  * This function parses the coreboot table for an entry that contains the base
  * address of the given entry tag. The coreboot table consists of a header
@@ -73,18 +140,58 @@ int coreboot_table_find(int tag, void *data, size_t 
data_size)
 }
 EXPORT_SYMBOL(coreboot_table_find);
 
-int coreboot_table_init(void __iomem *ptr)
+int coreboot_table_init(struct device *dev, void __iomem *ptr)
 {
+       int i, ret;
+       void *ptr_entry;
+       struct coreboot_device *device;
+       struct coreboot_table_entry entry;
+       struct coreboot_table_header header;
+
        ptr_header = ptr;
+       memcpy_fromio(&header, ptr_header, sizeof(header));
 
-       return 0;
+       if (strncmp(header.signature, "LBIO", sizeof(header.signature))) {
+               pr_warn("coreboot_table: coreboot table missing or corrupt!\n");
+               return -ENODEV;
+       }
+
+       ptr_entry = (void *)ptr_header + header.header_bytes;
+       for (i = 0; i < header.table_entries; i++) {
+               memcpy_fromio(&entry, ptr_entry, sizeof(entry));
+
+               device = kzalloc(sizeof(struct device) + entry.size, 
GFP_KERNEL);
+               if (!device) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               dev_set_name(&device->dev, "coreboot%d", i);
+               device->dev.parent = dev;
+               device->dev.bus = &coreboot_bus_type;
+               device->dev.release = coreboot_device_release;
+               memcpy_fromio(&device->entry, ptr_entry, entry.size);
+
+               ret = device_register(&device->dev);
+               if (ret) {
+                       put_device(&device->dev);
+                       break;
+               }
+
+               ptr_entry += entry.size;
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL(coreboot_table_init);
 
 int coreboot_table_exit(void)
 {
-       if (ptr_header)
+       if (ptr_header) {
+               bus_unregister(&coreboot_bus_type);
                iounmap(ptr_header);
+               ptr_header = NULL;
+       }
 
        return 0;
 }
diff --git a/drivers/firmware/google/coreboot_table.h 
b/drivers/firmware/google/coreboot_table.h
index 6eff1ae0c5d3..88e6a1c06028 100644
--- a/drivers/firmware/google/coreboot_table.h
+++ b/drivers/firmware/google/coreboot_table.h
@@ -4,6 +4,7 @@
  * Internal header for coreboot table access.
  *
  * Copyright 2017 Google Inc.
+ * Copyright 2017 Samuel Holland <sam...@sholland.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License v2.0 as published by
@@ -20,14 +21,6 @@
 
 #include <linux/io.h>
 
-/* List of coreboot entry structures that is used */
-struct lb_cbmem_ref {
-       uint32_t tag;
-       uint32_t size;
-
-       uint64_t cbmem_addr;
-};
-
 /* Coreboot table header structure */
 struct coreboot_table_header {
        char signature[4];
@@ -38,11 +31,49 @@ struct coreboot_table_header {
        u32 table_entries;
 };
 
+/* List of coreboot entry structures that is used */
+/* Generic */
+struct coreboot_table_entry {
+       u32 tag;
+       u32 size;
+};
+
+/* Points to a CBMEM entry */
+struct lb_cbmem_ref {
+       u32 tag;
+       u32 size;
+
+       u64 cbmem_addr;
+};
+
+/* A device, additionally with information from coreboot. */
+struct coreboot_device {
+       struct device dev;
+       union {
+               struct coreboot_table_entry entry;
+               struct lb_cbmem_ref cbmem_ref;
+       };
+};
+
+/* A driver for handling devices described in coreboot tables. */
+struct coreboot_driver {
+       int (*probe)(struct coreboot_device *);
+       int (*remove)(struct coreboot_device *);
+       struct device_driver drv;
+       u32 tag;
+};
+
+/* Register a driver that uses the data from a coreboot table. */
+int coreboot_driver_register(struct coreboot_driver *driver);
+
+/* Unregister a driver that uses the data from a coreboot table. */
+void coreboot_driver_unregister(struct coreboot_driver *driver);
+
 /* Retrieve coreboot table entry with tag *tag* and copy it to data */
 int coreboot_table_find(int tag, void *data, size_t data_size);
 
 /* Initialize coreboot table module given a pointer to iomem */
-int coreboot_table_init(void __iomem *ptr);
+int coreboot_table_init(struct device *dev, void __iomem *ptr);
 
 /* Cleanup coreboot table module */
 int coreboot_table_exit(void);
-- 
2.13.6

Reply via email to