ti_omaptimer.c revision 1.2 1 /* $NetBSD: ti_omaptimer.c,v 1.2 2019/10/27 17:59:21 jmcneill Exp $ */
2
3 #include <sys/cdefs.h>
4 __KERNEL_RCSID(0, "$NetBSD: ti_omaptimer.c,v 1.2 2019/10/27 17:59:21 jmcneill Exp $");
5
6 #include <sys/types.h>
7 #include <sys/param.h>
8 #include <sys/bus.h>
9 #include <sys/device.h>
10 #include <sys/timetc.h>
11 #include <sys/kernel.h>
12
13 #include <arm/locore.h>
14 #include <arm/fdt/arm_fdtvar.h>
15
16 #include <dev/fdt/fdtvar.h>
17
18 #include <arm/ti/ti_prcm.h>
19
20 #define TIMER_IRQENABLE_SET 0x2c
21 #define TIMER_IRQENABLE_CLR 0x30
22 #define MAT_EN_FLAG __BIT(0)
23 #define OVF_EN_FLAG __BIT(1)
24 #define TCAR_EN_FLAG __BIT(2)
25 #define TIMER_TCLR 0x38
26 #define TCLR_ST __BIT(0)
27 #define TCLR_AR __BIT(1)
28 #define TIMER_TCRR 0x3c
29 #define TIMER_TLDR 0x40
30
31 /* XXX */
32 #define IS_TIMER2(addr) ((addr) == 0x48040000)
33 #define IS_TIMER3(addr) ((addr) == 0x48042000)
34
35 static const char * const compatible[] = {
36 "ti,am335x-timer-1ms",
37 "ti,am335x-timer",
38 NULL
39 };
40
41 struct omaptimer_softc {
42 device_t sc_dev;
43 bus_space_tag_t sc_bst;
44 bus_space_handle_t sc_bsh;
45 int sc_phandle;
46 struct clk *sc_clk;
47 struct timecounter sc_tc;
48 };
49
50 static struct omaptimer_softc *timer_softc;
51
52 static int
53 omaptimer_intr(void *arg)
54 {
55 struct omaptimer_softc * const sc = timer_softc;
56 struct clockframe * const frame = arg;
57
58 bus_space_write_4(sc->sc_bst, sc->sc_bsh, 0x28, 2);
59 hardclock(frame);
60
61 return 1;
62 }
63
64 static void
65 omaptimer_cpu_initclocks(void)
66 {
67 struct omaptimer_softc * const sc = timer_softc;
68 char intrstr[128];
69 void *ih;
70
71 KASSERT(sc != NULL);
72 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr)))
73 panic("%s: failed to decode interrupt", __func__);
74 ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_CLOCK,
75 FDT_INTR_MPSAFE, omaptimer_intr, NULL);
76 if (ih == NULL)
77 panic("%s: failed to establish timer interrupt", __func__);
78
79 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
80
81 /* Enable interrupts */
82 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TIMER_IRQENABLE_SET, OVF_EN_FLAG);
83 }
84
85 static u_int
86 omaptimer_get_timecount(struct timecounter *tc)
87 {
88 struct omaptimer_softc * const sc = tc->tc_priv;
89
90 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, TIMER_TCRR);
91 }
92
93 static int
94 omaptimer_match(device_t parent, cfdata_t match, void *aux)
95 {
96 struct fdt_attach_args * const faa = aux;
97
98 return of_match_compatible(faa->faa_phandle, compatible);
99 }
100
101 static void
102 omaptimer_attach(device_t parent, device_t self, void *aux)
103 {
104 struct omaptimer_softc * const sc = device_private(self);
105 struct fdt_attach_args * const faa = aux;
106 const int phandle = faa->faa_phandle;
107 struct timecounter *tc = &sc->sc_tc;
108 bus_addr_t addr;
109 bus_size_t size;
110
111 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
112 aprint_error(": couldn't get registers\n");
113 return;
114 }
115
116 #if 0
117 if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) {
118 aprint_error(": couldn't get clock\n");
119 return;
120 }
121 #endif
122
123 sc->sc_dev = self;
124 sc->sc_phandle = phandle;
125 sc->sc_bst = faa->faa_bst;
126
127 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
128 device_printf(self, "unable to map bus space");
129 return;
130 }
131
132 if (ti_prcm_enable_hwmod(OF_parent(phandle), 0) != 0) {
133 aprint_error(": couldn't enable module\n");
134 return;
135 }
136
137 aprint_naive("\n");
138 aprint_normal(": Timer\n");
139
140 if (IS_TIMER2(addr)) {
141 /* Install timecounter */
142 tc->tc_get_timecount = omaptimer_get_timecount;
143 tc->tc_counter_mask = ~0u;
144 tc->tc_frequency = 24000000;
145 tc->tc_name = "Timer2";
146 tc->tc_quality = 200;
147 tc->tc_priv = sc;
148 tc_init(tc);
149 } else if (IS_TIMER3(addr)) {
150 /* Configure the timer */
151 const uint32_t value = (0xffffffff - ((24000000UL / hz) - 1));
152 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TIMER_TLDR, value);
153 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TIMER_TCRR, value);
154 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TIMER_IRQENABLE_CLR,
155 MAT_EN_FLAG | OVF_EN_FLAG | TCAR_EN_FLAG);
156 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TIMER_TCLR, TCLR_ST | TCLR_AR);
157
158 /* Use this as the OS timer in UP configurations */
159 if (!arm_has_mpext_p) {
160 timer_softc = sc;
161 arm_fdt_timer_register(omaptimer_cpu_initclocks);
162 }
163 }
164 }
165
166 CFATTACH_DECL_NEW(omaptimer, sizeof(struct omaptimer_softc),
167 omaptimer_match, omaptimer_attach, NULL, NULL);
168
169