Home | History | Annotate | Line # | Download | only in ti
      1 /* $NetBSD: am18xx_timer.c,v 1.4 2026/09/29 20:03:36 yurix Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2025 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Yuri Honegger.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Timer Interrupts for the TI AM18XX SOC
     34  */
     35 
     36 #include <sys/param.h>
     37 #include <sys/cdefs.h>
     38 #include <sys/device.h>
     39 #include <sys/timetc.h>
     40 
     41 #include <dev/clk/clk.h>
     42 #include <dev/fdt/fdtvar.h>
     43 
     44 #include <arm/fdt/arm_fdtvar.h>
     45 
     46 struct am18xx_timer_softc {
     47 	bus_space_tag_t sc_bst;
     48 	bus_space_handle_t sc_bsh;
     49 	struct timecounter sc_tc;
     50 	struct clk *sc_clk;
     51 };
     52 
     53 static int 	am18xx_timer_match(device_t, cfdata_t, void *);
     54 static void 	am18xx_timer_attach(device_t, device_t, void *);
     55 static void	am18xx_timer_cpu_initclocks(void);
     56 static int	am18xx_timer_irq(void *);
     57 static u_int	am18xx_timer_get_timecount(struct timecounter *);
     58 
     59 static struct am18xx_timer_softc *timer_sc;
     60 
     61 CFATTACH_DECL_NEW(am18xxtimer, sizeof(struct am18xx_timer_softc),
     62 		  am18xx_timer_match, am18xx_timer_attach, NULL, NULL);
     63 
     64 #define AM18XX_TIMER_TIM12 0x10
     65 #define AM18XX_TIMER_TIM34 0x14
     66 #define AM18XX_TIMER_PRD12 0x18
     67 #define AM18XX_TIMER_PRD34 0x1C
     68 #define AM18XX_TIMER_TCR 0x20
     69 #define AM18XX_TIMER_TGCR 0x24
     70 
     71 #define AM18XX_TIMER_TCR_ENAMODE12_CONTINUOUS 0x80
     72 #define AM18XX_TIMER_TCR_ENAMODE34_CONTINUOUS 0x800000
     73 #define AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED 0x4
     74 #define AM18XX_TIMER_TGCR_TIM12EN 0x1
     75 #define AM18XX_TIMER_TGCR_TIM34EN 0x2
     76 
     77 #define	TIMER_READ(sc, reg)					\
     78 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, reg)
     79 #define	TIMER_WRITE(sc, reg, val)				\
     80 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, reg, val)
     81 
     82 static const struct device_compatible_entry compat_data[] = {
     83 	{ .compat = "ti,da830-timer" },
     84 	DEVICE_COMPAT_EOL
     85 };
     86 
     87 static void
     88 am18xx_timer_cpu_initclocks(void)
     89 {
     90 	struct am18xx_timer_softc *sc = timer_sc;
     91 	uint32_t clk_rate = clk_get_rate(sc->sc_clk);
     92 	uint32_t timer_interval = clk_rate/hz;
     93 
     94 	/* disable counter to allow changing mode */
     95 	TIMER_WRITE(sc, AM18XX_TIMER_TCR, 0);
     96 	/* set mode to 32-bit unchained */
     97 	TIMER_WRITE(sc, AM18XX_TIMER_TGCR, AM18XX_TIMER_TGCR_TIMMODE32_UNCHAINED
     98 					   | AM18XX_TIMER_TGCR_TIM12EN
     99 					   | AM18XX_TIMER_TGCR_TIM34EN);
    100 	/* start counting from zero */
    101 	TIMER_WRITE(sc, AM18XX_TIMER_TIM12, 0);
    102 	TIMER_WRITE(sc, AM18XX_TIMER_TIM34, 0);
    103 	/* load period registers with maximum period */
    104 	TIMER_WRITE(sc, AM18XX_TIMER_PRD12, timer_interval);
    105 	TIMER_WRITE(sc, AM18XX_TIMER_PRD34, UINT32_MAX);
    106 	/* enable timer */
    107 	TIMER_WRITE(sc, AM18XX_TIMER_TCR, AM18XX_TIMER_TCR_ENAMODE12_CONTINUOUS
    108 					  | AM18XX_TIMER_TCR_ENAMODE34_CONTINUOUS);
    109 
    110 	/* enable the timecounter */
    111 	tc_init(&sc->sc_tc);
    112 }
    113 
    114 int
    115 am18xx_timer_irq(void *frame)
    116 {
    117 	hardclock(frame);
    118 
    119 	return 1;
    120 }
    121 
    122 static u_int
    123 am18xx_timer_get_timecount(struct timecounter *tc)
    124 {
    125 	struct am18xx_timer_softc * const sc = tc->tc_priv;
    126 
    127 	return TIMER_READ(sc, AM18XX_TIMER_TIM34);
    128 }
    129 
    130 int
    131 am18xx_timer_match(device_t parent, cfdata_t cf, void *aux)
    132 {
    133 	struct fdt_attach_args * const faa = aux;
    134 
    135 	return of_compatible_match(faa->faa_phandle, compat_data);
    136 }
    137 
    138 void
    139 am18xx_timer_attach(device_t parent, device_t self, void *aux)
    140 {
    141 	struct am18xx_timer_softc * const sc = device_private(self);
    142 	struct fdt_attach_args * const faa = aux;
    143 	const int phandle = faa->faa_phandle;
    144 	bus_addr_t addr;
    145 	bus_size_t size;
    146 	char intrstr[128];
    147 
    148 	sc->sc_bst = faa->faa_bst;
    149 	timer_sc = sc;
    150 
    151 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    152 		aprint_error(": couldn't get registers\n");
    153 		return;
    154 	}
    155 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    156 		aprint_error(": couldn't map registers\n");
    157 		return;
    158 	}
    159 
    160 	/* enable clock */
    161 	sc->sc_clk = fdtbus_clock_get_index(phandle, 0);
    162 	if (sc->sc_clk == NULL) {
    163 		aprint_error(": couldn't get clock\n");
    164 		return;
    165 	}
    166 	if (clk_enable(sc->sc_clk) != 0) {
    167 		aprint_error(": couldn't enable clock\n");
    168 		return;
    169 	}
    170 
    171 	/* establish interrupt */
    172 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
    173 		aprint_error(": failed to decode interrupt\n");
    174 		return;
    175 	}
    176 	void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_CLOCK, 0,
    177 					       am18xx_timer_irq, NULL,
    178 					       device_xname(self));
    179 	if (ih == NULL) {
    180 		aprint_error_dev(self, "couldn't install timer interrupt\n");
    181 		return;
    182 	}
    183 
    184 	sc->sc_tc.tc_get_timecount = am18xx_timer_get_timecount;
    185 	sc->sc_tc.tc_poll_pps = NULL;
    186 	sc->sc_tc.tc_counter_mask = 0xFFFFFFFF; /* 32-bit */
    187 	sc->sc_tc.tc_frequency = clk_get_rate(sc->sc_clk);
    188 	sc->sc_tc.tc_name = "da830-timer";
    189 	sc->sc_tc.tc_quality = 100;
    190 	sc->sc_tc.tc_priv = sc;
    191 
    192 	arm_fdt_timer_register(am18xx_timer_cpu_initclocks);
    193 
    194 	aprint_naive("\n");
    195 	aprint_normal(": timer on %s\n", intrstr);
    196 }
    197 
    198