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