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