1d384d6f4SThierry Escande /*
2d384d6f4SThierry Escande  * coreboot_table.c
3d384d6f4SThierry Escande  *
4d384d6f4SThierry Escande  * Module providing coreboot table access.
5d384d6f4SThierry Escande  *
6d384d6f4SThierry Escande  * Copyright 2017 Google Inc.
7570d30c2SSamuel Holland  * Copyright 2017 Samuel Holland <samuel@sholland.org>
8d384d6f4SThierry Escande  *
9d384d6f4SThierry Escande  * This program is free software; you can redistribute it and/or modify
10d384d6f4SThierry Escande  * it under the terms of the GNU General Public License v2.0 as published by
11d384d6f4SThierry Escande  * the Free Software Foundation.
12d384d6f4SThierry Escande  *
13d384d6f4SThierry Escande  * This program is distributed in the hope that it will be useful,
14d384d6f4SThierry Escande  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15d384d6f4SThierry Escande  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16d384d6f4SThierry Escande  * GNU General Public License for more details.
17d384d6f4SThierry Escande  */
18d384d6f4SThierry Escande 
19a28aad66SStephen Boyd #include <linux/acpi.h>
20570d30c2SSamuel Holland #include <linux/device.h>
21d384d6f4SThierry Escande #include <linux/err.h>
22d384d6f4SThierry Escande #include <linux/init.h>
23d384d6f4SThierry Escande #include <linux/io.h>
24d384d6f4SThierry Escande #include <linux/kernel.h>
25d384d6f4SThierry Escande #include <linux/module.h>
26a28aad66SStephen Boyd #include <linux/of.h>
27a28aad66SStephen Boyd #include <linux/platform_device.h>
28570d30c2SSamuel Holland #include <linux/slab.h>
29d384d6f4SThierry Escande 
30d384d6f4SThierry Escande #include "coreboot_table.h"
31d384d6f4SThierry Escande 
32570d30c2SSamuel Holland #define CB_DEV(d) container_of(d, struct coreboot_device, dev)
33570d30c2SSamuel Holland #define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
34d384d6f4SThierry Escande 
35570d30c2SSamuel Holland static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
36570d30c2SSamuel Holland {
37570d30c2SSamuel Holland 	struct coreboot_device *device = CB_DEV(dev);
38570d30c2SSamuel Holland 	struct coreboot_driver *driver = CB_DRV(drv);
39570d30c2SSamuel Holland 
40570d30c2SSamuel Holland 	return device->entry.tag == driver->tag;
41570d30c2SSamuel Holland }
42570d30c2SSamuel Holland 
43570d30c2SSamuel Holland static int coreboot_bus_probe(struct device *dev)
44570d30c2SSamuel Holland {
45570d30c2SSamuel Holland 	int ret = -ENODEV;
46570d30c2SSamuel Holland 	struct coreboot_device *device = CB_DEV(dev);
47570d30c2SSamuel Holland 	struct coreboot_driver *driver = CB_DRV(dev->driver);
48570d30c2SSamuel Holland 
49570d30c2SSamuel Holland 	if (driver->probe)
50570d30c2SSamuel Holland 		ret = driver->probe(device);
51570d30c2SSamuel Holland 
52570d30c2SSamuel Holland 	return ret;
53570d30c2SSamuel Holland }
54570d30c2SSamuel Holland 
55570d30c2SSamuel Holland static int coreboot_bus_remove(struct device *dev)
56570d30c2SSamuel Holland {
57570d30c2SSamuel Holland 	int ret = 0;
58570d30c2SSamuel Holland 	struct coreboot_device *device = CB_DEV(dev);
59570d30c2SSamuel Holland 	struct coreboot_driver *driver = CB_DRV(dev->driver);
60570d30c2SSamuel Holland 
61570d30c2SSamuel Holland 	if (driver->remove)
62570d30c2SSamuel Holland 		ret = driver->remove(device);
63570d30c2SSamuel Holland 
64570d30c2SSamuel Holland 	return ret;
65570d30c2SSamuel Holland }
66570d30c2SSamuel Holland 
67570d30c2SSamuel Holland static struct bus_type coreboot_bus_type = {
68570d30c2SSamuel Holland 	.name		= "coreboot",
69570d30c2SSamuel Holland 	.match		= coreboot_bus_match,
70570d30c2SSamuel Holland 	.probe		= coreboot_bus_probe,
71570d30c2SSamuel Holland 	.remove		= coreboot_bus_remove,
72570d30c2SSamuel Holland };
73570d30c2SSamuel Holland 
74570d30c2SSamuel Holland static void coreboot_device_release(struct device *dev)
75570d30c2SSamuel Holland {
76570d30c2SSamuel Holland 	struct coreboot_device *device = CB_DEV(dev);
77570d30c2SSamuel Holland 
78570d30c2SSamuel Holland 	kfree(device);
79570d30c2SSamuel Holland }
80570d30c2SSamuel Holland 
81570d30c2SSamuel Holland int coreboot_driver_register(struct coreboot_driver *driver)
82570d30c2SSamuel Holland {
83570d30c2SSamuel Holland 	driver->drv.bus = &coreboot_bus_type;
84570d30c2SSamuel Holland 
85570d30c2SSamuel Holland 	return driver_register(&driver->drv);
86570d30c2SSamuel Holland }
87570d30c2SSamuel Holland EXPORT_SYMBOL(coreboot_driver_register);
88570d30c2SSamuel Holland 
89570d30c2SSamuel Holland void coreboot_driver_unregister(struct coreboot_driver *driver)
90570d30c2SSamuel Holland {
91570d30c2SSamuel Holland 	driver_unregister(&driver->drv);
92570d30c2SSamuel Holland }
93570d30c2SSamuel Holland EXPORT_SYMBOL(coreboot_driver_unregister);
94570d30c2SSamuel Holland 
957adb05bbSStephen Boyd static int coreboot_table_populate(struct device *dev, void *ptr)
96d384d6f4SThierry Escande {
97570d30c2SSamuel Holland 	int i, ret;
98570d30c2SSamuel Holland 	void *ptr_entry;
99570d30c2SSamuel Holland 	struct coreboot_device *device;
100a7d9b5f0SStephen Boyd 	struct coreboot_table_entry *entry;
1017adb05bbSStephen Boyd 	struct coreboot_table_header *header = ptr;
102d384d6f4SThierry Escande 
1037adb05bbSStephen Boyd 	ptr_entry = ptr + header->header_bytes;
104a7d9b5f0SStephen Boyd 	for (i = 0; i < header->table_entries; i++) {
105a7d9b5f0SStephen Boyd 		entry = ptr_entry;
106570d30c2SSamuel Holland 
107a7d9b5f0SStephen Boyd 		device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
1087adb05bbSStephen Boyd 		if (!device)
1097adb05bbSStephen Boyd 			return -ENOMEM;
110570d30c2SSamuel Holland 
111570d30c2SSamuel Holland 		dev_set_name(&device->dev, "coreboot%d", i);
112570d30c2SSamuel Holland 		device->dev.parent = dev;
113570d30c2SSamuel Holland 		device->dev.bus = &coreboot_bus_type;
114570d30c2SSamuel Holland 		device->dev.release = coreboot_device_release;
115a7d9b5f0SStephen Boyd 		memcpy(&device->entry, ptr_entry, entry->size);
116570d30c2SSamuel Holland 
117570d30c2SSamuel Holland 		ret = device_register(&device->dev);
118570d30c2SSamuel Holland 		if (ret) {
119570d30c2SSamuel Holland 			put_device(&device->dev);
1207adb05bbSStephen Boyd 			return ret;
121570d30c2SSamuel Holland 		}
122570d30c2SSamuel Holland 
123a7d9b5f0SStephen Boyd 		ptr_entry += entry->size;
124570d30c2SSamuel Holland 	}
125b81e3140SStephen Boyd 
1267adb05bbSStephen Boyd 	return 0;
127d384d6f4SThierry Escande }
128d384d6f4SThierry Escande 
129a28aad66SStephen Boyd static int coreboot_table_probe(struct platform_device *pdev)
130a28aad66SStephen Boyd {
131a28aad66SStephen Boyd 	resource_size_t len;
132a7d9b5f0SStephen Boyd 	struct coreboot_table_header *header;
133a28aad66SStephen Boyd 	struct resource *res;
1347adb05bbSStephen Boyd 	struct device *dev = &pdev->dev;
135a7d9b5f0SStephen Boyd 	void *ptr;
1367adb05bbSStephen Boyd 	int ret;
137a28aad66SStephen Boyd 
138a28aad66SStephen Boyd 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
139a28aad66SStephen Boyd 	if (!res)
140a28aad66SStephen Boyd 		return -EINVAL;
141a28aad66SStephen Boyd 
142a28aad66SStephen Boyd 	len = resource_size(res);
143a28aad66SStephen Boyd 	if (!res->start || !len)
144a28aad66SStephen Boyd 		return -EINVAL;
145a28aad66SStephen Boyd 
1467adb05bbSStephen Boyd 	/* Check just the header first to make sure things are sane */
147a7d9b5f0SStephen Boyd 	header = memremap(res->start, sizeof(*header), MEMREMAP_WB);
1487adb05bbSStephen Boyd 	if (!header)
149a28aad66SStephen Boyd 		return -ENOMEM;
150a28aad66SStephen Boyd 
1517adb05bbSStephen Boyd 	len = header->header_bytes + header->table_bytes;
1527adb05bbSStephen Boyd 	ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
153a7d9b5f0SStephen Boyd 	memunmap(header);
1547adb05bbSStephen Boyd 	if (ret) {
1557adb05bbSStephen Boyd 		dev_warn(dev, "coreboot table missing or corrupt!\n");
1567adb05bbSStephen Boyd 		return -ENODEV;
1577adb05bbSStephen Boyd 	}
1587adb05bbSStephen Boyd 
1597adb05bbSStephen Boyd 	ptr = memremap(res->start, len, MEMREMAP_WB);
160a28aad66SStephen Boyd 	if (!ptr)
161a28aad66SStephen Boyd 		return -ENOMEM;
162a28aad66SStephen Boyd 
1637adb05bbSStephen Boyd 	ret = bus_register(&coreboot_bus_type);
1647adb05bbSStephen Boyd 	if (!ret) {
1657adb05bbSStephen Boyd 		ret = coreboot_table_populate(dev, ptr);
1667adb05bbSStephen Boyd 		if (ret)
1677adb05bbSStephen Boyd 			bus_unregister(&coreboot_bus_type);
1687adb05bbSStephen Boyd 	}
1697adb05bbSStephen Boyd 	memunmap(ptr);
1707adb05bbSStephen Boyd 
1717adb05bbSStephen Boyd 	return ret;
172a28aad66SStephen Boyd }
173a28aad66SStephen Boyd 
174a28aad66SStephen Boyd static int coreboot_table_remove(struct platform_device *pdev)
175d384d6f4SThierry Escande {
176570d30c2SSamuel Holland 	bus_unregister(&coreboot_bus_type);
177d384d6f4SThierry Escande 	return 0;
178d384d6f4SThierry Escande }
179d384d6f4SThierry Escande 
180a28aad66SStephen Boyd #ifdef CONFIG_ACPI
181a28aad66SStephen Boyd static const struct acpi_device_id cros_coreboot_acpi_match[] = {
182a28aad66SStephen Boyd 	{ "GOOGCB00", 0 },
183a28aad66SStephen Boyd 	{ "BOOT0000", 0 },
184a28aad66SStephen Boyd 	{ }
185a28aad66SStephen Boyd };
186a28aad66SStephen Boyd MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
187a28aad66SStephen Boyd #endif
188a28aad66SStephen Boyd 
189a28aad66SStephen Boyd #ifdef CONFIG_OF
190a28aad66SStephen Boyd static const struct of_device_id coreboot_of_match[] = {
191a28aad66SStephen Boyd 	{ .compatible = "coreboot" },
192a28aad66SStephen Boyd 	{}
193a28aad66SStephen Boyd };
194a28aad66SStephen Boyd MODULE_DEVICE_TABLE(of, coreboot_of_match);
195a28aad66SStephen Boyd #endif
196a28aad66SStephen Boyd 
197a28aad66SStephen Boyd static struct platform_driver coreboot_table_driver = {
198a28aad66SStephen Boyd 	.probe = coreboot_table_probe,
199a28aad66SStephen Boyd 	.remove = coreboot_table_remove,
200a28aad66SStephen Boyd 	.driver = {
201a28aad66SStephen Boyd 		.name = "coreboot_table",
202a28aad66SStephen Boyd 		.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
203a28aad66SStephen Boyd 		.of_match_table = of_match_ptr(coreboot_of_match),
204a28aad66SStephen Boyd 	},
205a28aad66SStephen Boyd };
206a28aad66SStephen Boyd module_platform_driver(coreboot_table_driver);
207d384d6f4SThierry Escande MODULE_AUTHOR("Google, Inc.");
208d384d6f4SThierry Escande MODULE_LICENSE("GPL");
209