diff options
Diffstat (limited to 'drivers/acpi/csrt.c')
| -rw-r--r-- | drivers/acpi/csrt.c | 159 | 
1 files changed, 159 insertions, 0 deletions
| diff --git a/drivers/acpi/csrt.c b/drivers/acpi/csrt.c new file mode 100644 index 000000000000..5c15a91faf0b --- /dev/null +++ b/drivers/acpi/csrt.c @@ -0,0 +1,159 @@ +/* + * Support for Core System Resources Table (CSRT) + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg <[email protected]> + *	    Andy Shevchenko <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) "ACPI: CSRT: " fmt + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> + +ACPI_MODULE_NAME("CSRT"); + +static int __init acpi_csrt_parse_shared_info(struct platform_device *pdev, +					      const struct acpi_csrt_group *grp) +{ +	const struct acpi_csrt_shared_info *si; +	struct resource res[3]; +	size_t nres; +	int ret; + +	memset(res, 0, sizeof(res)); +	nres = 0; + +	si = (const struct acpi_csrt_shared_info *)&grp[1]; +	/* +	 * The peripherals that are listed on CSRT typically support only +	 * 32-bit addresses so we only use the low part of MMIO base for +	 * now. +	 */ +	if (!si->mmio_base_high && si->mmio_base_low) { +		/* +		 * There is no size of the memory resource in shared_info +		 * so we assume that it is 4k here. +		 */ +		res[nres].start = si->mmio_base_low; +		res[nres].end = res[0].start + SZ_4K - 1; +		res[nres++].flags = IORESOURCE_MEM; +	} + +	if (si->gsi_interrupt) { +		int irq = acpi_register_gsi(NULL, si->gsi_interrupt, +					    si->interrupt_mode, +					    si->interrupt_polarity); +		res[nres].start = irq; +		res[nres].end = irq; +		res[nres++].flags = IORESOURCE_IRQ; +	} + +	if (si->base_request_line || si->num_handshake_signals) { +		/* +		 * We pass the driver a DMA resource describing the range +		 * of request lines the device supports. +		 */ +		res[nres].start = si->base_request_line; +		res[nres].end = res[nres].start + si->num_handshake_signals - 1; +		res[nres++].flags = IORESOURCE_DMA; +	} + +	ret = platform_device_add_resources(pdev, res, nres); +	if (ret) { +		if (si->gsi_interrupt) +			acpi_unregister_gsi(si->gsi_interrupt); +		return ret; +	} + +	return 0; +} + +static int __init +acpi_csrt_parse_resource_group(const struct acpi_csrt_group *grp) +{ +	struct platform_device *pdev; +	char vendor[5], name[16]; +	int ret, i; + +	vendor[0] = grp->vendor_id; +	vendor[1] = grp->vendor_id >> 8; +	vendor[2] = grp->vendor_id >> 16; +	vendor[3] = grp->vendor_id >> 24; +	vendor[4] = '\0'; + +	if (grp->shared_info_length != sizeof(struct acpi_csrt_shared_info)) +		return -ENODEV; + +	snprintf(name, sizeof(name), "%s%04X", vendor, grp->device_id); +	pdev = platform_device_alloc(name, PLATFORM_DEVID_AUTO); +	if (!pdev) +		return -ENOMEM; + +	/* Add resources based on the shared info */ +	ret = acpi_csrt_parse_shared_info(pdev, grp); +	if (ret) +		goto fail; + +	ret = platform_device_add(pdev); +	if (ret) +		goto fail; + +	for (i = 0; i < pdev->num_resources; i++) +		dev_dbg(&pdev->dev, "%pR\n", &pdev->resource[i]); + +	return 0; + +fail: +	platform_device_put(pdev); +	return ret; +} + +/* + * CSRT or Core System Resources Table is a proprietary ACPI table + * introduced by Microsoft. This table can contain devices that are not in + * the system DSDT table. In particular DMA controllers might be described + * here. + * + * We present these devices as normal platform devices that don't have ACPI + * IDs or handle. The platform device name will be something like + * <VENDOR><DEVID>.<n>.auto for example: INTL9C06.0.auto. + */ +void __init acpi_csrt_init(void) +{ +	struct acpi_csrt_group *grp, *end; +	struct acpi_table_csrt *csrt; +	acpi_status status; +	int ret; + +	status = acpi_get_table(ACPI_SIG_CSRT, 0, +				(struct acpi_table_header **)&csrt); +	if (ACPI_FAILURE(status)) { +		if (status != AE_NOT_FOUND) +			pr_warn("failed to get the CSRT table\n"); +		return; +	} + +	pr_debug("parsing CSRT table for devices\n"); + +	grp = (struct acpi_csrt_group *)(csrt + 1); +	end = (struct acpi_csrt_group *)((void *)csrt + csrt->header.length); + +	while (grp < end) { +		ret = acpi_csrt_parse_resource_group(grp); +		if (ret) { +			pr_warn("error in parsing resource group: %d\n", ret); +			return; +		} + +		grp = (struct acpi_csrt_group *)((void *)grp + grp->length); +	} +} |