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/init.h> 14 #include <linux/kernel.h> 15 #include <linux/platform_device.h> 16 #include "init.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 if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) { 39 res[num_res].start = node->base_address1; 40 res[num_res].end = node->base_address1 + SZ_4K - 1; 41 res[num_res].flags = IORESOURCE_MEM; 42 43 num_res++; 44 } 45 46 if (node->ovflw_irq != 0) { 47 trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE); 48 trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ? 49 ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 50 irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger, 51 ACPI_ACTIVE_HIGH); 52 53 if (irq <= 0) { 54 pr_warn("APMT could not register gsi hwirq %d\n", irq); 55 return num_res; 56 } 57 58 res[num_res].start = irq; 59 res[num_res].end = irq; 60 res[num_res].flags = IORESOURCE_IRQ; 61 62 num_res++; 63 } 64 65 return num_res; 66 } 67 68 /** 69 * apmt_add_platform_device() - Allocate a platform device for APMT node 70 * @node: Pointer to device ACPI APMT node 71 * @fwnode: fwnode associated with the APMT node 72 * 73 * Returns: 0 on success, <0 failure 74 */ 75 static int __init apmt_add_platform_device(struct acpi_apmt_node *node, 76 struct fwnode_handle *fwnode) 77 { 78 struct platform_device *pdev; 79 int ret, count, uid = node->id & INT_MAX; 80 struct resource res[DEV_MAX_RESOURCE_COUNT]; 81 82 if (uid != node->id) 83 pr_warn("Unexpectedly large UID 0x%x, truncated to 0x%x\n", node->id, uid); 84 pdev = platform_device_alloc(DEV_NAME, uid); 85 if (!pdev) 86 return -ENOMEM; 87 88 memset(res, 0, sizeof(res)); 89 90 count = apmt_init_resources(res, node); 91 92 ret = platform_device_add_resources(pdev, res, count); 93 if (ret) 94 goto dev_put; 95 96 /* 97 * Add a copy of APMT node pointer to platform_data to be used to 98 * retrieve APMT data information. 99 */ 100 ret = platform_device_add_data(pdev, &node, sizeof(node)); 101 if (ret) 102 goto dev_put; 103 104 platform_device_set_fwnode(pdev, fwnode); 105 106 ret = platform_device_add(pdev); 107 108 if (ret) 109 goto dev_put; 110 111 return 0; 112 113 dev_put: 114 platform_device_put(pdev); 115 116 return ret; 117 } 118 119 static int __init apmt_init_platform_devices(void) 120 { 121 struct acpi_apmt_node *apmt_node; 122 struct acpi_table_apmt *apmt; 123 struct fwnode_handle *fwnode; 124 u64 offset, end; 125 int ret; 126 127 /* 128 * apmt_table and apmt both point to the start of APMT table, but 129 * have different struct types 130 */ 131 apmt = (struct acpi_table_apmt *)apmt_table; 132 offset = sizeof(*apmt); 133 end = apmt->header.length; 134 135 while (offset < end) { 136 apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt, 137 offset); 138 139 fwnode = acpi_alloc_fwnode_static(); 140 if (!fwnode) 141 return -ENOMEM; 142 143 ret = apmt_add_platform_device(apmt_node, fwnode); 144 if (ret) { 145 acpi_free_fwnode_static(fwnode); 146 return ret; 147 } 148 149 offset += apmt_node->length; 150 } 151 152 return 0; 153 } 154 155 void __init acpi_apmt_init(void) 156 { 157 acpi_status status; 158 int ret; 159 160 /** 161 * APMT table nodes will be used at runtime after the apmt init, 162 * so we don't need to call acpi_put_table() to release 163 * the APMT table mapping. 164 */ 165 status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table); 166 167 if (ACPI_FAILURE(status)) { 168 if (status != AE_NOT_FOUND) { 169 const char *msg = acpi_format_exception(status); 170 171 pr_err("Failed to get APMT table, %s\n", msg); 172 } 173 174 return; 175 } 176 177 ret = apmt_init_platform_devices(); 178 if (ret) { 179 pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret); 180 acpi_put_table(apmt_table); 181 } 182 } 183