arm: move SPI interrupt offset calculation to FDT code

Most common values for interrupt-cells are 1 or 2.
- one cell: the single cell defines the index of the interrupt
  within the controller.
- two cells: the first cell defines the index of the interrupt
  within the controller, while the second cell is specifies
  interrupt flags like active-high/active-low, edge triggered
  or level-sensitive.

ARM Generic Interrupt Controller uses 3 cells:
- the 1st cell is the interrupt type: 0 for SPI, 1 for PPI
- the 2nd cell contains the interrupt number
- the 3rd cell contains interrupt flags, similarly to the 2-cell format

SPI interrupts are numbered from 0 in the device tree but
they start from 32 on the GIC so an offset should be applied.

On the other hand, ACPI tables contain interrupt numbers
as they are expected by the GIC so no offset should be applied
when interrupts are read from ACPI.

see:
https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.txt
https://developer.arm.com/documentation/198123/0301/Arm-CoreLink-GIC-fundamentals

Change-Id: Ia41371bd965347f89c17d62e391480d7b2083bae
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5490
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
David Karoly
2022-07-25 07:02:05 +00:00
committed by Adrien Destugues
parent 1a61e82bd4
commit 6a1f97581f
4 changed files with 58 additions and 25 deletions
@@ -2,7 +2,7 @@
* Copyright 2014, Ithamar R. Adema <[email protected]>
* All rights reserved. Distributed under the terms of the MIT License.
*
* Copyright 2015-2021, Haiku, Inc. All rights reserved.
* Copyright 2015-2022, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@@ -32,6 +32,15 @@ extern "C" {
#endif
#define GIC_INTERRUPT_CELL_TYPE 0
#define GIC_INTERRUPT_CELL_ID 1
#define GIC_INTERRUPT_CELL_FLAGS 2
#define GIC_INTERRUPT_TYPE_SPI 0
#define GIC_INTERRUPT_TYPE_PPI 1
#define GIC_INTERRUPT_BASE_SPI 32
#define GIC_INTERRUPT_BASE_PPI 16
extern void* gFDT;
device_manager_info* gDeviceManager;
@@ -403,7 +412,7 @@ fdt_get_interrupt_cells(uint32 interrupt_parent_phandle)
static bool
fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
fdt_device_get_interrupt(fdt_device* dev, uint32 index,
device_node** interruptController, uint64* interrupt)
{
ASSERT(dev != NULL);
@@ -413,29 +422,39 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
dev->node, "fdt/node", &fdtNode, false) >= B_OK);
int propLen;
const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
const uint32 *prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
&propLen);
if (prop == NULL) {
uint32 interruptParent = fdt_get_interrupt_parent(dev, fdtNode);
uint32 interruptCells = fdt_get_interrupt_cells(interruptParent);
prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
prop = (uint32*)fdt_getprop(gFDT, (int)fdtNode, "interrupts",
&propLen);
if (prop == NULL)
return false;
if ((ord + 1) * interruptCells * sizeof(uint32) > (uint32)propLen)
if ((index + 1) * interruptCells * sizeof(uint32) > (uint32)propLen)
return false;
uint32 offs;
if (interruptCells == 3) {
offs = 3 * ord + 1;
uint32 offset = interruptCells * index;
uint32 interruptNumber = 0;
if ((interruptCells == 1) || (interruptCells == 2)) {
interruptNumber = fdt32_to_cpu(*(prop + offset));
} else if (interruptCells == 3) {
uint32 interruptType = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_TYPE]);
interruptNumber = fdt32_to_cpu(prop[offset + GIC_INTERRUPT_CELL_ID]);
if (interruptType == GIC_INTERRUPT_TYPE_SPI)
interruptNumber += GIC_INTERRUPT_BASE_SPI;
else if (interruptType == GIC_INTERRUPT_TYPE_PPI)
interruptNumber += GIC_INTERRUPT_BASE_PPI;
} else {
offs = interruptCells * ord;
panic("unsupported interruptCells");
}
if (interrupt != NULL)
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + offs));
*interrupt = interruptNumber;
if (interruptController != NULL && interruptParent != 0) {
fdt_bus* bus;
@@ -446,11 +465,11 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
return true;
}
if ((ord + 1) * 8 > (uint32)propLen)
if ((index + 1) * 8 > (uint32)propLen)
return false;
if (interruptController != NULL) {
uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2 * ord));
uint32 phandle = fdt32_to_cpu(*(prop + 2 * index));
fdt_bus* bus;
ASSERT(gDeviceManager->get_driver(
@@ -460,7 +479,7 @@ fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
}
if (interrupt != NULL)
*interrupt = fdt32_to_cpu(*(((uint32*)prop) + 2*ord + 1));
*interrupt = fdt32_to_cpu(*(prop + 2 * index + 1));
return true;
}
+22 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Haiku, Inc. All rights reserved.
* Copyright 2019-2022 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -40,6 +40,15 @@ extern "C" {
#include "serial.h"
#define GIC_INTERRUPT_CELL_TYPE 0
#define GIC_INTERRUPT_CELL_ID 1
#define GIC_INTERRUPT_CELL_FLAGS 2
#define GIC_INTERRUPT_TYPE_SPI 0
#define GIC_INTERRUPT_TYPE_PPI 1
#define GIC_INTERRUPT_BASE_SPI 32
#define GIC_INTERRUPT_BASE_PPI 16
#define INFO(x...) dprintf("efi/fdt: " x)
#define ERROR(x...) dprintf("efi/fdt: " x)
@@ -455,10 +464,19 @@ dtb_get_interrupt(const void* fdt, int node)
return fdt32_to_cpu(*(prop + 1));
}
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts", NULL)) {
if (interruptCells == 3) {
return fdt32_to_cpu(*(prop + 1));
} else {
if ((interruptCells == 1) || (interruptCells == 2)) {
return fdt32_to_cpu(*prop);
} else if (interruptCells == 3) {
uint32 interruptType = fdt32_to_cpu(prop[GIC_INTERRUPT_CELL_TYPE]);
uint32 interruptNumber = fdt32_to_cpu(prop[GIC_INTERRUPT_CELL_ID]);
if (interruptType == GIC_INTERRUPT_TYPE_SPI)
interruptNumber += GIC_INTERRUPT_BASE_SPI;
else if (interruptType == GIC_INTERRUPT_TYPE_PPI)
interruptNumber += GIC_INTERRUPT_BASE_PPI;
return interruptNumber;
} else {
panic("unsupported interruptCells");
}
}
dprintf("[!] no interrupt field\n");
@@ -1,5 +1,5 @@
/*
* Copyright 2021 Haiku, Inc. All rights reserved.
* Copyright 2021-2022 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <int.h>
@@ -10,8 +10,6 @@
#include "arch_int_gicv2.h"
#include "gicv2_regs.h"
#define GIC_SPI_IRQ_START 32
GICv2InterruptController::GICv2InterruptController(uint32_t gicd_addr, uint32_t gicc_addr)
: InterruptController()
@@ -56,8 +54,6 @@ GICv2InterruptController::GICv2InterruptController(uint32_t gicd_addr, uint32_t
void GICv2InterruptController::EnableInterrupt(int irq)
{
irq += GIC_SPI_IRQ_START;
uint32_t ena_reg = GICD_REG_ISENABLER + irq / 32;
uint32_t ena_val = 1 << (irq % 32);
fGicdRegs[ena_reg] = ena_val;
@@ -71,7 +67,6 @@ void GICv2InterruptController::EnableInterrupt(int irq)
void GICv2InterruptController::DisableInterrupt(int irq)
{
irq += GIC_SPI_IRQ_START;
fGicdRegs[GICD_REG_ICENABLER + irq / 32] = 1 << (irq % 32);
}
@@ -83,7 +78,7 @@ void GICv2InterruptController::HandleInterrupt()
if ((irqnr == 1022) || (irqnr == 1023)) {
dprintf("spurious interrupt\n");
} else {
int_io_interrupt_handler(irqnr-GIC_SPI_IRQ_START, true);
int_io_interrupt_handler(irqnr, true);
}
fGiccRegs[GICC_REG_EOIR] = iar;
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Haiku, Inc. All Rights Reserved.
* Copyright 2019-2022 Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <int.h>
@@ -56,6 +56,7 @@ arch_int_assign_to_cpu(int32 irq, int32 cpu)
status_t
arch_int_init(kernel_args *args)
{
reserve_io_interrupt_vectors(128, 32, INTERRUPT_TYPE_IRQ);
return B_OK;
}