1/* $NetBSD: ti_omaptimer.c,v 1.11 2021/11/07 17:12:45 jmcneill Exp $ */ 2 3/* 4 * Copyright (c) 2017 Jonathan A. Kollasch 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: ti_omaptimer.c,v 1.11 2021/11/07 17:12:45 jmcneill Exp $"); 31 32#include <sys/types.h> 33#include <sys/param.h> 34#include <sys/bus.h> 35#include <sys/device.h> 36#include <sys/timetc.h> 37#include <sys/kernel.h> 38 39#include <arm/locore.h> 40#include <arm/fdt/arm_fdtvar.h> 41 42#include <dev/fdt/fdtvar.h> 43 44#include <arm/ti/ti_prcm.h> 45 46enum omaptimer_type { 47 DM_TIMER_AM335X, 48 DM_TIMER_OMAP3430, 49 _DM_NTIMER 50}; 51 52enum { 53 TIMER_TISR, 54 TIMER_TIER, 55 TIMER_TCLR, 56 TIMER_TCRR, 57 TIMER_TLDR, 58 _TIMER_NREG 59}; 60 61/* TISR bits */ 62#define OVF_IT_FLAG __BIT(1) 63 64/* TIER bits */ 65#define MAT_EN_FLAG __BIT(0) 66#define OVF_EN_FLAG __BIT(1) 67#define TCAR_EN_FLAG __BIT(2) 68 69/* TCLR bits */ 70#define TCLR_ST __BIT(0) 71#define TCLR_AR __BIT(1) 72 73static uint8_t omaptimer_regmap[_DM_NTIMER][_TIMER_NREG] = { 74 [DM_TIMER_AM335X] = { 75 [TIMER_TISR] = 0x28, 76 [TIMER_TIER] = 0x2c, 77 [TIMER_TCLR] = 0x38, 78 [TIMER_TCRR] = 0x3c, 79 [TIMER_TLDR] = 0x40, 80 }, 81 [DM_TIMER_OMAP3430] = { 82 [TIMER_TISR] = 0x18, 83 [TIMER_TIER] = 0x1c, 84 [TIMER_TCLR] = 0x24, 85 [TIMER_TCRR] = 0x28, 86 [TIMER_TLDR] = 0x2c, 87 }, 88}; 89 90static const struct device_compatible_entry compat_data[] = { 91 { .compat = "ti,am335x-timer-1ms", .value = DM_TIMER_AM335X }, 92 { .compat = "ti,am335x-timer", .value = DM_TIMER_AM335X }, 93 { .compat = "ti,omap3430-timer", .value = DM_TIMER_OMAP3430 }, 94 DEVICE_COMPAT_EOL 95}; 96 97struct omaptimer_softc { 98 device_t sc_dev; 99 bus_space_tag_t sc_bst; 100 bus_space_handle_t sc_bsh; 101 int sc_phandle; 102 enum omaptimer_type sc_type; 103 struct timecounter sc_tc; 104}; 105 106#define RD4(sc, reg) \ 107 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, omaptimer_regmap[(sc)->sc_type][(reg)]) 108#define WR4(sc, reg, val) \ 109 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, omaptimer_regmap[(sc)->sc_type][(reg)], val) 110 111static struct omaptimer_softc *timer_softc; 112 113static int 114omaptimer_intr(void *arg) 115{ 116 struct omaptimer_softc * const sc = timer_softc; 117 struct clockframe * const frame = arg; 118 119 WR4(sc, TIMER_TISR, OVF_IT_FLAG); 120 hardclock(frame); 121 122 return 1; 123} 124 125static void 126omaptimer_cpu_initclocks(void) 127{ 128 struct omaptimer_softc * const sc = timer_softc; 129 char intrstr[128]; 130 void *ih; 131 132 KASSERT(sc != NULL); 133 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 134 panic("%s: failed to decode interrupt", __func__); 135 ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, IPL_CLOCK, 136 FDT_INTR_MPSAFE, omaptimer_intr, NULL, device_xname(sc->sc_dev)); 137 if (ih == NULL) 138 panic("%s: failed to establish timer interrupt", __func__); 139 140 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 141 142 /* Enable interrupts */ 143 WR4(sc, TIMER_TIER, OVF_EN_FLAG); 144} 145 146static u_int 147omaptimer_get_timecount(struct timecounter *tc) 148{ 149 struct omaptimer_softc * const sc = tc->tc_priv; 150 151 return RD4(sc, TIMER_TCRR); 152} 153 154static void 155omaptimer_enable(struct omaptimer_softc *sc, uint32_t value) 156{ 157 /* Configure the timer */ 158 WR4(sc, TIMER_TLDR, value); 159 WR4(sc, TIMER_TCRR, value); 160 WR4(sc, TIMER_TIER, 0); 161 WR4(sc, TIMER_TCLR, TCLR_ST | TCLR_AR); 162} 163 164static int 165omaptimer_match(device_t parent, cfdata_t match, void *aux) 166{ 167 struct fdt_attach_args * const faa = aux; 168 169 return of_compatible_match(faa->faa_phandle, compat_data); 170} 171 172static void 173omaptimer_attach(device_t parent, device_t self, void *aux) 174{ 175 struct omaptimer_softc * const sc = device_private(self); 176 struct fdt_attach_args * const faa = aux; 177 const int phandle = faa->faa_phandle; 178 struct timecounter *tc = &sc->sc_tc; 179 struct clk *hwmod; 180 bus_addr_t addr; 181 bus_size_t size; 182 u_int rate; 183 184 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 185 aprint_error(": couldn't get registers\n"); 186 return; 187 } 188 189 sc->sc_dev = self; 190 sc->sc_phandle = phandle; 191 sc->sc_bst = faa->faa_bst; 192 sc->sc_type = of_compatible_lookup(phandle, compat_data)->value; 193 194 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 195 device_printf(self, "unable to map bus space"); 196 return; 197 } 198 199 hwmod = ti_prcm_get_hwmod(phandle, 0); 200 if (hwmod == NULL || clk_enable(hwmod) != 0) { 201 aprint_error(": couldn't enable module\n"); 202 return; 203 } 204 205 aprint_naive("\n"); 206 aprint_normal(": Timer\n"); 207 208 rate = clk_get_rate(hwmod); 209 210 if (device_unit(self) == 1) { 211 omaptimer_enable(sc, 0); 212 213 /* Install timecounter */ 214 tc->tc_get_timecount = omaptimer_get_timecount; 215 tc->tc_counter_mask = ~0u; 216 tc->tc_frequency = rate; 217 tc->tc_name = device_xname(self); 218 tc->tc_quality = 200; 219 tc->tc_priv = sc; 220 tc_init(tc); 221 222 } else if (device_unit(self) == 2) { 223 const uint32_t value = (0xffffffff - ((rate / hz) - 1)); 224 omaptimer_enable(sc, value); 225 226 /* Use this as the OS timer in UP configurations */ 227 if (!arm_has_mpext_p) { 228 timer_softc = sc; 229 arm_fdt_timer_register(omaptimer_cpu_initclocks); 230 } 231 } 232} 233 234CFATTACH_DECL_NEW(omaptimer, sizeof(struct omaptimer_softc), 235 omaptimer_match, omaptimer_attach, NULL, NULL); 236 237