xref: /openbmc/linux/drivers/acpi/arm64/apmt.c (revision 87b3b6d5)
16251d380SBesar Wicaksono // SPDX-License-Identifier: GPL-2.0
26251d380SBesar Wicaksono /*
36251d380SBesar Wicaksono  * ARM APMT table support.
46251d380SBesar Wicaksono  * Design document number: ARM DEN0117.
56251d380SBesar Wicaksono  *
66251d380SBesar Wicaksono  * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
76251d380SBesar Wicaksono  *
86251d380SBesar Wicaksono  */
96251d380SBesar Wicaksono 
106251d380SBesar Wicaksono #define pr_fmt(fmt)	"ACPI: APMT: " fmt
116251d380SBesar Wicaksono 
126251d380SBesar Wicaksono #include <linux/acpi.h>
136251d380SBesar Wicaksono #include <linux/init.h>
146251d380SBesar Wicaksono #include <linux/kernel.h>
156251d380SBesar Wicaksono #include <linux/platform_device.h>
166251d380SBesar Wicaksono #include "init.h"
176251d380SBesar Wicaksono 
186251d380SBesar Wicaksono #define DEV_NAME "arm-cs-arch-pmu"
196251d380SBesar Wicaksono 
206251d380SBesar Wicaksono /* There can be up to 3 resources: page 0 and 1 address, and interrupt. */
216251d380SBesar Wicaksono #define DEV_MAX_RESOURCE_COUNT 3
226251d380SBesar Wicaksono 
236251d380SBesar Wicaksono /* Root pointer to the mapped APMT table */
246251d380SBesar Wicaksono static struct acpi_table_header *apmt_table;
256251d380SBesar Wicaksono 
apmt_init_resources(struct resource * res,struct acpi_apmt_node * node)266251d380SBesar Wicaksono static int __init apmt_init_resources(struct resource *res,
276251d380SBesar Wicaksono 				      struct acpi_apmt_node *node)
286251d380SBesar Wicaksono {
296251d380SBesar Wicaksono 	int irq, trigger;
306251d380SBesar Wicaksono 	int num_res = 0;
316251d380SBesar Wicaksono 
326251d380SBesar Wicaksono 	res[num_res].start = node->base_address0;
336251d380SBesar Wicaksono 	res[num_res].end = node->base_address0 + SZ_4K - 1;
346251d380SBesar Wicaksono 	res[num_res].flags = IORESOURCE_MEM;
356251d380SBesar Wicaksono 
366251d380SBesar Wicaksono 	num_res++;
376251d380SBesar Wicaksono 
38*87b3b6d5SRobin Murphy 	if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) {
396251d380SBesar Wicaksono 		res[num_res].start = node->base_address1;
406251d380SBesar Wicaksono 		res[num_res].end = node->base_address1 + SZ_4K - 1;
416251d380SBesar Wicaksono 		res[num_res].flags = IORESOURCE_MEM;
426251d380SBesar Wicaksono 
436251d380SBesar Wicaksono 		num_res++;
44*87b3b6d5SRobin Murphy 	}
456251d380SBesar Wicaksono 
466251d380SBesar Wicaksono 	if (node->ovflw_irq != 0) {
476251d380SBesar Wicaksono 		trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE);
486251d380SBesar Wicaksono 		trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ?
496251d380SBesar Wicaksono 			ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
506251d380SBesar Wicaksono 		irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger,
516251d380SBesar Wicaksono 						ACPI_ACTIVE_HIGH);
526251d380SBesar Wicaksono 
536251d380SBesar Wicaksono 		if (irq <= 0) {
546251d380SBesar Wicaksono 			pr_warn("APMT could not register gsi hwirq %d\n", irq);
556251d380SBesar Wicaksono 			return num_res;
566251d380SBesar Wicaksono 		}
576251d380SBesar Wicaksono 
586251d380SBesar Wicaksono 		res[num_res].start = irq;
596251d380SBesar Wicaksono 		res[num_res].end = irq;
606251d380SBesar Wicaksono 		res[num_res].flags = IORESOURCE_IRQ;
616251d380SBesar Wicaksono 
626251d380SBesar Wicaksono 		num_res++;
636251d380SBesar Wicaksono 	}
646251d380SBesar Wicaksono 
656251d380SBesar Wicaksono 	return num_res;
666251d380SBesar Wicaksono }
676251d380SBesar Wicaksono 
686251d380SBesar Wicaksono /**
696251d380SBesar Wicaksono  * apmt_add_platform_device() - Allocate a platform device for APMT node
706251d380SBesar Wicaksono  * @node: Pointer to device ACPI APMT node
7139522031SBesar Wicaksono  * @fwnode: fwnode associated with the APMT node
726251d380SBesar Wicaksono  *
736251d380SBesar Wicaksono  * Returns: 0 on success, <0 failure
746251d380SBesar Wicaksono  */
apmt_add_platform_device(struct acpi_apmt_node * node,struct fwnode_handle * fwnode)756251d380SBesar Wicaksono static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
766251d380SBesar Wicaksono 					   struct fwnode_handle *fwnode)
776251d380SBesar Wicaksono {
786251d380SBesar Wicaksono 	struct platform_device *pdev;
796251d380SBesar Wicaksono 	int ret, count;
806251d380SBesar Wicaksono 	struct resource res[DEV_MAX_RESOURCE_COUNT];
816251d380SBesar Wicaksono 
826251d380SBesar Wicaksono 	pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
836251d380SBesar Wicaksono 	if (!pdev)
846251d380SBesar Wicaksono 		return -ENOMEM;
856251d380SBesar Wicaksono 
866251d380SBesar Wicaksono 	memset(res, 0, sizeof(res));
876251d380SBesar Wicaksono 
886251d380SBesar Wicaksono 	count = apmt_init_resources(res, node);
896251d380SBesar Wicaksono 
906251d380SBesar Wicaksono 	ret = platform_device_add_resources(pdev, res, count);
916251d380SBesar Wicaksono 	if (ret)
926251d380SBesar Wicaksono 		goto dev_put;
936251d380SBesar Wicaksono 
946251d380SBesar Wicaksono 	/*
956251d380SBesar Wicaksono 	 * Add a copy of APMT node pointer to platform_data to be used to
966251d380SBesar Wicaksono 	 * retrieve APMT data information.
976251d380SBesar Wicaksono 	 */
986251d380SBesar Wicaksono 	ret = platform_device_add_data(pdev, &node, sizeof(node));
996251d380SBesar Wicaksono 	if (ret)
1006251d380SBesar Wicaksono 		goto dev_put;
1016251d380SBesar Wicaksono 
1026251d380SBesar Wicaksono 	pdev->dev.fwnode = fwnode;
1036251d380SBesar Wicaksono 
1046251d380SBesar Wicaksono 	ret = platform_device_add(pdev);
1056251d380SBesar Wicaksono 
1066251d380SBesar Wicaksono 	if (ret)
1076251d380SBesar Wicaksono 		goto dev_put;
1086251d380SBesar Wicaksono 
1096251d380SBesar Wicaksono 	return 0;
1106251d380SBesar Wicaksono 
1116251d380SBesar Wicaksono dev_put:
1126251d380SBesar Wicaksono 	platform_device_put(pdev);
1136251d380SBesar Wicaksono 
1146251d380SBesar Wicaksono 	return ret;
1156251d380SBesar Wicaksono }
1166251d380SBesar Wicaksono 
apmt_init_platform_devices(void)1176251d380SBesar Wicaksono static int __init apmt_init_platform_devices(void)
1186251d380SBesar Wicaksono {
1196251d380SBesar Wicaksono 	struct acpi_apmt_node *apmt_node;
1206251d380SBesar Wicaksono 	struct acpi_table_apmt *apmt;
1216251d380SBesar Wicaksono 	struct fwnode_handle *fwnode;
1226251d380SBesar Wicaksono 	u64 offset, end;
1236251d380SBesar Wicaksono 	int ret;
1246251d380SBesar Wicaksono 
1256251d380SBesar Wicaksono 	/*
1266251d380SBesar Wicaksono 	 * apmt_table and apmt both point to the start of APMT table, but
1276251d380SBesar Wicaksono 	 * have different struct types
1286251d380SBesar Wicaksono 	 */
1296251d380SBesar Wicaksono 	apmt = (struct acpi_table_apmt *)apmt_table;
1306251d380SBesar Wicaksono 	offset = sizeof(*apmt);
1316251d380SBesar Wicaksono 	end = apmt->header.length;
1326251d380SBesar Wicaksono 
1336251d380SBesar Wicaksono 	while (offset < end) {
1346251d380SBesar Wicaksono 		apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,
1356251d380SBesar Wicaksono 				 offset);
1366251d380SBesar Wicaksono 
1376251d380SBesar Wicaksono 		fwnode = acpi_alloc_fwnode_static();
1386251d380SBesar Wicaksono 		if (!fwnode)
1396251d380SBesar Wicaksono 			return -ENOMEM;
1406251d380SBesar Wicaksono 
1416251d380SBesar Wicaksono 		ret = apmt_add_platform_device(apmt_node, fwnode);
1426251d380SBesar Wicaksono 		if (ret) {
1436251d380SBesar Wicaksono 			acpi_free_fwnode_static(fwnode);
1446251d380SBesar Wicaksono 			return ret;
1456251d380SBesar Wicaksono 		}
1466251d380SBesar Wicaksono 
1476251d380SBesar Wicaksono 		offset += apmt_node->length;
1486251d380SBesar Wicaksono 	}
1496251d380SBesar Wicaksono 
1506251d380SBesar Wicaksono 	return 0;
1516251d380SBesar Wicaksono }
1526251d380SBesar Wicaksono 
acpi_apmt_init(void)1536251d380SBesar Wicaksono void __init acpi_apmt_init(void)
1546251d380SBesar Wicaksono {
1556251d380SBesar Wicaksono 	acpi_status status;
1566251d380SBesar Wicaksono 	int ret;
1576251d380SBesar Wicaksono 
1586251d380SBesar Wicaksono 	/**
1596251d380SBesar Wicaksono 	 * APMT table nodes will be used at runtime after the apmt init,
1606251d380SBesar Wicaksono 	 * so we don't need to call acpi_put_table() to release
1616251d380SBesar Wicaksono 	 * the APMT table mapping.
1626251d380SBesar Wicaksono 	 */
1636251d380SBesar Wicaksono 	status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);
1646251d380SBesar Wicaksono 
1656251d380SBesar Wicaksono 	if (ACPI_FAILURE(status)) {
1666251d380SBesar Wicaksono 		if (status != AE_NOT_FOUND) {
1676251d380SBesar Wicaksono 			const char *msg = acpi_format_exception(status);
1686251d380SBesar Wicaksono 
1696251d380SBesar Wicaksono 			pr_err("Failed to get APMT table, %s\n", msg);
1706251d380SBesar Wicaksono 		}
1716251d380SBesar Wicaksono 
1726251d380SBesar Wicaksono 		return;
1736251d380SBesar Wicaksono 	}
1746251d380SBesar Wicaksono 
1756251d380SBesar Wicaksono 	ret = apmt_init_platform_devices();
1766251d380SBesar Wicaksono 	if (ret) {
1776251d380SBesar Wicaksono 		pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);
1786251d380SBesar Wicaksono 		acpi_put_table(apmt_table);
1796251d380SBesar Wicaksono 	}
1806251d380SBesar Wicaksono }
181