From 0fc693e42b934ad01e4ba8bb1b542676110f2f52 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 21:21:24 +0100 Subject: [PATCH 01/11] evbarm/am18: build lego mindstorms ev3 device tree --- sys/dtb/arm/ti/Makefile | 1 + sys/dtb/arm/ti/davinci/Makefile | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 sys/dtb/arm/ti/davinci/Makefile diff --git a/sys/dtb/arm/ti/Makefile b/sys/dtb/arm/ti/Makefile index 896a0a01a8c80..abefb44d7c1a6 100644 --- a/sys/dtb/arm/ti/Makefile +++ b/sys/dtb/arm/ti/Makefile @@ -3,5 +3,6 @@ TARGETS+= dtblist SUBDIR+= omap +SUBDIR+= davinci .include diff --git a/sys/dtb/arm/ti/davinci/Makefile b/sys/dtb/arm/ti/davinci/Makefile new file mode 100644 index 0000000000000..efe4779c6efbb --- /dev/null +++ b/sys/dtb/arm/ti/davinci/Makefile @@ -0,0 +1,10 @@ +# $NetBSD $ + +DTSSUBDIR= ti/davinci +.if !empty(MACHINE_ARCH:Mearmv5*) +DTSMAKEVARS= CONFIG_ARCH_DAVINCI=y +.endif +DTSFILESCMD= ${MAKE} -C ${ARCHDTSDIR}/${DTSSUBDIR} ${DTSMAKEVARS} -v dtb-y +DTS= ${DTSFILESCMD:sh} + +.include From 2e20234867222c041b032653036f38f44a5ba021 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 21:40:46 +0100 Subject: [PATCH 02/11] evbarm/am18: basic plaform code --- sys/arch/arm/ti/am18_platform.c | 219 ++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 2 + sys/arch/evbarm/conf/GENERIC_V5 | 2 + sys/arch/evbarm/conf/files.generic_v5 | 1 + 4 files changed, 224 insertions(+) create mode 100644 sys/arch/arm/ti/am18_platform.c diff --git a/sys/arch/arm/ti/am18_platform.c b/sys/arch/arm/ti/am18_platform.c new file mode 100644 index 0000000000000..4b0045c286951 --- /dev/null +++ b/sys/arch/arm/ti/am18_platform.c @@ -0,0 +1,219 @@ +/* $NetBSD $ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Yuri Honegger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Platform code for the TI AM18XX family of SOCs (AM1808, AM1810). In linux + * land and in the device trees, this platform is sometimes also called DA830 + * and DA850 (Davinci 8XX) because their silicon has a lot in common. + */ + +#include "opt_console.h" + +#include +__KERNEL_RCSID(0, "$NetBSD $"); + +#include + +#include +#include +#include + +#include +#include + +#define AM18_IO_VBASE KERNEL_IO_VBASE +#define AM18_IO_PBASE 0x01c00000 +#define AM18_IO_SIZE 0x00400000 +#define AM18_INTC_VBASE (AM18_IO_VBASE + AM18_IO_SIZE) +#define AM18_INTC_PBASE 0xfffee000 +#define AM18_INTC_SIZE 0x2000 + +#define AM18_TIMER1_BASE 0x01C21000 +#define AM18_TIMER1_SIZE 0x1000 +#define AM18_TIMER1_TIM12 0x10 +#define AM18_TIMER1_TIM34 0x14 +#define AM18_TIMER1_PRD12 0x18 +#define AM18_TIMER1_PRD34 0x1C +#define AM18_TIMER1_TCR 0x20 +#define AM18_TIMER1_TGCR 0x24 +#define AM18_TIMER1_WDTCR 0x28 + +#define AM18_TIMER_TCR_ENAMODE12_CONTINUOUS 0x80 +#define AM18_TIMER_TGCR_TIMMODE32_UNCHAINED 0x4 +#define AM18_TIMER_TGCR_TIMMODE64_WATCHDOG 0x8 +#define AM18_TIMER_TGCR_TIM12EN 1 +#define AM18_TIMER_TGCR_TIM34EN 2 +#define AM18_TIMER_WDTCR_WDEN 1 +#define AM18_TIMER_WDTCR_KEY0 0xa5c60000 +#define AM18_TIMER_WDTCR_KEY1 0xda7e0000 + + +void am18_platform_early_putchar(char); + +extern struct arm32_bus_dma_tag arm_generic_dma_tag; +extern struct bus_space arm_generic_bs_tag; + +void __noasan +am18_platform_early_putchar(char c) +{ +#ifdef CONSADDR +#define CONSADDR_VA (CONSADDR - AM18_IO_PBASE + KERNEL_IO_VBASE) + volatile uint32_t *uartaddr = cpu_earlydevice_va_p() + ? (volatile uint32_t *)CONSADDR_VA + : (volatile uint32_t *)CONSADDR; + + while ((le32toh(uartaddr[com_lsr]) & LSR_TXRDY) == 0) + continue; + + uartaddr[com_data] = htole32(c); +#endif +} + + +static const struct pmap_devmap * +am18_platform_devmap(void) +{ + static const struct pmap_devmap devmap[] = { + /* input/output registers */ + DEVMAP_ENTRY(AM18_IO_VBASE, + AM18_IO_PBASE, + AM18_IO_SIZE), + /* interrupt controller */ + DEVMAP_ENTRY(AM18_INTC_VBASE, + AM18_INTC_PBASE, + AM18_INTC_SIZE), + DEVMAP_ENTRY_END + }; + + return devmap; +} + +static void +am18_platform_init_attach_args(struct fdt_attach_args *faa) +{ + faa->faa_bst = &arm_generic_bs_tag; + faa->faa_dmat = &arm_generic_dma_tag; +} + +static void +am18_platform_delay(u_int n) +{ + /* Use Timer1 for delay. Timer0 is used*/ + static bus_space_tag_t bst = &arm_generic_bs_tag; + static bus_space_handle_t bsh = 0; + + if (bsh == 0) { + /* map Timer1 */ + bus_space_map(bst, AM18_TIMER1_BASE, AM18_TIMER1_SIZE, 0, &bsh); + + /* disable counter to allow changing mode */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TCR, 0); + /* set mode to 32-bit unchained */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TGCR, + AM18_TIMER_TGCR_TIMMODE32_UNCHAINED | + AM18_TIMER_TGCR_TIM12EN); + /* load period registers with maximum period */ + bus_space_write_4(bst, bsh, AM18_TIMER1_PRD12, 0xFFFFFFFF); + /* enable timer */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TCR, + AM18_TIMER_TCR_ENAMODE12_CONTINUOUS); + + } + + /* + * The counter is driven by PLL0_AUXCLK, which is taken from OSCIN. + * On the EV3, that is 24MHz. + * + * n is in microseconds (us) + * + * TODO: The frequency is board-dependent and we should get it from the + * device tree. + */ + long ticks = n * (24000000 / 1000000); + + uint32_t prev, cur; + prev = bus_space_read_4(bst, bsh, AM18_TIMER1_TIM12); + while (ticks > 0) { + cur = bus_space_read_4(bst, bsh, AM18_TIMER1_TIM12); + if (cur >= prev) + ticks -= (cur - prev); + else + ticks -= (UINT32_MAX - cur + prev); + prev = cur; + } +} + +/* + * To reset the am18, you have to crash the watchdog. + */ +static void +am18_platform_reset(void) +{ + /* map TIMER1 */ + bus_space_tag_t bst = &arm_generic_bs_tag; + bus_space_handle_t bsh = 0; + bus_space_map(bst, AM18_TIMER1_BASE, AM18_TIMER1_SIZE, 0, &bsh); + + /* disable counter to allow changing mode */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TCR, 0); + /* set mode to watchdog unchained */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TGCR, + AM18_TIMER_TGCR_TIMMODE64_WATCHDOG | + AM18_TIMER_TGCR_TIM12EN | + AM18_TIMER_TGCR_TIM34EN); + /* set counter and reload registers */ + bus_space_write_4(bst, bsh, AM18_TIMER1_TIM12, 0); + bus_space_write_4(bst, bsh, AM18_TIMER1_TIM34, 0); + bus_space_write_4(bst, bsh, AM18_TIMER1_PRD12, 0); + bus_space_write_4(bst, bsh, AM18_TIMER1_PRD34, 0); + + /* execute the watchdog enable sequence */ + bus_space_write_4(bst, bsh, AM18_TIMER1_WDTCR, + AM18_TIMER_WDTCR_KEY0 | AM18_TIMER_WDTCR_WDEN); + bus_space_write_4(bst, bsh, AM18_TIMER1_WDTCR, + AM18_TIMER_WDTCR_KEY1 | AM18_TIMER_WDTCR_WDEN); + /* trigger a reset by writing an invalid value to WDTCR*/ + bus_space_write_4(bst, bsh, AM18_TIMER1_WDTCR, 0xaffe); + + /* NOTREACHED */ +} + +static const struct fdt_platform am18_platform = { + .fp_devmap = am18_platform_devmap, + .fp_bootstrap = arm_fdt_cpu_bootstrap, + .fp_init_attach_args = am18_platform_init_attach_args, + .fp_uart_freq = NULL, + .fp_delay = am18_platform_delay, + .fp_reset = am18_platform_reset, +}; + +/* The device tree name for the AM1808 is da850 (davinci 850) */ +FDT_PLATFORM(am18, "ti,da850", &am18_platform); diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index f29bd3e1dbb97..25e2e70cb1414 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -2,6 +2,7 @@ # file arch/arm/ti/ti_cpufreq.c soc_ti +file arch/arm/ti/am18_platform.c soc_am18 file arch/arm/ti/am3_platform.c soc_am33xx file arch/arm/ti/omap3_platform.c soc_omap3 | soc_omap4 file arch/arm/ti/omap_smc.S soc_omap4 @@ -162,6 +163,7 @@ file arch/arm/ti/ti_wdt.c ti_wdt # SOC parameters XXX FDT_SYSCON defflag opt_soc.h SOC_TI: FDT_SYSCON +defflag opt_soc.h SOC_AM18: SOC_TI defflag opt_soc.h SOC_AM33XX: SOC_TI defflag opt_soc.h SOC_OMAP3: SOC_TI defflag opt_soc.h SOC_OMAP4: SOC_TI diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index 0274562fc0543..f6efd2a249275 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -10,6 +10,7 @@ include "arch/evbarm/conf/GENERIC.common" options CPU_ARM9E options SOC_IMX23 +options SOC_AM18 #options DIAGNOSTIC # internal consistency checks #options DEBUG @@ -30,6 +31,7 @@ options MSGBUFSIZE=32768 # EARLYCONS is required for early init messages from VERBOSE_INIT_ARM. #options EARLYCONS=imx23, CONSADDR=0x80070000 +#options EARLYCONS=am18, CONSADDR=0x01D0C000 # serial1 # Kernel Undefined Behavior Sanitizer (kUBSan). Use UBSAN_ALWAYS_FATAL # if you want panics instead of warnings. diff --git a/sys/arch/evbarm/conf/files.generic_v5 b/sys/arch/evbarm/conf/files.generic_v5 index fa87f12617397..00bb455327d81 100644 --- a/sys/arch/evbarm/conf/files.generic_v5 +++ b/sys/arch/evbarm/conf/files.generic_v5 @@ -18,6 +18,7 @@ file arch/arm/arm/bus_space_a4x.S # Add other board files here include "arch/arm/imx/files.imx23" +include "arch/arm/ti/files.ti" # # Stack-less Just-In-Time compiler From c9abb08ca6478e6117872a3eaa97c38032f95fcc Mon Sep 17 00:00:00 2001 From: recursivetree Date: Tue, 6 Jan 2026 23:24:44 +0100 Subject: [PATCH 03/11] evbarm/generic_v5: change kernel virtual address The old KERNEL_BASE_VIRT was conflicting with the MMIO registers of the am1808 soc. The new value should be out of the way of all boards using evbarm/generic_v5 --- sys/arch/evbarm/conf/std.generic_v5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/arch/evbarm/conf/std.generic_v5 b/sys/arch/evbarm/conf/std.generic_v5 index cd54c9cd2e9be..3ad7cc8a30eb1 100644 --- a/sys/arch/evbarm/conf/std.generic_v5 +++ b/sys/arch/evbarm/conf/std.generic_v5 @@ -43,7 +43,7 @@ makeoptions KERNEL_VOFFSET_RUNTIME=1 # The physical address is chosen by u-boot and determined by armv6_start.S. # The 64 byte offset is due to u-boot header. makeoptions KERNEL_BASE_PHYS="0x00000040" -makeoptions KERNEL_BASE_VIRT="0xc0000040" +makeoptions KERNEL_BASE_VIRT="0x81000040" # General options options CHILD_MAX=1024 # 160 is too few From 5aa3b7f69239ba39402936ec5ab397fac24ba096 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:05:12 +0100 Subject: [PATCH 04/11] evbarm/am18: add interrupt controller --- sys/arch/arm/ti/am18_intc.c | 298 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 303 insertions(+) create mode 100644 sys/arch/arm/ti/am18_intc.c diff --git a/sys/arch/arm/ti/am18_intc.c b/sys/arch/arm/ti/am18_intc.c new file mode 100644 index 0000000000000..e205c5aa33410 --- /dev/null +++ b/sys/arch/arm/ti/am18_intc.c @@ -0,0 +1,298 @@ +/* $NetBSD $ */ + +/*- +* Copyright (c) 2025 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Yuri Honegger. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Interrupt controller for the TI AM18XX SOC + */ + +#define _INTR_PRIVATE + +#include +#include +#include + +#include + +#include +#include + +struct am18_intc_softc { + struct pic_softc sc_pic; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_num_irqs; +}; + +static int am18_intc_match(device_t, cfdata_t, void *); +static void am18_intc_attach(device_t, device_t, void *); + +static void am18_intc_unblock_irqs(struct pic_softc *, size_t, uint32_t); +static void am18_intc_block_irqs(struct pic_softc *, size_t, uint32_t); +static void am18_intc_establish_irq(struct pic_softc *, + struct intrsource *); +static void am18_intc_set_priority(struct pic_softc *, int); +static void am18_intc_irq_handler(void *); +static void *am18_intc_fdt_establish(device_t, u_int *, int, int, + int (*)(void *), void *, + const char *); +static void am18_intc_fdt_disestablish(device_t, void *); +static bool am18_intc_fdt_intrstr(device_t, u_int *, char *, size_t); +static void am18_intc_enable_interrupts(struct am18_intc_softc *); + +#define AM18_AINTC_REGBASE 0xFFFEE000 + +#define AM18_AINTC_CR (0xFFFEE004 - AM18_AINTC_REGBASE) +#define AM18_AINTC_GER (0xFFFEE010 - AM18_AINTC_REGBASE) +#define AM18_AINTC_HIER (0xFFFEF500 - AM18_AINTC_REGBASE) +#define AM18_AINTC_SECR1 (0xFFFEE280 - AM18_AINTC_REGBASE) +#define AM18_AINTC_ESR1 (0xFFFEE300 - AM18_AINTC_REGBASE) +#define AM18_AINTC_ECR1 (0xFFFEE380 - AM18_AINTC_REGBASE) +#define AM18_AINTC_CMR0 (0xFFFEE400 - AM18_AINTC_REGBASE) + +#define AM18_AINTC_GER_ENABLE 1 +#define AM18_AINTC_HIER_IRQ 2 +#define AM18_AINTC_IRQ_CHANNEL 4 /* any number between 2 and 31 works */ + +#define INTC_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg) +#define INTC_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val) +#define PICTOSOFTC(pic) \ + ((struct am18_intc_softc *) \ + ((uintptr_t)(pic) - offsetof(struct am18_intc_softc, sc_pic))) + +static struct am18_intc_softc *intc_softc; + +CFATTACH_DECL_NEW(am18intc, sizeof(struct am18_intc_softc), + am18_intc_match, am18_intc_attach, NULL, NULL); + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,cp-intc" }, + DEVICE_COMPAT_EOL +}; + +static const struct fdtbus_interrupt_controller_func am18_intc_fdt_funcs = { + .establish = am18_intc_fdt_establish, + .disestablish = am18_intc_fdt_disestablish, + .intrstr = am18_intc_fdt_intrstr, +}; + +static const struct pic_ops am18_intc_picops = { + .pic_unblock_irqs = am18_intc_unblock_irqs, + .pic_block_irqs = am18_intc_block_irqs, + .pic_establish_irq = am18_intc_establish_irq, + .pic_set_priority = am18_intc_set_priority, +}; + +static void * +am18_intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, + int (*func)(void *), void *arg, const char *xname) +{ + struct am18_intc_softc * const sc = device_private(dev); + + const u_int irq = be32toh(specifier[0]); + if (irq >= sc->sc_num_irqs) { + device_printf(dev, "IRQ %u is out of range\n", irq); + return NULL; + } + + const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; + return intr_establish_xname(irq, ipl, IST_LEVEL | mpsafe, func, arg, + xname); +} + +static void +am18_intc_fdt_disestablish(device_t dev, void *ih) +{ + return intr_disestablish(ih); +} + +static bool +am18_intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, + size_t buflen) +{ + const u_int irq = be32toh(specifier[0]); + snprintf(buf, buflen, "irq %d", irq); + return true; +} + +static void +am18_intc_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t mask) +{ + struct am18_intc_softc * const sc = PICTOSOFTC(pic); + + const size_t group = irq_base / 32; + uint32_t esr_reg = AM18_AINTC_ESR1 + 4 * group; + INTC_WRITE(sc, esr_reg, mask); +} + +static void +am18_intc_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t mask) +{ + struct am18_intc_softc * const sc = PICTOSOFTC(pic); + + const size_t group = irq_base / 32; + uint32_t ecr_reg = AM18_AINTC_ECR1 + 4 * group; + INTC_WRITE(sc, ecr_reg, mask); +} + +static void +am18_intc_establish_irq(struct pic_softc *pic, struct intrsource *is) +{ + struct am18_intc_softc * const sc = PICTOSOFTC(pic); + + /* there is one CMR register per 4 interrupts*/ + uint32_t cmr_reg = AM18_AINTC_CMR0 + (is->is_irq & (~0x3)); + uint32_t cmr_shift = (is->is_irq % 4) * 8; + + /* update CMR register with channel */ + uint32_t cmr = INTC_READ(sc, cmr_reg); + cmr = cmr & ~(0xFF<ci_cpl = new_ipl; +} + +static int +find_pending_irqs(struct am18_intc_softc *sc, size_t group) +{ + uint32_t reg = AM18_AINTC_SECR1 + (4 * group); + uint32_t pending = INTC_READ(sc, reg); + + /* clear interrupts */ + INTC_WRITE(sc, reg, pending); + + if (pending == 0) + return 0; + + return pic_mark_pending_sources(&sc->sc_pic, group * 32, pending); +} + +static void +am18_intc_irq_handler(void *frame) +{ + struct cpu_info * const ci = curcpu(); + struct am18_intc_softc * const sc = intc_softc; + const int old_ipl = ci->ci_cpl; + const uint32_t oldipl_mask = __BIT(old_ipl); + int ipl_mask = 0; + + ci->ci_data.cpu_nintr++; + + for (int group = 0; group <= sc->sc_num_irqs/32; group++) { + ipl_mask |= find_pending_irqs(sc, group); + } + + if ((ipl_mask & ~oldipl_mask) > oldipl_mask) + pic_do_pending_ints(I32_bit, old_ipl, frame); +} + +/* + * Enable interrupt according to the TRM section 11.3.2 + */ +static void am18_intc_enable_interrupts(struct am18_intc_softc *sc) +{ + /* disable interrupts */ + INTC_WRITE(sc, AM18_AINTC_GER, 0); + INTC_WRITE(sc, AM18_AINTC_HIER, 0); + + /* disable nesting and prioritization in CR */ + INTC_WRITE(sc, AM18_AINTC_CR, 0); + + /* disable all interrupts, clear their status */ + for (int i = 0; i <= sc->sc_num_irqs / 32; i++) { + INTC_WRITE(sc, AM18_AINTC_ECR1 + 4 * i, 0xFFFFFFFF); + INTC_WRITE(sc, AM18_AINTC_SECR1 + 4 * i, 0xFFFFFFFF); + } + + /* enable IRQ interrupts */ + INTC_WRITE(sc, AM18_AINTC_HIER, AM18_AINTC_HIER_IRQ); + + /* set enable bit in GER */ + INTC_WRITE(sc, AM18_AINTC_GER, AM18_AINTC_GER_ENABLE); +} + +int +am18_intc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +void +am18_intc_attach(device_t parent, device_t self, void *aux) +{ + struct am18_intc_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + uint32_t maxsources; + + sc->sc_bst = faa->faa_bst; + intc_softc = sc; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + if (of_getprop_uint32(phandle, "ti,intc-size", &maxsources) != 0) { + aprint_error(": couldn't get max interrupt number\n"); + return; + } + sc->sc_num_irqs = maxsources; + + sc->sc_pic.pic_ops = &am18_intc_picops; + sc->sc_pic.pic_maxsources = maxsources; + strlcpy(sc->sc_pic.pic_name, device_xname(self), + sizeof(sc->sc_pic.pic_name)); + + pic_add(&sc->sc_pic, 0); + fdtbus_register_interrupt_controller(self, phandle, + &am18_intc_fdt_funcs); + + arm_fdt_irq_set_handler(am18_intc_irq_handler); + am18_intc_enable_interrupts(sc); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 25e2e70cb1414..84e05110d13af 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -12,6 +12,10 @@ device omapintc: pic, pic_splfuncs attach omapintc at fdt file arch/arm/ti/ti_omapintc.c omapintc +device am18intc: pic, pic_splfuncs +attach am18intc at fdt +file arch/arm/ti/am18_intc.c am18intc + # WakeupGen device omapwugen attach omapwugen at fdt with omapwugen diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index f6efd2a249275..8af990b6b47f2 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -61,6 +61,7 @@ cpu* at fdt? pass 0 # interrupt handlers imx23icoll* at fdt? pass 1 # imx23 interrupt driver +am18intc* at fdt? pass 1 # am18 interrupt controller # Timers imx23timrot* at fdt? pass 2 # imx23 timer From 660bd6ea367cc2905a86fd1034352e8f3efe3ce6 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:11:32 +0100 Subject: [PATCH 05/11] evbarm/generic_v5: fix setstatclockrate crash setstatclockrate is an optional function for timers that currently cannot be multiplexed through device trees. We don't provide an implementation in the am18 code, but we need to make sure the imx23 driver doesn't crash us. --- sys/arch/arm/imx/imx23_timrot.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sys/arch/arm/imx/imx23_timrot.c b/sys/arch/arm/imx/imx23_timrot.c index bc577546a5c2d..559171f08416a 100644 --- a/sys/arch/arm/imx/imx23_timrot.c +++ b/sys/arch/arm/imx/imx23_timrot.c @@ -78,7 +78,7 @@ static const struct device_compatible_entry compat_data[] = { DEVICE_COMPAT_EOL }; -static struct imx23_timrot_softc *timer_sc; +static struct imx23_timrot_softc *timer_sc = NULL; #define TIMROT_SOFT_RST_LOOP 455 /* At least 1 us ... */ #define TIMROT_READ(sc, reg) \ @@ -197,9 +197,11 @@ setstatclockrate(int newhz) { struct imx23_timrot_softc *sc = timer_sc; - TIMER_WRITE_2(sc, HW_TIMROT_TIMCOUNT1, - __SHIFTIN(SOURCE_32KHZ_HZ / newhz - 1, - HW_TIMROT_TIMCOUNT1_FIXED_COUNT)); + if(sc != NULL) { + TIMER_WRITE_2(sc, HW_TIMROT_TIMCOUNT1, + __SHIFTIN(SOURCE_32KHZ_HZ / newhz - 1, + HW_TIMROT_TIMCOUNT1_FIXED_COUNT)); + } } /* From a38486a09f503ac120543e0d71b490e5468a3972 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:13:16 +0100 Subject: [PATCH 06/11] evbarm/am18: enable interrupt controller in FDT --- sys/arch/arm/dts/da850-lego-ev3.dts | 38 +++++++++++++++++++++++++++++ sys/dtb/arm/ti/davinci/Makefile | 5 ++++ 2 files changed, 43 insertions(+) create mode 100644 sys/arch/arm/dts/da850-lego-ev3.dts diff --git a/sys/arch/arm/dts/da850-lego-ev3.dts b/sys/arch/arm/dts/da850-lego-ev3.dts new file mode 100644 index 0000000000000..4e9bb391adb20 --- /dev/null +++ b/sys/arch/arm/dts/da850-lego-ev3.dts @@ -0,0 +1,38 @@ +/* $NetBSD $ */ + +/*- + * Copyright (c) 2026 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Yuri Honegger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../../../external/gpl2/dts/dist/arch/arm/boot/dts/ti/davinci/da850-lego-ev3.dts" + +/ { + arm { + compatible = "simple-bus"; + }; +}; diff --git a/sys/dtb/arm/ti/davinci/Makefile b/sys/dtb/arm/ti/davinci/Makefile index efe4779c6efbb..0955c63e60518 100644 --- a/sys/dtb/arm/ti/davinci/Makefile +++ b/sys/dtb/arm/ti/davinci/Makefile @@ -7,4 +7,9 @@ DTSMAKEVARS= CONFIG_ARCH_DAVINCI=y DTSFILESCMD= ${MAKE} -C ${ARCHDTSDIR}/${DTSSUBDIR} ${DTSMAKEVARS} -v dtb-y DTS= ${DTSFILESCMD:sh} +# Extra .dts files from arch/arm/dts +.if !empty(MACHINE_ARCH:Mearmv5*) +DTS+= da850-lego-ev3.dts +.endif + .include From 32e7213847f532d88078112255fba6e306ba5732 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:20:26 +0100 Subject: [PATCH 07/11] evbarm/am18: add timer --- sys/arch/arm/ti/am18_timer.c | 156 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 161 insertions(+) create mode 100644 sys/arch/arm/ti/am18_timer.c diff --git a/sys/arch/arm/ti/am18_timer.c b/sys/arch/arm/ti/am18_timer.c new file mode 100644 index 0000000000000..f73b49a75e162 --- /dev/null +++ b/sys/arch/arm/ti/am18_timer.c @@ -0,0 +1,156 @@ +/* $NetBSD $ */ + +/*- +* Copyright (c) 2025 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Yuri Honegger. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Timer Interrupts for the TI AM18XX SOC + */ + +#include +#include +#include + +#include + +#include + +struct am18_timer_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; +}; + +static int am18_timer_match(device_t, cfdata_t, void *); +static void am18_timer_attach(device_t, device_t, void *); +static void am18_timer_cpu_initclocks(void); +static int am18_timer_irq(void *); + +static struct am18_timer_softc *timer_sc; + +CFATTACH_DECL_NEW(am18timer, sizeof(struct am18_timer_softc), + am18_timer_match, am18_timer_attach, NULL, NULL); + +#define AM18_TIMER_TIM12 0x10 +#define AM18_TIMER_TIM34 0x14 +#define AM18_TIMER_PRD12 0x18 +#define AM18_TIMER_PRD34 0x1C +#define AM18_TIMER_TCR 0x20 +#define AM18_TIMER_TGCR 0x24 + +#define AM18_TIMER_TCR_ENAMODE12_CONTINUOUS 0x80 +#define AM18_TIMER_TGCR_TIMMODE32_UNCHAINED 0x4 +#define AM18_TIMER_TGCR_TIM12EN 0x1 + +#define TIMER_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg) +#define TIMER_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val) + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da830-timer" }, + DEVICE_COMPAT_EOL +}; + +static void +am18_timer_cpu_initclocks(void) +{ + struct am18_timer_softc *sc = timer_sc; + uint32_t timer_interval = 24000000/hz; + + /* disable counter to allow changing mode */ + TIMER_WRITE(sc, AM18_TIMER_TCR, 0); + /* set mode to 32-bit unchained */ + TIMER_WRITE(sc, AM18_TIMER_TGCR, AM18_TIMER_TGCR_TIMMODE32_UNCHAINED | + AM18_TIMER_TGCR_TIM12EN); + /* start counting from zero */ + TIMER_WRITE(sc, AM18_TIMER_TIM12, 0); + TIMER_WRITE(sc, AM18_TIMER_TIM34, 0); + /* load period registers with maximum period */ + TIMER_WRITE(sc, AM18_TIMER_PRD12, timer_interval); + TIMER_WRITE(sc, AM18_TIMER_PRD34, 0); + /* enable timer */ + TIMER_WRITE(sc, AM18_TIMER_TCR, AM18_TIMER_TCR_ENAMODE12_CONTINUOUS); +} + +int +am18_timer_irq(void *frame) +{ + hardclock(frame); + + return 1; +} + +int +am18_timer_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +void +am18_timer_attach(device_t parent, device_t self, void *aux) +{ + struct am18_timer_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + char intrstr[128]; + + sc->sc_bst = faa->faa_bst; + timer_sc = sc; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + /* establish interrupt */ + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": failed to decode interrupt\n"); + return; + } + void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_CLOCK, 0, + am18_timer_irq, NULL, + device_xname(self)); + if (ih == NULL) { + aprint_error_dev(self, "couldn't install timer interrupt\n"); + return; + } + + arm_fdt_timer_register(am18_timer_cpu_initclocks); + + aprint_normal(": timer on %s\n", intrstr); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 84e05110d13af..f78c750c53b7c 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -80,6 +80,10 @@ device omaptimer attach omaptimer at fdt file arch/arm/ti/ti_omaptimer.c omaptimer +device am18timer +attach am18timer at fdt +file arch/arm/ti/am18_timer.c am18timer + # GPIO device tigpio: fdt_gpio, gpiobus attach tigpio at fdt with ti_gpio diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index 8af990b6b47f2..cac3bdfb7d489 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -65,6 +65,7 @@ am18intc* at fdt? pass 1 # am18 interrupt controller # Timers imx23timrot* at fdt? pass 2 # imx23 timer +am18timer* at fdt? pass 2 # am18 timer # DMA controller imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller From bf58aef65e7eb3f4336c2b5abb5192e188563bf5 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:29:47 +0100 Subject: [PATCH 08/11] evbarm/am18: add psc driver The PSC is the power and sleep controller. --- sys/arch/arm/ti/am18_psc.c | 326 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 331 insertions(+) create mode 100644 sys/arch/arm/ti/am18_psc.c diff --git a/sys/arch/arm/ti/am18_psc.c b/sys/arch/arm/ti/am18_psc.c new file mode 100644 index 0000000000000..87e488d8be987 --- /dev/null +++ b/sys/arch/arm/ti/am18_psc.c @@ -0,0 +1,326 @@ +/* $NetBSD $ */ + +/*- +* Copyright (c) 2025 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Yuri Honegger. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Power&Sleep Controller for the TI AM18XX SOC. + */ + +#include +#include +#include + +#include +#include + +#include + +#define MAX_PARENT_CLOCKS 5 + +struct am18_psc_clk { + struct clk clk_base; + int clk_index; + union { + const char *clk_parent_name; + struct clk *clk_parent; + } u; +}; + +struct am18_psc_config { + struct am18_psc_clk *clks; + int clknum; +}; + +struct am18_psc_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + struct clk_domain sc_clkdom; + struct clk parent_clocks[MAX_PARENT_CLOCKS]; + struct am18_psc_clk *sc_clks; + int sc_clknum; +}; + +static int am18_psc_match(device_t, cfdata_t, void *); +static void am18_psc_attach(device_t, device_t, void *); +static struct clk * am18_psc_decode(device_t, int, const void *, size_t); +static struct clk * am18_psc_clk_get(void *, const char *); +static u_int am18_psc_clk_get_rate(void *, struct clk *); +static int am18_psc_clk_enable(void *, struct clk *); +static int am18_psc_clk_disable(void *, struct clk *); +static struct clk * am18_psc_clk_get_parent(void *, struct clk *); +static void am18_psc_clk_transition(struct am18_psc_softc *, + struct am18_psc_clk *, uint32_t); + +CFATTACH_DECL_NEW(am18psc, sizeof(struct am18_psc_softc), + am18_psc_match, am18_psc_attach, NULL, NULL); + +#define AM18_PSC_PTCMD 0x120 +#define AM18_PSC_PTSTAT 0x128 +#define AM18_PSC_MDCTL0 0xA00 + +#define AM18_PSC_PTCMD_GO_ALL 0x3 +#define AM18_PSC_PTSTAT_MASK 0x3 +#define AM18_PSC_MDCTL_DISABLE 0x2 +#define AM18_PSC_MDCTL_ENABLE 0x3 + +#define PSC_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg) +#define PSC_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val) + +#define PSC_CLK(_i, _name, _p) \ + { \ + .clk_index = (_i), \ + .u.clk_parent_name = (_p), \ + .clk_base.name = (_name), \ + .clk_base.flags = 0, \ + } + +static struct am18_psc_clk am18_psc0_clks[] = { + PSC_CLK(0, "edma0_cc0", "pll0_sysclk2"), + PSC_CLK(1, "edma0_tc0", "pll0_sysclk2"), + PSC_CLK(2, "edma0_tc1", "pll0_sysclk2"), + PSC_CLK(3, "emifa", "async1"), + PSC_CLK(4, "spi0", "pll0_sysclk2"), + PSC_CLK(5, "sdmmc0", "pll0_sysclk2"), + PSC_CLK(6, "aintc", "pll0_sysclk4"), + PSC_CLK(7, "imem", "pll0_sysclk2"), + PSC_CLK(8, NULL, NULL), /* unused */ + PSC_CLK(9, "uart0", "pll0_sysclk2"), + PSC_CLK(10, NULL, NULL), /* scr0; purpose and parent clk unclear */ + PSC_CLK(11, NULL, NULL), /* scr1; purpose and parent clk unclear */ + PSC_CLK(12, NULL, NULL), /* scr2; purpose and parent clk unclear */ + PSC_CLK(13, "pru", "pll0_sysclk2"), + PSC_CLK(14, "arm", "pll0_sysclk6"), +}; + +static struct am18_psc_clk am18_psc1_clks[] = { + PSC_CLK(0, "edma1_cc0", "pll0_sysclk2"), + PSC_CLK(1, "usb2_0", "pll0_sysclk2"), + PSC_CLK(2, "usb1_1", "pll0_sysclk4"), + PSC_CLK(3, "gpio", "pll0_sysclk4"), + PSC_CLK(4, "hpi", "pll0_sysclk2"), + PSC_CLK(5, "emac", "pll0_sysclk4"), + PSC_CLK(6, "ddr", "pll0_sysclk2"), + PSC_CLK(7, "mcasp0", "async3"), + PSC_CLK(8, "sata", "pll0_sysclk2"), + PSC_CLK(9, "vpif", "pll0_sysclk2"), + PSC_CLK(10, "spi1", "async3"), + PSC_CLK(11, "i2c1", "pll0_sysclk4"), + PSC_CLK(12, "uart1", "async3"), + PSC_CLK(13, "uart2", "async3"), + PSC_CLK(14, "mcbsp0", "async3"), + PSC_CLK(15, "mcbsp1", "async3"), + PSC_CLK(16, "lcdc", "pll0_sysclk2"), + PSC_CLK(17, "ehrpwm", "async3"), + PSC_CLK(18, "sdmmc1", "pll0_sysclk2"), + PSC_CLK(19, "upp", "pll0_sysclk2"), + PSC_CLK(20, "ecap", "async3"), + PSC_CLK(21, "edma1_tc0", "pll0_sysclk2"), +}; + +static const struct am18_psc_config am18_psc0_config = { + .clks = am18_psc0_clks, + .clknum = __arraycount(am18_psc0_clks) +}; + +static const struct am18_psc_config am18_psc1_config = { + .clks = am18_psc1_clks, + .clknum = __arraycount(am18_psc1_clks) +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-psc0", .data = &am18_psc0_config }, + { .compat = "ti,da850-psc1", .data = &am18_psc1_config }, + DEVICE_COMPAT_EOL +}; + +static const struct fdtbus_clock_controller_func am18_psc_clk_fdt_funcs = { + .decode = am18_psc_decode, +}; + +static const struct clk_funcs am18_psc_clk_funcs = { + .get = am18_psc_clk_get, + .get_rate = am18_psc_clk_get_rate, + .enable = am18_psc_clk_enable, + .disable = am18_psc_clk_disable, + .get_parent = am18_psc_clk_get_parent +}; + +static void +am18_psc_clk_transition(struct am18_psc_softc *sc, + struct am18_psc_clk *clk, uint32_t state) +{ + /* update clock gate state */ + uint32_t mdctl_reg = AM18_PSC_MDCTL0 + 4*clk->clk_index; + PSC_WRITE(sc, mdctl_reg, state); + PSC_WRITE(sc, AM18_PSC_PTCMD, AM18_PSC_PTCMD_GO_ALL); + + /* wait for clock state transition to finish */ + while (PSC_READ(sc, AM18_PSC_PTSTAT) & AM18_PSC_PTSTAT_MASK) + continue; +} + +static struct clk * +am18_psc_clk_get(void *priv, const char *name) +{ + struct am18_psc_softc * const sc = priv; + + for (int i = 0; i < sc->sc_clknum; i++) { + if (strcmp(sc->sc_clks[i].clk_base.name, name) == 0) + return &sc->sc_clks[i].clk_base; + } + + return NULL; +} + +static u_int +am18_psc_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18_psc_clk *clk = (struct am18_psc_clk *)clkp; + + /* The PSC is only for clock gates, rates are passed through */ + return clk_get_rate(clk->u.clk_parent); +} + +static int +am18_psc_clk_enable(void *priv, struct clk *clkp) +{ + struct am18_psc_softc * const sc = priv; + struct am18_psc_clk *clk = (struct am18_psc_clk *)clkp; + + am18_psc_clk_transition(sc, clk, AM18_PSC_MDCTL_ENABLE); + + return 0; +} + +static int +am18_psc_clk_disable(void *priv, struct clk *clkp) +{ + struct am18_psc_softc * const sc = priv; + struct am18_psc_clk *clk = (struct am18_psc_clk *)clkp; + + am18_psc_clk_transition(sc, clk, AM18_PSC_MDCTL_DISABLE); + + return 0; +} + +static struct clk * +am18_psc_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18_psc_clk *clk = (struct am18_psc_clk *)clkp; + + return clk->u.clk_parent; +} + +static struct clk * +am18_psc_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18_psc_softc * const sc = device_private(dev); + const u_int *cells = data; + + if (len != 4) + return NULL; + const u_int clock_index = be32toh(cells[0]); + + if (clock_index >= sc->sc_clknum) + return NULL; + + struct am18_psc_clk *clk = &sc->sc_clks[clock_index]; + + return &clk->clk_base; +} + +int +am18_psc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +void +am18_psc_attach(device_t parent, device_t self, void *aux) +{ + struct am18_psc_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + const struct am18_psc_config *config; + + sc->sc_bst = faa->faa_bst; + + /* map PSC control registers */ + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + /* initialize clock domain */ + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &am18_psc_clk_funcs; + sc->sc_clkdom.priv = sc; + + /* wire up different clock tables depending on if it is psc0 or psc1 */ + config = of_compatible_lookup(phandle, compat_data)->data; + sc->sc_clks = config->clks; + sc->sc_clknum = config->clknum; + + /* get parent clock, then attach the clk gate */ + for (int i = 0; i < sc->sc_clknum; i++) { + /* skip unused clock gates */ + if (sc->sc_clks[i].clk_base.name == NULL) + continue; + + struct clk *parent_clk = + fdtbus_clock_get(phandle, sc->sc_clks[i].u.clk_parent_name); + if (parent_clk == NULL) { + aprint_error(": couldn't get clock parent for %s\n", + sc->sc_clks[i].clk_base.name); + return; + } + sc->sc_clks[i].u.clk_parent = parent_clk; + sc->sc_clks[i].clk_base.domain = &sc->sc_clkdom; + + clk_attach(&sc->sc_clks[i].clk_base); + } + + /* register it int he fdt subsystem */ + fdtbus_register_clock_controller(self, phandle, + &am18_psc_clk_fdt_funcs); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index f78c750c53b7c..70227b7157676 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -71,6 +71,10 @@ device ticompclk attach ticompclk at fdt with ti_comp_clock file arch/arm/ti/ti_comp_clock.c ti_comp_clock +device am18psc: fdt_clock +attach am18psc at fdt +file arch/arm/ti/am18_psc.c am18psc + # UART attach com at fdt with ti_com: ti_prcm file arch/arm/ti/ti_com.c ti_com needs-flag diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index cac3bdfb7d489..0e1fceb0c4262 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -72,6 +72,7 @@ imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller # Clock Controllers imx23clkctrl* at fdt? pass 1 # i.MX23 clock controller +am18psc* at fdt? pass 3 # am18 power&sleep controller # general FDT devices fclock* at fdt? pass 1 From 1fc2c3fb447a57dfead66c3f64ecda085915e6c3 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:40:09 +0100 Subject: [PATCH 09/11] evbarm/am18: add pll controller --- sys/arch/arm/ti/am18_pllc.c | 321 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 326 insertions(+) create mode 100644 sys/arch/arm/ti/am18_pllc.c diff --git a/sys/arch/arm/ti/am18_pllc.c b/sys/arch/arm/ti/am18_pllc.c new file mode 100644 index 0000000000000..8bcaf4607eb96 --- /dev/null +++ b/sys/arch/arm/ti/am18_pllc.c @@ -0,0 +1,321 @@ +/* $NetBSD $ */ + +/*- +* Copyright (c) 2026 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Yuri Honegger. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * PLL Controller for the TI AM18XX SOC. + */ + +#include +#include +#include +#include + +#include +#include + +#include + +struct am18_pllc_softc; + +struct am18_pllc_clk { + struct clk clk_base; /* must be first */ + u_int (*get_rate)(struct am18_pllc_softc *, struct am18_pllc_clk *); + u_int clk_index; +}; + +struct am18_pllc_config { + struct am18_pllc_clk *sysclks; + struct am18_pllc_clk *auxclk; + int num_sysclk; +}; + +struct am18_pllc_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_sysclk_phandle; + int sc_auxclk_phandle; + const struct am18_pllc_config *sc_config; + struct clk_domain sc_clkdom; + struct clk *sc_ref_clk; +}; + +static int am18_pllc_match(device_t, cfdata_t, void *); +static void am18_pllc_attach(device_t, device_t, void *); +static u_int am18_pllc_get_sysclk_rate(struct am18_pllc_softc *, + struct am18_pllc_clk *); +static u_int am18_pllc_get_auxclk_rate(struct am18_pllc_softc *, + struct am18_pllc_clk *); +static struct clk * am18_pllc_decode(device_t, int, const void *, size_t); +static struct clk * am18_pllc_clk_get(void *, const char *); +static u_int am18_pllc_clk_get_rate(void *, struct clk *); +static struct clk * am18_pllc_clk_get_parent(void *, struct clk *); + +CFATTACH_DECL_NEW(am18pllc, sizeof(struct am18_pllc_softc), + am18_pllc_match, am18_pllc_attach, NULL, NULL); + +#define AM18_PLLC_PLLCTL 0x100 +#define AM18_PLLC_PLLM 0x110 +#define AM18_PLLC_PREDIV 0x114 +#define AM18_PLLC_PLLDIV1 0x118 +#define AM18_PLLC_POSTDIV 0x128 +#define AM18_PLLC_PLLDIV4 0x160 + +#define AM18_PLLC_PLLCTL_PLLEN __BIT(0) +#define AM18_PLLC_PLLCTL_EXTCLKSRC __BIT(9) +#define AM18_PLLC_PLLM_MULTIPLIER __BITS(4,0) +#define AM18_PLLC_PREDIV_RATIO __BITS(4,0) +#define AM18_PLLC_POSTDIV_RATIO __BITS(4,0) +#define AM18_PLLC_PLLDIV_RATIO __BITS(4,0) + +#define PLLC_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg) +#define PLLC_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val) + +#define PLLC_CLK(_i, _name, _rate) \ + { \ + .clk_base.name = (_name), \ + .clk_base.flags = 0, \ + .get_rate = (_rate), \ + .clk_index = (_i), \ + } + +static struct am18_pllc_clk am18_pllc_pll0_auxclk = + PLLC_CLK(0, "pll0_auxclk", &am18_pllc_get_auxclk_rate); + +static struct am18_pllc_clk am18_pllc_pll0_sysclks[] = { + PLLC_CLK(0, "pll0_sysclk1", &am18_pllc_get_sysclk_rate), + PLLC_CLK(1, "pll0_sysclk2", &am18_pllc_get_sysclk_rate), + PLLC_CLK(2, "pll0_sysclk3", &am18_pllc_get_sysclk_rate), + PLLC_CLK(3, "pll0_sysclk4", &am18_pllc_get_sysclk_rate), + PLLC_CLK(4, "pll0_sysclk5", &am18_pllc_get_sysclk_rate), + PLLC_CLK(5, "pll0_sysclk6", &am18_pllc_get_sysclk_rate), + PLLC_CLK(6, "pll0_sysclk7", &am18_pllc_get_sysclk_rate), +}; + +static const struct am18_pllc_config am18_pllc_pll0_config = { + .auxclk = &am18_pllc_pll0_auxclk, + .sysclks = am18_pllc_pll0_sysclks, + .num_sysclk = __arraycount(am18_pllc_pll0_sysclks), +}; + +static const struct fdtbus_clock_controller_func am18_pllc_clk_fdt_funcs = { + .decode = am18_pllc_decode, +}; + +static const struct clk_funcs am18_pllc_clk_funcs = { + .get = am18_pllc_clk_get, + .get_rate = am18_pllc_clk_get_rate, + .get_parent = am18_pllc_clk_get_parent, +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-pll0", .data = &am18_pllc_pll0_config }, + DEVICE_COMPAT_EOL +}; + +static struct clk * +am18_pllc_clk_get(void *priv, const char *name) +{ + struct am18_pllc_softc * const sc = priv; + + /* check if it is the auxclk */ + if (strcmp(sc->sc_config->auxclk->clk_base.name, name) == 0) { + return &sc->sc_config->auxclk->clk_base; + } + + /* check if it is a sysclk */ + for (int i = 0; i < sc->sc_config->num_sysclk; i++) { + if (strcmp(sc->sc_config->sysclks[i].clk_base.name, name) == 0) + return &sc->sc_config->sysclks[i].clk_base; + } + + return NULL; +} + +static u_int +am18_pllc_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18_pllc_softc * const sc = priv; + struct am18_pllc_clk *clk = (struct am18_pllc_clk *)clkp; + + return clk->get_rate(sc, clk); +} + +static struct clk * +am18_pllc_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18_pllc_softc * const sc = priv; + + return sc->sc_ref_clk; +} + +static u_int +am18_pllc_get_sysclk_rate(struct am18_pllc_softc *sc, + struct am18_pllc_clk *clk) +{ + uint32_t pllctl_reg = PLLC_READ(sc, AM18_PLLC_PLLCTL); + + uint32_t prediv_reg = PLLC_READ(sc, AM18_PLLC_PREDIV); + uint32_t prediv_ratio = (prediv_reg & AM18_PLLC_PREDIV_RATIO)+1; + + uint32_t pllm_reg = PLLC_READ(sc, AM18_PLLC_PLLM); + uint32_t pllm_multiplier = (pllm_reg & AM18_PLLC_PLLM_MULTIPLIER)+1; + + uint32_t postdiv_reg = PLLC_READ(sc, AM18_PLLC_POSTDIV); + uint32_t postdiv_ratio = (postdiv_reg & AM18_PLLC_POSTDIV_RATIO)+1; + + uint32_t plldiv_regaddr; + if (clk->clk_index <= 2) { + plldiv_regaddr = AM18_PLLC_PLLDIV1 + 4 * clk->clk_index; + } else { + plldiv_regaddr = AM18_PLLC_PLLDIV4 + 4 * (clk->clk_index - 3); + } + uint32_t plldiv_reg = PLLC_READ(sc, plldiv_regaddr); + uint32_t plldiv_ratio = (plldiv_reg & AM18_PLLC_PLLDIV_RATIO)+1; + + u_int ref_clk_rate = clk_get_rate(sc->sc_ref_clk); + + if (pllctl_reg & AM18_PLLC_PLLCTL_PLLEN) { + /* PLL enabled */ + ref_clk_rate /= prediv_ratio; + ref_clk_rate *= pllm_multiplier; + ref_clk_rate /= postdiv_ratio; + } else { + /* bypass mode (ensure we aren't using the other PLL)*/ + KASSERT((pllctl_reg & AM18_PLLC_PLLCTL_EXTCLKSRC) == 0); + } + + ref_clk_rate /= plldiv_ratio; + + return ref_clk_rate; +} + +static u_int +am18_pllc_get_auxclk_rate(struct am18_pllc_softc *sc, + struct am18_pllc_clk *clk) +{ + return clk_get_rate(sc->sc_ref_clk); +} + +static struct clk * +am18_pllc_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18_pllc_softc * const sc = device_private(dev); + const u_int *cells = data; + + if (cc_phandle == sc->sc_sysclk_phandle) { + if (len != 4) + return NULL; + const u_int clock_index = be32toh(cells[0]) - 1; + if (clock_index >= sc->sc_config->num_sysclk) { + return NULL; + } + + return &sc->sc_config->sysclks[clock_index].clk_base; + } else if (cc_phandle == sc->sc_auxclk_phandle) { + return &sc->sc_config->auxclk->clk_base; + } + + return NULL; +} + +int +am18_pllc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +void +am18_pllc_attach(device_t parent, device_t self, void *aux) +{ + struct am18_pllc_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + + sc->sc_bst = faa->faa_bst; + sc->sc_config = of_compatible_lookup(phandle, compat_data)->data; + + /* map PSC control registers */ + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + /* get parent clock */ + sc->sc_ref_clk = fdtbus_clock_get_index(phandle, 0); + if (sc->sc_ref_clk == NULL) { + aprint_error(": couldn't get reference clock\n"); + return; + } + + /* initialize clock domain */ + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &am18_pllc_clk_funcs; + sc->sc_clkdom.priv = sc; + + /* initialize the clocks */ + sc->sc_config->auxclk->clk_base.domain = &sc->sc_clkdom; + clk_attach(&sc->sc_config->auxclk->clk_base); + for (int i = 0; i < sc->sc_config->num_sysclk; i++) { + sc->sc_config->sysclks[i].clk_base.domain = &sc->sc_clkdom; + clk_attach(&sc->sc_config->sysclks[i].clk_base); + } + + /* register auxclk fdt controller*/ + sc->sc_auxclk_phandle = of_find_firstchild_byname(phandle, "auxclk"); + if (sc->sc_auxclk_phandle < 0) { + aprint_error(": couldn't get pll0_auxclk child\n"); + return; + } + fdtbus_register_clock_controller(self, sc->sc_auxclk_phandle, + &am18_pllc_clk_fdt_funcs); + + /* register sysclk fdt controller*/ + sc->sc_sysclk_phandle = of_find_firstchild_byname(phandle, "sysclk"); + if (sc->sc_sysclk_phandle < 0) { + aprint_error(": couldn't get pll0_sysclk child\n"); + return; + } + fdtbus_register_clock_controller(self, sc->sc_sysclk_phandle, + &am18_pllc_clk_fdt_funcs); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 70227b7157676..3c92f3e4e01e6 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -75,6 +75,10 @@ device am18psc: fdt_clock attach am18psc at fdt file arch/arm/ti/am18_psc.c am18psc +device am18pllc: fdt_clock +attach am18pllc at fdt +file arch/arm/ti/am18_pllc.c am18pllc + # UART attach com at fdt with ti_com: ti_prcm file arch/arm/ti/ti_com.c ti_com needs-flag diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index 0e1fceb0c4262..c7d89236ef65a 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -73,6 +73,7 @@ imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller # Clock Controllers imx23clkctrl* at fdt? pass 1 # i.MX23 clock controller am18psc* at fdt? pass 3 # am18 power&sleep controller +am18pllc* at fdt? pass 2 # am18 pll controller # general FDT devices fclock* at fdt? pass 1 From e64222045afda07355c8911de364ec1427047c6c Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:41:15 +0100 Subject: [PATCH 10/11] evbarm/am18: choose serial port Serial 1 is sensor port 1 on the EV3 --- sys/arch/arm/dts/da850-lego-ev3.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/arch/arm/dts/da850-lego-ev3.dts b/sys/arch/arm/dts/da850-lego-ev3.dts index 4e9bb391adb20..2a51da195c192 100644 --- a/sys/arch/arm/dts/da850-lego-ev3.dts +++ b/sys/arch/arm/dts/da850-lego-ev3.dts @@ -32,6 +32,10 @@ #include "../../../external/gpl2/dts/dist/arch/arm/boot/dts/ti/davinci/da850-lego-ev3.dts" / { + chosen { + stdout-path = &serial1; + }; + arm { compatible = "simple-bus"; }; From 215e989478f3308f40815de7d7bd9b4796bcf4bf Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:49:13 +0100 Subject: [PATCH 11/11] arm/am18: add async-clk clkgate driver --- sys/arch/arm/ti/am18_aclk.c | 193 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 12 +- 3 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 sys/arch/arm/ti/am18_aclk.c diff --git a/sys/arch/arm/ti/am18_aclk.c b/sys/arch/arm/ti/am18_aclk.c new file mode 100644 index 0000000000000..a3f6e3bf0d9c5 --- /dev/null +++ b/sys/arch/arm/ti/am18_aclk.c @@ -0,0 +1,193 @@ +/* $NetBSD $ */ + +/*- +* Copyright (c) 2026 The NetBSD Foundation, Inc. +* All rights reserved. +* +* This code is derived from software contributed to The NetBSD Foundation +* by Yuri Honegger. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Drivers for the async clock gates in the syscfg block of the TI AM1808. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct am18_aclk_config { + struct clk *clk; /* double wrap clk because the config must be const */ + uint32_t mask; +}; + +struct am18_aclk_softc { + struct clk_domain sc_clkdom; + const struct am18_aclk_config *sc_config; + struct clk *sc_parent_clk; +}; + +static int am18_aclk_match(device_t, cfdata_t, void *); +static void am18_aclk_attach(device_t, device_t, void *); +static struct clk * am18_aclk_decode(device_t, int, const void *, size_t); +static struct clk * am18_aclk_clk_get(void *, const char *); +static u_int am18_aclk_clk_get_rate(void *, struct clk *); +static struct clk * am18_aclk_clk_get_parent(void *, struct clk *); + +CFATTACH_DECL_NEW(am18aclk, sizeof(struct am18_aclk_softc), + am18_aclk_match, am18_aclk_attach, NULL, NULL); + +#define AM18_ACLK_CFGCHIP3 0xC +#define AM18_ACLK_CFGCHIP3_ASYNC3_CLKSRC __BIT(4) +#define AM18_ACLK_CFGCHIP3_ASYNC1_CLKSRC __BIT(2) + +static struct clk am18_aclk_async1_clk = { + .name = "async1", + .flags = 0 +}; +static struct clk am18_aclk_async3_clk = { + .name = "async3", + .flags = 0 +}; + +static const struct am18_aclk_config am18_aclk_async1_config = { + .clk = &am18_aclk_async1_clk, + .mask = AM18_ACLK_CFGCHIP3_ASYNC1_CLKSRC, +}; +static const struct am18_aclk_config am18_aclk_async3_config = { + .clk = &am18_aclk_async3_clk, + .mask = AM18_ACLK_CFGCHIP3_ASYNC3_CLKSRC, +}; + +static const struct fdtbus_clock_controller_func am18_aclk_clk_fdt_funcs = { + .decode = am18_aclk_decode, +}; + +static const struct clk_funcs am18_aclk_clk_funcs = { + .get = am18_aclk_clk_get, + .get_rate = am18_aclk_clk_get_rate, + .get_parent = am18_aclk_clk_get_parent, +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-async1-clksrc", + .data = &am18_aclk_async1_config }, + { .compat = "ti,da850-async3-clksrc", + .data = &am18_aclk_async3_config }, + DEVICE_COMPAT_EOL +}; + +static struct clk * +am18_aclk_clk_get(void *priv, const char *name) +{ + struct am18_aclk_softc * const sc = priv; + + if (strcmp(sc->sc_config->clk->name, name) == 0) { + return sc->sc_config->clk; + } + + return NULL; +} + +static u_int +am18_aclk_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18_aclk_softc * const sc = priv; + + return clk_get_rate(sc->sc_parent_clk); +} + +static struct clk * +am18_aclk_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18_aclk_softc * const sc = priv; + + return sc->sc_parent_clk; +} + +static struct clk * +am18_aclk_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18_aclk_softc * const sc = device_private(dev); + + return sc->sc_config->clk; +} + +int +am18_aclk_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +void +am18_aclk_attach(device_t parent, device_t self, void *aux) +{ + struct am18_aclk_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + + sc->sc_config = of_compatible_lookup(phandle, compat_data)->data; + + /* ensure we have a clock parent */ + sc->sc_parent_clk = fdtbus_clock_get_index(phandle, 0); + if (sc->sc_parent_clk == NULL) { + aprint_error(": couldn't get parent clock"); + return; + } + + /* ensure clock gate bits are as expected */ + struct syscon *syscon = fdtbus_syscon_lookup(OF_parent(phandle)); + if (syscon == NULL) { + aprint_error(": couldn't get syscon registers\n"); + return; + } + syscon_lock(syscon); + uint32_t cfgchip_reg3 = syscon_read_4(syscon, AM18_ACLK_CFGCHIP3); + syscon_unlock(syscon); + if ((cfgchip_reg3 & sc->sc_config->mask) != 0) { + aprint_error(": unexpected clock gate bits\n"); + return; + } + + /* attach a clock controller */ + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &am18_aclk_clk_funcs; + sc->sc_clkdom.priv = sc; + + sc->sc_config->clk->domain = &sc->sc_clkdom; + clk_attach(sc->sc_config->clk); + + fdtbus_register_clock_controller(self, phandle, &am18_aclk_clk_fdt_funcs); + + aprint_normal("\n"); +} diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 3c92f3e4e01e6..54b036d3c8d5e 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -79,6 +79,10 @@ device am18pllc: fdt_clock attach am18pllc at fdt file arch/arm/ti/am18_pllc.c am18pllc +device am18aclk: fdt_clock +attach am18aclk at fdt +file arch/arm/ti/am18_aclk.c am18aclk + # UART attach com at fdt with ti_com: ti_prcm file arch/arm/ti/ti_com.c ti_com needs-flag diff --git a/sys/arch/evbarm/conf/GENERIC_V5 b/sys/arch/evbarm/conf/GENERIC_V5 index c7d89236ef65a..cb061b025179a 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -72,8 +72,12 @@ imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller # Clock Controllers imx23clkctrl* at fdt? pass 1 # i.MX23 clock controller -am18psc* at fdt? pass 3 # am18 power&sleep controller -am18pllc* at fdt? pass 2 # am18 pll controller +am18pllc* at fdt? pass 2 # TI AM18XX pll controller +am18aclk* at fdt? pass 3 # TI AM18XX async-clk clkgates +am18psc* at fdt? pass 4 # TI AM18XX power&sleep controller + +# System Controller +syscon* at fdt? pass 1 # Generic System Controller # general FDT devices fclock* at fdt? pass 1 @@ -101,8 +105,8 @@ imx23pctl* at fdt? pass 1 # imx23 pin control+gpio gpio* at gpiobus? # UART -com* at fdt? pass 4 # UART -plcom* at fdt? pass 4 # ARM PL011 UART +com* at fdt? pass 5 # UART +plcom* at fdt? pass 5 # ARM PL011 UART # Various imx23digctl* at fdt? pass 1 # i.MX23 digctl block