1*eb199291Sjmcneill /* $NetBSD: a9tmr_fdt.c,v 1.8 2022/11/01 11:05:18 jmcneill Exp $ */
252049049Shkenken
352049049Shkenken /*-
452049049Shkenken * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
552049049Shkenken * All rights reserved.
652049049Shkenken *
752049049Shkenken * Redistribution and use in source and binary forms, with or without
852049049Shkenken * modification, are permitted provided that the following conditions
952049049Shkenken * are met:
1052049049Shkenken * 1. Redistributions of source code must retain the above copyright
1152049049Shkenken * notice, this list of conditions and the following disclaimer.
1252049049Shkenken * 2. Redistributions in binary form must reproduce the above copyright
1352049049Shkenken * notice, this list of conditions and the following disclaimer in the
1452049049Shkenken * documentation and/or other materials provided with the distribution.
1552049049Shkenken *
1652049049Shkenken * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1752049049Shkenken * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1852049049Shkenken * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1952049049Shkenken * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2052049049Shkenken * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2152049049Shkenken * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2252049049Shkenken * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2352049049Shkenken * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2452049049Shkenken * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2552049049Shkenken * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2652049049Shkenken * SUCH DAMAGE.
2752049049Shkenken */
2852049049Shkenken
2952049049Shkenken #include <sys/cdefs.h>
30*eb199291Sjmcneill __KERNEL_RCSID(0, "$NetBSD: a9tmr_fdt.c,v 1.8 2022/11/01 11:05:18 jmcneill Exp $");
3152049049Shkenken
3252049049Shkenken #include <sys/param.h>
3352049049Shkenken #include <sys/bus.h>
3452049049Shkenken #include <sys/device.h>
3552049049Shkenken #include <sys/intr.h>
3652049049Shkenken #include <sys/systm.h>
3752049049Shkenken #include <sys/kernel.h>
3852049049Shkenken #include <sys/kmem.h>
3952049049Shkenken
4052049049Shkenken #include <arm/cortex/a9tmr_intr.h>
4152049049Shkenken #include <arm/cortex/mpcore_var.h>
4252049049Shkenken #include <arm/cortex/a9tmr_var.h>
4352049049Shkenken
44*eb199291Sjmcneill #include <arm/armreg.h>
45*eb199291Sjmcneill
4652049049Shkenken #include <dev/fdt/fdtvar.h>
4752049049Shkenken #include <arm/fdt/arm_fdtvar.h>
4852049049Shkenken
4952049049Shkenken static int a9tmr_fdt_match(device_t, cfdata_t, void *);
5052049049Shkenken static void a9tmr_fdt_attach(device_t, device_t, void *);
5152049049Shkenken
5252049049Shkenken static void a9tmr_fdt_cpu_hatch(void *, struct cpu_info *);
538dbb374eSjmcneill static void a9tmr_fdt_speed_changed(device_t);
5452049049Shkenken
558dbb374eSjmcneill struct a9tmr_fdt_softc {
568dbb374eSjmcneill device_t sc_dev;
578dbb374eSjmcneill struct clk *sc_clk;
588dbb374eSjmcneill };
598dbb374eSjmcneill
608dbb374eSjmcneill CFATTACH_DECL_NEW(a9tmr_fdt, sizeof(struct a9tmr_fdt_softc),
618dbb374eSjmcneill a9tmr_fdt_match, a9tmr_fdt_attach, NULL, NULL);
6252049049Shkenken
636e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
646e54367aSthorpej { .compat = "arm,cortex-a5-global-timer" },
656e54367aSthorpej { .compat = "arm,cortex-a9-global-timer" },
666e54367aSthorpej DEVICE_COMPAT_EOL
676e54367aSthorpej };
686e54367aSthorpej
6952049049Shkenken static int
a9tmr_fdt_match(device_t parent,cfdata_t cf,void * aux)7052049049Shkenken a9tmr_fdt_match(device_t parent, cfdata_t cf, void *aux)
7152049049Shkenken {
7252049049Shkenken struct fdt_attach_args * const faa = aux;
7352049049Shkenken
746e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
7552049049Shkenken }
7652049049Shkenken
7752049049Shkenken static void
a9tmr_fdt_attach(device_t parent,device_t self,void * aux)7852049049Shkenken a9tmr_fdt_attach(device_t parent, device_t self, void *aux)
7952049049Shkenken {
808dbb374eSjmcneill struct a9tmr_fdt_softc * const sc = device_private(self);
8152049049Shkenken struct fdt_attach_args * const faa = aux;
8252049049Shkenken const int phandle = faa->faa_phandle;
8352049049Shkenken bus_space_handle_t bsh;
84*eb199291Sjmcneill uint32_t mpidr;
85*eb199291Sjmcneill bool is_hardclock;
8652049049Shkenken
878dbb374eSjmcneill sc->sc_dev = self;
888dbb374eSjmcneill sc->sc_clk = fdtbus_clock_get_index(phandle, 0);
898dbb374eSjmcneill if (sc->sc_clk == NULL) {
9052049049Shkenken aprint_error(": couldn't get clock\n");
9152049049Shkenken return;
9252049049Shkenken }
938dbb374eSjmcneill if (clk_enable(sc->sc_clk) != 0) {
9452049049Shkenken aprint_error(": couldn't enable clock\n");
9552049049Shkenken return;
9652049049Shkenken }
9752049049Shkenken
988dbb374eSjmcneill uint32_t rate = clk_get_rate(sc->sc_clk);
9952049049Shkenken prop_dictionary_t dict = device_properties(self);
10052049049Shkenken prop_dictionary_set_uint32(dict, "frequency", rate);
10152049049Shkenken
10252049049Shkenken char intrstr[128];
10352049049Shkenken if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
10452049049Shkenken aprint_error(": failed to decode interrupt\n");
10552049049Shkenken return;
10652049049Shkenken }
10752049049Shkenken
108912cfa14Sjmcneill aprint_naive("\n");
109912cfa14Sjmcneill aprint_normal("\n");
110912cfa14Sjmcneill
111*eb199291Sjmcneill mpidr = armreg_mpidr_read();
112*eb199291Sjmcneill is_hardclock = (mpidr & MPIDR_U) != 0; /* Global timer for UP */
113*eb199291Sjmcneill
114*eb199291Sjmcneill if (is_hardclock) {
11564e248edSryo void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_CLOCK,
11664e248edSryo FDT_INTR_MPSAFE, a9tmr_intr, NULL, device_xname(self));
11752049049Shkenken if (ih == NULL) {
11852049049Shkenken aprint_error_dev(self, "couldn't install interrupt handler\n");
11952049049Shkenken return;
12052049049Shkenken }
12152049049Shkenken aprint_normal_dev(self, "interrupting on %s\n", intrstr);
122*eb199291Sjmcneill }
12352049049Shkenken
12452049049Shkenken bus_addr_t addr;
12552049049Shkenken bus_size_t size;
12652049049Shkenken if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
12752049049Shkenken aprint_error(": couldn't get distributor address\n");
12852049049Shkenken return;
12952049049Shkenken }
13052049049Shkenken if (bus_space_map(faa->faa_bst, addr, size, 0, &bsh)) {
13152049049Shkenken aprint_error(": couldn't map registers\n");
13252049049Shkenken return;
13352049049Shkenken }
13452049049Shkenken
13552049049Shkenken struct mpcore_attach_args mpcaa = {
13652049049Shkenken .mpcaa_name = "arma9tmr",
13752049049Shkenken .mpcaa_memt = faa->faa_bst,
13852049049Shkenken .mpcaa_memh = bsh,
13952049049Shkenken .mpcaa_irq = -1,
14052049049Shkenken };
14152049049Shkenken
142c7fb772bSthorpej config_found(self, &mpcaa, NULL, CFARGS_NONE);
14352049049Shkenken
144*eb199291Sjmcneill if (is_hardclock) {
14552049049Shkenken arm_fdt_cpu_hatch_register(self, a9tmr_fdt_cpu_hatch);
14652049049Shkenken arm_fdt_timer_register(a9tmr_cpu_initclocks);
147*eb199291Sjmcneill }
1488dbb374eSjmcneill
1498dbb374eSjmcneill pmf_event_register(self, PMFE_SPEED_CHANGED, a9tmr_fdt_speed_changed, true);
15052049049Shkenken }
15152049049Shkenken
15252049049Shkenken static void
a9tmr_fdt_cpu_hatch(void * priv,struct cpu_info * ci)15352049049Shkenken a9tmr_fdt_cpu_hatch(void *priv, struct cpu_info *ci)
15452049049Shkenken {
15552049049Shkenken a9tmr_init_cpu_clock(ci);
15652049049Shkenken }
1578dbb374eSjmcneill
1588dbb374eSjmcneill static void
a9tmr_fdt_speed_changed(device_t dev)1598dbb374eSjmcneill a9tmr_fdt_speed_changed(device_t dev)
1608dbb374eSjmcneill {
1618dbb374eSjmcneill struct a9tmr_fdt_softc * const sc = device_private(dev);
1628dbb374eSjmcneill prop_dictionary_t dict = device_properties(dev);
1638dbb374eSjmcneill uint32_t rate;
1648dbb374eSjmcneill
1658dbb374eSjmcneill rate = clk_get_rate(sc->sc_clk);
1668dbb374eSjmcneill prop_dictionary_set_uint32(dict, "frequency", rate);
1678dbb374eSjmcneill
1688dbb374eSjmcneill a9tmr_update_freq(rate);
1698dbb374eSjmcneill }
170