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