Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * coreboot_table.c
  4 *
  5 * Module providing coreboot table access.
  6 *
  7 * Copyright 2017 Google Inc.
  8 * Copyright 2017 Samuel Holland <samuel@sholland.org>
  9 */
 10
 11#include <linux/acpi.h>
 12#include <linux/device.h>
 13#include <linux/err.h>
 14#include <linux/init.h>
 15#include <linux/io.h>
 16#include <linux/kernel.h>
 17#include <linux/module.h>
 18#include <linux/of.h>
 19#include <linux/platform_device.h>
 20#include <linux/slab.h>
 21
 22#include "coreboot_table.h"
 23
 24#define CB_DEV(d) container_of(d, struct coreboot_device, dev)
 25#define CB_DRV(d) container_of(d, struct coreboot_driver, drv)
 26
 27static int coreboot_bus_match(struct device *dev, struct device_driver *drv)
 28{
 29	struct coreboot_device *device = CB_DEV(dev);
 30	struct coreboot_driver *driver = CB_DRV(drv);
 31
 32	return device->entry.tag == driver->tag;
 33}
 34
 35static int coreboot_bus_probe(struct device *dev)
 36{
 37	int ret = -ENODEV;
 38	struct coreboot_device *device = CB_DEV(dev);
 39	struct coreboot_driver *driver = CB_DRV(dev->driver);
 40
 41	if (driver->probe)
 42		ret = driver->probe(device);
 43
 44	return ret;
 45}
 46
 47static int coreboot_bus_remove(struct device *dev)
 48{
 49	int ret = 0;
 50	struct coreboot_device *device = CB_DEV(dev);
 51	struct coreboot_driver *driver = CB_DRV(dev->driver);
 52
 53	if (driver->remove)
 54		ret = driver->remove(device);
 55
 56	return ret;
 57}
 58
 59static struct bus_type coreboot_bus_type = {
 60	.name		= "coreboot",
 61	.match		= coreboot_bus_match,
 62	.probe		= coreboot_bus_probe,
 63	.remove		= coreboot_bus_remove,
 64};
 65
 66static void coreboot_device_release(struct device *dev)
 67{
 68	struct coreboot_device *device = CB_DEV(dev);
 69
 70	kfree(device);
 71}
 72
 73int coreboot_driver_register(struct coreboot_driver *driver)
 74{
 75	driver->drv.bus = &coreboot_bus_type;
 76
 77	return driver_register(&driver->drv);
 78}
 79EXPORT_SYMBOL(coreboot_driver_register);
 80
 81void coreboot_driver_unregister(struct coreboot_driver *driver)
 82{
 83	driver_unregister(&driver->drv);
 84}
 85EXPORT_SYMBOL(coreboot_driver_unregister);
 86
 87static int coreboot_table_populate(struct device *dev, void *ptr)
 88{
 89	int i, ret;
 90	void *ptr_entry;
 91	struct coreboot_device *device;
 92	struct coreboot_table_entry *entry;
 93	struct coreboot_table_header *header = ptr;
 94
 95	ptr_entry = ptr + header->header_bytes;
 96	for (i = 0; i < header->table_entries; i++) {
 97		entry = ptr_entry;
 98
 99		device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
100		if (!device)
101			return -ENOMEM;
102
103		dev_set_name(&device->dev, "coreboot%d", i);
104		device->dev.parent = dev;
105		device->dev.bus = &coreboot_bus_type;
106		device->dev.release = coreboot_device_release;
107		memcpy(&device->entry, ptr_entry, entry->size);
108
109		ret = device_register(&device->dev);
110		if (ret) {
111			put_device(&device->dev);
112			return ret;
113		}
114
115		ptr_entry += entry->size;
116	}
117
118	return 0;
119}
120
121static int coreboot_table_probe(struct platform_device *pdev)
122{
123	resource_size_t len;
124	struct coreboot_table_header *header;
125	struct resource *res;
126	struct device *dev = &pdev->dev;
127	void *ptr;
128	int ret;
129
130	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
131	if (!res)
132		return -EINVAL;
133
134	len = resource_size(res);
135	if (!res->start || !len)
136		return -EINVAL;
137
138	/* Check just the header first to make sure things are sane */
139	header = memremap(res->start, sizeof(*header), MEMREMAP_WB);
140	if (!header)
141		return -ENOMEM;
142
143	len = header->header_bytes + header->table_bytes;
144	ret = strncmp(header->signature, "LBIO", sizeof(header->signature));
145	memunmap(header);
146	if (ret) {
147		dev_warn(dev, "coreboot table missing or corrupt!\n");
148		return -ENODEV;
149	}
150
151	ptr = memremap(res->start, len, MEMREMAP_WB);
152	if (!ptr)
153		return -ENOMEM;
154
155	ret = bus_register(&coreboot_bus_type);
156	if (!ret) {
157		ret = coreboot_table_populate(dev, ptr);
158		if (ret)
159			bus_unregister(&coreboot_bus_type);
160	}
161	memunmap(ptr);
162
163	return ret;
164}
165
166static int __cb_dev_unregister(struct device *dev, void *dummy)
167{
168	device_unregister(dev);
169	return 0;
170}
171
172static int coreboot_table_remove(struct platform_device *pdev)
173{
174	bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister);
175	bus_unregister(&coreboot_bus_type);
176	return 0;
177}
178
179#ifdef CONFIG_ACPI
180static const struct acpi_device_id cros_coreboot_acpi_match[] = {
181	{ "GOOGCB00", 0 },
182	{ "BOOT0000", 0 },
183	{ }
184};
185MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match);
186#endif
187
188#ifdef CONFIG_OF
189static const struct of_device_id coreboot_of_match[] = {
190	{ .compatible = "coreboot" },
191	{}
192};
193MODULE_DEVICE_TABLE(of, coreboot_of_match);
194#endif
195
196static struct platform_driver coreboot_table_driver = {
197	.probe = coreboot_table_probe,
198	.remove = coreboot_table_remove,
199	.driver = {
200		.name = "coreboot_table",
201		.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
202		.of_match_table = of_match_ptr(coreboot_of_match),
203	},
204};
205module_platform_driver(coreboot_table_driver);
206MODULE_AUTHOR("Google, Inc.");
207MODULE_LICENSE("GPL");