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