Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: ARM Performance Monitoring Unit Table (APMT) initial support

ARM Performance Monitoring Unit Table describes the properties of PMU
support in ARM-based system. The APMT table contains a list of nodes,
each represents a PMU in the system that conforms to ARM CoreSight PMU
architecture. The properties of each node include information required
to access the PMU (e.g. MMIO base address, interrupt number) and also
identification. For more detailed information, please refer to the
specification below:
* APMT: https://developer.arm.com/documentation/den0117/latest
* ARM Coresight PMU:
https://developer.arm.com/documentation/ihi0091/latest

The initial support adds the detection of APMT table and generic
infrastructure to create platform devices for ARM CoreSight PMUs.
Similar to IORT the root pointer of APMT is preserved during runtime
and each PMU platform device is given a pointer to the corresponding
APMT node.

Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Sudeep Holla <sudeep.holla@arm.com>
Link: https://lore.kernel.org/r/20220929002834.32664-1-bwicaksono@nvidia.com
Signed-off-by: Will Deacon <will@kernel.org>

authored by

Besar Wicaksono and committed by
Will Deacon
6251d380 f0c4d9fc

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