├── .gitignore ├── .gitmodules ├── Config.in ├── README.md ├── board └── raspberrypi │ └── raspberrypi-pico2 │ ├── genimage.cfg │ ├── patches │ ├── .hidden │ └── linux │ │ ├── 0001-Add-interrupt-controller.patch │ │ ├── 0001-starts-to-work.patch │ │ ├── 0001-timer.patch │ │ └── 0002-patch-interrupt-controller.patch │ ├── post-build.sh │ ├── raspberrypi-pico2.config │ ├── raspberrypi-pico2.dts │ └── rootfs_overlay │ ├── .hidden │ └── etc │ └── inittab ├── configs └── raspberrypi-pico2_defconfig ├── external.desc ├── external.mk ├── images └── booting.png └── psram-bootloader ├── CMakeLists.txt ├── Makefile ├── debug.gdb ├── demo ├── Makefile └── src │ ├── demo.c │ ├── demo.lds │ └── start.S ├── partition_table.json ├── pico_sdk_import.cmake ├── run-gdb.sh └── src └── bootloader.c /.gitignore: -------------------------------------------------------------------------------- 1 | psram-bootloader/build 2 | psram-bootloader/demo/build 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "buildroot"] 2 | path = buildroot 3 | url = https://github.com/buildroot/buildroot.git 4 | -------------------------------------------------------------------------------- /Config.in: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Pico 2 Buildroot 2 | 3 | How to build: 4 | 5 | ```bash 6 | git clone https://github.com/Mr-Bossman/pi-pico2-linux 7 | 8 | cd pi-pico2-linux 9 | 10 | git submodule update --init 11 | 12 | # cd buildroot 13 | # make BR2_EXTERNAL=$PWD/../ raspberrypi-pico2_defconfig 14 | make -C buildroot BR2_EXTERNAL=$PWD/ raspberrypi-pico2_defconfig 15 | 16 | make -C buildroot 17 | 18 | make -C psram-bootloader flash-kernel 19 | ``` 20 | 21 | ## Designed to work with [SparkFun Pro Micro - RP2350](https://www.sparkfun.com/products/24870) 22 | 23 | ![Image of boot](images/booting.png) 24 | 25 | #### NOTES on SDK 26 | This project uses the sdk and tools installed to `~/.pico-sdk/`. 27 | To use the tools and sdk installed to another location see [this issue](https://github.com/raspberrypi/pico-sdk/pull/1820#issuecomment-2291611448) 28 | or the following. 29 | 30 | ```bash 31 | export PICO_SDK_PATH=~/example_sdk/sdk/2.0.0/ 32 | export PICO_TOOLCHAIN_PATH=~/example_sdk/toolchain/13_2_Rel1 33 | export pioasm_DIR=~/example_sdk/tools/2.0.0/pioasm 34 | export picotool_DIR=~/example_sdk/picotool/2.0.0/picotool 35 | 36 | make flash-kernel 37 | ``` 38 | 39 | #### NOTES on Atomics 40 | On page 307 of the RP2350 Datasheet MCAUSE register CODE 7 says: 41 | > Store/AMO access fault. A store/AMO failed a PMP check, or 42 | encountered a downstream bus error. Also set if an AMO is attempted on a 43 | region that does not support atomics (on RP2350, anything but SRAM). 44 | 45 | Atomics will only work in SRAM, the kernel is located PSRAM, not SRAM. 46 | The `lr` and `sr` atomic load and store will always return error in this region causing most code using them to behave incorrectly. 47 | Most implementations assume `lr` and `sr` will eventually succeed. 48 | 49 | ### Use on other boards 50 | 51 | This only works on the RP2350 RISC-V cores. 52 | 53 | If you want to run this on other boards, please change the `PICO_BOARD` variable in `CMakeLists.txt`. 54 | `set(PICO_BOARD sparkfun_promicro_rp2350 CACHE STRING "Board type")` 55 | 56 | You will also need to set the psram CS pin with the `SFE_RP2350_XIP_CSI_PIN` macro in `bootloader.c`. 57 | As of now the only psram chip tested is the `APS6404L` and any others may not work. 58 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/genimage.cfg: -------------------------------------------------------------------------------- 1 | image flash-image.bin { 2 | hdimage { 3 | partition-table-type = "none" 4 | } 5 | 6 | partition raspberrypi-pico2.dtb { 7 | in-partition-table = "no" 8 | image = "raspberrypi-pico2.dtb" 9 | offset = 0 10 | } 11 | 12 | partition Image { 13 | in-partition-table = "no" 14 | image = "Image" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/patches/.hidden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mr-Bossman/pi-pico2-linux/3ee6e964c249d3d95916fc6bc375e255ceea528e/board/raspberrypi/raspberrypi-pico2/patches/.hidden -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/patches/linux/0001-Add-interrupt-controller.patch: -------------------------------------------------------------------------------- 1 | From afd7222c20d0371d359006dc2adecdf8339037ca Mon Sep 17 00:00:00 2001 2 | From: Jesse Taube 3 | Date: Sun, 8 Sep 2024 02:01:20 -0400 4 | Subject: [PATCH] Add interrupt controller 5 | 6 | --- 7 | drivers/irqchip/Kconfig | 8 ++ 8 | drivers/irqchip/Makefile | 1 + 9 | drivers/irqchip/irq-simple-intc.c | 137 ++++++++++++++++++++++++++++++ 10 | 3 files changed, 146 insertions(+) 11 | create mode 100644 drivers/irqchip/irq-simple-intc.c 12 | 13 | diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig 14 | index 14464716bacb..5cc76fe2ee89 100644 15 | --- a/drivers/irqchip/Kconfig 16 | +++ b/drivers/irqchip/Kconfig 17 | @@ -582,6 +582,14 @@ config STARFIVE_JH8100_INTC 18 | 19 | If you don't know what to do here, say Y. 20 | 21 | +config SIMPLE_INTC 22 | + bool "Simple External Interrupt Controller" 23 | + select IRQ_DOMAIN_HIERARCHY 24 | + help 25 | + This enables support for a simple INTC chip. 26 | + 27 | + If you don't know what to do here, say Y. 28 | + 29 | config EXYNOS_IRQ_COMBINER 30 | bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST 31 | depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST 32 | diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile 33 | index d9dc3d99aaa8..cc92ff458331 100644 34 | --- a/drivers/irqchip/Makefile 35 | +++ b/drivers/irqchip/Makefile 36 | @@ -124,3 +124,4 @@ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o 37 | obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o 38 | obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o 39 | obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o 40 | +obj-$(CONFIG_SIMPLE_INTC) += irq-simple-intc.o 41 | diff --git a/drivers/irqchip/irq-simple-intc.c b/drivers/irqchip/irq-simple-intc.c 42 | new file mode 100644 43 | index 000000000000..ebd3c8e6dd02 44 | --- /dev/null 45 | +++ b/drivers/irqchip/irq-simple-intc.c 46 | @@ -0,0 +1,137 @@ 47 | +// SPDX-License-Identifier: GPL-2.0 48 | +/* 49 | + * Simple External Interrupt Controller driver 50 | + * 51 | + * Copyright (C) 52 | + * 53 | + * Author: Jesse Taube 54 | + */ 55 | + 56 | +#define pr_fmt(fmt) "irq-simple: " fmt 57 | + 58 | +#include 59 | +#include 60 | +#include 61 | +#include 62 | +#include 63 | +#include 64 | +#include 65 | +#include 66 | +#include 67 | +#include 68 | + 69 | +#define MAX_IRQ_NUM 512 70 | + 71 | +#define CSR_MEINEXT 0xbe4 72 | +#define CSR_MEIEA 0xbe0 73 | + 74 | +struct simple_irq_chip { 75 | + void __iomem *base; 76 | + struct irq_domain *domain; 77 | + raw_spinlock_t lock; 78 | +}; 79 | + 80 | +static void simple_intc_unmask(struct irq_data *d) 81 | +{ 82 | + struct simple_irq_chip *irqc = irq_data_get_irq_chip_data(d); 83 | + uint32_t index = d->hwirq / 16; 84 | + uint32_t mask = BIT(d->hwirq % 16); 85 | + 86 | + raw_spin_lock(&irqc->lock); 87 | + csr_set(CSR_MEIEA, index | (mask << 16)); 88 | + raw_spin_unlock(&irqc->lock); 89 | +} 90 | + 91 | +static void simple_intc_mask(struct irq_data *d) 92 | +{ 93 | + struct simple_irq_chip *irqc = irq_data_get_irq_chip_data(d); 94 | + uint32_t index = d->hwirq / 16; 95 | + uint32_t mask = BIT(d->hwirq % 16); 96 | + 97 | + raw_spin_lock(&irqc->lock); 98 | + csr_clear(CSR_MEIEA, index | (mask << 16)); 99 | + raw_spin_unlock(&irqc->lock); 100 | +} 101 | + 102 | +static struct irq_chip intc_dev = { 103 | + .name = "Simple INTC", 104 | + .irq_unmask = simple_intc_unmask, 105 | + .irq_mask = simple_intc_mask, 106 | +}; 107 | + 108 | +static int simple_intc_map(struct irq_domain *d, unsigned int irq, 109 | + irq_hw_number_t hwirq) 110 | +{ 111 | + irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data, 112 | + handle_level_irq, NULL, NULL); 113 | + 114 | + return 0; 115 | +} 116 | + 117 | +static const struct irq_domain_ops simple_intc_domain_ops = { 118 | + .xlate = irq_domain_xlate_onecell, 119 | + .map = simple_intc_map, 120 | +}; 121 | + 122 | +static void simple_intc_irq_handler(struct irq_desc *desc) 123 | +{ 124 | + struct simple_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); 125 | + struct irq_chip *chip = irq_desc_get_chip(desc); 126 | + int hwirq; 127 | + 128 | + chained_irq_enter(chip, desc); 129 | + hwirq = (csr_read_set(CSR_MEINEXT, 0x1) >> 2) & 0x1ff; 130 | + 131 | + generic_handle_domain_irq(irqc->domain, hwirq); 132 | + 133 | + chained_irq_exit(chip, desc); 134 | +} 135 | + 136 | +static int __init simple_intc_init(struct device_node *intc, 137 | + struct device_node *parent) 138 | +{ 139 | + struct simple_irq_chip *irqc; 140 | + int parent_irq; 141 | + int ret; 142 | + 143 | + irqc = kzalloc(sizeof(*irqc), GFP_KERNEL); 144 | + if (!irqc) 145 | + return -ENOMEM; 146 | + 147 | + raw_spin_lock_init(&irqc->lock); 148 | + 149 | + irqc->domain = irq_domain_add_linear(intc, MAX_IRQ_NUM, 150 | + &simple_intc_domain_ops, irqc); 151 | + if (!irqc->domain) { 152 | + pr_err("Unable to create IRQ domain\n"); 153 | + ret = -EINVAL; 154 | + goto err_free; 155 | + } 156 | + 157 | + parent_irq = of_irq_get(intc, 0); 158 | + if (parent_irq < 0) { 159 | + pr_err("Failed to get main IRQ: %d\n", parent_irq); 160 | + ret = parent_irq; 161 | + goto err_remove_domain; 162 | + } 163 | + 164 | + irq_set_chained_handler_and_data(parent_irq, simple_intc_irq_handler, irqc); 165 | + 166 | + pr_info("Interrupt controller register, nr_irqs %d\n", MAX_IRQ_NUM); 167 | + 168 | + return 0; 169 | + 170 | +err_remove_domain: 171 | + irq_domain_remove(irqc->domain); 172 | +err_free: 173 | + kfree(irqc); 174 | + return ret; 175 | +} 176 | + 177 | +IRQCHIP_PLATFORM_DRIVER_BEGIN(simple_intc) 178 | +IRQCHIP_MATCH("generic,simple-intc", simple_intc_init) 179 | +IRQCHIP_PLATFORM_DRIVER_END(simple_intc) 180 | + 181 | +MODULE_DESCRIPTION("Simple External Interrupt Controller driver"); 182 | +MODULE_LICENSE("GPL"); 183 | +MODULE_AUTHOR("Jesse Taube "); 184 | -- 185 | 2.45.2 186 | 187 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/patches/linux/0001-starts-to-work.patch: -------------------------------------------------------------------------------- 1 | From 8f8417ebd5320a3b21bc9f12f5d93c8d46b592a5 Mon Sep 17 00:00:00 2001 2 | From: Jesse Taube 3 | Date: Sun, 25 Aug 2024 19:38:12 -0400 4 | Subject: [PATCH] starts to work 5 | 6 | --- 7 | arch/riscv/Kconfig | 2 +- 8 | arch/riscv/Kconfig.socs | 1 + 9 | arch/riscv/include/asm/atomic.h | 20 +++++------- 10 | arch/riscv/include/asm/cmpxchg.h | 19 +++++------- 11 | arch/riscv/include/asm/futex.h | 5 ++- 12 | arch/riscv/kernel/entry.S | 2 +- 13 | arch/riscv/kernel/traps.c | 52 ++++++++++++++++++++++++++++++++ 14 | 7 files changed, 73 insertions(+), 28 deletions(-) 15 | 16 | diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig 17 | index 0525ee2d63c7..52fbb35f306e 100644 18 | --- a/arch/riscv/Kconfig 19 | +++ b/arch/riscv/Kconfig 20 | @@ -263,7 +263,7 @@ config MMU 21 | 22 | config PAGE_OFFSET 23 | hex 24 | - default 0x80000000 if !MMU && RISCV_M_MODE 25 | + default 0x11000000 if !MMU && RISCV_M_MODE 26 | default 0x80200000 if !MMU 27 | default 0xc0000000 if 32BIT 28 | default 0xff60000000000000 if 64BIT 29 | diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs 30 | index f51bb24bc84c..211012ca6831 100644 31 | --- a/arch/riscv/Kconfig.socs 32 | +++ b/arch/riscv/Kconfig.socs 33 | @@ -62,6 +62,7 @@ config ARCH_VIRT 34 | select PM_GENERIC_DOMAINS if PM 35 | select PM_GENERIC_DOMAINS_OF if PM && OF 36 | select RISCV_SBI_CPUIDLE if CPU_IDLE && RISCV_SBI 37 | + select ARM_AMBA 38 | help 39 | This enables support for QEMU Virt Machine. 40 | 41 | diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h 42 | index 5b96c2f61adb..b2391c62d718 100644 43 | --- a/arch/riscv/include/asm/atomic.h 44 | +++ b/arch/riscv/include/asm/atomic.h 45 | @@ -198,11 +198,10 @@ ATOMIC_OPS(xor, xor, i) 46 | #define _arch_atomic_fetch_add_unless(_prev, _rc, counter, _a, _u, sfx) \ 47 | ({ \ 48 | __asm__ __volatile__ ( \ 49 | - "0: lr." sfx " %[p], %[c]\n" \ 50 | + "0: l" sfx " %[p], %[c]\n" \ 51 | " beq %[p], %[u], 1f\n" \ 52 | " add %[rc], %[p], %[a]\n" \ 53 | - " sc." sfx ".rl %[rc], %[rc], %[c]\n" \ 54 | - " bnez %[rc], 0b\n" \ 55 | + " s" sfx " %[rc], %[c]\n" \ 56 | " fence rw, rw\n" \ 57 | "1:\n" \ 58 | : [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \ 59 | @@ -237,11 +236,10 @@ static __always_inline s64 arch_atomic64_fetch_add_unless(atomic64_t *v, s64 a, 60 | #define _arch_atomic_inc_unless_negative(_prev, _rc, counter, sfx) \ 61 | ({ \ 62 | __asm__ __volatile__ ( \ 63 | - "0: lr." sfx " %[p], %[c]\n" \ 64 | + "0: l" sfx " %[p], %[c]\n" \ 65 | " bltz %[p], 1f\n" \ 66 | " addi %[rc], %[p], 1\n" \ 67 | - " sc." sfx ".rl %[rc], %[rc], %[c]\n" \ 68 | - " bnez %[rc], 0b\n" \ 69 | + " s" sfx " %[rc], %[c]\n" \ 70 | " fence rw, rw\n" \ 71 | "1:\n" \ 72 | : [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \ 73 | @@ -263,11 +261,10 @@ static __always_inline bool arch_atomic_inc_unless_negative(atomic_t *v) 74 | #define _arch_atomic_dec_unless_positive(_prev, _rc, counter, sfx) \ 75 | ({ \ 76 | __asm__ __volatile__ ( \ 77 | - "0: lr." sfx " %[p], %[c]\n" \ 78 | + "0: l" sfx " %[p], %[c]\n" \ 79 | " bgtz %[p], 1f\n" \ 80 | " addi %[rc], %[p], -1\n" \ 81 | - " sc." sfx ".rl %[rc], %[rc], %[c]\n" \ 82 | - " bnez %[rc], 0b\n" \ 83 | + " s" sfx " %[rc], %[c]\n" \ 84 | " fence rw, rw\n" \ 85 | "1:\n" \ 86 | : [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \ 87 | @@ -289,11 +286,10 @@ static __always_inline bool arch_atomic_dec_unless_positive(atomic_t *v) 88 | #define _arch_atomic_dec_if_positive(_prev, _rc, counter, sfx) \ 89 | ({ \ 90 | __asm__ __volatile__ ( \ 91 | - "0: lr." sfx " %[p], %[c]\n" \ 92 | + "0: l" sfx " %[p], %[c]\n" \ 93 | " addi %[rc], %[p], -1\n" \ 94 | " bltz %[rc], 1f\n" \ 95 | - " sc." sfx ".rl %[rc], %[rc], %[c]\n" \ 96 | - " bnez %[rc], 0b\n" \ 97 | + " s" sfx " %[rc], %[c]\n" \ 98 | " fence rw, rw\n" \ 99 | "1:\n" \ 100 | : [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \ 101 | diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h 102 | index 808b4c78462e..34f1e29ec8e7 100644 103 | --- a/arch/riscv/include/asm/cmpxchg.h 104 | +++ b/arch/riscv/include/asm/cmpxchg.h 105 | @@ -22,11 +22,10 @@ 106 | \ 107 | __asm__ __volatile__ ( \ 108 | prepend \ 109 | - "0: lr.w %0, %2\n" \ 110 | + "0: lw %0, %2\n" \ 111 | " and %1, %0, %z4\n" \ 112 | " or %1, %1, %z3\n" \ 113 | - " sc.w" sc_sfx " %1, %1, %2\n" \ 114 | - " bnez %1, 0b\n" \ 115 | + " sw %1, %1, %2\n" \ 116 | append \ 117 | : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ 118 | : "rJ" (__newx), "rJ" (~__mask) \ 119 | @@ -117,13 +116,12 @@ 120 | \ 121 | __asm__ __volatile__ ( \ 122 | prepend \ 123 | - "0: lr.w %0, %2\n" \ 124 | + "0: lw %0, %2\n" \ 125 | " and %1, %0, %z5\n" \ 126 | " bne %1, %z3, 1f\n" \ 127 | " and %1, %0, %z6\n" \ 128 | " or %1, %1, %z4\n" \ 129 | - " sc.w" sc_sfx " %1, %1, %2\n" \ 130 | - " bnez %1, 0b\n" \ 131 | + " sw %1, %2\n" \ 132 | append \ 133 | "1:\n" \ 134 | : "=&r" (__retx), "=&r" (__rc), "+A" (*(__ptr32b)) \ 135 | @@ -140,10 +138,9 @@ 136 | \ 137 | __asm__ __volatile__ ( \ 138 | prepend \ 139 | - "0: lr" lr_sfx " %0, %2\n" \ 140 | + "0: l" lr_sfx " %0, %2\n" \ 141 | " bne %0, %z3, 1f\n" \ 142 | - " sc" sc_sfx " %1, %z4, %2\n" \ 143 | - " bnez %1, 0b\n" \ 144 | + " s" sc_sfx " %z4, %2\n" \ 145 | append \ 146 | "1:\n" \ 147 | : "=&r" (r), "=&r" (__rc), "+A" (*(p)) \ 148 | @@ -165,11 +162,11 @@ 149 | __ret, __ptr, __old, __new); \ 150 | break; \ 151 | case 4: \ 152 | - __arch_cmpxchg(".w", ".w" sc_sfx, prepend, append, \ 153 | + __arch_cmpxchg("w", "w", prepend, append, \ 154 | __ret, __ptr, (long), __old, __new); \ 155 | break; \ 156 | case 8: \ 157 | - __arch_cmpxchg(".d", ".d" sc_sfx, prepend, append, \ 158 | + __arch_cmpxchg("d", "d", prepend, append, \ 159 | __ret, __ptr, /**/, __old, __new); \ 160 | break; \ 161 | default: \ 162 | diff --git a/arch/riscv/include/asm/futex.h b/arch/riscv/include/asm/futex.h 163 | index fc8130f995c1..efef8d1bd20f 100644 164 | --- a/arch/riscv/include/asm/futex.h 165 | +++ b/arch/riscv/include/asm/futex.h 166 | @@ -85,10 +85,9 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, 167 | 168 | __enable_user_access(); 169 | __asm__ __volatile__ ( 170 | - "1: lr.w.aqrl %[v],%[u] \n" 171 | + "1: lw %[v],%[u] \n" 172 | " bne %[v],%z[ov],3f \n" 173 | - "2: sc.w.aqrl %[t],%z[nv],%[u] \n" 174 | - " bnez %[t],1b \n" 175 | + "2: sw %z[nv],%[u] \n" 176 | "3: \n" 177 | _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %[r]) \ 178 | _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %[r]) \ 179 | diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S 180 | index 68a24cf9481a..49b53e3775d3 100644 181 | --- a/arch/riscv/kernel/entry.S 182 | +++ b/arch/riscv/kernel/entry.S 183 | @@ -166,7 +166,7 @@ SYM_CODE_START_NOALIGN(ret_from_exception) 184 | * arbitrarily large. 185 | */ 186 | REG_L a2, PT_EPC(sp) 187 | - REG_SC x0, a2, PT_EPC(sp) 188 | + REG_S a2, PT_EPC(sp) 189 | 190 | csrw CSR_STATUS, a0 191 | csrw CSR_EPC, a2 192 | diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c 193 | index 05a16b1f0aee..a3fefd7948e3 100644 194 | --- a/arch/riscv/kernel/traps.c 195 | +++ b/arch/riscv/kernel/traps.c 196 | @@ -33,6 +33,50 @@ 197 | #include 198 | #include 199 | 200 | +static inline unsigned long* regs_get_register_p(struct pt_regs *regs, 201 | + unsigned int offset) 202 | +{ 203 | + static unsigned long zero = 0; 204 | + if (offset == 0) 205 | + return &zero; 206 | + return &(((unsigned long*)regs)[offset]); 207 | +} 208 | + 209 | +static void do_atomic(struct pt_regs *regs){ 210 | + uint32_t op = *(uint32_t*)regs->epc; 211 | + uint32_t irmid = (op>>27)&0x1f; 212 | + unsigned long rs1 = *regs_get_register_p(regs,(op >> 15) & 0x1f); 213 | + unsigned long rs2 = *regs_get_register_p(regs,(op >> 20) & 0x1f); 214 | + unsigned long* rsd = regs_get_register_p(regs,(op >> 7) & 0x1f); 215 | + unsigned long* dst = (unsigned long*)rs1; 216 | + if((op >> 7) & 0x1f) 217 | + *rsd = *dst; 218 | + switch( irmid ){ 219 | + case 0b00010: break; //LR.W 220 | + case 0b00011: { 221 | + if((op >> 7) & 0x1f) 222 | + *rsd = 0; 223 | + *dst = rs2; 224 | + break; //SC.W (Lie and always say it's good) 225 | + } 226 | + case 0b00001: *dst = rs2; break; //AMOSWAP.W 227 | + case 0b00000: *dst += rs2; break; //AMOADD.W 228 | + case 0b00100: *dst ^= rs2; break; //AMOXOR.W 229 | + case 0b01100: *dst &= rs2; break; //AMOAND.W 230 | + case 0b01000: *dst |= rs2; break; //AMOOR.W 231 | + default: break; 232 | + } 233 | + 234 | +} 235 | + 236 | +static int do_exinsn(struct pt_regs *regs){ 237 | + uint32_t op = *(uint32_t*)regs->epc; 238 | + switch (op & 0x7f) { 239 | + case 0b0101111: do_atomic(regs); return 1; 240 | + default: return 0; 241 | + } 242 | +} 243 | + 244 | int show_unhandled_signals = 1; 245 | 246 | static DEFINE_SPINLOCK(die_lock); 247 | @@ -136,6 +180,14 @@ static void do_trap_error(struct pt_regs *regs, int signo, int code, 248 | if (user_mode(regs)) { 249 | do_trap(regs, signo, code, addr); 250 | } else { 251 | + /* If illegal instruction and we can emulate, then 252 | + * we need to emulate and skip the instruction. 253 | + */ 254 | + if(code == SEGV_ACCERR && do_exinsn(regs)){ 255 | + regs->epc += 4; 256 | + regs->badaddr += 4; 257 | + return; 258 | + } 259 | if (!fixup_exception(regs)) 260 | die(regs, str); 261 | } 262 | -- 263 | 2.45.2 264 | 265 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/patches/linux/0001-timer.patch: -------------------------------------------------------------------------------- 1 | From 690a3eec78fa36bec207845ab6787e802d2cdab3 Mon Sep 17 00:00:00 2001 2 | From: Jesse Taube 3 | Date: Fri, 6 Sep 2024 20:32:13 -0400 4 | Subject: [PATCH] timer 5 | 6 | --- 7 | drivers/clocksource/timer-clint.c | 6 +++--- 8 | 1 file changed, 3 insertions(+), 3 deletions(-) 9 | 10 | diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c 11 | index 0bdd9d7ec545..aab46f2ce27f 100644 12 | --- a/drivers/clocksource/timer-clint.c 13 | +++ b/drivers/clocksource/timer-clint.c 14 | @@ -28,9 +28,9 @@ 15 | #include 16 | #endif 17 | 18 | -#define CLINT_IPI_OFF 0 19 | -#define CLINT_TIMER_CMP_OFF 0x4000 20 | -#define CLINT_TIMER_VAL_OFF 0xbff8 21 | +#define CLINT_IPI_OFF 0x1a0 22 | +#define CLINT_TIMER_CMP_OFF 0x1b8 23 | +#define CLINT_TIMER_VAL_OFF 0x1b0 24 | 25 | /* CLINT manages IPI and Timer for RISC-V M-mode */ 26 | static u32 __iomem *clint_ipi_base; 27 | -- 28 | 2.45.2 29 | 30 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/patches/linux/0002-patch-interrupt-controller.patch: -------------------------------------------------------------------------------- 1 | diff --color -urN a/drivers/irqchip/irq-simple-intc.c b/drivers/irqchip/irq-simple-intc.c 2 | --- a/drivers/irqchip/irq-simple-intc.c 2024-10-10 13:30:53.202884121 +0000 3 | +++ b/drivers/irqchip/irq-simple-intc.c 2024-10-10 13:52:17.643680867 +0000 4 | @@ -22,6 +22,7 @@ 5 | 6 | #define MAX_IRQ_NUM 512 7 | 8 | +#define CSR_MEICONTEXT 0xbe5 9 | #define CSR_MEINEXT 0xbe4 10 | #define CSR_MEIEA 0xbe0 11 | 12 | @@ -49,6 +50,7 @@ 13 | uint32_t mask = BIT(d->hwirq % 16); 14 | 15 | raw_spin_lock(&irqc->lock); 16 | + 17 | csr_clear(CSR_MEIEA, index | (mask << 16)); 18 | raw_spin_unlock(&irqc->lock); 19 | } 20 | @@ -78,13 +80,29 @@ 21 | struct simple_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data); 22 | struct irq_chip *chip = irq_desc_get_chip(desc); 23 | int hwirq; 24 | + int meicontext; 25 | + 26 | + meicontext = csr_read_set(CSR_MEICONTEXT,0x02); // save the meicontext register and disable parent interrupts 27 | 28 | chained_irq_enter(chip, desc); 29 | - hwirq = (csr_read_set(CSR_MEINEXT, 0x1) >> 2) & 0x1ff; 30 | - 31 | - generic_handle_domain_irq(irqc->domain, hwirq); 32 | - 33 | + while ((hwirq = csr_read(CSR_MEINEXT)) >= 0) { 34 | + hwirq = ((hwirq) >> 2) & 0x1ff; 35 | + generic_handle_domain_irq(irqc->domain, hwirq); 36 | + } 37 | chained_irq_exit(chip, desc); 38 | + 39 | + // to restore the 3-level preemtion-stack the following should work: 40 | + // 41 | + // csr_write(CSR_MEICONTEXT, meicontext); 42 | + // 43 | + // but it doesn't work always, so we have to do a workaround for now to restore the preemption stack 44 | + // something between here and the mret instruction clears bit 0 of the meicontext register 45 | + // this happens when other traps are taken in the meantime 46 | + 47 | + int ppreempt = ((meicontext >> 28) & 0xf) << 24; // extract new ppreempt level 48 | + int preempt = ((meicontext >> 24) & 0xf) << 16; // extract new preemptrob level 49 | + int parentirq = meicontext & 0xc; // extract disabled parent interrupts 50 | + csr_write(CSR_MEICONTEXT, ppreempt | preempt | parentirq); // write back the meicontext register 51 | } 52 | 53 | static int __init simple_intc_init(struct device_node *intc, 54 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/post-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | BOARD_DIR="$(dirname "$0")" 3 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/raspberrypi-pico2.config: -------------------------------------------------------------------------------- 1 | CONFIG_CMDLINE="earlycon=pl011,mmio32,0x40070000 console=ttyAMA0" 2 | CONFIG_CMDLINE_FALLBACK=y 3 | # CONFIG_CMDLINE_FORCE is not set 4 | CONFIG_SERIAL_AMBA_PL011=y 5 | CONFIG_SERIAL_AMBA_PL011_CONSOLE=y 6 | # CONFIG_DEBUG_INFO_NONE is not set 7 | CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y 8 | CONFIG_GDB_SCRIPTS=y 9 | CONFIG_SMP=n 10 | CONFIG_RISCV_ISA_V=n 11 | CONFIG_RISCV_ISA_C=n 12 | CONFIG_FPU=n 13 | CONFIG_RISCV_ISA_ZBB=n 14 | CONFIG_EXT4_FS=y 15 | CONFIG_PHYS_RAM_BASE_FIXED=y 16 | CONFIG_PHYS_RAM_BASE=0x11000000 17 | CONFIG_SIMPLE_INTC=y 18 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/raspberrypi-pico2.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | #address-cells = <1>; 5 | #size-cells = <1>; 6 | compatible = "riscv-minimal-nommu"; 7 | model = "riscv-minimal-nommu,qemu"; 8 | 9 | chosen { 10 | bootargs = "earlycon=pl011,mmio32,0x40070000 console=ttyAMA0"; 11 | }; 12 | 13 | memory@11000000 { 14 | device_type = "memory"; 15 | reg = <0x11000000 0x800000>; /* 8MB */ 16 | }; 17 | 18 | cpus { 19 | #address-cells = <1>; 20 | #size-cells = <0>; 21 | /* sys timer is in Hz */ 22 | timebase-frequency = <1000000>; 23 | 24 | cpu0: cpu@0 { 25 | reg = <0>; 26 | device_type = "cpu"; 27 | compatible = "riscv"; 28 | riscv,isa = "rv32ima"; 29 | riscv,isa-base = "rv32i"; 30 | riscv,isa-extensions = "i", "m", "a"; 31 | mmu-type = "riscv,none"; 32 | status = "okay"; 33 | 34 | cpu0_intc: interrupt-controller { 35 | #interrupt-cells = <1>; 36 | #address-cells = <1>; 37 | interrupt-controller; 38 | compatible = "riscv,cpu-intc"; 39 | }; 40 | }; 41 | }; 42 | 43 | soc { 44 | #address-cells = <1>; 45 | #size-cells = <1>; 46 | compatible = "simple-bus"; 47 | ranges; 48 | 49 | uart0: serial@40070000 { 50 | compatible = "arm,sbsa-uart"; 51 | current-speed = <115200>; 52 | interrupt-parent = <&plic0>; 53 | interrupts = <33>; 54 | reg = <0x40070000 0x100>; 55 | status = "okay"; 56 | }; 57 | 58 | uart1: serial@40078000 { 59 | compatible = "arm,sbsa-uart"; 60 | current-speed = <115200>; 61 | interrupt-parent = <&plic0>; 62 | interrupts = <34>; 63 | reg = <0x40078000 0x100>; 64 | status = "okay"; 65 | }; 66 | 67 | plic0: interrupt-controller@0 { 68 | reg = <0x0 0x0>; 69 | interrupts-extended = <&cpu0_intc 11>; 70 | interrupt-controller; 71 | compatible = "generic,simple-intc"; 72 | #address-cells = <0x00>; 73 | #interrupt-cells = <0x01>; 74 | }; 75 | 76 | clint: clint@d0000000 { 77 | interrupts-extended = <&cpu0_intc 3>, 78 | <&cpu0_intc 7>; 79 | reg = <0xd0000000 0x400>; 80 | compatible = "riscv,clint0"; 81 | }; 82 | }; 83 | }; 84 | -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/rootfs_overlay/.hidden: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mr-Bossman/pi-pico2-linux/3ee6e964c249d3d95916fc6bc375e255ceea528e/board/raspberrypi/raspberrypi-pico2/rootfs_overlay/.hidden -------------------------------------------------------------------------------- /board/raspberrypi/raspberrypi-pico2/rootfs_overlay/etc/inittab: -------------------------------------------------------------------------------- 1 | # /etc/inittab 2 | # 3 | # Copyright (C) 2001 Erik Andersen 4 | # 5 | # Note: BusyBox init doesn't support runlevels. The runlevels field is 6 | # completely ignored by BusyBox init. If you want runlevels, use 7 | # sysvinit. 8 | # 9 | # Format for each entry: ::: 10 | # 11 | # id == tty to run on, or empty for /dev/console 12 | # runlevels == ignored 13 | # action == one of sysinit, respawn, askfirst, wait, and once 14 | # process == program to run 15 | 16 | # Startup the system 17 | ::sysinit:/bin/mount -t proc proc /proc 18 | #::sysinit:/bin/mount -o remount,rw / 19 | ::sysinit:/bin/mkdir -p /dev/pts /dev/shm 20 | ::sysinit:/bin/mount -a 21 | ::sysinit:/bin/mkdir -p /run/lock/subsys 22 | 23 | console::respawn:/bin/sh 24 | 25 | #::sysinit:/sbin/swapon -a 26 | #null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd 27 | #null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin 28 | #null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout 29 | #null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr 30 | #::sysinit:/bin/hostname -F /etc/hostname 31 | # now run any rc scripts 32 | #::sysinit:/etc/init.d/rcS 33 | 34 | # Put a getty on the serial port 35 | #console::respawn:/sbin/getty -L console 0 vt100 # GENERIC_SERIAL 36 | 37 | # Stuff to do for the 3-finger salute 38 | #::ctrlaltdel:/sbin/reboot 39 | 40 | # Stuff to do before rebooting 41 | ::shutdown:/etc/init.d/rcK 42 | #::shutdown:/sbin/swapoff -a 43 | ::shutdown:/bin/umount -a -r 44 | -------------------------------------------------------------------------------- /configs/raspberrypi-pico2_defconfig: -------------------------------------------------------------------------------- 1 | # Architecture 2 | BR2_riscv=y 3 | BR2_riscv_custom=y 4 | BR2_RISCV_ISA_RVM=y 5 | BR2_RISCV_ISA_RVC=y 6 | BR2_RISCV_32=y 7 | # BR2_RISCV_USE_MMU is not set 8 | 9 | # System 10 | BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/patches" 11 | BR2_PTHREADS_NONE=y 12 | BR2_OPTIMIZE_S=y 13 | 14 | # Filesystem 15 | BR2_TARGET_ROOTFS_INITRAMFS=y 16 | BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/rootfs_overlay" 17 | 18 | # Image 19 | BR2_ROOTFS_POST_IMAGE_SCRIPT="support/scripts/genimage.sh" 20 | BR2_ROOTFS_POST_BUILD_SCRIPT="$(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/post-build.sh" 21 | BR2_ROOTFS_POST_SCRIPT_ARGS="-c $(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/genimage.cfg" 22 | 23 | # Linux headers same as kernel 24 | BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_6_10=y 25 | 26 | # Kernel 27 | BR2_LINUX_KERNEL=y 28 | BR2_LINUX_KERNEL_CUSTOM_VERSION=y 29 | BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.10" 30 | BR2_LINUX_KERNEL_DEFCONFIG="rv32_nommu_virt" 31 | BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/raspberrypi-pico2.config" 32 | BR2_LINUX_KERNEL_DTS_SUPPORT=y 33 | BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="$(BR2_EXTERNAL)/board/raspberrypi/raspberrypi-pico2/raspberrypi-pico2.dts" 34 | 35 | # Host tools 36 | BR2_PACKAGE_HOST_GENIMAGE=y 37 | -------------------------------------------------------------------------------- /external.desc: -------------------------------------------------------------------------------- 1 | name: RPI_PICO2 2 | desc: Test distro for RPI PICO 2 3 | -------------------------------------------------------------------------------- /external.mk: -------------------------------------------------------------------------------- 1 | include $(sort $(wildcard $(BR2_EXTERNAL)/package/*/*.mk)) 2 | -------------------------------------------------------------------------------- /images/booting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mr-Bossman/pi-pico2-linux/3ee6e964c249d3d95916fc6bc375e255ceea528e/images/booting.png -------------------------------------------------------------------------------- /psram-bootloader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated Cmake Pico project file 2 | 3 | cmake_minimum_required(VERSION 3.13) 4 | 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 8 | 9 | # Initialise pico_sdk from installed location 10 | # (note this can come from environment, CMake cache etc) 11 | 12 | # == DO NEVER EDIT THE NEXT LINES for Raspberry Pi Pico VS Code Extension to work == 13 | if(WIN32) 14 | set(USERHOME $ENV{USERPROFILE}) 15 | else() 16 | set(USERHOME $ENV{HOME}) 17 | endif() 18 | set(sdkVersion 2.0.0) 19 | set(toolchainVersion RISCV_COREV_MAY_24) 20 | set(picotoolVersion 2.0.0) 21 | set(pico_vscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) 22 | if (EXISTS ${pico_vscode}) 23 | include(${pico_vscode}) 24 | endif() 25 | # ==================================================================================== 26 | 27 | set(PICO_PLATFORM rp2350-riscv CACHE STRING "Pico Platform") 28 | set(PICO_COMPILER pico_riscv_gcc_zcb_zcmp) 29 | 30 | set(PICO_BOARD sparkfun_promicro_rp2350 CACHE STRING "Board type") 31 | 32 | # Pull in Raspberry Pi Pico SDK (must be before project) 33 | include(pico_sdk_import.cmake) 34 | 35 | if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") 36 | message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") 37 | endif() 38 | 39 | project(psram-bootloader C CXX ASM) 40 | 41 | # Initialise the Raspberry Pi Pico SDK 42 | pico_sdk_init() 43 | 44 | # Add executable. Default name is the project name, version 0.1 45 | 46 | add_executable(psram-bootloader 47 | src/bootloader.c 48 | ) 49 | 50 | # pull in common dependencies 51 | target_link_libraries(psram-bootloader pico_stdlib) 52 | 53 | pico_enable_stdio_usb(psram-bootloader 1) 54 | pico_enable_stdio_uart(psram-bootloader 1) 55 | 56 | # add partition table 57 | pico_embed_pt_in_binary(psram-bootloader ${CMAKE_CURRENT_LIST_DIR}/partition_table.json) 58 | 59 | # create absolute uf2, and package in flash 60 | pico_set_uf2_family(psram-bootloader "absolute") 61 | pico_package_uf2_output(psram-bootloader 0x10000000) 62 | 63 | # create map/bin/hex file etc. 64 | pico_add_extra_outputs(psram-bootloader) 65 | -------------------------------------------------------------------------------- /psram-bootloader/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean flash picotool demo flash-demo flash-kernel 2 | 3 | all: build/Makefile 4 | @$(MAKE) -C build 5 | 6 | build: 7 | mkdir -p build 8 | 9 | build/Makefile: CMakeLists.txt | build 10 | cd build && cmake .. 11 | 12 | picotool: 13 | ifeq (0, $(shell id -u)) 14 | picotool $(PICOTOOL_ARGS) 15 | else 16 | @echo You must be root! 17 | ifeq (, $(shell which sudo)) 18 | su -c "$(MAKE) $@ PICOTOOL_ARGS='$(PICOTOOL_ARGS)'" 19 | else 20 | sudo $(MAKE) $@ PICOTOOL_ARGS='$(PICOTOOL_ARGS)' 21 | endif 22 | endif 23 | 24 | copy: 25 | $(eval MNT_PATH := $(shell lsblk -o MOUNTPOINTS | grep RP2350)) 26 | $(if $(value MNT_PATH),,$(error No RP2350 device found)) 27 | cp build/psram-bootloader.uf2 "$(MNT_PATH)"/ 28 | 29 | flash: all 30 | ifeq (, $(shell which picotool)) 31 | @$(MAKE) copy 32 | else 33 | @$(MAKE) picotool PICOTOOL_ARGS="load -fu build/psram-bootloader.uf2" 34 | endif 35 | 36 | # make CROSS_COMPILE=riscv32-unknown-elf- LD=riscv64-linux-gnu-ld demo 37 | demo: | build 38 | @$(MAKE) -C demo 39 | 40 | flash-demo: demo flash 41 | sleep 2 42 | @$(MAKE) picotool PICOTOOL_ARGS="load -fxup 0 build/demo.bin" 43 | 44 | flash-kernel: ../buildroot/output/images/flash-image.bin flash 45 | sleep 3 46 | @$(MAKE) picotool PICOTOOL_ARGS="load -fxup 0 ../buildroot/output/images/flash-image.bin" 47 | 48 | clean: 49 | @$(MAKE) -C demo clean 50 | rm -rf build 51 | -------------------------------------------------------------------------------- /psram-bootloader/debug.gdb: -------------------------------------------------------------------------------- 1 | set architecture riscv:rv32 2 | target remote localhost:3333 3 | add-auto-load-safe-path ../buildroot/output/build/linux-6.10 4 | cd ../buildroot/output/build/linux-6.10 5 | layout asm 6 | layout reg split 7 | add-symbol-file vmlinux 8 | focus cmd 9 | -------------------------------------------------------------------------------- /psram-bootloader/demo/Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR = build 2 | 3 | CC = $(CROSS_COMPILE)gcc 4 | OBJCOPY = $(CROSS_COMPILE)objcopy 5 | LD = $(CROSS_COMPILE)ld 6 | # LD may need to be LD=riscv64-linux-gnu-ld 7 | 8 | CFLAGS = -march=rv32izicsr -mabi=ilp32 -O2 -Wall -Wextra 9 | LDFLAGS = -m elf32lriscv -nostdlib --gc-sections --no-dynamic-linker 10 | 11 | S_SOURCES = src/start.S 12 | C_SOURCES = src/demo.c 13 | 14 | OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o))) 15 | OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(S_SOURCES:.S=.o))) 16 | DEPS = $(OBJECTS:%.o=%.d) 17 | 18 | vpath %.c $(sort $(dir $(C_SOURCES))) 19 | vpath %.S $(sort $(dir $(S_SOURCES))) 20 | vpath %.lds src 21 | vpath %.o $(BUILD_DIR) 22 | vpath %.elf $(BUILD_DIR) 23 | vpath %.bin $(BUILD_DIR) 24 | 25 | .PHONY: all clean demo 26 | 27 | all: demo 28 | 29 | demo: demo.bin 30 | cp $(BUILD_DIR)/demo.bin ../build/ 31 | 32 | $(OBJECTS): | $(BUILD_DIR) 33 | 34 | $(BUILD_DIR): 35 | mkdir -p $@ 36 | 37 | -include $(DEPS) 38 | $(BUILD_DIR)/%.o: %.c 39 | $(CC) -MMD -c $(CFLAGS) $< -o $@ 40 | 41 | -include $(DEPS) 42 | $(BUILD_DIR)/%.o: %.S 43 | $(CC) -MMD -c $(CFLAGS) $< -o $@ 44 | 45 | $(BUILD_DIR)/demo.elf: demo.lds $(OBJECTS) 46 | $(LD) $(LDFLAGS) -T $^ -o $@ 47 | 48 | $(BUILD_DIR)/demo.bin: demo.elf 49 | $(OBJCOPY) -O binary $< $@ 50 | 51 | clean: 52 | rm -rf $(BUILD_DIR) 53 | -------------------------------------------------------------------------------- /psram-bootloader/demo/src/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int entry(void* data); 6 | 7 | void _entry_(void* data) { 8 | uint32_t *regs = (uint32_t*)data; 9 | // a0 is in regs[9] which is the first argument and return value 10 | regs[9] = entry((void *)regs[9]); 11 | } 12 | 13 | int entry(void *data) { 14 | int (*pi_printf)(const char * format, ...) = (int (*)(const char * format, ...))data; 15 | pi_printf("this is super cursed\n"); 16 | pi_printf("this is super cursed\n"); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /psram-bootloader/demo/src/demo.lds: -------------------------------------------------------------------------------- 1 | __stack_size = 0x200; 2 | ENTRY(_start) 3 | 4 | SECTIONS 5 | { 6 | . = 0x11000000; 7 | .text : ALIGN(16) { 8 | *(.asm) 9 | *(.entry.text) 10 | *(.init.literal) 11 | *(.init) 12 | *(.text) 13 | } 14 | 15 | .data : ALIGN(16) { 16 | __DATA_BEGIN__ = .; 17 | *(.rodata) 18 | *(.rodata.*) 19 | *(.gnu.linkonce.r.*) 20 | *(.rodata1) 21 | *(.dynsbss) 22 | *(.gnu.linkonce.sb.*) 23 | *(.scommon) 24 | *(.gnu.linkonce.sb2.*) 25 | *(.sbss) 26 | *(.sbss.*) 27 | *(.sbss2) 28 | *(.sbss2.*) 29 | *(.dynbss) 30 | *(.data) 31 | *(.data.*) 32 | *(.got) 33 | *(.got.*) 34 | __DATA_END__ = .; 35 | } 36 | 37 | .bss : ALIGN( 16 ) { 38 | __BSS_BEGIN__ = .; 39 | *(.bss) 40 | *(.bss.*) 41 | __BSS_END__ = .; 42 | } 43 | 44 | .stack : ALIGN( 16 ) { 45 | _estack = .; 46 | . = . + __stack_size; 47 | _sstack = .; 48 | } 49 | /DISCARD/ : { *(.riscv.attributes) } 50 | } 51 | -------------------------------------------------------------------------------- /psram-bootloader/demo/src/start.S: -------------------------------------------------------------------------------- 1 | .section .asm, "ax" 2 | .global _start 3 | #define REGS_ARR_SZ (4*31) 4 | #define STORE_REGS(reg) sw x##reg, ((reg*4) - 0x4)(sp) 5 | #define LOAD_REGS(reg) lw x##reg, ((reg*4) - 0x4)(sp) 6 | 7 | .align 4 8 | _start: 9 | //Save ra to old stack 10 | sw ra, -16(sp) 11 | //Move sp into ra 12 | mv ra, sp 13 | 14 | la sp, _sstack 15 | addi sp,sp,-REGS_ARR_SZ 16 | 17 | // Save sp on stack 18 | sw ra, 0x4(sp) 19 | // load ra from old stack into ra 20 | lw ra, -16(ra) 21 | 22 | STORE_REGS(1) 23 | 24 | STORE_REGS(3) 25 | STORE_REGS(4) 26 | STORE_REGS(5) 27 | STORE_REGS(6) 28 | STORE_REGS(7) 29 | STORE_REGS(8) 30 | STORE_REGS(9) 31 | STORE_REGS(10) 32 | STORE_REGS(11) 33 | STORE_REGS(12) 34 | STORE_REGS(13) 35 | STORE_REGS(14) 36 | STORE_REGS(15) 37 | STORE_REGS(16) 38 | STORE_REGS(17) 39 | STORE_REGS(18) 40 | STORE_REGS(19) 41 | STORE_REGS(20) 42 | STORE_REGS(21) 43 | STORE_REGS(22) 44 | STORE_REGS(23) 45 | STORE_REGS(24) 46 | STORE_REGS(25) 47 | STORE_REGS(26) 48 | STORE_REGS(27) 49 | STORE_REGS(28) 50 | STORE_REGS(29) 51 | STORE_REGS(30) 52 | STORE_REGS(31) 53 | 54 | mv a0, sp 55 | jal ra, _entry_ 56 | 57 | LOAD_REGS(1) 58 | LOAD_REGS(3) 59 | LOAD_REGS(4) 60 | LOAD_REGS(5) 61 | LOAD_REGS(6) 62 | LOAD_REGS(7) 63 | LOAD_REGS(8) 64 | LOAD_REGS(9) 65 | LOAD_REGS(10) 66 | LOAD_REGS(11) 67 | LOAD_REGS(12) 68 | LOAD_REGS(13) 69 | LOAD_REGS(14) 70 | LOAD_REGS(15) 71 | LOAD_REGS(16) 72 | LOAD_REGS(17) 73 | LOAD_REGS(18) 74 | LOAD_REGS(19) 75 | LOAD_REGS(20) 76 | LOAD_REGS(21) 77 | LOAD_REGS(22) 78 | LOAD_REGS(23) 79 | LOAD_REGS(24) 80 | LOAD_REGS(25) 81 | LOAD_REGS(26) 82 | LOAD_REGS(27) 83 | LOAD_REGS(28) 84 | LOAD_REGS(29) 85 | LOAD_REGS(30) 86 | LOAD_REGS(31) 87 | 88 | // Load sp last 89 | LOAD_REGS(2) 90 | ret 91 | -------------------------------------------------------------------------------- /psram-bootloader/partition_table.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": [1, 0], 3 | "unpartitioned": { 4 | "families": ["absolute"], 5 | "permissions": { 6 | "secure": "rw", 7 | "nonsecure": "rw", 8 | "bootloader": "rw" 9 | } 10 | }, 11 | "partitions": [ 12 | { 13 | "name": "LINUX", 14 | "id": 0, 15 | "__comment__0_": "Start should be moved to avoid conflict with program memory", 16 | "start": "64K", 17 | "__comment__1_": "Size should be the size of kernel", 18 | "size": "8192K", 19 | "families": ["rp2350-riscv"], 20 | "permissions": { 21 | "secure": "rw", 22 | "nonsecure": "rw", 23 | "bootloader": "rw" 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /psram-bootloader/pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) 22 | set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) 23 | message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") 24 | endif () 25 | 26 | if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) 27 | set(PICO_SDK_FETCH_FROM_GIT_TAG "master") 28 | message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") 29 | endif() 30 | 31 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 32 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 33 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 34 | set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") 35 | 36 | if (NOT PICO_SDK_PATH) 37 | if (PICO_SDK_FETCH_FROM_GIT) 38 | include(FetchContent) 39 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 40 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 41 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 42 | endif () 43 | # GIT_SUBMODULES_RECURSE was added in 3.17 44 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 45 | FetchContent_Declare( 46 | pico_sdk 47 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 48 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 49 | GIT_SUBMODULES_RECURSE FALSE 50 | ) 51 | else () 52 | FetchContent_Declare( 53 | pico_sdk 54 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 55 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 56 | ) 57 | endif () 58 | 59 | if (NOT pico_sdk) 60 | message("Downloading Raspberry Pi Pico SDK") 61 | FetchContent_Populate(pico_sdk) 62 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 63 | endif () 64 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 65 | else () 66 | message(FATAL_ERROR 67 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 68 | ) 69 | endif () 70 | endif () 71 | 72 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 73 | if (NOT EXISTS ${PICO_SDK_PATH}) 74 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 75 | endif () 76 | 77 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 78 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 79 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 80 | endif () 81 | 82 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 83 | 84 | include(${PICO_SDK_INIT_CMAKE_FILE}) 85 | -------------------------------------------------------------------------------- /psram-bootloader/run-gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Compile https://github.com/raspberrypi/openocd to support Raspberry Pi Pico2 3 | sudo openocd -f interface/jlink.cfg -f target/rp2350-riscv.cfg > /dev/null 2>&1 & 4 | gdb-multiarch build/psram-bootloader.elf --command="debug.gdb" 5 | -------------------------------------------------------------------------------- /psram-bootloader/src/bootloader.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "pico/stdlib.h" 12 | #include "pico/bootrom.h" 13 | #include "boot/picobin.h" 14 | #include "hardware/watchdog.h" 15 | #include "hardware/structs/qmi.h" 16 | #include "hardware/structs/xip_ctrl.h" 17 | 18 | #define min(a, b) ((a) < (b) ? (a) : (b)) 19 | 20 | #if _BYTE_ORDER == _LITTLE_ENDIAN 21 | 22 | #define htobe16(x) __bswap16(x) 23 | #define htole16(x) (x) 24 | #define be16toh(x) __bswap16(x) 25 | #define le16toh(x) (x) 26 | 27 | #define htobe32(x) __bswap32(x) 28 | #define htole32(x) (x) 29 | #define be32toh(x) __bswap32(x) 30 | #define le32toh(x) (x) 31 | 32 | #define htobe64(x) __bswap64(x) 33 | #define htole64(x) (x) 34 | #define be64toh(x) __bswap64(x) 35 | #define le64toh(x) (x) 36 | 37 | #else 38 | 39 | #define htobe16(x) (x) 40 | #define htole16(x) __bswap16(x) 41 | #define be16toh(x) (x) 42 | #define le16toh(x) __bswap16(x) 43 | 44 | #define htobe32(x) (x) 45 | #define htole32(x) __bswap32(x) 46 | #define be32toh(x) (x) 47 | #define le32toh(x) __bswap32(x) 48 | 49 | #define htobe64(x) (x) 50 | #define htole64(x) __bswap64(x) 51 | #define be64toh(x) (x) 52 | #define le64toh(x) __bswap64(x) 53 | 54 | #endif 55 | 56 | #if defined(SPARKFUN_PROMICRO_RP2350) 57 | 58 | // For the pro micro rp2350 59 | #define SFE_RP2350_XIP_CSI_PIN 19 60 | #endif 61 | 62 | #define PSRAM_LOCATION _u(0x11000000) 63 | 64 | 65 | #define PART_LOC_FIRST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> \ 66 | PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB ) 67 | #define PART_LOC_LAST(x) ( ((x) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> \ 68 | PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB ) 69 | 70 | static size_t _psram_size = 0; 71 | 72 | static size_t __no_inline_not_in_flash_func(setup_psram)(uint psram_cs_pin); 73 | static void hexdump(const void *data, size_t size); 74 | static int rom_test(void **data_addr, size_t* data_size); 75 | static int psram_setup_and_test(void); 76 | static int test_executability(void* addr); 77 | static int wait_for_input(const char *msg); 78 | static size_t get_devicetree_size(const void *data); 79 | 80 | int main() { 81 | int ret; 82 | uint32_t jump_ret, data; 83 | size_t data_size, kernel_offset; 84 | void *data_addr, *ram_addr = (void *)PSRAM_LOCATION; 85 | 86 | stdio_init_all(); 87 | 88 | ret = psram_setup_and_test(); 89 | if (ret) { 90 | goto exit; 91 | } 92 | 93 | ret = rom_test(&data_addr, &data_size); 94 | if (ret) { 95 | goto exit; 96 | } 97 | 98 | if (data_size > _psram_size) { 99 | printf("Data size 0x%04x is larger than PSRAM size 0x%04x\n", 100 | data_size, _psram_size); 101 | goto exit; 102 | } 103 | 104 | if(data_size < 8) { 105 | printf("Data size is 0\n"); 106 | goto exit; 107 | } 108 | 109 | printf("\nRom dump:\n"); 110 | hexdump(data_addr, min(data_size, 0x20)); 111 | 112 | kernel_offset = get_devicetree_size(data_addr); 113 | if (kernel_offset) { 114 | printf("Kernel offset: 0x%08x\n", kernel_offset); 115 | } else { 116 | printf("No kernel + device tree found\n"); 117 | } 118 | 119 | memcpy(ram_addr, data_addr + kernel_offset, data_size - kernel_offset); 120 | printf("\nRam dump:\n"); 121 | hexdump(ram_addr, 0x20); 122 | 123 | if (kernel_offset) { 124 | typedef void (*image_entry_arg_t)(unsigned long hart, void *dtb); 125 | image_entry_arg_t image_entry = (image_entry_arg_t)ram_addr; 126 | 127 | printf("\nJumping to kernel at 0x%08x and DT at 0x%08x\n", ram_addr, data_addr); 128 | printf("If you are using USB serial, please connect over the hardware serial port.\n"); 129 | image_entry(0, data_addr); 130 | } 131 | 132 | ret = wait_for_input("Press y to jump to PSRAM...\r"); 133 | if (ret == 'y' || ret == 'Y') { 134 | data = (uint32_t)printf; 135 | jump_ret = ((uint32_t (*)(void *))ram_addr)((void *)data); 136 | printf("Jump to PSRAM returned 0x%08x\n", jump_ret); 137 | } 138 | 139 | exit: 140 | wait_for_input("Press any key to reset.\r"); 141 | puts("Resetting..."); 142 | watchdog_reboot(0, 0, 0); 143 | return 0; 144 | } 145 | 146 | static int wait_for_input(const char *msg) { 147 | int ret; 148 | 149 | while (1) { 150 | printf(msg); 151 | ret = getchar_timeout_us(0); 152 | if (ret != PICO_ERROR_TIMEOUT) { 153 | puts(""); 154 | return ret; 155 | } 156 | sleep_ms(1000); 157 | } 158 | } 159 | 160 | static size_t get_devicetree_size(const void *data) { 161 | const uint32_t *data_ptr = (const uint32_t *)data; 162 | 163 | if (be32toh(data_ptr[0]) == 0xd00dfeed) { 164 | return be32toh(data_ptr[1]); 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | static int test_executability(void* addr) { 171 | const uint16_t ret_inst = 0x8082; 172 | size_t addr_aligned = ((size_t)addr) & ~1; 173 | volatile uint16_t* addr_ptr = (volatile uint16_t*)addr_aligned; 174 | 175 | __mem_fence_acquire(); 176 | *addr_ptr = ret_inst; 177 | __mem_fence_release(); 178 | 179 | printf("Jumping to 0x%08x, aligned from 0x%08x\n", addr_aligned, (size_t)addr); 180 | printf("Function pointers: 0x%02x 0x%02x\n", ((uint8_t*)addr_ptr)[0], ((uint8_t*)addr_ptr)[1]); 181 | 182 | if (*addr_ptr != ret_inst) { 183 | printf("ERROR: Expected 0x%04x, got 0x%04x\n", ret_inst, *addr_ptr); 184 | return -1; 185 | } 186 | 187 | ((void (*)(void))addr_aligned)(); 188 | return 0; 189 | } 190 | 191 | static int psram_setup_and_test(void) { 192 | _psram_size = setup_psram(SFE_RP2350_XIP_CSI_PIN); 193 | 194 | if (!_psram_size) { 195 | printf("PSRAM setup failed\n"); 196 | return -1; 197 | } 198 | 199 | printf("PSRAM setup complete. PSRAM size 0x%lX (%d)\n", _psram_size, _psram_size); 200 | 201 | return test_executability((size_t *)(PSRAM_LOCATION + _psram_size - 4)); 202 | } 203 | 204 | static int rom_test(void **data_addr, size_t* data_size) { 205 | static __attribute__((aligned(4))) uint32_t workarea[1024]; 206 | uint32_t data_end_addr, *data_start_addr = ((uint32_t *)data_addr); 207 | int rc; 208 | 209 | rc = rom_load_partition_table((uint8_t *)workarea, sizeof(workarea), false); 210 | if (rc) { 211 | printf("Partition Table Load failed %d - resetting\n", rc); 212 | return -1; 213 | } 214 | 215 | rc = rom_get_partition_table_info((uint32_t*)workarea, 0x8, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION); 216 | if (rc != 3) { 217 | printf("No boot partition - assuming bin at start of flash\n"); 218 | return -1; 219 | } 220 | 221 | *data_start_addr = PART_LOC_FIRST(workarea[1]) * 0x1000; 222 | data_end_addr = (PART_LOC_LAST(workarea[1]) + 1) * 0x1000; 223 | *data_size = data_end_addr - *data_start_addr; 224 | *data_start_addr += XIP_BASE; 225 | printf("Partition Start 0x%04x, End 0x%04x, Size: 0x%04x\n", 226 | *data_start_addr, data_end_addr, *data_size); 227 | 228 | return 0; 229 | } 230 | 231 | static void hexdump(const void *data, size_t size) { 232 | const uint8_t *data_ptr = (const uint8_t *)data; 233 | size_t i, b; 234 | 235 | for (i = 0; i < size; i++) { 236 | if (i % 16 == 0) { 237 | printf("%08x ", (uint32_t)data_ptr + i); 238 | } 239 | if (i % 8 == 0) { 240 | printf(" "); 241 | } 242 | printf("%02x ", data_ptr[i]); 243 | if (i % 16 == 15) { 244 | printf(" |"); 245 | for (b = 0; b < 16; b++){ 246 | if (isprint(data_ptr[i + b - 15])) { 247 | printf("%c", data_ptr[i + b - 15]); 248 | } else { 249 | printf("."); 250 | } 251 | } 252 | printf("|\n"); 253 | } 254 | } 255 | printf("%08x\n", 16 + size - (size%16)); 256 | } 257 | 258 | // The ID check is from the Circuit Python code that was downloaded from: 259 | // https://github.com/raspberrypi/pico-sdk-rp2350/issues/12#issuecomment-2055274428 260 | // 261 | // in the file supervisor/port.c -- the function setup_psram() 262 | 263 | // NOTE: The PSRAM IC used by the Circuit Python example is: 264 | // https://www.adafruit.com/product/4677 265 | // 266 | // The PSRAM IC used by the SparkFun Pro Micro is: apmemory APS6404L-3SQR-ZR 267 | // https://www.mouser.com/ProductDetail/AP-Memory/APS6404L-3SQR-ZR?qs=IS%252B4QmGtzzpDOdsCIglviw%3D%3D 268 | // 269 | // The datasheets from both these IC's are almost identical (word for word), with the first being ESP32 branded 270 | // 271 | 272 | static size_t __no_inline_not_in_flash_func(setup_psram)(uint psram_cs_pin) 273 | { 274 | gpio_set_function(psram_cs_pin, GPIO_FUNC_XIP_CS1); 275 | 276 | size_t psram_size = 0; 277 | uint32_t intr_stash = save_and_disable_interrupts(); 278 | 279 | // Try and read the PSRAM ID via direct_csr. 280 | qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS; 281 | // Need to poll for the cooldown on the last XIP transfer to expire 282 | // (via direct-mode BUSY flag) before it is safe to perform the first 283 | // direct-mode operation 284 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) 285 | { 286 | } 287 | 288 | // Exit out of QMI in case we've inited already 289 | qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; 290 | // Transmit as quad. 291 | qmi_hw->direct_tx = QMI_DIRECT_TX_OE_BITS | QMI_DIRECT_TX_IWIDTH_VALUE_Q << QMI_DIRECT_TX_IWIDTH_LSB | 0xf5; 292 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) 293 | { 294 | } 295 | (void)qmi_hw->direct_rx; 296 | qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); 297 | 298 | // Read the id 299 | qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; 300 | uint8_t kgd = 0; 301 | uint8_t eid = 0; 302 | for (size_t i = 0; i < 7; i++) 303 | { 304 | if (i == 0) 305 | { 306 | qmi_hw->direct_tx = 0x9f; 307 | } 308 | else 309 | { 310 | qmi_hw->direct_tx = 0xff; 311 | } 312 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_TXEMPTY_BITS) == 0) 313 | { 314 | } 315 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) 316 | { 317 | } 318 | if (i == 5) 319 | { 320 | kgd = qmi_hw->direct_rx; 321 | } 322 | else if (i == 6) 323 | { 324 | eid = qmi_hw->direct_rx; 325 | } 326 | else 327 | { 328 | (void)qmi_hw->direct_rx; 329 | } 330 | } 331 | // Disable direct csr. 332 | qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); 333 | 334 | if (kgd != 0x5D) 335 | { 336 | printf("Invalid PSRAM ID: %x\n", kgd); 337 | restore_interrupts(intr_stash); 338 | return psram_size; 339 | } 340 | 341 | // Enable quad mode. 342 | qmi_hw->direct_csr = 30 << QMI_DIRECT_CSR_CLKDIV_LSB | QMI_DIRECT_CSR_EN_BITS; 343 | // Need to poll for the cooldown on the last XIP transfer to expire 344 | // (via direct-mode BUSY flag) before it is safe to perform the first 345 | // direct-mode operation 346 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) 347 | { 348 | } 349 | 350 | // RESETEN, RESET and quad enable 351 | for (uint8_t i = 0; i < 3; i++) 352 | { 353 | qmi_hw->direct_csr |= QMI_DIRECT_CSR_ASSERT_CS1N_BITS; 354 | if (i == 0) 355 | { 356 | qmi_hw->direct_tx = 0x66; 357 | } 358 | else if (i == 1) 359 | { 360 | qmi_hw->direct_tx = 0x99; 361 | } 362 | else 363 | { 364 | qmi_hw->direct_tx = 0x35; 365 | } 366 | while ((qmi_hw->direct_csr & QMI_DIRECT_CSR_BUSY_BITS) != 0) 367 | { 368 | } 369 | qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS); 370 | for (size_t j = 0; j < 20; j++) 371 | { 372 | asm("nop"); 373 | } 374 | (void)qmi_hw->direct_rx; 375 | } 376 | // Disable direct csr. 377 | qmi_hw->direct_csr &= ~(QMI_DIRECT_CSR_ASSERT_CS1N_BITS | QMI_DIRECT_CSR_EN_BITS); 378 | 379 | qmi_hw->m[1].timing = 380 | QMI_M1_TIMING_PAGEBREAK_VALUE_1024 << QMI_M1_TIMING_PAGEBREAK_LSB | // Break between pages. 381 | 3 << QMI_M1_TIMING_SELECT_HOLD_LSB | // Delay releasing CS for 3 extra system cycles. 382 | 1 << QMI_M1_TIMING_COOLDOWN_LSB | 1 << QMI_M1_TIMING_RXDELAY_LSB | 383 | 16 << QMI_M1_TIMING_MAX_SELECT_LSB | // In units of 64 system clock cycles. PSRAM says 8us max. 8 / 0.00752 /64 384 | // = 16.62 385 | 7 << QMI_M1_TIMING_MIN_DESELECT_LSB | // In units of system clock cycles. PSRAM says 50ns.50 / 7.52 = 6.64 386 | 2 << QMI_M1_TIMING_CLKDIV_LSB; 387 | qmi_hw->m[1].rfmt = (QMI_M1_RFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_PREFIX_WIDTH_LSB | 388 | QMI_M1_RFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_RFMT_ADDR_WIDTH_LSB | 389 | QMI_M1_RFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_RFMT_SUFFIX_WIDTH_LSB | 390 | QMI_M1_RFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_RFMT_DUMMY_WIDTH_LSB | 391 | QMI_M1_RFMT_DUMMY_LEN_VALUE_24 << QMI_M1_RFMT_DUMMY_LEN_LSB | 392 | QMI_M1_RFMT_DATA_WIDTH_VALUE_Q << QMI_M1_RFMT_DATA_WIDTH_LSB | 393 | QMI_M1_RFMT_PREFIX_LEN_VALUE_8 << QMI_M1_RFMT_PREFIX_LEN_LSB | 394 | QMI_M1_RFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_RFMT_SUFFIX_LEN_LSB); 395 | qmi_hw->m[1].rcmd = 0xeb << QMI_M1_RCMD_PREFIX_LSB | 0 << QMI_M1_RCMD_SUFFIX_LSB; 396 | qmi_hw->m[1].wfmt = (QMI_M1_WFMT_PREFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_PREFIX_WIDTH_LSB | 397 | QMI_M1_WFMT_ADDR_WIDTH_VALUE_Q << QMI_M1_WFMT_ADDR_WIDTH_LSB | 398 | QMI_M1_WFMT_SUFFIX_WIDTH_VALUE_Q << QMI_M1_WFMT_SUFFIX_WIDTH_LSB | 399 | QMI_M1_WFMT_DUMMY_WIDTH_VALUE_Q << QMI_M1_WFMT_DUMMY_WIDTH_LSB | 400 | QMI_M1_WFMT_DUMMY_LEN_VALUE_NONE << QMI_M1_WFMT_DUMMY_LEN_LSB | 401 | QMI_M1_WFMT_DATA_WIDTH_VALUE_Q << QMI_M1_WFMT_DATA_WIDTH_LSB | 402 | QMI_M1_WFMT_PREFIX_LEN_VALUE_8 << QMI_M1_WFMT_PREFIX_LEN_LSB | 403 | QMI_M1_WFMT_SUFFIX_LEN_VALUE_NONE << QMI_M1_WFMT_SUFFIX_LEN_LSB); 404 | qmi_hw->m[1].wcmd = 0x38 << QMI_M1_WCMD_PREFIX_LSB | 0 << QMI_M1_WCMD_SUFFIX_LSB; 405 | 406 | psram_size = 1024 * 1024; // 1 MiB 407 | uint8_t size_id = eid >> 5; 408 | if (eid == 0x26 || size_id == 2) 409 | { 410 | psram_size *= 8; 411 | } 412 | else if (size_id == 0) 413 | { 414 | psram_size *= 2; 415 | } 416 | else if (size_id == 1) 417 | { 418 | psram_size *= 4; 419 | } 420 | 421 | // Mark that we can write to PSRAM. 422 | xip_ctrl_hw->ctrl |= XIP_CTRL_WRITABLE_M1_BITS; 423 | restore_interrupts(intr_stash); 424 | // printf("PSRAM ID: %x %x\n", kgd, eid); 425 | return psram_size; 426 | } 427 | --------------------------------------------------------------------------------