162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARM APMT table support. 462306a36Sopenharmony_ci * Design document number: ARM DEN0117. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "ACPI: APMT: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include "init.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DEV_NAME "arm-cs-arch-pmu" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* There can be up to 3 resources: page 0 and 1 address, and interrupt. */ 2162306a36Sopenharmony_ci#define DEV_MAX_RESOURCE_COUNT 3 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Root pointer to the mapped APMT table */ 2462306a36Sopenharmony_cistatic struct acpi_table_header *apmt_table; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int __init apmt_init_resources(struct resource *res, 2762306a36Sopenharmony_ci struct acpi_apmt_node *node) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int irq, trigger; 3062306a36Sopenharmony_ci int num_res = 0; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci res[num_res].start = node->base_address0; 3362306a36Sopenharmony_ci res[num_res].end = node->base_address0 + SZ_4K - 1; 3462306a36Sopenharmony_ci res[num_res].flags = IORESOURCE_MEM; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci num_res++; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) { 3962306a36Sopenharmony_ci res[num_res].start = node->base_address1; 4062306a36Sopenharmony_ci res[num_res].end = node->base_address1 + SZ_4K - 1; 4162306a36Sopenharmony_ci res[num_res].flags = IORESOURCE_MEM; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci num_res++; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (node->ovflw_irq != 0) { 4762306a36Sopenharmony_ci trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE); 4862306a36Sopenharmony_ci trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ? 4962306a36Sopenharmony_ci ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; 5062306a36Sopenharmony_ci irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger, 5162306a36Sopenharmony_ci ACPI_ACTIVE_HIGH); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (irq <= 0) { 5462306a36Sopenharmony_ci pr_warn("APMT could not register gsi hwirq %d\n", irq); 5562306a36Sopenharmony_ci return num_res; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci res[num_res].start = irq; 5962306a36Sopenharmony_ci res[num_res].end = irq; 6062306a36Sopenharmony_ci res[num_res].flags = IORESOURCE_IRQ; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci num_res++; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return num_res; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/** 6962306a36Sopenharmony_ci * apmt_add_platform_device() - Allocate a platform device for APMT node 7062306a36Sopenharmony_ci * @node: Pointer to device ACPI APMT node 7162306a36Sopenharmony_ci * @fwnode: fwnode associated with the APMT node 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Returns: 0 on success, <0 failure 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic int __init apmt_add_platform_device(struct acpi_apmt_node *node, 7662306a36Sopenharmony_ci struct fwnode_handle *fwnode) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct platform_device *pdev; 7962306a36Sopenharmony_ci int ret, count; 8062306a36Sopenharmony_ci struct resource res[DEV_MAX_RESOURCE_COUNT]; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO); 8362306a36Sopenharmony_ci if (!pdev) 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci memset(res, 0, sizeof(res)); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci count = apmt_init_resources(res, node); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = platform_device_add_resources(pdev, res, count); 9162306a36Sopenharmony_ci if (ret) 9262306a36Sopenharmony_ci goto dev_put; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Add a copy of APMT node pointer to platform_data to be used to 9662306a36Sopenharmony_ci * retrieve APMT data information. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci ret = platform_device_add_data(pdev, &node, sizeof(node)); 9962306a36Sopenharmony_ci if (ret) 10062306a36Sopenharmony_ci goto dev_put; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci pdev->dev.fwnode = fwnode; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = platform_device_add(pdev); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (ret) 10762306a36Sopenharmony_ci goto dev_put; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cidev_put: 11262306a36Sopenharmony_ci platform_device_put(pdev); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return ret; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int __init apmt_init_platform_devices(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct acpi_apmt_node *apmt_node; 12062306a36Sopenharmony_ci struct acpi_table_apmt *apmt; 12162306a36Sopenharmony_ci struct fwnode_handle *fwnode; 12262306a36Sopenharmony_ci u64 offset, end; 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * apmt_table and apmt both point to the start of APMT table, but 12762306a36Sopenharmony_ci * have different struct types 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci apmt = (struct acpi_table_apmt *)apmt_table; 13062306a36Sopenharmony_ci offset = sizeof(*apmt); 13162306a36Sopenharmony_ci end = apmt->header.length; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci while (offset < end) { 13462306a36Sopenharmony_ci apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt, 13562306a36Sopenharmony_ci offset); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci fwnode = acpi_alloc_fwnode_static(); 13862306a36Sopenharmony_ci if (!fwnode) 13962306a36Sopenharmony_ci return -ENOMEM; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = apmt_add_platform_device(apmt_node, fwnode); 14262306a36Sopenharmony_ci if (ret) { 14362306a36Sopenharmony_ci acpi_free_fwnode_static(fwnode); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci offset += apmt_node->length; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_civoid __init acpi_apmt_init(void) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci acpi_status status; 15662306a36Sopenharmony_ci int ret; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /** 15962306a36Sopenharmony_ci * APMT table nodes will be used at runtime after the apmt init, 16062306a36Sopenharmony_ci * so we don't need to call acpi_put_table() to release 16162306a36Sopenharmony_ci * the APMT table mapping. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 16662306a36Sopenharmony_ci if (status != AE_NOT_FOUND) { 16762306a36Sopenharmony_ci const char *msg = acpi_format_exception(status); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci pr_err("Failed to get APMT table, %s\n", msg); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ret = apmt_init_platform_devices(); 17662306a36Sopenharmony_ci if (ret) { 17762306a36Sopenharmony_ci pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret); 17862306a36Sopenharmony_ci acpi_put_table(apmt_table); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 181