brintos

brintos / linux-shallow public Read only

0
0
Text · 3.9 KiB · bb010f6 Raw
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