a9tmr_fdt.c revision 1.8
11.8Sjmcneill/* $NetBSD: a9tmr_fdt.c,v 1.8 2022/11/01 11:05:18 jmcneill Exp $ */ 21.1Shkenken 31.1Shkenken/*- 41.1Shkenken * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 51.1Shkenken * All rights reserved. 61.1Shkenken * 71.1Shkenken * Redistribution and use in source and binary forms, with or without 81.1Shkenken * modification, are permitted provided that the following conditions 91.1Shkenken * are met: 101.1Shkenken * 1. Redistributions of source code must retain the above copyright 111.1Shkenken * notice, this list of conditions and the following disclaimer. 121.1Shkenken * 2. Redistributions in binary form must reproduce the above copyright 131.1Shkenken * notice, this list of conditions and the following disclaimer in the 141.1Shkenken * documentation and/or other materials provided with the distribution. 151.1Shkenken * 161.1Shkenken * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Shkenken * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Shkenken * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Shkenken * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Shkenken * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Shkenken * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Shkenken * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Shkenken * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Shkenken * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Shkenken * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Shkenken * SUCH DAMAGE. 271.1Shkenken */ 281.1Shkenken 291.1Shkenken#include <sys/cdefs.h> 301.8Sjmcneill__KERNEL_RCSID(0, "$NetBSD: a9tmr_fdt.c,v 1.8 2022/11/01 11:05:18 jmcneill Exp $"); 311.1Shkenken 321.1Shkenken#include <sys/param.h> 331.1Shkenken#include <sys/bus.h> 341.1Shkenken#include <sys/device.h> 351.1Shkenken#include <sys/intr.h> 361.1Shkenken#include <sys/systm.h> 371.1Shkenken#include <sys/kernel.h> 381.1Shkenken#include <sys/kmem.h> 391.1Shkenken 401.1Shkenken#include <arm/cortex/a9tmr_intr.h> 411.1Shkenken#include <arm/cortex/mpcore_var.h> 421.1Shkenken#include <arm/cortex/a9tmr_var.h> 431.1Shkenken 441.8Sjmcneill#include <arm/armreg.h> 451.8Sjmcneill 461.1Shkenken#include <dev/fdt/fdtvar.h> 471.1Shkenken#include <arm/fdt/arm_fdtvar.h> 481.1Shkenken 491.1Shkenkenstatic int a9tmr_fdt_match(device_t, cfdata_t, void *); 501.1Shkenkenstatic void a9tmr_fdt_attach(device_t, device_t, void *); 511.1Shkenken 521.1Shkenkenstatic void a9tmr_fdt_cpu_hatch(void *, struct cpu_info *); 531.3Sjmcneillstatic void a9tmr_fdt_speed_changed(device_t); 541.1Shkenken 551.3Sjmcneillstruct a9tmr_fdt_softc { 561.3Sjmcneill device_t sc_dev; 571.3Sjmcneill struct clk *sc_clk; 581.3Sjmcneill}; 591.3Sjmcneill 601.3SjmcneillCFATTACH_DECL_NEW(a9tmr_fdt, sizeof(struct a9tmr_fdt_softc), 611.3Sjmcneill a9tmr_fdt_match, a9tmr_fdt_attach, NULL, NULL); 621.1Shkenken 631.5Sthorpejstatic const struct device_compatible_entry compat_data[] = { 641.5Sthorpej { .compat = "arm,cortex-a5-global-timer" }, 651.5Sthorpej { .compat = "arm,cortex-a9-global-timer" }, 661.5Sthorpej DEVICE_COMPAT_EOL 671.5Sthorpej}; 681.5Sthorpej 691.1Shkenkenstatic int 701.1Shkenkena9tmr_fdt_match(device_t parent, cfdata_t cf, void *aux) 711.1Shkenken{ 721.1Shkenken struct fdt_attach_args * const faa = aux; 731.1Shkenken 741.5Sthorpej return of_compatible_match(faa->faa_phandle, compat_data); 751.1Shkenken} 761.1Shkenken 771.1Shkenkenstatic void 781.1Shkenkena9tmr_fdt_attach(device_t parent, device_t self, void *aux) 791.1Shkenken{ 801.3Sjmcneill struct a9tmr_fdt_softc * const sc = device_private(self); 811.1Shkenken struct fdt_attach_args * const faa = aux; 821.1Shkenken const int phandle = faa->faa_phandle; 831.1Shkenken bus_space_handle_t bsh; 841.8Sjmcneill uint32_t mpidr; 851.8Sjmcneill bool is_hardclock; 861.1Shkenken 871.3Sjmcneill sc->sc_dev = self; 881.3Sjmcneill sc->sc_clk = fdtbus_clock_get_index(phandle, 0); 891.3Sjmcneill if (sc->sc_clk == NULL) { 901.1Shkenken aprint_error(": couldn't get clock\n"); 911.1Shkenken return; 921.1Shkenken } 931.3Sjmcneill if (clk_enable(sc->sc_clk) != 0) { 941.1Shkenken aprint_error(": couldn't enable clock\n"); 951.1Shkenken return; 961.1Shkenken } 971.1Shkenken 981.3Sjmcneill uint32_t rate = clk_get_rate(sc->sc_clk); 991.1Shkenken prop_dictionary_t dict = device_properties(self); 1001.1Shkenken prop_dictionary_set_uint32(dict, "frequency", rate); 1011.1Shkenken 1021.1Shkenken char intrstr[128]; 1031.1Shkenken if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 1041.1Shkenken aprint_error(": failed to decode interrupt\n"); 1051.1Shkenken return; 1061.1Shkenken } 1071.1Shkenken 1081.2Sjmcneill aprint_naive("\n"); 1091.2Sjmcneill aprint_normal("\n"); 1101.2Sjmcneill 1111.8Sjmcneill mpidr = armreg_mpidr_read(); 1121.8Sjmcneill is_hardclock = (mpidr & MPIDR_U) != 0; /* Global timer for UP */ 1131.8Sjmcneill 1141.8Sjmcneill if (is_hardclock) { 1151.8Sjmcneill void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_CLOCK, 1161.8Sjmcneill FDT_INTR_MPSAFE, a9tmr_intr, NULL, device_xname(self)); 1171.8Sjmcneill if (ih == NULL) { 1181.8Sjmcneill aprint_error_dev(self, "couldn't install interrupt handler\n"); 1191.8Sjmcneill return; 1201.8Sjmcneill } 1211.8Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr); 1221.1Shkenken } 1231.1Shkenken 1241.1Shkenken bus_addr_t addr; 1251.1Shkenken bus_size_t size; 1261.1Shkenken if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 1271.1Shkenken aprint_error(": couldn't get distributor address\n"); 1281.1Shkenken return; 1291.1Shkenken } 1301.1Shkenken if (bus_space_map(faa->faa_bst, addr, size, 0, &bsh)) { 1311.1Shkenken aprint_error(": couldn't map registers\n"); 1321.1Shkenken return; 1331.1Shkenken } 1341.1Shkenken 1351.1Shkenken struct mpcore_attach_args mpcaa = { 1361.1Shkenken .mpcaa_name = "arma9tmr", 1371.1Shkenken .mpcaa_memt = faa->faa_bst, 1381.1Shkenken .mpcaa_memh = bsh, 1391.1Shkenken .mpcaa_irq = -1, 1401.1Shkenken }; 1411.1Shkenken 1421.7Sthorpej config_found(self, &mpcaa, NULL, CFARGS_NONE); 1431.1Shkenken 1441.8Sjmcneill if (is_hardclock) { 1451.8Sjmcneill arm_fdt_cpu_hatch_register(self, a9tmr_fdt_cpu_hatch); 1461.8Sjmcneill arm_fdt_timer_register(a9tmr_cpu_initclocks); 1471.8Sjmcneill } 1481.3Sjmcneill 1491.3Sjmcneill pmf_event_register(self, PMFE_SPEED_CHANGED, a9tmr_fdt_speed_changed, true); 1501.1Shkenken} 1511.1Shkenken 1521.1Shkenkenstatic void 1531.1Shkenkena9tmr_fdt_cpu_hatch(void *priv, struct cpu_info *ci) 1541.1Shkenken{ 1551.1Shkenken a9tmr_init_cpu_clock(ci); 1561.1Shkenken} 1571.3Sjmcneill 1581.3Sjmcneillstatic void 1591.3Sjmcneilla9tmr_fdt_speed_changed(device_t dev) 1601.3Sjmcneill{ 1611.3Sjmcneill struct a9tmr_fdt_softc * const sc = device_private(dev); 1621.3Sjmcneill prop_dictionary_t dict = device_properties(dev); 1631.3Sjmcneill uint32_t rate; 1641.3Sjmcneill 1651.3Sjmcneill rate = clk_get_rate(sc->sc_clk); 1661.3Sjmcneill prop_dictionary_set_uint32(dict, "frequency", rate); 1671.3Sjmcneill 1681.3Sjmcneill a9tmr_update_freq(rate); 1691.3Sjmcneill} 170