xref: /openbsd-src/sys/arch/armv7/sunxi/sxitimer.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1*0f9e9ec2Sjsg /*	$OpenBSD: sxitimer.c,v 1.25 2024/05/13 01:15:50 jsg Exp $	*/
219606781Sjasper /*
319606781Sjasper  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
419606781Sjasper  * Copyright (c) 2013 Raphael Graf <r@undefined.ch>
519606781Sjasper  * Copyright (c) 2013 Artturi Alm
619606781Sjasper  *
719606781Sjasper  * Permission to use, copy, modify, and distribute this software for any
819606781Sjasper  * purpose with or without fee is hereby granted, provided that the above
919606781Sjasper  * copyright notice and this permission notice appear in all copies.
1019606781Sjasper  *
1119606781Sjasper  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1219606781Sjasper  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1319606781Sjasper  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1419606781Sjasper  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1519606781Sjasper  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1619606781Sjasper  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1719606781Sjasper  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1819606781Sjasper  */
1919606781Sjasper 
2019606781Sjasper #include <sys/param.h>
2119606781Sjasper #include <sys/systm.h>
2219606781Sjasper #include <sys/kernel.h>
231286a7b8Scheloha #include <sys/clockintr.h>
2419606781Sjasper #include <sys/device.h>
251286a7b8Scheloha #include <sys/stdint.h>
2619606781Sjasper #include <sys/timetc.h>
2719606781Sjasper 
2819606781Sjasper #include <machine/bus.h>
290ca55686Skettenis #include <machine/fdt.h>
3019606781Sjasper #include <machine/intr.h>
3119606781Sjasper 
320ea1c705Spatrick #include <dev/fdt/sunxireg.h>
3319606781Sjasper 
340ca55686Skettenis #include <dev/ofw/openfirm.h>
350ca55686Skettenis #include <dev/ofw/fdt.h>
360ca55686Skettenis 
3719606781Sjasper #define	TIMER_IER 		0x00
3819606781Sjasper #define	TIMER_ISR 		0x04
3919606781Sjasper #define	TIMER_IRQ(x)		(1 << (x))
4019606781Sjasper 
4119606781Sjasper #define	TIMER_CTRL(x)		(0x10 + (0x10 * (x)))
4219606781Sjasper #define	TIMER_INTV(x)		(0x14 + (0x10 * (x)))
4319606781Sjasper #define	TIMER_CURR(x)		(0x18 + (0x10 * (x)))
4419606781Sjasper 
4519606781Sjasper /* A1X counter */
4619606781Sjasper #define	CNT64_CTRL		0xa0
4719606781Sjasper #define	CNT64_LOW		0xa4
4819606781Sjasper #define	CNT64_HIGH		0xa8
4919606781Sjasper 
5019606781Sjasper #define	CNT64_CLR_EN		(1 << 0) /* clear enable */
5119606781Sjasper #define	CNT64_RL_EN		(1 << 1) /* read latch enable */
5219606781Sjasper 
5319606781Sjasper #define	TIMER_ENABLE		(1 << 0)
5419606781Sjasper #define	TIMER_RELOAD		(1 << 1)
5519606781Sjasper #define	TIMER_CLK_SRC_MASK	(3 << 2)
5619606781Sjasper #define	TIMER_OSC24M		(1 << 2)
5719606781Sjasper #define	TIMER_PLL6_6		(2 << 2)
5819606781Sjasper #define	TIMER_PRESC_1		(0 << 4)
5919606781Sjasper #define	TIMER_PRESC_2		(1 << 4)
6019606781Sjasper #define	TIMER_PRESC_4		(2 << 4)
6119606781Sjasper #define	TIMER_PRESC_8		(3 << 4)
6219606781Sjasper #define	TIMER_PRESC_16		(4 << 4)
6319606781Sjasper #define	TIMER_PRESC_32		(5 << 4)
6419606781Sjasper #define	TIMER_PRESC_64		(6 << 4)
6519606781Sjasper #define	TIMER_PRESC_128		(7 << 4)
6619606781Sjasper #define	TIMER_CONTINOUS		(0 << 7)
6719606781Sjasper #define	TIMER_SINGLESHOT	(1 << 7)
6819606781Sjasper 
6919606781Sjasper #define	TICKTIMER		0
7019606781Sjasper #define	STATTIMER		1
7119606781Sjasper #define	CNTRTIMER		2
7219606781Sjasper 
731526eed6Skettenis #define TIMER_SYNC		3
741526eed6Skettenis 
750ca55686Skettenis int	sxitimer_match(struct device *, void *, void *);
7619606781Sjasper void	sxitimer_attach(struct device *, struct device *, void *);
7719606781Sjasper int	sxitimer_tickintr(void *);
7819606781Sjasper void	sxitimer_cpu_initclocks(void);
7911d1f9b2Scheloha void	sxitimer_cpu_startclock(void);
8019606781Sjasper void	sxitimer_setstatclockrate(int);
8119606781Sjasper uint64_t	sxitimer_readcnt64(void);
8219606781Sjasper uint32_t	sxitimer_readcnt32(void);
831526eed6Skettenis void	sxitimer_sync(void);
8419606781Sjasper void	sxitimer_delay(u_int);
8519606781Sjasper 
8619606781Sjasper u_int sxitimer_get_timecount(struct timecounter *);
8719606781Sjasper 
8819606781Sjasper static struct timecounter sxitimer_timecounter = {
898611d3cdScheloha 	.tc_get_timecount = sxitimer_get_timecount,
908611d3cdScheloha 	.tc_counter_mask = 0xffffffff,
918611d3cdScheloha 	.tc_frequency = 0,
928611d3cdScheloha 	.tc_name = "sxitimer",
938611d3cdScheloha 	.tc_quality = 0,
948611d3cdScheloha 	.tc_priv = NULL,
958611d3cdScheloha 	.tc_user = 0,
9619606781Sjasper };
9719606781Sjasper 
981286a7b8Scheloha uint64_t sxitimer_nsec_cycle_ratio;
991286a7b8Scheloha uint64_t sxitimer_nsec_max;
1001286a7b8Scheloha 
1011286a7b8Scheloha void sxitimer_rearm(void *, uint64_t);
1021286a7b8Scheloha void sxitimer_trigger(void *);
1031286a7b8Scheloha 
1041286a7b8Scheloha const struct intrclock sxitimer_intrclock = {
1051286a7b8Scheloha 	.ic_rearm = sxitimer_rearm,
1061286a7b8Scheloha 	.ic_trigger = sxitimer_trigger
1071286a7b8Scheloha };
1081286a7b8Scheloha 
10919606781Sjasper bus_space_tag_t		sxitimer_iot;
11019606781Sjasper bus_space_handle_t	sxitimer_ioh;
11119606781Sjasper 
11219606781Sjasper uint32_t sxitimer_freq[] = {
11319606781Sjasper 	TIMER0_FREQUENCY,
11419606781Sjasper 	TIMER1_FREQUENCY,
11519606781Sjasper 	TIMER2_FREQUENCY,
11619606781Sjasper 	0
11719606781Sjasper };
11819606781Sjasper 
11919606781Sjasper uint32_t sxitimer_irq[] = {
12019606781Sjasper 	TIMER0_IRQ,
12119606781Sjasper 	TIMER1_IRQ,
12219606781Sjasper 	TIMER2_IRQ,
12319606781Sjasper 	0
12419606781Sjasper };
12519606781Sjasper 
12619606781Sjasper struct sxitimer_softc {
12719606781Sjasper 	struct device		sc_dev;
12819606781Sjasper };
12919606781Sjasper 
1309fdf0c62Smpi const struct cfattach sxitimer_ca = {
1310ca55686Skettenis 	sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach
13219606781Sjasper };
13319606781Sjasper 
13419606781Sjasper struct cfdriver sxitimer_cd = {
13519606781Sjasper 	NULL, "sxitimer", DV_DULL
13619606781Sjasper };
13719606781Sjasper 
1380ca55686Skettenis int
sxitimer_match(struct device * parent,void * match,void * aux)1390ca55686Skettenis sxitimer_match(struct device *parent, void *match, void *aux)
14019606781Sjasper {
1410ca55686Skettenis 	struct fdt_attach_args *faa = aux;
14225dfca20Skettenis 	int node;
14325dfca20Skettenis 
14425dfca20Skettenis 	node = OF_finddevice("/");
14525dfca20Skettenis 	if (!OF_is_compatible(node, "allwinner,sun4i-a10") &&
14625dfca20Skettenis 	    !OF_is_compatible(node, "allwinner,sun5i-a10s") &&
14725dfca20Skettenis 	    !OF_is_compatible(node, "allwinner,sun5i-a13"))
14825dfca20Skettenis 		return 0;
1490ca55686Skettenis 
1500ca55686Skettenis 	return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-timer");
1510ca55686Skettenis }
1520ca55686Skettenis 
1530ca55686Skettenis void
sxitimer_attach(struct device * parent,struct device * self,void * aux)1540ca55686Skettenis sxitimer_attach(struct device *parent, struct device *self, void *aux)
1550ca55686Skettenis {
1560ca55686Skettenis 	struct fdt_attach_args *faa = aux;
15719606781Sjasper 
1580ca55686Skettenis 	KASSERT(faa->fa_nreg > 0);
15919606781Sjasper 
1600ca55686Skettenis 	sxitimer_iot = faa->fa_iot;
1610ca55686Skettenis 	if (bus_space_map(sxitimer_iot, faa->fa_reg[0].addr,
1620ca55686Skettenis 	    faa->fa_reg[0].size, 0, &sxitimer_ioh))
1630ca55686Skettenis 		panic("%s: bus_space_map failed!", __func__);
16419606781Sjasper 
16519606781Sjasper 	/* clear counter, loop until ready */
166fab3c340Sjsg 	bus_space_write_4(sxitimer_iot, sxitimer_ioh, CNT64_CTRL,
16719606781Sjasper 	    CNT64_CLR_EN); /* XXX as a side-effect counter clk src=OSC24M */
168fab3c340Sjsg 	while (bus_space_read_4(sxitimer_iot, sxitimer_ioh, CNT64_CTRL)
16919606781Sjasper 	    & CNT64_CLR_EN)
17019606781Sjasper 		continue;
17119606781Sjasper 
17219606781Sjasper 	/* stop timer, and set clk src */
17319606781Sjasper 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
1740ca55686Skettenis 	    TIMER_CTRL(TICKTIMER), TIMER_OSC24M);
17519606781Sjasper 
1761286a7b8Scheloha 	sxitimer_nsec_cycle_ratio =
1771286a7b8Scheloha 	    sxitimer_freq[TICKTIMER] * (1ULL << 32) / 1000000000;
1781286a7b8Scheloha 	sxitimer_nsec_max = UINT64_MAX / sxitimer_nsec_cycle_ratio;
17919606781Sjasper 
1801286a7b8Scheloha 	stathz = hz;
1811286a7b8Scheloha 	profhz = stathz * 10;
182b3ef18bdScheloha 	statclock_is_randomized = 1;
1830ca55686Skettenis 
1840ca55686Skettenis 	/* stop timer, and set clk src */
1850ca55686Skettenis 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
1860ca55686Skettenis 	    TIMER_CTRL(CNTRTIMER), TIMER_OSC24M);
1871286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
1881286a7b8Scheloha 	    TIMER_INTV(CNTRTIMER), UINT32_MAX);
1890ca55686Skettenis 
1901286a7b8Scheloha 	sxitimer_timecounter.tc_frequency = sxitimer_freq[CNTRTIMER];
19119606781Sjasper 	tc_init(&sxitimer_timecounter);
1921286a7b8Scheloha 
19319606781Sjasper 	arm_clock_register(sxitimer_cpu_initclocks, sxitimer_delay,
19411d1f9b2Scheloha 	    sxitimer_setstatclockrate, sxitimer_cpu_startclock);
19519606781Sjasper 
1961286a7b8Scheloha 	printf(": %d kHz", sxitimer_freq[CNTRTIMER] / 1000);
19719606781Sjasper 
19819606781Sjasper 	printf("\n");
19919606781Sjasper }
20019606781Sjasper 
20119606781Sjasper /*
20219606781Sjasper  * would be interesting to play with trigger mode while having one timer
2032252d02cSkettenis  * in 32kHz mode, and the other timer running in sysclk mode and use
20419606781Sjasper  * the high resolution speeds (matters more for delay than tick timer)
20519606781Sjasper  */
20619606781Sjasper 
20719606781Sjasper void
sxitimer_cpu_initclocks(void)20819606781Sjasper sxitimer_cpu_initclocks(void)
20919606781Sjasper {
2108b0205e3Skettenis 	uint32_t isr, ier, ctrl;
21119606781Sjasper 
2121286a7b8Scheloha 	/* establish interrupt */
21319606781Sjasper 	arm_intr_establish(sxitimer_irq[TICKTIMER], IPL_CLOCK,
21419606781Sjasper 	    sxitimer_tickintr, NULL, "tick");
21519606781Sjasper 
21619606781Sjasper 	/* clear timer interrupt pending bits */
21719606781Sjasper 	isr = bus_space_read_4(sxitimer_iot, sxitimer_ioh, TIMER_ISR);
2181286a7b8Scheloha 	isr |= TIMER_IRQ(TICKTIMER);
21919606781Sjasper 	bus_space_write_4(sxitimer_iot, sxitimer_ioh, TIMER_ISR, isr);
22019606781Sjasper 
2211286a7b8Scheloha 	/* enable timer IRQ */
22219606781Sjasper 	ier = bus_space_read_4(sxitimer_iot, sxitimer_ioh, TIMER_IER);
2231286a7b8Scheloha 	ier |= TIMER_IRQ(TICKTIMER);
22419606781Sjasper 	bus_space_write_4(sxitimer_iot, sxitimer_ioh, TIMER_IER, ier);
22519606781Sjasper 
22619606781Sjasper 	/* enable timers */
2278b0205e3Skettenis 	ctrl = bus_space_read_4(sxitimer_iot, sxitimer_ioh,
2288b0205e3Skettenis 	    TIMER_CTRL(CNTRTIMER));
22919606781Sjasper 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
23019606781Sjasper 	    TIMER_CTRL(CNTRTIMER),
2318b0205e3Skettenis 	    ctrl | TIMER_ENABLE | TIMER_RELOAD | TIMER_CONTINOUS);
23211d1f9b2Scheloha }
23319606781Sjasper 
23411d1f9b2Scheloha void
sxitimer_cpu_startclock(void)23511d1f9b2Scheloha sxitimer_cpu_startclock(void)
23611d1f9b2Scheloha {
2371286a7b8Scheloha 	/* start clock interrupt cycle */
2381286a7b8Scheloha 	clockintr_cpu_init(&sxitimer_intrclock);
2391286a7b8Scheloha 	clockintr_trigger();
24019606781Sjasper }
24119606781Sjasper 
24219606781Sjasper int
sxitimer_tickintr(void * frame)24319606781Sjasper sxitimer_tickintr(void *frame)
24419606781Sjasper {
24519606781Sjasper 	splassert(IPL_CLOCK);
24619606781Sjasper 
24719606781Sjasper 	/* clear timer pending interrupt bit */
24819606781Sjasper 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
24919606781Sjasper 	    TIMER_ISR, TIMER_IRQ(TICKTIMER));
25019606781Sjasper 
2511286a7b8Scheloha 	return clockintr_dispatch(frame);
25219606781Sjasper }
25319606781Sjasper 
25419606781Sjasper uint64_t
sxitimer_readcnt64(void)25519606781Sjasper sxitimer_readcnt64(void)
25619606781Sjasper {
25719606781Sjasper 	uint32_t low, high;
25819606781Sjasper 
25919606781Sjasper 	/* latch counter, loop until ready */
260fab3c340Sjsg 	bus_space_write_4(sxitimer_iot, sxitimer_ioh, CNT64_CTRL, CNT64_RL_EN);
261fab3c340Sjsg 	while (bus_space_read_4(sxitimer_iot, sxitimer_ioh, CNT64_CTRL)
26219606781Sjasper 	    & CNT64_RL_EN)
26319606781Sjasper 		continue;
26419606781Sjasper 
26519606781Sjasper 	/*
26619606781Sjasper 	 * A10 usermanual doesn't mention anything about order, but fwiw
26719606781Sjasper 	 * iirc. A20 manual mentions that low should be read first.
26819606781Sjasper 	 */
26919606781Sjasper 	/* XXX check above */
270fab3c340Sjsg 	low = bus_space_read_4(sxitimer_iot, sxitimer_ioh, CNT64_LOW);
271fab3c340Sjsg 	high = bus_space_read_4(sxitimer_iot, sxitimer_ioh, CNT64_HIGH);
27219606781Sjasper 	return (uint64_t)high << 32 | low;
27319606781Sjasper }
27419606781Sjasper 
27519606781Sjasper uint32_t
sxitimer_readcnt32(void)27619606781Sjasper sxitimer_readcnt32(void)
27719606781Sjasper {
27819606781Sjasper 	return bus_space_read_4(sxitimer_iot, sxitimer_ioh,
27919606781Sjasper 	    TIMER_CURR(CNTRTIMER));
28019606781Sjasper }
28119606781Sjasper 
28219606781Sjasper void
sxitimer_sync(void)2831526eed6Skettenis sxitimer_sync(void)
2841526eed6Skettenis {
2851526eed6Skettenis 	uint32_t now = sxitimer_readcnt32();
2861526eed6Skettenis 
2871526eed6Skettenis 	while ((now - sxitimer_readcnt32()) < TIMER_SYNC)
2881526eed6Skettenis 		CPU_BUSY_CYCLE();
2891526eed6Skettenis }
2901526eed6Skettenis 
2911526eed6Skettenis void
sxitimer_delay(u_int usecs)29219606781Sjasper sxitimer_delay(u_int usecs)
29319606781Sjasper {
29419606781Sjasper 	uint64_t oclock, timeout;
29519606781Sjasper 
29619606781Sjasper 	oclock = sxitimer_readcnt64();
29719606781Sjasper 	timeout = oclock + (COUNTER_FREQUENCY / 1000000) * usecs;
29819606781Sjasper 
29919606781Sjasper 	while (oclock < timeout)
30019606781Sjasper 		oclock = sxitimer_readcnt64();
30119606781Sjasper }
30219606781Sjasper 
30319606781Sjasper void
sxitimer_setstatclockrate(int newhz)30419606781Sjasper sxitimer_setstatclockrate(int newhz)
30519606781Sjasper {
30619606781Sjasper }
30719606781Sjasper 
30819606781Sjasper u_int
sxitimer_get_timecount(struct timecounter * tc)30919606781Sjasper sxitimer_get_timecount(struct timecounter *tc)
31019606781Sjasper {
31119606781Sjasper 	return (u_int)UINT_MAX - sxitimer_readcnt32();
31219606781Sjasper }
3131286a7b8Scheloha 
3141286a7b8Scheloha void
sxitimer_rearm(void * unused,uint64_t nsecs)3151286a7b8Scheloha sxitimer_rearm(void *unused, uint64_t nsecs)
3161286a7b8Scheloha {
3171286a7b8Scheloha 	uint32_t ctrl, cycles;
3181286a7b8Scheloha 
3191286a7b8Scheloha 	if (nsecs > sxitimer_nsec_max)
3201286a7b8Scheloha 		nsecs = sxitimer_nsec_max;
3211286a7b8Scheloha 	cycles = (nsecs * sxitimer_nsec_cycle_ratio) >> 32;
3221286a7b8Scheloha 	if (cycles < 10)
3231286a7b8Scheloha 		cycles = 10;	/* XXX Why do we need to round up to 10? */
3241286a7b8Scheloha 
3251286a7b8Scheloha 	ctrl = bus_space_read_4(sxitimer_iot, sxitimer_ioh,
3261286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER));
3271286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3281286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER), ctrl & ~TIMER_ENABLE);
3291286a7b8Scheloha 
3301286a7b8Scheloha 	sxitimer_sync();
3311286a7b8Scheloha 
3321286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3331286a7b8Scheloha 	    TIMER_INTV(TICKTIMER), cycles);
3341286a7b8Scheloha 
3351286a7b8Scheloha 	ctrl = bus_space_read_4(sxitimer_iot, sxitimer_ioh,
3361286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER));
3371286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3381286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER),
3391286a7b8Scheloha 	    ctrl | TIMER_ENABLE | TIMER_RELOAD | TIMER_SINGLESHOT);
3401286a7b8Scheloha }
3411286a7b8Scheloha 
3421286a7b8Scheloha void
sxitimer_trigger(void * unused)3431286a7b8Scheloha sxitimer_trigger(void *unused)
3441286a7b8Scheloha {
3451286a7b8Scheloha 	uint32_t ctrl;
3461286a7b8Scheloha 
3471286a7b8Scheloha 	ctrl = bus_space_read_4(sxitimer_iot, sxitimer_ioh,
3481286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER));
3491286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3501286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER), ctrl & ~TIMER_ENABLE);
3511286a7b8Scheloha 
3521286a7b8Scheloha 	sxitimer_sync();
3531286a7b8Scheloha 
3541286a7b8Scheloha 	/* XXX Why do we need to round up to 10? */
3551286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3561286a7b8Scheloha 	    TIMER_INTV(TICKTIMER), 10);
3571286a7b8Scheloha 
3581286a7b8Scheloha 	ctrl = bus_space_read_4(sxitimer_iot, sxitimer_ioh,
3591286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER));
3601286a7b8Scheloha 	bus_space_write_4(sxitimer_iot, sxitimer_ioh,
3611286a7b8Scheloha 	    TIMER_CTRL(TICKTIMER),
3621286a7b8Scheloha 	    ctrl | TIMER_ENABLE | TIMER_RELOAD | TIMER_SINGLESHOT);
3631286a7b8Scheloha }
364