xref: /openbmc/linux/drivers/acpi/arm64/apmt.c (revision 6251d380)
1*6251d380SBesar Wicaksono // SPDX-License-Identifier: GPL-2.0
2*6251d380SBesar Wicaksono /*
3*6251d380SBesar Wicaksono  * ARM APMT table support.
4*6251d380SBesar Wicaksono  * Design document number: ARM DEN0117.
5*6251d380SBesar Wicaksono  *
6*6251d380SBesar Wicaksono  * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
7*6251d380SBesar Wicaksono  *
8*6251d380SBesar Wicaksono  */
9*6251d380SBesar Wicaksono 
10*6251d380SBesar Wicaksono #define pr_fmt(fmt)	"ACPI: APMT: " fmt
11*6251d380SBesar Wicaksono 
12*6251d380SBesar Wicaksono #include <linux/acpi.h>
13*6251d380SBesar Wicaksono #include <linux/acpi_apmt.h>
14*6251d380SBesar Wicaksono #include <linux/init.h>
15*6251d380SBesar Wicaksono #include <linux/kernel.h>
16*6251d380SBesar Wicaksono #include <linux/platform_device.h>
17*6251d380SBesar Wicaksono 
18*6251d380SBesar Wicaksono #define DEV_NAME "arm-cs-arch-pmu"
19*6251d380SBesar Wicaksono 
20*6251d380SBesar Wicaksono /* There can be up to 3 resources: page 0 and 1 address, and interrupt. */
21*6251d380SBesar Wicaksono #define DEV_MAX_RESOURCE_COUNT 3
22*6251d380SBesar Wicaksono 
23*6251d380SBesar Wicaksono /* Root pointer to the mapped APMT table */
24*6251d380SBesar Wicaksono static struct acpi_table_header *apmt_table;
25*6251d380SBesar Wicaksono 
26*6251d380SBesar Wicaksono static int __init apmt_init_resources(struct resource *res,
27*6251d380SBesar Wicaksono 					      struct acpi_apmt_node *node)
28*6251d380SBesar Wicaksono {
29*6251d380SBesar Wicaksono 	int irq, trigger;
30*6251d380SBesar Wicaksono 	int num_res = 0;
31*6251d380SBesar Wicaksono 
32*6251d380SBesar Wicaksono 	res[num_res].start = node->base_address0;
33*6251d380SBesar Wicaksono 	res[num_res].end = node->base_address0 + SZ_4K - 1;
34*6251d380SBesar Wicaksono 	res[num_res].flags = IORESOURCE_MEM;
35*6251d380SBesar Wicaksono 
36*6251d380SBesar Wicaksono 	num_res++;
37*6251d380SBesar Wicaksono 
38*6251d380SBesar Wicaksono 	res[num_res].start = node->base_address1;
39*6251d380SBesar Wicaksono 	res[num_res].end = node->base_address1 + SZ_4K - 1;
40*6251d380SBesar Wicaksono 	res[num_res].flags = IORESOURCE_MEM;
41*6251d380SBesar Wicaksono 
42*6251d380SBesar Wicaksono 	num_res++;
43*6251d380SBesar Wicaksono 
44*6251d380SBesar Wicaksono 	if (node->ovflw_irq != 0) {
45*6251d380SBesar Wicaksono 		trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE);
46*6251d380SBesar Wicaksono 		trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ?
47*6251d380SBesar Wicaksono 			ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
48*6251d380SBesar Wicaksono 		irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger,
49*6251d380SBesar Wicaksono 						ACPI_ACTIVE_HIGH);
50*6251d380SBesar Wicaksono 
51*6251d380SBesar Wicaksono 		if (irq <= 0) {
52*6251d380SBesar Wicaksono 			pr_warn("APMT could not register gsi hwirq %d\n", irq);
53*6251d380SBesar Wicaksono 			return num_res;
54*6251d380SBesar Wicaksono 		}
55*6251d380SBesar Wicaksono 
56*6251d380SBesar Wicaksono 		res[num_res].start = irq;
57*6251d380SBesar Wicaksono 		res[num_res].end = irq;
58*6251d380SBesar Wicaksono 		res[num_res].flags = IORESOURCE_IRQ;
59*6251d380SBesar Wicaksono 
60*6251d380SBesar Wicaksono 		num_res++;
61*6251d380SBesar Wicaksono 	}
62*6251d380SBesar Wicaksono 
63*6251d380SBesar Wicaksono 	return num_res;
64*6251d380SBesar Wicaksono }
65*6251d380SBesar Wicaksono 
66*6251d380SBesar Wicaksono /**
67*6251d380SBesar Wicaksono  * apmt_add_platform_device() - Allocate a platform device for APMT node
68*6251d380SBesar Wicaksono  * @node: Pointer to device ACPI APMT node
69*6251d380SBesar Wicaksono  *
70*6251d380SBesar Wicaksono  * Returns: 0 on success, <0 failure
71*6251d380SBesar Wicaksono  */
72*6251d380SBesar Wicaksono static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
73*6251d380SBesar Wicaksono 							struct fwnode_handle *fwnode)
74*6251d380SBesar Wicaksono {
75*6251d380SBesar Wicaksono 	struct platform_device *pdev;
76*6251d380SBesar Wicaksono 	int ret, count;
77*6251d380SBesar Wicaksono 	struct resource res[DEV_MAX_RESOURCE_COUNT];
78*6251d380SBesar Wicaksono 
79*6251d380SBesar Wicaksono 	pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
80*6251d380SBesar Wicaksono 	if (!pdev)
81*6251d380SBesar Wicaksono 		return -ENOMEM;
82*6251d380SBesar Wicaksono 
83*6251d380SBesar Wicaksono 	memset(res, 0, sizeof(res));
84*6251d380SBesar Wicaksono 
85*6251d380SBesar Wicaksono 	count = apmt_init_resources(res, node);
86*6251d380SBesar Wicaksono 
87*6251d380SBesar Wicaksono 	ret = platform_device_add_resources(pdev, res, count);
88*6251d380SBesar Wicaksono 	if (ret)
89*6251d380SBesar Wicaksono 		goto dev_put;
90*6251d380SBesar Wicaksono 
91*6251d380SBesar Wicaksono 	/*
92*6251d380SBesar Wicaksono 	 * Add a copy of APMT node pointer to platform_data to be used to
93*6251d380SBesar Wicaksono 	 * retrieve APMT data information.
94*6251d380SBesar Wicaksono 	 */
95*6251d380SBesar Wicaksono 	ret = platform_device_add_data(pdev, &node, sizeof(node));
96*6251d380SBesar Wicaksono 	if (ret)
97*6251d380SBesar Wicaksono 		goto dev_put;
98*6251d380SBesar Wicaksono 
99*6251d380SBesar Wicaksono 	pdev->dev.fwnode = fwnode;
100*6251d380SBesar Wicaksono 
101*6251d380SBesar Wicaksono 	ret = platform_device_add(pdev);
102*6251d380SBesar Wicaksono 
103*6251d380SBesar Wicaksono 	if (ret)
104*6251d380SBesar Wicaksono 		goto dev_put;
105*6251d380SBesar Wicaksono 
106*6251d380SBesar Wicaksono 	return 0;
107*6251d380SBesar Wicaksono 
108*6251d380SBesar Wicaksono dev_put:
109*6251d380SBesar Wicaksono 	platform_device_put(pdev);
110*6251d380SBesar Wicaksono 
111*6251d380SBesar Wicaksono 	return ret;
112*6251d380SBesar Wicaksono }
113*6251d380SBesar Wicaksono 
114*6251d380SBesar Wicaksono static int __init apmt_init_platform_devices(void)
115*6251d380SBesar Wicaksono {
116*6251d380SBesar Wicaksono 	struct acpi_apmt_node *apmt_node;
117*6251d380SBesar Wicaksono 	struct acpi_table_apmt *apmt;
118*6251d380SBesar Wicaksono 	struct fwnode_handle *fwnode;
119*6251d380SBesar Wicaksono 	u64 offset, end;
120*6251d380SBesar Wicaksono 	int ret;
121*6251d380SBesar Wicaksono 
122*6251d380SBesar Wicaksono 	/*
123*6251d380SBesar Wicaksono 	 * apmt_table and apmt both point to the start of APMT table, but
124*6251d380SBesar Wicaksono 	 * have different struct types
125*6251d380SBesar Wicaksono 	 */
126*6251d380SBesar Wicaksono 	apmt = (struct acpi_table_apmt *)apmt_table;
127*6251d380SBesar Wicaksono 	offset = sizeof(*apmt);
128*6251d380SBesar Wicaksono 	end = apmt->header.length;
129*6251d380SBesar Wicaksono 
130*6251d380SBesar Wicaksono 	while (offset < end) {
131*6251d380SBesar Wicaksono 		apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,
132*6251d380SBesar Wicaksono 				 offset);
133*6251d380SBesar Wicaksono 
134*6251d380SBesar Wicaksono 		fwnode = acpi_alloc_fwnode_static();
135*6251d380SBesar Wicaksono 		if (!fwnode)
136*6251d380SBesar Wicaksono 			return -ENOMEM;
137*6251d380SBesar Wicaksono 
138*6251d380SBesar Wicaksono 		ret = apmt_add_platform_device(apmt_node, fwnode);
139*6251d380SBesar Wicaksono 		if (ret) {
140*6251d380SBesar Wicaksono 			acpi_free_fwnode_static(fwnode);
141*6251d380SBesar Wicaksono 			return ret;
142*6251d380SBesar Wicaksono 		}
143*6251d380SBesar Wicaksono 
144*6251d380SBesar Wicaksono 		offset += apmt_node->length;
145*6251d380SBesar Wicaksono 	}
146*6251d380SBesar Wicaksono 
147*6251d380SBesar Wicaksono 	return 0;
148*6251d380SBesar Wicaksono }
149*6251d380SBesar Wicaksono 
150*6251d380SBesar Wicaksono void __init acpi_apmt_init(void)
151*6251d380SBesar Wicaksono {
152*6251d380SBesar Wicaksono 	acpi_status status;
153*6251d380SBesar Wicaksono 	int ret;
154*6251d380SBesar Wicaksono 
155*6251d380SBesar Wicaksono 	/**
156*6251d380SBesar Wicaksono 	 * APMT table nodes will be used at runtime after the apmt init,
157*6251d380SBesar Wicaksono 	 * so we don't need to call acpi_put_table() to release
158*6251d380SBesar Wicaksono 	 * the APMT table mapping.
159*6251d380SBesar Wicaksono 	 */
160*6251d380SBesar Wicaksono 	status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);
161*6251d380SBesar Wicaksono 
162*6251d380SBesar Wicaksono 	if (ACPI_FAILURE(status)) {
163*6251d380SBesar Wicaksono 		if (status != AE_NOT_FOUND) {
164*6251d380SBesar Wicaksono 			const char *msg = acpi_format_exception(status);
165*6251d380SBesar Wicaksono 
166*6251d380SBesar Wicaksono 			pr_err("Failed to get APMT table, %s\n", msg);
167*6251d380SBesar Wicaksono 		}
168*6251d380SBesar Wicaksono 
169*6251d380SBesar Wicaksono 		return;
170*6251d380SBesar Wicaksono 	}
171*6251d380SBesar Wicaksono 
172*6251d380SBesar Wicaksono 	ret = apmt_init_platform_devices();
173*6251d380SBesar Wicaksono 	if (ret) {
174*6251d380SBesar Wicaksono 		pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);
175*6251d380SBesar Wicaksono 		acpi_put_table(apmt_table);
176*6251d380SBesar Wicaksono 	}
177*6251d380SBesar Wicaksono }
178