xref: /openbsd/sys/arch/riscv64/dev/sxitimer.c (revision 5dea098c)
1 /*	$OpenBSD: sxitimer.c,v 1.1 2024/01/27 12:05:40 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/clockintr.h>
21 #include <sys/device.h>
22 #include <sys/sensors.h>
23 
24 #include <machine/intr.h>
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/ofw/openfirm.h>
29 #include <dev/ofw/ofw_clock.h>
30 #include <dev/ofw/fdt.h>
31 
32 /* Registers */
33 #define TMR_IRQ_EN		0x0000
34 #define  TMR1_IRQ_EN		(1 << 1)
35 #define  TMR0_IRQ_EN		(1 << 0)
36 #define TMR_IRQ_STA		0x0004
37 #define  TMR1_IRQ_PEND		(1 << 1)
38 #define  TMR0_IRQ_PEND		(1 << 0)
39 #define TMR0_CTRL		0x0010
40 #define  TMR0_MODE_SINGLE	(1 << 7)
41 #define  TMR0_CLK_PRES_1	(0 << 4)
42 #define  TMR0_CLK_SRC_OSC24M	(1 << 2)
43 #define  TMR0_RELOAD		(1 << 1)
44 #define  TMR0_EN		(1 << 0)
45 #define TMR0_INTV_VALUE		0x0014
46 
47 #define HREAD4(sc, reg)							\
48 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
49 #define HWRITE4(sc, reg, val)						\
50 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
51 
52 struct sxitimer_softc {
53 	struct device		sc_dev;
54 	bus_space_tag_t		sc_iot;
55 	bus_space_handle_t	sc_ioh;
56 
57 	uint32_t		sc_ticks_per_second;
58 	uint64_t		sc_nsec_cycle_ratio;
59 	uint64_t		sc_nsec_max;
60 	void			*sc_ih;
61 };
62 
63 int	sxitimer_match(struct device *, void *, void *);
64 void	sxitimer_attach(struct device *, struct device *, void *);
65 
66 const struct cfattach sxitimer_ca = {
67 	sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach
68 };
69 
70 struct cfdriver sxitimer_cd = {
71 	NULL, "sxitimer", DV_DULL
72 };
73 
74 void	sxitimer_startclock(void);
75 int	sxitimer_intr(void *);
76 void	sxitimer_rearm(void *, uint64_t);
77 void	sxitimer_trigger(void *);
78 
79 struct intrclock sxitimer_intrclock = {
80 	.ic_rearm = sxitimer_rearm,
81 	.ic_trigger = sxitimer_trigger
82 };
83 
84 int
85 sxitimer_match(struct device *parent, void *match, void *aux)
86 {
87 	struct fdt_attach_args *faa = aux;
88 
89 	return OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-timer");
90 }
91 
92 void
93 sxitimer_attach(struct device *parent, struct device *self, void *aux)
94 {
95 	struct sxitimer_softc *sc = (struct sxitimer_softc *)self;
96 	struct fdt_attach_args *faa = aux;
97 
98 	if (faa->fa_nreg < 1) {
99 		printf(": no registers\n");
100 		return;
101 	}
102 
103 	sc->sc_iot = faa->fa_iot;
104 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
105 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
106 		printf(": can't map registers\n");
107 		return;
108 	}
109 
110 	HWRITE4(sc, TMR_IRQ_EN, 0);
111 
112 	sc->sc_ticks_per_second = clock_get_frequency(faa->fa_node, NULL);
113 	sc->sc_nsec_cycle_ratio =
114 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
115 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
116 
117 	sxitimer_intrclock.ic_cookie = sc;
118 	cpu_startclock_fcn = sxitimer_startclock;
119 
120 	sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_CLOCK,
121 	    sxitimer_intr, NULL, sc->sc_dev.dv_xname);
122 	if (sc->sc_ih == NULL) {
123 		printf("can't establish interrupt\n");
124 		return;
125 	}
126 
127 	HWRITE4(sc, TMR0_INTV_VALUE, 0);
128 	HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 |
129 	    TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN);
130 	HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND);
131 	HWRITE4(sc, TMR_IRQ_EN, TMR0_IRQ_EN);
132 
133 	printf(": %u kHz\n", sc->sc_ticks_per_second / 1000);
134 }
135 
136 void
137 sxitimer_startclock(void)
138 {
139 	clockintr_cpu_init(&sxitimer_intrclock);
140 	clockintr_trigger();
141 }
142 
143 int
144 sxitimer_intr(void *frame)
145 {
146 	struct sxitimer_softc *sc = sxitimer_intrclock.ic_cookie;
147 
148 	HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND);
149 	return clockintr_dispatch(frame);
150 }
151 
152 void
153 sxitimer_rearm(void *cookie, uint64_t nsecs)
154 {
155 	struct sxitimer_softc *sc = cookie;
156 	uint32_t cycles;
157 
158 	if (nsecs > sc->sc_nsec_max)
159 		nsecs = sc->sc_nsec_max;
160 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
161 	if (cycles > UINT32_MAX)
162 		cycles = UINT32_MAX;
163 	if (cycles < 1)
164 		cycles = 1;
165 	HWRITE4(sc, TMR0_INTV_VALUE, cycles);
166 	HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 |
167 	    TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN);
168 }
169 
170 void
171 sxitimer_trigger(void *cookie)
172 {
173 	struct sxitimer_softc *sc = cookie;
174 
175 	HWRITE4(sc, TMR0_INTV_VALUE, 1);
176 	HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 |
177 	    TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN);
178 }
179