181 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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: " fmt11 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 322 23/* Root pointer to the mapped APMT table */24static struct acpi_table_header *apmt_table;25 26static 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 node70 * @node: Pointer to device ACPI APMT node71 * @fwnode: fwnode associated with the APMT node72 *73 * Returns: 0 on success, <0 failure74 */75static 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;80 struct resource res[DEV_MAX_RESOURCE_COUNT];81 82 pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);83 if (!pdev)84 return -ENOMEM;85 86 memset(res, 0, sizeof(res));87 88 count = apmt_init_resources(res, node);89 90 ret = platform_device_add_resources(pdev, res, count);91 if (ret)92 goto dev_put;93 94 /*95 * Add a copy of APMT node pointer to platform_data to be used to96 * retrieve APMT data information.97 */98 ret = platform_device_add_data(pdev, &node, sizeof(node));99 if (ret)100 goto dev_put;101 102 pdev->dev.fwnode = fwnode;103 104 ret = platform_device_add(pdev);105 106 if (ret)107 goto dev_put;108 109 return 0;110 111dev_put:112 platform_device_put(pdev);113 114 return ret;115}116 117static int __init apmt_init_platform_devices(void)118{119 struct acpi_apmt_node *apmt_node;120 struct acpi_table_apmt *apmt;121 struct fwnode_handle *fwnode;122 u64 offset, end;123 int ret;124 125 /*126 * apmt_table and apmt both point to the start of APMT table, but127 * have different struct types128 */129 apmt = (struct acpi_table_apmt *)apmt_table;130 offset = sizeof(*apmt);131 end = apmt->header.length;132 133 while (offset < end) {134 apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,135 offset);136 137 fwnode = acpi_alloc_fwnode_static();138 if (!fwnode)139 return -ENOMEM;140 141 ret = apmt_add_platform_device(apmt_node, fwnode);142 if (ret) {143 acpi_free_fwnode_static(fwnode);144 return ret;145 }146 147 offset += apmt_node->length;148 }149 150 return 0;151}152 153void __init acpi_apmt_init(void)154{155 acpi_status status;156 int ret;157 158 /**159 * APMT table nodes will be used at runtime after the apmt init,160 * so we don't need to call acpi_put_table() to release161 * the APMT table mapping.162 */163 status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);164 165 if (ACPI_FAILURE(status)) {166 if (status != AE_NOT_FOUND) {167 const char *msg = acpi_format_exception(status);168 169 pr_err("Failed to get APMT table, %s\n", msg);170 }171 172 return;173 }174 175 ret = apmt_init_platform_devices();176 if (ret) {177 pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);178 acpi_put_table(apmt_table);179 }180}181