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..2a51da195c192 --- /dev/null +++ b/sys/arch/arm/dts/da850-lego-ev3.dts @@ -0,0 +1,42 @@ +/* $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" + +/ { + chosen { + stdout-path = &serial1; + }; + + arm { + compatible = "simple-bus"; + }; +}; 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)); + } } /* 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/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/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/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/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/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 f29bd3e1dbb97..54b036d3c8d5e 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 @@ -11,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 @@ -66,6 +71,18 @@ 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 + +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 @@ -75,6 +92,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 @@ -162,6 +183,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..cb061b025179a 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. @@ -59,15 +61,23 @@ 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 +am18timer* at fdt? pass 2 # am18 timer # DMA controller imx23apbdma* at fdt? pass 1 # NXP i.MX23 DMA controller # Clock Controllers imx23clkctrl* at fdt? pass 1 # i.MX23 clock 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 @@ -95,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 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 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 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..0955c63e60518 --- /dev/null +++ b/sys/dtb/arm/ti/davinci/Makefile @@ -0,0 +1,15 @@ +# $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} + +# Extra .dts files from arch/arm/dts +.if !empty(MACHINE_ARCH:Mearmv5*) +DTS+= da850-lego-ev3.dts +.endif + +.include