diff --git a/headers/private/kernel/boot/interrupt_controller.h b/headers/private/kernel/boot/interrupt_controller.h index 61f91da4d4..71bd96352f 100644 --- a/headers/private/kernel/boot/interrupt_controller.h +++ b/headers/private/kernel/boot/interrupt_controller.h @@ -14,6 +14,7 @@ #define INTC_KIND_GICV2 "gicv2" #define INTC_KIND_OMAP3 "omap3" #define INTC_KIND_PXA "pxa" +#define INTC_KIND_SUN4I "sun4i" typedef struct { diff --git a/src/system/boot/platform/efi/arch/arm/arch_dtb.cpp b/src/system/boot/platform/efi/arch/arm/arch_dtb.cpp index fc2f1fcbfd..5dff8d61d1 100644 --- a/src/system/boot/platform/efi/arch/arm/arch_dtb.cpp +++ b/src/system/boot/platform/efi/arch/arm/arch_dtb.cpp @@ -28,6 +28,7 @@ const struct supported_interrupt_controllers { { "arm,gic-400", INTC_KIND_GICV2 }, { "ti,omap3-intc", INTC_KIND_OMAP3 }, { "marvell,pxa-intc", INTC_KIND_PXA }, + { "allwinner,sun4i-a10-ic", INTC_KIND_SUN4I }, }; diff --git a/src/system/kernel/arch/arm/Jamfile b/src/system/kernel/arch/arm/Jamfile index b16b39235b..2f1627fd20 100644 --- a/src/system/kernel/arch/arm/Jamfile +++ b/src/system/kernel/arch/arm/Jamfile @@ -42,6 +42,7 @@ KernelMergeObject kernel_arch_arm.o : soc.cpp soc_pxa.cpp soc_omap3.cpp + soc_sun4i.cpp # paging arm_physical_page_mapper_large_memory.cpp diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index e8f3d1a6bd..cb9a116ce8 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -41,6 +41,7 @@ #include "soc_pxa.h" #include "soc_omap3.h" +#include "soc_sun4i.h" #define TRACE_ARCH_INT #ifdef TRACE_ARCH_INT @@ -180,6 +181,12 @@ arch_int_init_post_vm(kernel_args *args) args->arch_args.interrupt_controller.regs1.start); if (ic == NULL) return B_NO_MEMORY; + } else if (strncmp(args->arch_args.interrupt_controller.kind, INTC_KIND_SUN4I, + sizeof(args->arch_args.interrupt_controller.kind)) == 0) { + InterruptController *ic = new(std::nothrow) Sun4iInterruptController( + args->arch_args.interrupt_controller.regs1.start); + if (ic == NULL) + return B_NO_MEMORY; } else { panic("No interrupt controllers found!\n"); } diff --git a/src/system/kernel/arch/arm/soc_sun4i.cpp b/src/system/kernel/arch/arm/soc_sun4i.cpp new file mode 100644 index 0000000000..b16f37d60b --- /dev/null +++ b/src/system/kernel/arch/arm/soc_sun4i.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2022, Adrien Destugues, pulkomandy@pulkomandy.tk + * Distributed under the terms of the MIT License. + */ + +#include + +#include "soc_sun4i.h" + + +#define SUN4I_INTC_PEND_REG0 0x10 +#define SUN4I_INTC_PEND_REG1 0x14 +#define SUN4I_INTC_PEND_REG2 0x18 +#define SUN4I_INTC_MASK_REG0 0x50 +#define SUN4I_INTC_MASK_REG1 0x54 +#define SUN4I_INTC_MASK_REG2 0x58 + + +void +Sun4iInterruptController::EnableInterrupt(int irq) +{ + if (irq <= 31) { + fRegBase[SUN4I_INTC_MASK_REG0] |= 1 << irq; + return; + } + + if (irq <= 63) { + fRegBase[SUN4I_INTC_MASK_REG1] |= 1 << (irq - 32); + return; + } + + fRegBase[SUN4I_INTC_MASK_REG2] |= 1 << (irq - 64); +} + + +void +Sun4iInterruptController::DisableInterrupt(int irq) +{ + if (irq <= 31) { + fRegBase[SUN4I_INTC_MASK_REG0] &= ~(1 << irq); + return; + } + + if (irq <= 63) { + fRegBase[SUN4I_INTC_MASK_REG1] &= ~(1 << (irq - 32)); + return; + } + + fRegBase[SUN4I_INTC_MASK_REG1] &= ~(1 << (irq - 64)); +} + + +void +Sun4iInterruptController::HandleInterrupt() +{ + // FIXME can we use the hardware managed interrupt vector instead? + for (int i=0; i < 32; i++) { + if (fRegBase[SUN4I_INTC_PEND_REG0] & (1 << i)) + int_io_interrupt_handler(i, true); + } + + for (int i=0; i < 32; i++) { + if (fRegBase[SUN4I_INTC_PEND_REG1] & (1 << i)) + int_io_interrupt_handler(i + 32, true); + } + + for (int i=0; i < 32; i++) { + if (fRegBase[SUN4I_INTC_PEND_REG2] & (1 << i)) + int_io_interrupt_handler(i + 64, true); + } +} + + +Sun4iInterruptController::Sun4iInterruptController(uint32_t reg_base) +{ + fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "intc-sun4i", (void**)&fRegBase, + B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, + reg_base, false); + if (fRegArea < 0) + panic("Sun4iInterruptController: cannot map registers!"); + + fRegBase[SUN4I_INTC_MASK_REG0] = 0; + fRegBase[SUN4I_INTC_MASK_REG1] = 0; + fRegBase[SUN4I_INTC_MASK_REG2] = 0; +} diff --git a/src/system/kernel/arch/arm/soc_sun4i.h b/src/system/kernel/arch/arm/soc_sun4i.h new file mode 100644 index 0000000000..3ad1c99643 --- /dev/null +++ b/src/system/kernel/arch/arm/soc_sun4i.h @@ -0,0 +1,26 @@ +/* + * Copyright 2022, Adrien Destugues, pulkomandy@pulkomandy.tk + * Distributed under the terms of the MIT License. + */ + +#ifndef ARCH_ARM_SOC_SUN4I_H +#define ARCH_ARM_SOC_SUN4I_H + + +#include "soc.h" + + +class Sun4iInterruptController : public InterruptController { +public: + Sun4iInterruptController(uint32_t reg_base); + void EnableInterrupt(int irq); + void DisableInterrupt(int irq); + void HandleInterrupt(); + +protected: + area_id fRegArea; + uint32 *fRegBase; +}; + + +#endif /* !SOC_SUN4I_H */