File Index
Files and directories
Sign in
FILE

irq.c

File preview with syntax highlighting.

linux-6.19.14/drivers/acpi/riscv/irq.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2023-2024, Ventana Micro Systems Inc
  4 *	Author: Sunil V L <sunilvl@ventanamicro.com>
  5 */
  6
  7#include <linux/acpi.h>
  8#include <linux/sort.h>
  9#include <linux/irq.h>
 10
 11#include "init.h"
 12
 13#define RISCV_ACPI_INTC_FLAG_PENDING BIT(0)
 14
 15struct riscv_ext_intc_list {
 16	acpi_handle		handle;
 17	u32			gsi_base;
 18	u32			nr_irqs;
 19	u32			nr_idcs;
 20	u32			id;
 21	u32			type;
 22	u32			flag;
 23	struct list_head	list;
 24};
 25
 26struct acpi_irq_dep_ctx {
 27	int		rc;
 28	unsigned int	index;
 29	acpi_handle	handle;
 30};
 31
 32LIST_HEAD(ext_intc_list);
 33
 34static int irqchip_cmp_func(const void *in0, const void *in1)
 35{
 36	struct acpi_probe_entry *elem0 = (struct acpi_probe_entry *)in0;
 37	struct acpi_probe_entry *elem1 = (struct acpi_probe_entry *)in1;
 38
 39	return (elem0->type > elem1->type) - (elem0->type < elem1->type);
 40}
 41
 42/*
 43 * On RISC-V, RINTC structures in MADT should be probed before any other
 44 * interrupt controller structures and IMSIC before APLIC. The interrupt
 45 * controller subtypes in MADT of ACPI spec for RISC-V are defined in
 46 * the incremental order like RINTC(24)->IMSIC(25)->APLIC(26)->PLIC(27).
 47 * Hence, simply sorting the subtypes in incremental order will
 48 * establish the required order.
 49 */
 50void arch_sort_irqchip_probe(struct acpi_probe_entry *ap_head, int nr)
 51{
 52	struct acpi_probe_entry *ape = ap_head;
 53
 54	if (nr == 1 || !ACPI_COMPARE_NAMESEG(ACPI_SIG_MADT, ape->id))
 55		return;
 56	sort(ape, nr, sizeof(*ape), irqchip_cmp_func, NULL);
 57}
 58
 59static acpi_status riscv_acpi_update_gsi_handle(u32 gsi_base, acpi_handle handle)
 60{
 61	struct riscv_ext_intc_list *ext_intc_element;
 62	struct list_head *i, *tmp;
 63
 64	list_for_each_safe(i, tmp, &ext_intc_list) {
 65		ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
 66		if (gsi_base == ext_intc_element->gsi_base) {
 67			ext_intc_element->handle = handle;
 68			return AE_OK;
 69		}
 70	}
 71
 72	return AE_NOT_FOUND;
 73}
 74
 75int riscv_acpi_update_gsi_range(u32 gsi_base, u32 nr_irqs)
 76{
 77	struct riscv_ext_intc_list *ext_intc_element;
 78
 79	list_for_each_entry(ext_intc_element, &ext_intc_list, list) {
 80		if (gsi_base == ext_intc_element->gsi_base &&
 81		    (ext_intc_element->flag & RISCV_ACPI_INTC_FLAG_PENDING)) {
 82			ext_intc_element->nr_irqs = nr_irqs;
 83			ext_intc_element->flag &= ~RISCV_ACPI_INTC_FLAG_PENDING;
 84			return 0;
 85		}
 86	}
 87
 88	return -ENODEV;
 89}
 90
 91int riscv_acpi_get_gsi_info(struct fwnode_handle *fwnode, u32 *gsi_base,
 92			    u32 *id, u32 *nr_irqs, u32 *nr_idcs)
 93{
 94	struct riscv_ext_intc_list *ext_intc_element;
 95	struct list_head *i;
 96
 97	list_for_each(i, &ext_intc_list) {
 98		ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
 99		if (ext_intc_element->handle == ACPI_HANDLE_FWNODE(fwnode)) {
100			*gsi_base = ext_intc_element->gsi_base;
101			*id = ext_intc_element->id;
102			*nr_irqs = ext_intc_element->nr_irqs;
103			if (nr_idcs)
104				*nr_idcs = ext_intc_element->nr_idcs;
105
106			return 0;
107		}
108	}
109
110	return -ENODEV;
111}
112
113struct fwnode_handle *riscv_acpi_get_gsi_domain_id(u32 gsi)
114{
115	struct riscv_ext_intc_list *ext_intc_element;
116	struct acpi_device *adev;
117	struct list_head *i;
118
119	list_for_each(i, &ext_intc_list) {
120		ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
121		if (gsi >= ext_intc_element->gsi_base &&
122		    gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs)) {
123			adev = acpi_fetch_acpi_dev(ext_intc_element->handle);
124			if (!adev)
125				return NULL;
126
127			return acpi_fwnode_handle(adev);
128		}
129	}
130
131	return NULL;
132}
133
134static int __init riscv_acpi_register_ext_intc(u32 gsi_base, u32 nr_irqs, u32 nr_idcs,
135					       u32 id, u32 type)
136{
137	struct riscv_ext_intc_list *ext_intc_element, *node, *prev;
138
139	ext_intc_element = kzalloc(sizeof(*ext_intc_element), GFP_KERNEL);
140	if (!ext_intc_element)
141		return -ENOMEM;
142
143	ext_intc_element->gsi_base = gsi_base;
144
145	/* If nr_irqs is zero, indicate it in flag and set to max range possible */
146	if (nr_irqs) {
147		ext_intc_element->nr_irqs = nr_irqs;
148	} else {
149		ext_intc_element->flag |= RISCV_ACPI_INTC_FLAG_PENDING;
150		ext_intc_element->nr_irqs = U32_MAX - ext_intc_element->gsi_base;
151	}
152
153	ext_intc_element->nr_idcs = nr_idcs;
154	ext_intc_element->id = id;
155	list_for_each_entry(node, &ext_intc_list, list) {
156		if (node->gsi_base < ext_intc_element->gsi_base)
157			break;
158	}
159
160	/* Adjust the previous node's GSI range if that has pending registration */
161	prev = list_prev_entry(node, list);
162	if (!list_entry_is_head(prev, &ext_intc_list, list)) {
163		if (prev->flag & RISCV_ACPI_INTC_FLAG_PENDING)
164			prev->nr_irqs = ext_intc_element->gsi_base - prev->gsi_base;
165	}
166
167	list_add_tail(&ext_intc_element->list, &node->list);
168	return 0;
169}
170
171static acpi_status __init riscv_acpi_create_gsi_map_smsi(acpi_handle handle, u32 level,
172							 void *context, void **return_value)
173{
174	acpi_status status;
175	u64 gbase;
176
177	if (!acpi_has_method(handle, "_GSB")) {
178		acpi_handle_err(handle, "_GSB method not found\n");
179		return AE_ERROR;
180	}
181
182	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
183	if (ACPI_FAILURE(status)) {
184		acpi_handle_err(handle, "failed to evaluate _GSB method\n");
185		return status;
186	}
187
188	riscv_acpi_register_ext_intc(gbase, 0, 0, 0, ACPI_RISCV_IRQCHIP_SMSI);
189	status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
190	if (ACPI_FAILURE(status)) {
191		acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
192		return status;
193	}
194
195	return AE_OK;
196}
197
198static acpi_status __init riscv_acpi_create_gsi_map(acpi_handle handle, u32 level,
199						    void *context, void **return_value)
200{
201	acpi_status status;
202	u64 gbase;
203
204	if (!acpi_has_method(handle, "_GSB")) {
205		acpi_handle_err(handle, "_GSB method not found\n");
206		return AE_ERROR;
207	}
208
209	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gbase);
210	if (ACPI_FAILURE(status)) {
211		acpi_handle_err(handle, "failed to evaluate _GSB method\n");
212		return status;
213	}
214
215	status = riscv_acpi_update_gsi_handle((u32)gbase, handle);
216	if (ACPI_FAILURE(status)) {
217		acpi_handle_err(handle, "failed to find the GSI mapping entry\n");
218		return status;
219	}
220
221	return AE_OK;
222}
223
224static int __init riscv_acpi_aplic_parse_madt(union acpi_subtable_headers *header,
225					      const unsigned long end)
226{
227	struct acpi_madt_aplic *aplic = (struct acpi_madt_aplic *)header;
228
229	return riscv_acpi_register_ext_intc(aplic->gsi_base, aplic->num_sources, aplic->num_idcs,
230					    aplic->id, ACPI_RISCV_IRQCHIP_APLIC);
231}
232
233static int __init riscv_acpi_plic_parse_madt(union acpi_subtable_headers *header,
234					     const unsigned long end)
235{
236	struct acpi_madt_plic *plic = (struct acpi_madt_plic *)header;
237
238	return riscv_acpi_register_ext_intc(plic->gsi_base, plic->num_irqs, 0,
239					    plic->id, ACPI_RISCV_IRQCHIP_PLIC);
240}
241
242void __init riscv_acpi_init_gsi_mapping(void)
243{
244	/* There can be either PLIC or APLIC */
245	if (acpi_table_parse_madt(ACPI_MADT_TYPE_PLIC, riscv_acpi_plic_parse_madt, 0) > 0) {
246		acpi_get_devices("RSCV0001", riscv_acpi_create_gsi_map, NULL, NULL);
247		return;
248	}
249
250	if (acpi_table_parse_madt(ACPI_MADT_TYPE_APLIC, riscv_acpi_aplic_parse_madt, 0) > 0)
251		acpi_get_devices("RSCV0002", riscv_acpi_create_gsi_map, NULL, NULL);
252
253	/* Unlike PLIC/APLIC, SYSMSI doesn't have MADT */
254	acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL);
255}
256
257static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)
258{
259	struct riscv_ext_intc_list *ext_intc_element;
260	struct list_head *i;
261
262	list_for_each(i, &ext_intc_list) {
263		ext_intc_element = list_entry(i, struct riscv_ext_intc_list, list);
264		if (gsi >= ext_intc_element->gsi_base &&
265		    gsi < (ext_intc_element->gsi_base + ext_intc_element->nr_irqs))
266			return ext_intc_element->handle;
267	}
268
269	return NULL;
270}
271
272static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context)
273{
274	struct acpi_irq_dep_ctx *ctx = context;
275	struct acpi_resource_irq *irq;
276	struct acpi_resource_extended_irq *eirq;
277
278	switch (ares->type) {
279	case ACPI_RESOURCE_TYPE_IRQ:
280		irq = &ares->data.irq;
281		if (ctx->index >= irq->interrupt_count) {
282			ctx->index -= irq->interrupt_count;
283			return AE_OK;
284		}
285		ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]);
286		return AE_CTRL_TERMINATE;
287	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
288		eirq = &ares->data.extended_irq;
289		if (eirq->producer_consumer == ACPI_PRODUCER)
290			return AE_OK;
291
292		if (ctx->index >= eirq->interrupt_count) {
293			ctx->index -= eirq->interrupt_count;
294			return AE_OK;
295		}
296
297		/* Support GSIs only */
298		if (eirq->resource_source.string_length)
299			return AE_OK;
300
301		ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]);
302		return AE_CTRL_TERMINATE;
303	}
304
305	return AE_OK;
306}
307
308static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle)
309{
310	struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL};
311
312	if (!gsi_handle)
313		return 0;
314
315	acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx);
316	*gsi_handle = ctx.handle;
317	if (*gsi_handle)
318		return 1;
319
320	return 0;
321}
322
323static u32 riscv_acpi_add_prt_dep(acpi_handle handle)
324{
325	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
326	struct acpi_pci_routing_table *entry;
327	struct acpi_handle_list dep_devices;
328	acpi_handle gsi_handle;
329	acpi_handle link_handle;
330	acpi_status status;
331	u32 count = 0;
332
333	status = acpi_get_irq_routing_table(handle, &buffer);
334	if (ACPI_FAILURE(status)) {
335		acpi_handle_err(handle, "failed to get IRQ routing table\n");
336		kfree(buffer.pointer);
337		return 0;
338	}
339
340	entry = buffer.pointer;
341	while (entry && (entry->length > 0)) {
342		if (entry->source[0]) {
343			acpi_get_handle(handle, entry->source, &link_handle);
344			dep_devices.count = 1;
345			dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
346			if (!dep_devices.handles) {
347				acpi_handle_err(handle, "failed to allocate memory\n");
348				continue;
349			}
350
351			dep_devices.handles[0] = link_handle;
352			count += acpi_scan_add_dep(handle, &dep_devices);
353		} else {
354			gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index);
355			dep_devices.count = 1;
356			dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
357			if (!dep_devices.handles) {
358				acpi_handle_err(handle, "failed to allocate memory\n");
359				continue;
360			}
361
362			dep_devices.handles[0] = gsi_handle;
363			count += acpi_scan_add_dep(handle, &dep_devices);
364		}
365
366		entry = (struct acpi_pci_routing_table *)
367			((unsigned long)entry + entry->length);
368	}
369
370	kfree(buffer.pointer);
371	return count;
372}
373
374static u32 riscv_acpi_add_irq_dep(acpi_handle handle)
375{
376	struct acpi_handle_list dep_devices;
377	acpi_handle gsi_handle;
378	u32 count = 0;
379	int i;
380
381	for (i = 0;
382	     riscv_acpi_irq_get_dep(handle, i, &gsi_handle);
383	     i++) {
384		dep_devices.count = 1;
385		dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
386		if (!dep_devices.handles) {
387			acpi_handle_err(handle, "failed to allocate memory\n");
388			continue;
389		}
390
391		dep_devices.handles[0] = gsi_handle;
392		count += acpi_scan_add_dep(handle, &dep_devices);
393	}
394
395	return count;
396}
397
398u32 arch_acpi_add_auto_dep(acpi_handle handle)
399{
400	if (acpi_has_method(handle, "_PRT"))
401		return riscv_acpi_add_prt_dep(handle);
402
403	return riscv_acpi_add_irq_dep(handle);
404}
~/ Read-only file preview.