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