From 68305b90b2469a7569db2538efbacdbf2937bd08 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 21:21:24 +0100 Subject: [PATCH 01/10] evbarm/am18xx: 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 4b083965a85066d22f0fc3e1d632bebd60bfbdd5 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 21:40:46 +0100 Subject: [PATCH 02/10] evbarm/am18xx: basic plaform code --- sys/arch/arm/ti/am18xx_platform.c | 220 ++++++++++++++++++++++++++ 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, 225 insertions(+) create mode 100644 sys/arch/arm/ti/am18xx_platform.c diff --git a/sys/arch/arm/ti/am18xx_platform.c b/sys/arch/arm/ti/am18xx_platform.c new file mode 100644 index 0000000000000..16803731a4436 --- /dev/null +++ b/sys/arch/arm/ti/am18xx_platform.c @@ -0,0 +1,220 @@ +/* $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 AM18XX_IO_VBASE KERNEL_IO_VBASE +#define AM18XX_IO_PBASE 0x01c00000 +#define AM18XX_IO_SIZE 0x00400000 +#define AM18XX_INTC_VBASE (AM18XX_IO_VBASE + AM18XX_IO_SIZE) +#define AM18XX_INTC_PBASE 0xfffee000 +#define AM18XX_INTC_SIZE 0x2000 + +#define AM18XX_TIMER1_BASE 0x01C21000 +#define AM18XX_TIMER1_SIZE 0x1000 +#define AM18XX_TIMER1_TIM12 0x10 +#define AM18XX_TIMER1_TIM34 0x14 +#define AM18XX_TIMER1_PRD12 0x18 +#define AM18XX_TIMER1_PRD34 0x1C +#define AM18XX_TIMER1_TCR 0x20 +#define AM18XX_TIMER1_TGCR 0x24 +#define AM18XX_TIMER1_WDTCR 0x28 + +#define AM18XX_TIMER_TCR_ENAMODE12_CONTINUOUS 0x80 +#define AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED 0x4 +#define AM18XX_TIMER_TGCR_TIMMODE64_WATCHDOG 0x8 +#define AM18XX_TIMER_TGCR_TIM12EN 1 +#define AM18XX_TIMER_TGCR_TIM34EN 2 +#define AM18XX_TIMER_WDTCR_WDEN 1 +#define AM18XX_TIMER_WDTCR_KEY0 0xa5c60000 +#define AM18XX_TIMER_WDTCR_KEY1 0xda7e0000 + + +void am18xx_platform_early_putchar(char); + +extern struct arm32_bus_dma_tag arm_generic_dma_tag; +extern struct bus_space arm_generic_bs_tag; + +void __noasan +am18xx_platform_early_putchar(char c) +{ +#ifdef CONSADDR +#define CONSADDR_VA (CONSADDR - AM18XX_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 * +am18xx_platform_devmap(void) +{ + static const struct pmap_devmap devmap[] = { + /* input/output registers */ + DEVMAP_ENTRY(AM18XX_IO_VBASE, + AM18XX_IO_PBASE, + AM18XX_IO_SIZE), + /* interrupt controller */ + DEVMAP_ENTRY(AM18XX_INTC_VBASE, + AM18XX_INTC_PBASE, + AM18XX_INTC_SIZE), + DEVMAP_ENTRY_END + }; + + return devmap; +} + +static void +am18xx_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 +am18xx_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, AM18XX_TIMER1_BASE, AM18XX_TIMER1_SIZE, + 0, &bsh); + + /* disable counter to allow changing mode */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TCR, 0); + /* set mode to 32-bit unchained */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TGCR, + AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED | + AM18XX_TIMER_TGCR_TIM12EN); + /* load period registers with maximum period */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_PRD12, 0xFFFFFFFF); + /* enable timer */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TCR, + AM18XX_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, AM18XX_TIMER1_TIM12); + while (ticks > 0) { + cur = bus_space_read_4(bst, bsh, AM18XX_TIMER1_TIM12); + if (cur >= prev) + ticks -= (cur - prev); + else + ticks -= (UINT32_MAX - cur + prev); + prev = cur; + } +} + +/* + * To reset the AM1808, you have to crash the watchdog. + */ +static void +am18xx_platform_reset(void) +{ + /* map TIMER1 */ + bus_space_tag_t bst = &arm_generic_bs_tag; + bus_space_handle_t bsh = 0; + bus_space_map(bst, AM18XX_TIMER1_BASE, AM18XX_TIMER1_SIZE, 0, &bsh); + + /* disable counter to allow changing mode */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TCR, 0); + /* set mode to watchdog unchained */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TGCR, + AM18XX_TIMER_TGCR_TIMMODE64_WATCHDOG | + AM18XX_TIMER_TGCR_TIM12EN | + AM18XX_TIMER_TGCR_TIM34EN); + /* set counter and reload registers */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TIM12, 0); + bus_space_write_4(bst, bsh, AM18XX_TIMER1_TIM34, 0); + bus_space_write_4(bst, bsh, AM18XX_TIMER1_PRD12, 0); + bus_space_write_4(bst, bsh, AM18XX_TIMER1_PRD34, 0); + + /* execute the watchdog enable sequence */ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_WDTCR, + AM18XX_TIMER_WDTCR_KEY0 | AM18XX_TIMER_WDTCR_WDEN); + bus_space_write_4(bst, bsh, AM18XX_TIMER1_WDTCR, + AM18XX_TIMER_WDTCR_KEY1 | AM18XX_TIMER_WDTCR_WDEN); + /* trigger a reset by writing an invalid value to WDTCR*/ + bus_space_write_4(bst, bsh, AM18XX_TIMER1_WDTCR, 0xaffe); + + /* NOTREACHED */ +} + +static const struct fdt_platform am18xx_platform = { + .fp_devmap = am18xx_platform_devmap, + .fp_bootstrap = arm_fdt_cpu_bootstrap, + .fp_init_attach_args = am18xx_platform_init_attach_args, + .fp_uart_freq = NULL, + .fp_delay = am18xx_platform_delay, + .fp_reset = am18xx_platform_reset, +}; + +/* The device tree name for the AM1808 is da850 (davinci 850) */ +FDT_PLATFORM(am18xx, "ti,da850", &am18xx_platform); diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index f29bd3e1dbb97..e484cfd36b290 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/am18xx_platform.c soc_am18xx 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_AM18XX: 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..19ceacada50bc 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_AM18XX #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=am18xx, 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 2546f752aa8e4a5cb6632914158f356c8d9587c5 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:05:12 +0100 Subject: [PATCH 03/10] evbarm/am18xx: add interrupt controller --- sys/arch/arm/ti/am18xx_intc.c | 296 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 301 insertions(+) create mode 100644 sys/arch/arm/ti/am18xx_intc.c diff --git a/sys/arch/arm/ti/am18xx_intc.c b/sys/arch/arm/ti/am18xx_intc.c new file mode 100644 index 0000000000000..6a1ef5a06a5a0 --- /dev/null +++ b/sys/arch/arm/ti/am18xx_intc.c @@ -0,0 +1,296 @@ +/* $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 am18xx_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 am18xx_intc_match(device_t, cfdata_t, void *); +static void am18xx_intc_attach(device_t, device_t, void *); + +static void am18xx_intc_unblock_irqs(struct pic_softc *, size_t, uint32_t); +static void am18xx_intc_block_irqs(struct pic_softc *, size_t, uint32_t); +static void am18xx_intc_establish_irq(struct pic_softc *, + struct intrsource *); +static void am18xx_intc_set_priority(struct pic_softc *, int); +static void am18xx_intc_irq_handler(void *); +static void *am18xx_intc_fdt_establish(device_t, u_int *, int, int, + int (*)(void *), void *, + const char *); +static void am18xx_intc_fdt_disestablish(device_t, void *); +static bool am18xx_intc_fdt_intrstr(device_t, u_int *, char *, size_t); +static void am18xx_intc_enable_interrupts(struct am18xx_intc_softc *); + +#define AM18XX_AINTC_CR 0x4 +#define AM18XX_AINTC_GER 0x10 +#define AM18XX_AINTC_SECR1 0x280 +#define AM18XX_AINTC_ESR1 0x300 +#define AM18XX_AINTC_ECR1 0x380 +#define AM18XX_AINTC_CMR0 0x400 +#define AM18XX_AINTC_HIER 0x1500 + +#define AM18XX_AINTC_GER_ENABLE 1 +#define AM18XX_AINTC_HIER_IRQ 2 +#define AM18XX_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 am18xx_intc_softc *) \ + ((uintptr_t)(pic) - offsetof(struct am18xx_intc_softc, sc_pic))) + +static struct am18xx_intc_softc *intc_softc; + +CFATTACH_DECL_NEW(am18xxintc, sizeof(struct am18xx_intc_softc), + am18xx_intc_match, am18xx_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 am18xx_intc_fdt_funcs = { + .establish = am18xx_intc_fdt_establish, + .disestablish = am18xx_intc_fdt_disestablish, + .intrstr = am18xx_intc_fdt_intrstr, +}; + +static const struct pic_ops am18xx_intc_picops = { + .pic_unblock_irqs = am18xx_intc_unblock_irqs, + .pic_block_irqs = am18xx_intc_block_irqs, + .pic_establish_irq = am18xx_intc_establish_irq, + .pic_set_priority = am18xx_intc_set_priority, +}; + +static void * +am18xx_intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, + int (*func)(void *), void *arg, const char *xname) +{ + struct am18xx_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 +am18xx_intc_fdt_disestablish(device_t dev, void *ih) +{ + return intr_disestablish(ih); +} + +static bool +am18xx_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 +am18xx_intc_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t mask) +{ + struct am18xx_intc_softc * const sc = PICTOSOFTC(pic); + + const size_t group = irq_base / 32; + uint32_t esr_reg = AM18XX_AINTC_ESR1 + 4 * group; + INTC_WRITE(sc, esr_reg, mask); +} + +static void +am18xx_intc_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t mask) +{ + struct am18xx_intc_softc * const sc = PICTOSOFTC(pic); + + const size_t group = irq_base / 32; + uint32_t ecr_reg = AM18XX_AINTC_ECR1 + 4 * group; + INTC_WRITE(sc, ecr_reg, mask); +} + +static void +am18xx_intc_establish_irq(struct pic_softc *pic, struct intrsource *is) +{ + struct am18xx_intc_softc * const sc = PICTOSOFTC(pic); + + /* there is one CMR register per 4 interrupts*/ + uint32_t cmr_reg = AM18XX_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 am18xx_intc_softc *sc, size_t group) +{ + uint32_t reg = AM18XX_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 +am18xx_intc_irq_handler(void *frame) +{ + struct cpu_info * const ci = curcpu(); + struct am18xx_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 am18xx_intc_enable_interrupts(struct am18xx_intc_softc *sc) +{ + /* disable interrupts */ + INTC_WRITE(sc, AM18XX_AINTC_GER, 0); + INTC_WRITE(sc, AM18XX_AINTC_HIER, 0); + + /* disable nesting and prioritization in CR */ + INTC_WRITE(sc, AM18XX_AINTC_CR, 0); + + /* disable all interrupts, clear their status */ + for (int i = 0; i <= sc->sc_num_irqs / 32; i++) { + INTC_WRITE(sc, AM18XX_AINTC_ECR1 + 4 * i, 0xFFFFFFFF); + INTC_WRITE(sc, AM18XX_AINTC_SECR1 + 4 * i, 0xFFFFFFFF); + } + + /* enable IRQ interrupts */ + INTC_WRITE(sc, AM18XX_AINTC_HIER, AM18XX_AINTC_HIER_IRQ); + + /* set enable bit in GER */ + INTC_WRITE(sc, AM18XX_AINTC_GER, AM18XX_AINTC_GER_ENABLE); +} + +int +am18xx_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 +am18xx_intc_attach(device_t parent, device_t self, void *aux) +{ + struct am18xx_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 = &am18xx_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, + &am18xx_intc_fdt_funcs); + + arm_fdt_irq_set_handler(am18xx_intc_irq_handler); + am18xx_intc_enable_interrupts(sc); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index e484cfd36b290..ed06bf3913b2a 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 am18xxintc: pic, pic_splfuncs +attach am18xxintc at fdt +file arch/arm/ti/am18xx_intc.c am18xxintc + # 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 19ceacada50bc..3f4d16c43a5c8 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 +am18xxintc* at fdt? pass 1 # TI AM18XX interrupt controller # Timers imx23timrot* at fdt? pass 2 # imx23 timer From 9265fb42584bb7127343b4b2b1158088dac47f06 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:11:32 +0100 Subject: [PATCH 04/10] 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 791a375f6e2a10fef2ad7106e1e465684b6ade24 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:13:16 +0100 Subject: [PATCH 05/10] evbarm/am18xx: 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 61e62e53ed629035e24f5a9a9cee69b948a30902 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:20:26 +0100 Subject: [PATCH 06/10] evbarm/am18xx: add timer --- sys/arch/arm/ti/am18xx_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/am18xx_timer.c diff --git a/sys/arch/arm/ti/am18xx_timer.c b/sys/arch/arm/ti/am18xx_timer.c new file mode 100644 index 0000000000000..bf31252f6096a --- /dev/null +++ b/sys/arch/arm/ti/am18xx_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 am18xx_timer_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; +}; + +static int am18xx_timer_match(device_t, cfdata_t, void *); +static void am18xx_timer_attach(device_t, device_t, void *); +static void am18xx_timer_cpu_initclocks(void); +static int am18xx_timer_irq(void *); + +static struct am18xx_timer_softc *timer_sc; + +CFATTACH_DECL_NEW(am18xxtimer, sizeof(struct am18xx_timer_softc), + am18xx_timer_match, am18xx_timer_attach, NULL, NULL); + +#define AM18XX_TIMER_TIM12 0x10 +#define AM18XX_TIMER_TIM34 0x14 +#define AM18XX_TIMER_PRD12 0x18 +#define AM18XX_TIMER_PRD34 0x1C +#define AM18XX_TIMER_TCR 0x20 +#define AM18XX_TIMER_TGCR 0x24 + +#define AM18XX_TIMER_TCR_ENAMODE12_CONTINUOUS 0x80 +#define AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED 0x4 +#define AM18XX_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 +am18xx_timer_cpu_initclocks(void) +{ + struct am18xx_timer_softc *sc = timer_sc; + uint32_t timer_interval = 24000000/hz; + + /* disable counter to allow changing mode */ + TIMER_WRITE(sc, AM18XX_TIMER_TCR, 0); + /* set mode to 32-bit unchained */ + TIMER_WRITE(sc, AM18XX_TIMER_TGCR, AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED + | AM18XX_TIMER_TGCR_TIM12EN); + /* start counting from zero */ + TIMER_WRITE(sc, AM18XX_TIMER_TIM12, 0); + TIMER_WRITE(sc, AM18XX_TIMER_TIM34, 0); + /* load period registers with maximum period */ + TIMER_WRITE(sc, AM18XX_TIMER_PRD12, timer_interval); + TIMER_WRITE(sc, AM18XX_TIMER_PRD34, 0); + /* enable timer */ + TIMER_WRITE(sc, AM18XX_TIMER_TCR, AM18XX_TIMER_TCR_ENAMODE12_CONTINUOUS); +} + +int +am18xx_timer_irq(void *frame) +{ + hardclock(frame); + + return 1; +} + +int +am18xx_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 +am18xx_timer_attach(device_t parent, device_t self, void *aux) +{ + struct am18xx_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, + am18xx_timer_irq, NULL, + device_xname(self)); + if (ih == NULL) { + aprint_error_dev(self, "couldn't install timer interrupt\n"); + return; + } + + arm_fdt_timer_register(am18xx_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 ed06bf3913b2a..478fea6a8aa66 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 am18xxtimer +attach am18xxtimer at fdt +file arch/arm/ti/am18xx_timer.c am18xxtimer + # 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 3f4d16c43a5c8..14446c1280ff4 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -65,6 +65,7 @@ am18xxintc* at fdt? pass 1 # TI AM18XX interrupt controller # Timers imx23timrot* at fdt? pass 2 # imx23 timer +am18xxtimer* at fdt? pass 2 # TI AM18XX timer # DMA controller imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller From 1d640493dd551564431b495ff03739171896401b Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:29:47 +0100 Subject: [PATCH 07/10] evbarm/am18xx: add psc driver The PSC is the power and sleep controller. --- sys/arch/arm/ti/am18xx_psc.c | 327 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 1 + 3 files changed, 332 insertions(+) create mode 100644 sys/arch/arm/ti/am18xx_psc.c diff --git a/sys/arch/arm/ti/am18xx_psc.c b/sys/arch/arm/ti/am18xx_psc.c new file mode 100644 index 0000000000000..9f1ec1a923106 --- /dev/null +++ b/sys/arch/arm/ti/am18xx_psc.c @@ -0,0 +1,327 @@ +/* $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 am18xx_psc_clk { + struct clk clk_base; + int clk_index; + union { + const char *clk_parent_name; + struct clk *clk_parent; + } u; +}; + +struct am18xx_psc_config { + struct am18xx_psc_clk *clks; + int clknum; +}; + +struct am18xx_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 am18xx_psc_clk *sc_clks; + int sc_clknum; +}; + +static int am18xx_psc_match(device_t, cfdata_t, void *); +static void am18xx_psc_attach(device_t, device_t, void *); +static struct clk * am18xx_psc_decode(device_t, int, const void *, size_t); +static struct clk * am18xx_psc_clk_get(void *, const char *); +static u_int am18xx_psc_clk_get_rate(void *, struct clk *); +static int am18xx_psc_clk_enable(void *, struct clk *); +static int am18xx_psc_clk_disable(void *, struct clk *); +static struct clk * am18xx_psc_clk_get_parent(void *, struct clk *); +static void am18xx_psc_clk_transition(struct am18xx_psc_softc *, + struct am18xx_psc_clk *, + uint32_t); + +CFATTACH_DECL_NEW(am18xxpsc, sizeof(struct am18xx_psc_softc), + am18xx_psc_match, am18xx_psc_attach, NULL, NULL); + +#define AM18XX_PSC_PTCMD 0x120 +#define AM18XX_PSC_PTSTAT 0x128 +#define AM18XX_PSC_MDCTL0 0xA00 + +#define AM18XX_PSC_PTCMD_GO_ALL 0x3 +#define AM18XX_PSC_PTSTAT_MASK 0x3 +#define AM18XX_PSC_MDCTL_DISABLE 0x2 +#define AM18XX_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 am18xx_psc_clk am18xx_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 am18xx_psc_clk am18xx_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 am18xx_psc_config am18xx_psc0_config = { + .clks = am18xx_psc0_clks, + .clknum = __arraycount(am18xx_psc0_clks) +}; + +static const struct am18xx_psc_config am18xx_psc1_config = { + .clks = am18xx_psc1_clks, + .clknum = __arraycount(am18xx_psc1_clks) +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-psc0", .data = &am18xx_psc0_config }, + { .compat = "ti,da850-psc1", .data = &am18xx_psc1_config }, + DEVICE_COMPAT_EOL +}; + +static const struct fdtbus_clock_controller_func am18xx_psc_clk_fdt_funcs = { + .decode = am18xx_psc_decode, +}; + +static const struct clk_funcs am18xx_psc_clk_funcs = { + .get = am18xx_psc_clk_get, + .get_rate = am18xx_psc_clk_get_rate, + .enable = am18xx_psc_clk_enable, + .disable = am18xx_psc_clk_disable, + .get_parent = am18xx_psc_clk_get_parent +}; + +static void +am18xx_psc_clk_transition(struct am18xx_psc_softc *sc, + struct am18xx_psc_clk *clk, uint32_t state) +{ + /* update clock gate state */ + uint32_t mdctl_reg = AM18XX_PSC_MDCTL0 + 4*clk->clk_index; + PSC_WRITE(sc, mdctl_reg, state); + PSC_WRITE(sc, AM18XX_PSC_PTCMD, AM18XX_PSC_PTCMD_GO_ALL); + + /* wait for clock state transition to finish */ + while (PSC_READ(sc, AM18XX_PSC_PTSTAT) & AM18XX_PSC_PTSTAT_MASK) + continue; +} + +static struct clk * +am18xx_psc_clk_get(void *priv, const char *name) +{ + struct am18xx_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 +am18xx_psc_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp; + + /* The PSC is only for clock gates, rates are passed through */ + return clk_get_rate(clk->u.clk_parent); +} + +static int +am18xx_psc_clk_enable(void *priv, struct clk *clkp) +{ + struct am18xx_psc_softc * const sc = priv; + struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp; + + am18xx_psc_clk_transition(sc, clk, AM18XX_PSC_MDCTL_ENABLE); + + return 0; +} + +static int +am18xx_psc_clk_disable(void *priv, struct clk *clkp) +{ + struct am18xx_psc_softc * const sc = priv; + struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp; + + am18xx_psc_clk_transition(sc, clk, AM18XX_PSC_MDCTL_DISABLE); + + return 0; +} + +static struct clk * +am18xx_psc_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18xx_psc_clk *clk = (struct am18xx_psc_clk *)clkp; + + return clk->u.clk_parent; +} + +static struct clk * +am18xx_psc_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18xx_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 am18xx_psc_clk *clk = &sc->sc_clks[clock_index]; + + return &clk->clk_base; +} + +int +am18xx_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 +am18xx_psc_attach(device_t parent, device_t self, void *aux) +{ + struct am18xx_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 am18xx_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 = &am18xx_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, + &am18xx_psc_clk_fdt_funcs); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 478fea6a8aa66..4a109b6c5de40 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 am18xxpsc: fdt_clock +attach am18xxpsc at fdt +file arch/arm/ti/am18xx_psc.c am18xxpsc + # 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 14446c1280ff4..cf9b45c608a68 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 +am18xxpsc* at fdt? pass 3 # TI AM18XX power&sleep controller # general FDT devices fclock* at fdt? pass 1 From 6500f5b1bde42b92a57ceadd04bcd00291304529 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:40:09 +0100 Subject: [PATCH 08/10] evbarm/am18xx: add pll controller --- sys/arch/arm/ti/am18xx_pllc.c | 321 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 6 +- 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 sys/arch/arm/ti/am18xx_pllc.c diff --git a/sys/arch/arm/ti/am18xx_pllc.c b/sys/arch/arm/ti/am18xx_pllc.c new file mode 100644 index 0000000000000..2e5be08b5574f --- /dev/null +++ b/sys/arch/arm/ti/am18xx_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 am18xx_pllc_softc; + +struct am18xx_pllc_clk { + struct clk clk_base; /* must be first */ + u_int (*get_rate)(struct am18xx_pllc_softc *, struct am18xx_pllc_clk *); + u_int clk_index; +}; + +struct am18xx_pllc_config { + struct am18xx_pllc_clk *sysclks; + struct am18xx_pllc_clk *auxclk; + int num_sysclk; +}; + +struct am18xx_pllc_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_sysclk_phandle; + int sc_auxclk_phandle; + const struct am18xx_pllc_config *sc_config; + struct clk_domain sc_clkdom; + struct clk *sc_ref_clk; +}; + +static int am18xx_pllc_match(device_t, cfdata_t, void *); +static void am18xx_pllc_attach(device_t, device_t, void *); +static u_int am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *, + struct am18xx_pllc_clk *); +static u_int am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *, + struct am18xx_pllc_clk *); +static struct clk * am18xx_pllc_decode(device_t, int, const void *, size_t); +static struct clk * am18xx_pllc_clk_get(void *, const char *); +static u_int am18xx_pllc_clk_get_rate(void *, struct clk *); +static struct clk * am18xx_pllc_clk_get_parent(void *, struct clk *); + +CFATTACH_DECL_NEW(am18xxpllc, sizeof(struct am18xx_pllc_softc), + am18xx_pllc_match, am18xx_pllc_attach, NULL, NULL); + +#define AM18XX_PLLC_PLLCTL 0x100 +#define AM18XX_PLLC_PLLM 0x110 +#define AM18XX_PLLC_PREDIV 0x114 +#define AM18XX_PLLC_PLLDIV1 0x118 +#define AM18XX_PLLC_POSTDIV 0x128 +#define AM18XX_PLLC_PLLDIV4 0x160 + +#define AM18XX_PLLC_PLLCTL_PLLEN __BIT(0) +#define AM18XX_PLLC_PLLCTL_EXTCLKSRC __BIT(9) +#define AM18XX_PLLC_PLLM_MULTIPLIER __BITS(4,0) +#define AM18XX_PLLC_PREDIV_RATIO __BITS(4,0) +#define AM18XX_PLLC_POSTDIV_RATIO __BITS(4,0) +#define AM18XX_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 am18xx_pllc_clk am18xx_pllc_pll0_auxclk = + PLLC_CLK(0, "pll0_auxclk", &am18xx_pllc_get_auxclk_rate); + +static struct am18xx_pllc_clk am18xx_pllc_pll0_sysclks[] = { + PLLC_CLK(0, "pll0_sysclk1", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(1, "pll0_sysclk2", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(2, "pll0_sysclk3", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(3, "pll0_sysclk4", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(4, "pll0_sysclk5", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(5, "pll0_sysclk6", &am18xx_pllc_get_sysclk_rate), + PLLC_CLK(6, "pll0_sysclk7", &am18xx_pllc_get_sysclk_rate), +}; + +static const struct am18xx_pllc_config am18xx_pllc_pll0_config = { + .auxclk = &am18xx_pllc_pll0_auxclk, + .sysclks = am18xx_pllc_pll0_sysclks, + .num_sysclk = __arraycount(am18xx_pllc_pll0_sysclks), +}; + +static const struct fdtbus_clock_controller_func am18xx_pllc_clk_fdt_funcs = { + .decode = am18xx_pllc_decode, +}; + +static const struct clk_funcs am18xx_pllc_clk_funcs = { + .get = am18xx_pllc_clk_get, + .get_rate = am18xx_pllc_clk_get_rate, + .get_parent = am18xx_pllc_clk_get_parent, +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-pll0", .data = &am18xx_pllc_pll0_config }, + DEVICE_COMPAT_EOL +}; + +static struct clk * +am18xx_pllc_clk_get(void *priv, const char *name) +{ + struct am18xx_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 +am18xx_pllc_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18xx_pllc_softc * const sc = priv; + struct am18xx_pllc_clk *clk = (struct am18xx_pllc_clk *)clkp; + + return clk->get_rate(sc, clk); +} + +static struct clk * +am18xx_pllc_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18xx_pllc_softc * const sc = priv; + + return sc->sc_ref_clk; +} + +static u_int +am18xx_pllc_get_sysclk_rate(struct am18xx_pllc_softc *sc, + struct am18xx_pllc_clk *clk) +{ + uint32_t pllctl_reg = PLLC_READ(sc, AM18XX_PLLC_PLLCTL); + + uint32_t prediv_reg = PLLC_READ(sc, AM18XX_PLLC_PREDIV); + uint32_t prediv_ratio = (prediv_reg & AM18XX_PLLC_PREDIV_RATIO) + 1; + + uint32_t pllm_reg = PLLC_READ(sc, AM18XX_PLLC_PLLM); + uint32_t pllm_multiplier = (pllm_reg & AM18XX_PLLC_PLLM_MULTIPLIER) + 1; + + uint32_t postdiv_reg = PLLC_READ(sc, AM18XX_PLLC_POSTDIV); + uint32_t postdiv_ratio = (postdiv_reg & AM18XX_PLLC_POSTDIV_RATIO) + 1; + + uint32_t plldiv_regaddr; + if (clk->clk_index <= 2) { + plldiv_regaddr = AM18XX_PLLC_PLLDIV1 + 4 * clk->clk_index; + } else { + plldiv_regaddr = AM18XX_PLLC_PLLDIV4 + 4 * (clk->clk_index - 3); + } + uint32_t plldiv_reg = PLLC_READ(sc, plldiv_regaddr); + uint32_t plldiv_ratio = (plldiv_reg & AM18XX_PLLC_PLLDIV_RATIO) + 1; + + u_int ref_clk_rate = clk_get_rate(sc->sc_ref_clk); + + if (pllctl_reg & AM18XX_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 & AM18XX_PLLC_PLLCTL_EXTCLKSRC) == 0); + } + + ref_clk_rate /= plldiv_ratio; + + return ref_clk_rate; +} + +static u_int +am18xx_pllc_get_auxclk_rate(struct am18xx_pllc_softc *sc, + struct am18xx_pllc_clk *clk) +{ + return clk_get_rate(sc->sc_ref_clk); +} + +static struct clk * +am18xx_pllc_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18xx_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 +am18xx_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 +am18xx_pllc_attach(device_t parent, device_t self, void *aux) +{ + struct am18xx_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 = &am18xx_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, + &am18xx_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, + &am18xx_pllc_clk_fdt_funcs); + + aprint_normal("\n"); +} + diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 4a109b6c5de40..25534e42a0f64 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -75,6 +75,10 @@ device am18xxpsc: fdt_clock attach am18xxpsc at fdt file arch/arm/ti/am18xx_psc.c am18xxpsc +device am18xxpllc: fdt_clock +attach am18xxpllc at fdt +file arch/arm/ti/am18xx_pllc.c am18xxpllc + # 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 cf9b45c608a68..549868ba2a19d 100644 --- a/sys/arch/evbarm/conf/GENERIC_V5 +++ b/sys/arch/evbarm/conf/GENERIC_V5 @@ -72,7 +72,11 @@ imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller # Clock Controllers imx23clkctrl* at fdt? pass 1 # i.MX23 clock controller -am18xxpsc* at fdt? pass 3 # TI AM18XX power&sleep controller +am18xxpllc* at fdt? pass 2 # TI AM18XX pll controller +am18xxpsc* 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 From e847e2aba54226a974844614b65aeae5da72e06f Mon Sep 17 00:00:00 2001 From: recursivetree Date: Thu, 26 Mar 2026 22:41:15 +0100 Subject: [PATCH 09/10] evbarm/am18xx: 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 b047e16be99a3da9af1aa0e7ac3cf8c37fd724c2 Mon Sep 17 00:00:00 2001 From: recursivetree Date: Mon, 30 Mar 2026 09:35:29 +0200 Subject: [PATCH 10/10] evbarm/am18xx: add async-clk clkgate driver --- sys/arch/arm/ti/am18xx_aclk.c | 193 ++++++++++++++++++++++++++++++++ sys/arch/arm/ti/files.ti | 4 + sys/arch/evbarm/conf/GENERIC_V5 | 5 +- 3 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 sys/arch/arm/ti/am18xx_aclk.c diff --git a/sys/arch/arm/ti/am18xx_aclk.c b/sys/arch/arm/ti/am18xx_aclk.c new file mode 100644 index 0000000000000..8a92569af2b28 --- /dev/null +++ b/sys/arch/arm/ti/am18xx_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 am18xx_aclk_config { + struct clk *clk; /* double wrap clk because the config must be const */ + uint32_t mask; +}; + +struct am18xx_aclk_softc { + struct clk_domain sc_clkdom; + const struct am18xx_aclk_config *sc_config; + struct clk *sc_parent_clk; +}; + +static int am18xx_aclk_match(device_t, cfdata_t, void *); +static void am18xx_aclk_attach(device_t, device_t, void *); +static struct clk * am18xx_aclk_decode(device_t, int, const void *, size_t); +static struct clk * am18xx_aclk_clk_get(void *, const char *); +static u_int am18xx_aclk_clk_get_rate(void *, struct clk *); +static struct clk * am18xx_aclk_clk_get_parent(void *, struct clk *); + +CFATTACH_DECL_NEW(am18xxaclk, sizeof(struct am18xx_aclk_softc), + am18xx_aclk_match, am18xx_aclk_attach, NULL, NULL); + +#define AM18XX_ACLK_CFGCHIP3 0xC +#define AM18XX_ACLK_CFGCHIP3_ASYNC3_CLKSRC __BIT(4) +#define AM18XX_ACLK_CFGCHIP3_ASYNC1_CLKSRC __BIT(2) + +static struct clk am18xx_aclk_async1_clk = { + .name = "async1", + .flags = 0 +}; +static struct clk am18xx_aclk_async3_clk = { + .name = "async3", + .flags = 0 +}; + +static const struct am18xx_aclk_config am18xx_aclk_async1_config = { + .clk = &am18xx_aclk_async1_clk, + .mask = AM18XX_ACLK_CFGCHIP3_ASYNC1_CLKSRC, +}; +static const struct am18xx_aclk_config am18xx_aclk_async3_config = { + .clk = &am18xx_aclk_async3_clk, + .mask = AM18XX_ACLK_CFGCHIP3_ASYNC3_CLKSRC, +}; + +static const struct fdtbus_clock_controller_func am18xx_aclk_clk_fdt_funcs = { + .decode = am18xx_aclk_decode, +}; + +static const struct clk_funcs am18xx_aclk_clk_funcs = { + .get = am18xx_aclk_clk_get, + .get_rate = am18xx_aclk_clk_get_rate, + .get_parent = am18xx_aclk_clk_get_parent, +}; + +static const struct device_compatible_entry compat_data[] = { + { .compat = "ti,da850-async1-clksrc", + .data = &am18xx_aclk_async1_config }, + { .compat = "ti,da850-async3-clksrc", + .data = &am18xx_aclk_async3_config }, + DEVICE_COMPAT_EOL +}; + +static struct clk * +am18xx_aclk_clk_get(void *priv, const char *name) +{ + struct am18xx_aclk_softc * const sc = priv; + + if (strcmp(sc->sc_config->clk->name, name) == 0) { + return sc->sc_config->clk; + } + + return NULL; +} + +static u_int +am18xx_aclk_clk_get_rate(void *priv, struct clk *clkp) +{ + struct am18xx_aclk_softc * const sc = priv; + + return clk_get_rate(sc->sc_parent_clk); +} + +static struct clk * +am18xx_aclk_clk_get_parent(void *priv, struct clk *clkp) +{ + struct am18xx_aclk_softc * const sc = priv; + + return sc->sc_parent_clk; +} + +static struct clk * +am18xx_aclk_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct am18xx_aclk_softc * const sc = device_private(dev); + + return sc->sc_config->clk; +} + +int +am18xx_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 +am18xx_aclk_attach(device_t parent, device_t self, void *aux) +{ + struct am18xx_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, AM18XX_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 = &am18xx_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, &am18xx_aclk_clk_fdt_funcs); + + aprint_normal("\n"); +} diff --git a/sys/arch/arm/ti/files.ti b/sys/arch/arm/ti/files.ti index 25534e42a0f64..74af746ac202c 100644 --- a/sys/arch/arm/ti/files.ti +++ b/sys/arch/arm/ti/files.ti @@ -79,6 +79,10 @@ device am18xxpllc: fdt_clock attach am18xxpllc at fdt file arch/arm/ti/am18xx_pllc.c am18xxpllc +device am18xxaclk: fdt_clock +attach am18xxaclk at fdt +file arch/arm/ti/am18xx_aclk.c am18xxaclk + # 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 549868ba2a19d..ab89c40329c02 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 am18xxpllc* at fdt? pass 2 # TI AM18XX pll controller +am18xxaclk* at fdt? pass 3 # TI AM18XX async-clk clkgates am18xxpsc* at fdt? pass 4 # TI AM18XX power&sleep controller # System Controller @@ -104,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