16251d380SBesar Wicaksono // SPDX-License-Identifier: GPL-2.0
26251d380SBesar Wicaksono /*
36251d380SBesar Wicaksono * ARM APMT table support.
46251d380SBesar Wicaksono * Design document number: ARM DEN0117.
56251d380SBesar Wicaksono *
66251d380SBesar Wicaksono * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
76251d380SBesar Wicaksono *
86251d380SBesar Wicaksono */
96251d380SBesar Wicaksono
106251d380SBesar Wicaksono #define pr_fmt(fmt) "ACPI: APMT: " fmt
116251d380SBesar Wicaksono
126251d380SBesar Wicaksono #include <linux/acpi.h>
136251d380SBesar Wicaksono #include <linux/init.h>
146251d380SBesar Wicaksono #include <linux/kernel.h>
156251d380SBesar Wicaksono #include <linux/platform_device.h>
16fcea0ccfSSudeep Holla #include "init.h"
176251d380SBesar Wicaksono
186251d380SBesar Wicaksono #define DEV_NAME "arm-cs-arch-pmu"
196251d380SBesar Wicaksono
206251d380SBesar Wicaksono /* There can be up to 3 resources: page 0 and 1 address, and interrupt. */
216251d380SBesar Wicaksono #define DEV_MAX_RESOURCE_COUNT 3
226251d380SBesar Wicaksono
236251d380SBesar Wicaksono /* Root pointer to the mapped APMT table */
246251d380SBesar Wicaksono static struct acpi_table_header *apmt_table;
256251d380SBesar Wicaksono
apmt_init_resources(struct resource * res,struct acpi_apmt_node * node)266251d380SBesar Wicaksono static int __init apmt_init_resources(struct resource *res,
276251d380SBesar Wicaksono struct acpi_apmt_node *node)
286251d380SBesar Wicaksono {
296251d380SBesar Wicaksono int irq, trigger;
306251d380SBesar Wicaksono int num_res = 0;
316251d380SBesar Wicaksono
326251d380SBesar Wicaksono res[num_res].start = node->base_address0;
336251d380SBesar Wicaksono res[num_res].end = node->base_address0 + SZ_4K - 1;
346251d380SBesar Wicaksono res[num_res].flags = IORESOURCE_MEM;
356251d380SBesar Wicaksono
366251d380SBesar Wicaksono num_res++;
376251d380SBesar Wicaksono
38*87b3b6d5SRobin Murphy if (node->flags & ACPI_APMT_FLAGS_DUAL_PAGE) {
396251d380SBesar Wicaksono res[num_res].start = node->base_address1;
406251d380SBesar Wicaksono res[num_res].end = node->base_address1 + SZ_4K - 1;
416251d380SBesar Wicaksono res[num_res].flags = IORESOURCE_MEM;
426251d380SBesar Wicaksono
436251d380SBesar Wicaksono num_res++;
44*87b3b6d5SRobin Murphy }
456251d380SBesar Wicaksono
466251d380SBesar Wicaksono if (node->ovflw_irq != 0) {
476251d380SBesar Wicaksono trigger = (node->ovflw_irq_flags & ACPI_APMT_OVFLW_IRQ_FLAGS_MODE);
486251d380SBesar Wicaksono trigger = (trigger == ACPI_APMT_OVFLW_IRQ_FLAGS_MODE_LEVEL) ?
496251d380SBesar Wicaksono ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
506251d380SBesar Wicaksono irq = acpi_register_gsi(NULL, node->ovflw_irq, trigger,
516251d380SBesar Wicaksono ACPI_ACTIVE_HIGH);
526251d380SBesar Wicaksono
536251d380SBesar Wicaksono if (irq <= 0) {
546251d380SBesar Wicaksono pr_warn("APMT could not register gsi hwirq %d\n", irq);
556251d380SBesar Wicaksono return num_res;
566251d380SBesar Wicaksono }
576251d380SBesar Wicaksono
586251d380SBesar Wicaksono res[num_res].start = irq;
596251d380SBesar Wicaksono res[num_res].end = irq;
606251d380SBesar Wicaksono res[num_res].flags = IORESOURCE_IRQ;
616251d380SBesar Wicaksono
626251d380SBesar Wicaksono num_res++;
636251d380SBesar Wicaksono }
646251d380SBesar Wicaksono
656251d380SBesar Wicaksono return num_res;
666251d380SBesar Wicaksono }
676251d380SBesar Wicaksono
686251d380SBesar Wicaksono /**
696251d380SBesar Wicaksono * apmt_add_platform_device() - Allocate a platform device for APMT node
706251d380SBesar Wicaksono * @node: Pointer to device ACPI APMT node
7139522031SBesar Wicaksono * @fwnode: fwnode associated with the APMT node
726251d380SBesar Wicaksono *
736251d380SBesar Wicaksono * Returns: 0 on success, <0 failure
746251d380SBesar Wicaksono */
apmt_add_platform_device(struct acpi_apmt_node * node,struct fwnode_handle * fwnode)756251d380SBesar Wicaksono static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
766251d380SBesar Wicaksono struct fwnode_handle *fwnode)
776251d380SBesar Wicaksono {
786251d380SBesar Wicaksono struct platform_device *pdev;
796251d380SBesar Wicaksono int ret, count;
806251d380SBesar Wicaksono struct resource res[DEV_MAX_RESOURCE_COUNT];
816251d380SBesar Wicaksono
826251d380SBesar Wicaksono pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
836251d380SBesar Wicaksono if (!pdev)
846251d380SBesar Wicaksono return -ENOMEM;
856251d380SBesar Wicaksono
866251d380SBesar Wicaksono memset(res, 0, sizeof(res));
876251d380SBesar Wicaksono
886251d380SBesar Wicaksono count = apmt_init_resources(res, node);
896251d380SBesar Wicaksono
906251d380SBesar Wicaksono ret = platform_device_add_resources(pdev, res, count);
916251d380SBesar Wicaksono if (ret)
926251d380SBesar Wicaksono goto dev_put;
936251d380SBesar Wicaksono
946251d380SBesar Wicaksono /*
956251d380SBesar Wicaksono * Add a copy of APMT node pointer to platform_data to be used to
966251d380SBesar Wicaksono * retrieve APMT data information.
976251d380SBesar Wicaksono */
986251d380SBesar Wicaksono ret = platform_device_add_data(pdev, &node, sizeof(node));
996251d380SBesar Wicaksono if (ret)
1006251d380SBesar Wicaksono goto dev_put;
1016251d380SBesar Wicaksono
1026251d380SBesar Wicaksono pdev->dev.fwnode = fwnode;
1036251d380SBesar Wicaksono
1046251d380SBesar Wicaksono ret = platform_device_add(pdev);
1056251d380SBesar Wicaksono
1066251d380SBesar Wicaksono if (ret)
1076251d380SBesar Wicaksono goto dev_put;
1086251d380SBesar Wicaksono
1096251d380SBesar Wicaksono return 0;
1106251d380SBesar Wicaksono
1116251d380SBesar Wicaksono dev_put:
1126251d380SBesar Wicaksono platform_device_put(pdev);
1136251d380SBesar Wicaksono
1146251d380SBesar Wicaksono return ret;
1156251d380SBesar Wicaksono }
1166251d380SBesar Wicaksono
apmt_init_platform_devices(void)1176251d380SBesar Wicaksono static int __init apmt_init_platform_devices(void)
1186251d380SBesar Wicaksono {
1196251d380SBesar Wicaksono struct acpi_apmt_node *apmt_node;
1206251d380SBesar Wicaksono struct acpi_table_apmt *apmt;
1216251d380SBesar Wicaksono struct fwnode_handle *fwnode;
1226251d380SBesar Wicaksono u64 offset, end;
1236251d380SBesar Wicaksono int ret;
1246251d380SBesar Wicaksono
1256251d380SBesar Wicaksono /*
1266251d380SBesar Wicaksono * apmt_table and apmt both point to the start of APMT table, but
1276251d380SBesar Wicaksono * have different struct types
1286251d380SBesar Wicaksono */
1296251d380SBesar Wicaksono apmt = (struct acpi_table_apmt *)apmt_table;
1306251d380SBesar Wicaksono offset = sizeof(*apmt);
1316251d380SBesar Wicaksono end = apmt->header.length;
1326251d380SBesar Wicaksono
1336251d380SBesar Wicaksono while (offset < end) {
1346251d380SBesar Wicaksono apmt_node = ACPI_ADD_PTR(struct acpi_apmt_node, apmt,
1356251d380SBesar Wicaksono offset);
1366251d380SBesar Wicaksono
1376251d380SBesar Wicaksono fwnode = acpi_alloc_fwnode_static();
1386251d380SBesar Wicaksono if (!fwnode)
1396251d380SBesar Wicaksono return -ENOMEM;
1406251d380SBesar Wicaksono
1416251d380SBesar Wicaksono ret = apmt_add_platform_device(apmt_node, fwnode);
1426251d380SBesar Wicaksono if (ret) {
1436251d380SBesar Wicaksono acpi_free_fwnode_static(fwnode);
1446251d380SBesar Wicaksono return ret;
1456251d380SBesar Wicaksono }
1466251d380SBesar Wicaksono
1476251d380SBesar Wicaksono offset += apmt_node->length;
1486251d380SBesar Wicaksono }
1496251d380SBesar Wicaksono
1506251d380SBesar Wicaksono return 0;
1516251d380SBesar Wicaksono }
1526251d380SBesar Wicaksono
acpi_apmt_init(void)1536251d380SBesar Wicaksono void __init acpi_apmt_init(void)
1546251d380SBesar Wicaksono {
1556251d380SBesar Wicaksono acpi_status status;
1566251d380SBesar Wicaksono int ret;
1576251d380SBesar Wicaksono
1586251d380SBesar Wicaksono /**
1596251d380SBesar Wicaksono * APMT table nodes will be used at runtime after the apmt init,
1606251d380SBesar Wicaksono * so we don't need to call acpi_put_table() to release
1616251d380SBesar Wicaksono * the APMT table mapping.
1626251d380SBesar Wicaksono */
1636251d380SBesar Wicaksono status = acpi_get_table(ACPI_SIG_APMT, 0, &apmt_table);
1646251d380SBesar Wicaksono
1656251d380SBesar Wicaksono if (ACPI_FAILURE(status)) {
1666251d380SBesar Wicaksono if (status != AE_NOT_FOUND) {
1676251d380SBesar Wicaksono const char *msg = acpi_format_exception(status);
1686251d380SBesar Wicaksono
1696251d380SBesar Wicaksono pr_err("Failed to get APMT table, %s\n", msg);
1706251d380SBesar Wicaksono }
1716251d380SBesar Wicaksono
1726251d380SBesar Wicaksono return;
1736251d380SBesar Wicaksono }
1746251d380SBesar Wicaksono
1756251d380SBesar Wicaksono ret = apmt_init_platform_devices();
1766251d380SBesar Wicaksono if (ret) {
1776251d380SBesar Wicaksono pr_err("Failed to initialize APMT platform devices, ret: %d\n", ret);
1786251d380SBesar Wicaksono acpi_put_table(apmt_table);
1796251d380SBesar Wicaksono }
1806251d380SBesar Wicaksono }
181