diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index cfd2a8bf60741c2614e7c67c1fb54f618e3ebecb..67a1ad792931a8457697a3242becb1eeaa8d60de 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -20,7 +20,7 @@ config CPU_CORTEX config ARM_CUSTOM_INTERRUPT_CONTROLLER bool - depends on !CPU_CORTEX_M + depends on !CPU_CORTEX_M || SOC_BCM2837 help This option indicates that the ARM CPU is connected to a custom (i.e. non-GIC) interrupt controller. diff --git a/arch/arm/core/aarch64/Kconfig b/arch/arm/core/aarch64/Kconfig index 3127c5061aa557661bfbad33bc57895c4274834e..e5e03c495011c6186e637fb76d3cbc9d085a2f18 100644 --- a/arch/arm/core/aarch64/Kconfig +++ b/arch/arm/core/aarch64/Kconfig @@ -10,7 +10,6 @@ config CPU_CORTEX_A select USE_SWITCH select USE_SWITCH_SUPPORTED select HAS_ARM_SMCCC - select SCHED_IPI_SUPPORTED if SMP help This option signifies the use of a CPU of the Cortex-A family. diff --git a/arch/arm/core/aarch64/smp.c b/arch/arm/core/aarch64/smp.c index 14c4f5014dff9233f1fb7b4e276d2c9b4b476181..8f1e055be4a908bda7a5230db92f9a4919e2e53d 100644 --- a/arch/arm/core/aarch64/smp.c +++ b/arch/arm/core/aarch64/smp.c @@ -38,6 +38,18 @@ volatile struct { */ volatile _cpu_t *_curr_cpu[CONFIG_MP_NUM_CPUS]; +#ifdef CONFIG_SOC_BCM2837 +static ALWAYS_INLINE void __SEV(void) +{ + __asm__ volatile ("sev" : : : "memory"); +} + +static void sys_write64(uint64_t val, uintptr_t addr) { + sys_write32(val, addr); + sys_write32(val >> 32, addr + sizeof(uint32_t)); +} +#endif + extern void __start(void); /* Called from Zephyr initialization */ void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz, @@ -55,9 +67,15 @@ void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz, arch_dcache_range((void *)&arm64_cpu_init[cpu_num], sizeof(arm64_cpu_init[cpu_num]), K_CACHE_WB_INVD); +#ifdef CONFIG_SOC_BCM2837 + sys_write64((uint64_t) &__start, 0xd8 + 0x8 * cpu_num); + __DSB(); + __SEV(); +#else /* TODO: get mpidr from device tree, using cpu_num */ if (pm_cpu_on(cpu_num, (uint64_t)&__start)) printk("Failed to boot CPU%d\n", cpu_num); +#endif /* Wait secondary cores up, see z_arm64_secondary_start */ while (arm64_cpu_init[cpu_num].fn) { @@ -74,9 +92,11 @@ void z_arm64_secondary_start(void) z_arm64_mmu_init(); #ifdef CONFIG_SMP +#ifndef CONFIG_SOC_BCM2837 arm_gic_secondary_init(); irq_enable(SGI_SCHED_IPI); +#endif #endif fn = arm64_cpu_init[cpu_num].fn; @@ -94,6 +114,7 @@ void z_arm64_secondary_start(void) } #ifdef CONFIG_SMP +#ifndef CONFIG_SOC_BCM2837 void sched_ipi_handler(const void *unused) { ARG_UNUSED(unused); @@ -131,3 +152,4 @@ static int arm64_smp_init(const struct device *dev) } SYS_INIT(arm64_smp_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); #endif +#endif diff --git a/boards/arm/raspi3/Kconfig.board b/boards/arm/raspi3/Kconfig.board new file mode 100644 index 0000000000000000000000000000000000000000..e15f9d03c8e6534a1fd40372045f56b014720f89 --- /dev/null +++ b/boards/arm/raspi3/Kconfig.board @@ -0,0 +1,8 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_RASPI3 + bool "Raspberry Pi 3 Model B+ and QEMU Machine raspi3" + depends on SOC_BCM2837 + select ARM64 + select QEMU_TARGET diff --git a/boards/arm/raspi3/Kconfig.defconfig b/boards/arm/raspi3/Kconfig.defconfig new file mode 100644 index 0000000000000000000000000000000000000000..90c9ba689cc6b72b69d6fddd4d1e43c810730c8b --- /dev/null +++ b/boards/arm/raspi3/Kconfig.defconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_RASPI3 + +config BUILD_OUTPUT_BIN + default y + +config BOARD + default "raspi3" + +endif # BOARD_RASPI3 diff --git a/boards/arm/raspi3/board.cmake b/boards/arm/raspi3/board.cmake new file mode 100644 index 0000000000000000000000000000000000000000..68c4452cab15ce0bf8655332bc91c5e0e5b8b434 --- /dev/null +++ b/boards/arm/raspi3/board.cmake @@ -0,0 +1,17 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +set(EMU_PLATFORM qemu) +set(QEMU_ARCH aarch64) +set(QEMU_KERNEL_OPTION "-kernel;${ZEPHYR_BINARY_DIR}/zephyr.bin") + +# https://github.com/s-matyukevich/raspberry-pi-os/issues/8#issuecomment-411024746 +set(QEMU_FLAGS_${ARCH} + -M raspi3 + -smp 4 + -nographic + -no-reboot + # To see Mini UART on qemu's stdout (default is PL011): + # -serial null -serial chardev:con -monitor none + ) +board_set_debugger_ifnset(qemu) diff --git a/boards/arm/raspi3/raspi3.dts b/boards/arm/raspi3/raspi3.dts new file mode 100644 index 0000000000000000000000000000000000000000..b812a1224b8f321604513bb4d2e372210bfef8c9 --- /dev/null +++ b/boards/arm/raspi3/raspi3.dts @@ -0,0 +1,106 @@ +/* +* Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +* +* SPDX-License-Identifier: Apache-2.0 +* +*/ + +/dts-v1/; + +#include <mem.h> +#include <arm/armv8-a.dtsi> + +/ { + interrupt-parent = <&intc>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <1>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <2>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <3>; + }; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&local_intc>; + /* TODO: Is the intc nested into the local_intc? Maybe we can tell zephyr to generate the unique interrupt number for us? */ + interrupts = <72 0 0>, <73 0 0>, <75 0 0>, <74 0 0>; /* CNTPSIRQ, CNTPNSIRQ, CNTVIRQ, CNTHPIRQ */ + label = "arch_timer"; + }; + + uartclk: apb-pclk { + compatible = "fixed-clock"; + /* BUG: This is not used, instead the parameters are hardcored in uart_pl011. */ + clock-frequency = <14000000>; + #clock-cells = <0>; + }; + + soc { + /* https://github.com/s-matyukevich/raspberry-pi-os/blob/master/docs/lesson03/rpi-os.md#configuring-interrupt-controller */ + intc: interrupt-controller@3f00b200 { + compatible = "brcm,bcm2835-armctrl-ic"; + reg = <0x3f00b200 0x200>; + interrupt-controller; + #interrupt-cells = <3>; + label = "intc"; + }; + + local_intc: local_intc@40000000 { + compatible = "brcm,bcm2835-l1-intc"; + reg = <0x40000000 0x100>; + interrupt-controller; + #interrupt-cells = <3>; + label = "l1-intc"; + }; + + uart0: uart@3f201000 { + compatible = "arm,pl011"; + /* #clock-cells = <1>; */ + reg = <0x3f201000 0x1000>; + status = "disabled"; + interrupts = <57 0 0>; + interrupt-names = "irq_0"; + clocks = <&uartclk>; + label = "UART_0"; + }; + }; +}; + +/ { + model = "Raspberry Pi 3 Model B+"; + compatible = "qemu,arm-cortex-a53"; + + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + /* TODO: why has this no effect? falls back to size 0xffffffffffff6000 */ + /* zephyr,sram = &sram; */ + }; +}; + +&uart0 { + status = "okay"; + current-speed = <115200>; +}; diff --git a/boards/arm/raspi3/raspi3.yaml b/boards/arm/raspi3/raspi3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..eda12d153090a5bb29d72649753339a81971f22c --- /dev/null +++ b/boards/arm/raspi3/raspi3.yaml @@ -0,0 +1,14 @@ +identifier: raspi3 +name: Raspberry Pi 3 +type: qemu +simulation: qemu +arch: arm +toolchain: + - zephyr + - cross-compile +ram: 256 +testing: + default: true + ignore_tags: + - net + - bluetooth diff --git a/boards/arm/raspi3/raspi3_defconfig b/boards/arm/raspi3/raspi3_defconfig new file mode 100644 index 0000000000000000000000000000000000000000..1a265e0e90cd093967f593b2212eb3362ca830c7 --- /dev/null +++ b/boards/arm/raspi3/raspi3_defconfig @@ -0,0 +1,47 @@ +CONFIG_SOC_BCM2837=y +CONFIG_BOARD_RASPI3=y +CONFIG_XIP=n +CONFIG_QEMU_ICOUNT=n + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable serial port +CONFIG_UART_PL011=y +CONFIG_UART_PL011_PORT0=y +CONFIG_UART_INTERRUPT_DRIVEN=n + +CONFIG_SMP=y +CONFIG_MP_NUM_CPUS=4 +CONFIG_CACHE_MANAGEMENT=y +CONFIG_TIMEOUT_64BIT=y +CONFIG_ARMV8_A_NS=y + +CONFIG_AARCH64_IMAGE_HEADER=y + +# Memory offset to which the kerenl image is loaded by the bootloader. +# If this is wrong, references to static data in the kernel image are off. +# https://wiki.osdev.org/Raspberry_Pi_Bare_Bones#Pi_3.2C_4 +CONFIG_SRAM_BASE_ADDRESS=0x200000 +CONFIG_KERNEL_VM_BASE=0x200000 + +# In KiB. +CONFIG_SRAM_SIZE=256 + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y + +CONFIG_ARM_ARCH_TIMER=y + +CONFIG_DEBUG=y +CONFIG_ASSERT=y +CONFIG_ASSERT_VERBOSE=y +CONFIG_STACK_SENTINEL=y +CONFIG_INIT_STACKS=y + +CONFIG_TICKLESS_KERNEL=n +CONFIG_LOG_PRINTK=n diff --git a/drivers/serial/uart_pl011.c b/drivers/serial/uart_pl011.c index a5eb3757cd86cedbc69f49335ad16d2d051210c2..11578b35b71e37d7e668737d8a39b4438b82974b 100644 --- a/drivers/serial/uart_pl011.c +++ b/drivers/serial/uart_pl011.c @@ -185,8 +185,13 @@ static int pl011_set_baudrate(const struct device *dev, return -EINVAL; } +#ifdef CONFIG_BOARD_RASPI3 + PL011_REGS(dev)->ibrd = 26; + PL011_REGS(dev)->fbrd = 3; +#else PL011_REGS(dev)->ibrd = bauddiv >> PL011_FBRD_WIDTH; PL011_REGS(dev)->fbrd = bauddiv & ((1u << PL011_FBRD_WIDTH) - 1u); +#endif __DMB(); @@ -356,11 +361,51 @@ static const struct uart_driver_api pl011_driver_api = { #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ }; +#ifdef CONFIG_BOARD_RASPI3 +#define PBASE 0x3F000000 + +#define GPFSEL1 (PBASE + 0x00200004) +#define GPSET0 (PBASE + 0x0020001C) +#define GPCLR0 (PBASE + 0x00200028) +#define GPPUD (PBASE + 0x00200094) +#define GPPUDCLK0 (PBASE + 0x00200098) + +static void write32_sys(uintptr_t addr, uint32_t val) { + sys_write32(val, addr); +} + +#define GPIO_ALT0 4 // UART0/PL011 +#define GPIO_ALT5 2 // UART1/Mini/AUX + +static void raspi3_gpio_enable_pl011(void) +{ + unsigned int selector; // 32 bits + + selector = sys_read32(GPFSEL1); + selector &= ~(7 << 12); + selector |= GPIO_ALT0 << 12; // Set GPIO14 + selector &= ~(7 << 15); + selector |= GPIO_ALT0 << 15; // Set GPIO15 + write32_sys(GPFSEL1, selector); + + // Set GPIO pull-up/down. Common to Mini UART (AUX) and PL011. + write32_sys(GPPUD, 0); + k_busy_wait(150); /* 150 cycles ~= 150us */ + write32_sys(GPPUDCLK0, (1 << 14) | (1 << 15)); + k_busy_wait(150); + write32_sys(GPPUDCLK0, 0); +} +#endif + static int pl011_init(const struct device *dev) { int ret; uint32_t lcrh; +#ifdef CONFIG_BOARD_RASPI3 + raspi3_gpio_enable_pl011(); +#endif + /* * If working in SBSA mode, we assume that UART is already configured, * or does not require configuration at all (if UART is emulated by diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 7e6c75bdde145703f95c317ce42fd5c1c6d82d20..7b9dade22c1fd9f832921424c0bf6ae7a4e1f1c9 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -81,7 +81,6 @@ config ARCV2_TIMER_IRQ_PRIORITY config ARM_ARCH_TIMER bool "ARM architected timer" - depends on GIC select ARCH_HAS_CUSTOM_BUSY_WAIT select TICKLESS_CAPABLE help diff --git a/dts/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.yaml b/dts/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.yaml new file mode 100644 index 0000000000000000000000000000000000000000..93386a80ec19df90323fc7331560862041686e9f --- /dev/null +++ b/dts/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Marvell +# SPDX-License-Identifier: Apache-2.0 + +description: Interrupt Controller + +compatible: "brcm,bcm2835-armctrl-ic" + +include: base.yaml + +properties: + reg: + required: true + + label: + required: true + +# GIC-compatible to allow reuse of arm,arm-timer. flags and priority are always 0. +interrupt-cells: + - irq + - flags + - priority diff --git a/dts/bindings/interrupt-controller/brcm,bcm2835-l1-intc.yaml b/dts/bindings/interrupt-controller/brcm,bcm2835-l1-intc.yaml new file mode 100644 index 0000000000000000000000000000000000000000..917b981413662d8f274342a93db585749a91da9c --- /dev/null +++ b/dts/bindings/interrupt-controller/brcm,bcm2835-l1-intc.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Marvell +# SPDX-License-Identifier: Apache-2.0 + +description: Interrupt Controller + +compatible: "brcm,bcm2835-l1-intc" + +include: base.yaml + +properties: + reg: + required: true + + label: + required: true + +# GIC-compatible to allow reuse of arm,arm-timer. flags and priority are always 0. +interrupt-cells: + - irq + - flags + - priority diff --git a/dts/bindings/serial/arm,raspi3.yaml b/dts/bindings/serial/arm,raspi3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f1731792e479dd1d6b3bd2ee85d4712bb947ee9a --- /dev/null +++ b/dts/bindings/serial/arm,raspi3.yaml @@ -0,0 +1,9 @@ +description: raspi3 UART + +compatible: "arm,raspi3" + +include: uart-controller.yaml + +properties: + reg: + required: true diff --git a/soc/arm/bcm2837/CMakeLists.txt b/soc/arm/bcm2837/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..584f43dab059861d6e234012098733b0ab5b5e0a --- /dev/null +++ b/soc/arm/bcm2837/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_sources_ifdef(CONFIG_ARM_MMU mmu_regions.c) + +zephyr_sources_ifdef(CONFIG_SOC_BCM2837 plat_core.S) +zephyr_sources_ifdef(CONFIG_SOC_BCM2837 intc_bcm2837.c) diff --git a/soc/arm/bcm2837/Kconfig.defconfig b/soc/arm/bcm2837/Kconfig.defconfig new file mode 100644 index 0000000000000000000000000000000000000000..801c488d498e2e6eceb0e170116e171dcc54017c --- /dev/null +++ b/soc/arm/bcm2837/Kconfig.defconfig @@ -0,0 +1,30 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +if SOC_BCM2837 + +config SOC + default "bcm2837" + +config NUM_IRQS + # must be >= the highest interrupt number used + # - include the UART interrupts + # via BCM2837-PM Page 113 + default 128 + +config SYS_CLOCK_HW_CYCLES_PER_SEC + default 62500000 + +# Workaround for not being able to have commas in macro arguments +DT_CHOSEN_Z_FLASH := zephyr,flash + +config FLASH_SIZE + default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_FLASH),0,K) + +config FLASH_BASE_ADDRESS + default $(dt_chosen_reg_addr_hex,$(DT_CHOSEN_Z_FLASH)) + +config ARM_CUSTOM_INTERRUPT_CONTROLLER + default y + +endif # SOC_BCM2837 diff --git a/soc/arm/bcm2837/Kconfig.soc b/soc/arm/bcm2837/Kconfig.soc new file mode 100644 index 0000000000000000000000000000000000000000..a5a92e4766464bbca765441363e1613ae10f7789 --- /dev/null +++ b/soc/arm/bcm2837/Kconfig.soc @@ -0,0 +1,7 @@ +# Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> +# SPDX-License-Identifier: Apache-2.0 + +config SOC_BCM2837 + bool "BCM2837 and BCM2837B0 SoC, QEMU virt platform (raspi3)" + select ARM + select CPU_CORTEX_A53 diff --git a/soc/arm/bcm2837/README.md b/soc/arm/bcm2837/README.md new file mode 100644 index 0000000000000000000000000000000000000000..135a4cdd1ef6f801d2d9d9deb94eef94787886b8 --- /dev/null +++ b/soc/arm/bcm2837/README.md @@ -0,0 +1,9 @@ +# BCM2837 + +SoC of the Raspberry Pi +- 2 Mod. B v1.2, +- 3 Mod. A+ (assumed to be qemu's `-M raspi3`) +- 3 Mod. B +- 3 Mod. B+. + +Peripheral Manual (BCM2837-PM): https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf diff --git a/soc/arm/bcm2837/intc_bcm2837.c b/soc/arm/bcm2837/intc_bcm2837.c new file mode 100644 index 0000000000000000000000000000000000000000..1746574b0de0fb8f86c4f48497d7cf755c49bc7f --- /dev/null +++ b/soc/arm/bcm2837/intc_bcm2837.c @@ -0,0 +1,207 @@ +/* + * Copyright 2020 Broadcom + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <sys/__assert.h> +#include <arch/arm/aarch64/irq.h> + +/* TODO: Get these from device tree. */ +#define PBASE 0x3f000000 +#define BCM2836_ARMCTRL_BASE (PBASE+0x0000B200) + +/* Definitions copied from linux's irq-bcm2835.c: */ + +/* Put the bank and irq (32 bits) into the hwirq */ +#define MAKE_HWIRQ(b, n) ((b << 5) | (n)) +#define HWIRQ_BANK(i) (i >> 5) +#define HWIRQ_BIT(i) BIT(i & 0x1f) + +#define NR_IRQS_BANK0 8 +#define BANK0_HWIRQ_MASK 0xff +/* Shortcuts can't be disabled so any unknown new ones need to be masked */ +#define SHORTCUT1_MASK 0x00007c00 +#define SHORTCUT2_MASK 0x001f8000 +#define SHORTCUT_SHIFT 10 +#define BANK1_HWIRQ BIT(8) +#define BANK2_HWIRQ BIT(9) +#define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \ + | SHORTCUT1_MASK | SHORTCUT2_MASK) + +#define REG_FIQ_CONTROL 0x0c +#define FIQ_CONTROL_ENABLE BIT(7) + +#define NR_BANKS 3 +#define IRQS_PER_BANK 32 + +static const int armctrl_reg_pending[] = { 0x04, 0x08, 0x00 }; +static const int armctrl_reg_enable[] = { 0x10, 0x14, 0x18 }; +static const int armctrl_reg_disable[] = { 0x1c, 0x20, 0x24 }; +static const int armctrl_bank_bits[] = { 32, 32, 8 }; + +/* Copied from https://github.com/xinu-os/xinu/blob/master/system/platforms/arm-rpi/dispatch.c: */ + +/* Number of IRQs shared between the GPU and ARM. These correspond to the IRQs + * that show up in the IRQ_pending_1 and IRQ_pending_2 registers. */ +#define ARMCTRL_NUM_GPU_SHARED_IRQS 64 + +/* Number of ARM-specific IRQs. These correspond to IRQs that show up in the + * first 8 bits of IRQ_basic_pending. */ +#define ARMCTRL_NUM_ARM_SPECIFIC_IRQS 8 + +#define ARMCTRL_NUM_IRQS (ARMCTRL_NUM_GPU_SHARED_IRQS + ARMCTRL_NUM_ARM_SPECIFIC_IRQS) +#define L1_NUM_IRQS (32) +#define NUM_IRQS (ARMCTRL_NUM_IRQS + L1_NUM_IRQS) + +static bool irq_enabled[NUM_IRQS]; + +static void armctrl_irq_to_bank(unsigned int irq, int *bank, int *bank_bit) { + __ASSERT_NO_MSG(irq < ARMCTRL_NUM_IRQS); + *bank = 0; + *bank_bit = irq; + while (*bank_bit >= armctrl_bank_bits[*bank]) { + *bank_bit -= armctrl_bank_bits[*bank]; + *bank += 1; + + __ASSERT_NO_MSG(*bank <= 2); + } + __ASSERT_NO_MSG(0 <= *bank); + __ASSERT_NO_MSG(*bank <= 2); + __ASSERT_NO_MSG(0 <= *bank_bit); + __ASSERT_NO_MSG(*bank_bit < 32); +} + +static void armctrl_irq_enable(unsigned int irq) { + int bank, bank_bit; + armctrl_irq_to_bank(irq, &bank, &bank_bit); + uintptr_t addr = BCM2836_ARMCTRL_BASE + armctrl_reg_enable[bank]; + + printk("armctrl_irq_enable irq=%d, bank=%d, bit=%d, addr=%p\n", irq, bank, bank_bit, (char *) addr); + sys_write32((1 << bank_bit), addr); +} + +static void armctrl_irq_disable(unsigned int irq) { + int bank, bank_bit; + armctrl_irq_to_bank(irq, &bank, &bank_bit); + uintptr_t addr = BCM2836_ARMCTRL_BASE + armctrl_reg_disable[bank]; + + printk("armctrl_irq_enable irq=%d, bank=%d, bit=%d, addr=%p\n", irq, bank, bank_bit, (char *) addr); + sys_write32((1 << bank_bit), addr); +} + +/* BCM2836 ARM-local peripherals manual, section 4.10 "Core interrupt sources" */ +/* QEMU Device Implementation: https://github.com/qemu/qemu/blob/918c81a53eb18ec4e9979876a39e86610cc565f4/hw/intc/bcm2836_control.c */ + +static void l1_irq_enable(unsigned int l1_irq) { + __ASSERT_NO_MSG(l1_irq < L1_NUM_IRQS); + + if (0 <= l1_irq && l1_irq <= 3) { + /* Core timer interrupts: CNT_PS_IRQ = 0, CNT_PNS_IRQ = 1, CNT_HP_IRQ = 2, CNT_V_IRQ = 3 */ + int cpuid = arch_curr_cpu()->id; + intptr_t core_timer_interrupt_control_addr = 0x40000040 + cpuid * 4; + uint32_t val = sys_read32(core_timer_interrupt_control_addr); + val |= (1 << l1_irq); + sys_write32(val, core_timer_interrupt_control_addr); + } else { + printk("Warning: l1_irq_enable(%d) not implemented.\n", l1_irq); + } +} + +static void l1_irq_disable(unsigned int l1_irq) { + __ASSERT_NO_MSG(l1_irq < L1_NUM_IRQS); + printk("Warning: l1_irq_disable(%d) not implemented.\n", l1_irq); +} + +void z_soc_irq_init(void) { + /* Don't print anything here as C might not be initialized yet. */ +} + +int z_soc_irq_is_enabled(unsigned int irq) { + /* Manual does not explicitly state that reading from armctrl_reg_enable + * indicates if an IRQ was enabled, also Xinu does not do it like + * this. That's why we have irq_enabled. */ + __ASSERT_NO_MSG(irq < ARMCTRL_NUM_IRQS); + return irq_enabled[irq]; +} + +void z_soc_irq_enable(unsigned int irq) { + if (irq < ARMCTRL_NUM_IRQS) { + armctrl_irq_enable(irq); + irq_enabled[irq] = true; + } else { + l1_irq_enable(irq - ARMCTRL_NUM_IRQS); + } +} + +void z_soc_irq_disable(unsigned int irq) { + if (irq < ARMCTRL_NUM_IRQS) { + armctrl_irq_disable(irq); + irq_enabled[irq] = false; + } else { + l1_irq_disable(irq - ARMCTRL_NUM_IRQS); + } +} + +void z_soc_irq_priority_set(unsigned int irq, unsigned int prio, unsigned int flags) { + /* Not supported. */ + + /* Peripheral Manual: There is no priority for any interrupt. If one + * interrupt is much more important then all others it canbe routed to + * the FIQ. */ + return; +} + +/* Called from HW IRQ context with interrupts disabled: Retrieve the currently + * active IRQ that should be handled. + * + * Resources: + * - https://embedded-xinu.readthedocs.io/en/latest/arm/rpi/BCM2835-Interrupt-Controller.html + */ +unsigned int z_soc_irq_get_active(void) { + int cpuid = arch_curr_cpu()->id; + + /* printk("%s on cpu%d, stack at ~%p\n", __func__, cpuid, &cpuid); */ + + for (unsigned int irq = 0; irq < ARMCTRL_NUM_IRQS; irq++) { + int bank, bank_bit; + armctrl_irq_to_bank(irq, &bank, &bank_bit); + + unsigned int bank_pending = sys_read32(BCM2836_ARMCTRL_BASE + armctrl_reg_pending[bank]); + bool is_pending = (bank_pending & (1 << bank_bit)) != 0; + + if (is_pending) { + /* Clear pending to allow us to detect when this IRQ occurs again (nested irq). */ + sys_write32(bank_pending & ~(1 << bank_bit), BCM2836_ARMCTRL_BASE + armctrl_reg_pending[bank]); + return irq; + } + } + + intptr_t core_interrupt_sources_addr = 0x40000060 + cpuid * 4; + uint32_t pending = sys_read32(core_interrupt_sources_addr); + for (unsigned int l1_irq = 0; l1_irq < L1_NUM_IRQS; l1_irq++) { + unsigned int irq = ARMCTRL_NUM_IRQS + l1_irq; + if (pending & (1 << l1_irq)) { + /* https://developer.arm.com/architectures/learn-the-architecture/generic-timer/single-page */ + if (l1_irq == 3) { + arm_arch_timer_set_irq_mask(true); + } + return irq; + } + } + + __ASSERT_NO_MSG(false); + CODE_UNREACHABLE; +} + +/* Mark irq as inactive (called in HW IRQ context after handler). */ +void z_soc_irq_eoi(unsigned int irq) { + if (irq < ARMCTRL_NUM_IRQS) { + printk("Warning: %s(%d)\n", __func__, irq); + } else { + unsigned int l1_irq = irq - ARMCTRL_NUM_IRQS; + if (l1_irq == 3) { + arm_arch_timer_set_irq_mask(false); + } + } +} diff --git a/soc/arm/bcm2837/linker.ld b/soc/arm/bcm2837/linker.ld new file mode 100644 index 0000000000000000000000000000000000000000..fd1bad9e1d6d30ed1a4450dd67a4ce721c9ddd6a --- /dev/null +++ b/soc/arm/bcm2837/linker.ld @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include <arch/arm/aarch64/scripts/linker.ld> diff --git a/soc/arm/bcm2837/mmu_regions.c b/soc/arm/bcm2837/mmu_regions.c new file mode 100644 index 0000000000000000000000000000000000000000..0cb8238581abc3af69aa2cb8fdf8ae6c10022961 --- /dev/null +++ b/soc/arm/bcm2837/mmu_regions.c @@ -0,0 +1,54 @@ +/* + * Copyright 2019 Broadcom + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <soc.h> +#include <arch/arm/aarch64/arm_mmu.h> + +#define SZ_1K 1024 + +static const struct arm_mmu_region mmu_regions[] = { +#ifdef CONFIG_GIC_V3 + MMU_REGION_FLAT_ENTRY("GIC", + DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 0), + DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 0), + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), + + MMU_REGION_FLAT_ENTRY("GIC", + DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1), + DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 1), + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +#else + MMU_REGION_FLAT_ENTRY("INTC_BCM2837_L1", + 0x40000000, + 0x1000, + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), + MMU_REGION_FLAT_ENTRY("INTC_BCM2837_ARMCTRK", + 0x3f00b000, + 0x1000, + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +#endif + MMU_REGION_FLAT_ENTRY("GP", + 0x3f200000, + 0x1000, + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +#ifdef CONFIG_UART_PL011 + MMU_REGION_FLAT_ENTRY("UART", + DT_REG_ADDR(DT_INST(0, arm_pl011)), + DT_REG_SIZE(DT_INST(0, arm_pl011)), + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +#endif +#if defined(CONFIG_SMP) || defined(CONFIG_ZTEST) + MMU_REGION_FLAT_ENTRY("SPINTABLE", + 0x00000000, + 0x1000, + MT_DEVICE_nGnRnE | MT_P_RW_U_NA | MT_DEFAULT_SECURE_STATE), +#endif +}; + +const struct arm_mmu_config mmu_config = { + .num_regions = ARRAY_SIZE(mmu_regions), + .mmu_regions = mmu_regions, +}; diff --git a/soc/arm/bcm2837/plat_core.S b/soc/arm/bcm2837/plat_core.S new file mode 100644 index 0000000000000000000000000000000000000000..c1ee72db6c1caf7c0ebaca6ef767916fc5dbac23 --- /dev/null +++ b/soc/arm/bcm2837/plat_core.S @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Carlo Caione <ccaione@baylibre.com> + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + *@file + *@brief plat/core specific init +*/ + +#include <toolchain.h> +#include <linker/sections.h> +#include <arch/cpu.h> + +_ASM_FILE_PROLOGUE + +GTEXT(z_arch_el3_plat_init) + +SECTION_FUNC(TEXT, z_arch_el3_plat_init) + + mov x20, x30 + +#ifdef CONFIG_GIC_V3 + /* Enable GIC v3 system interface */ + mov_imm x0, (ICC_SRE_ELx_DFB | ICC_SRE_ELx_DIB | \ + ICC_SRE_ELx_SRE | ICC_SRE_EL3_EN) + msr ICC_SRE_EL3, x0 +#endif + + mov x30, x20 + ret diff --git a/soc/arm/bcm2837/soc.h b/soc/arm/bcm2837/soc.h new file mode 100644 index 0000000000000000000000000000000000000000..889f634fe3e0997dc1e1afd4177eae669f915675 --- /dev/null +++ b/soc/arm/bcm2837/soc.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2019 Carlo Caione <ccaione@baylibre.com> + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef _SOC_H_ +#define _SOC_H_ + +#include <sys/util.h> + +#ifndef _ASMLANGUAGE + +#include <device.h> + +#endif /* !_ASMLANGUAGE */ + +#endif /* _SOC_H_ */