xref: /openbsd-src/sys/arch/armv7/omap/gptimer.c (revision 0ed1bf01ac7e45759b65902d0ab711deb359316d)
1*0ed1bf01Scheloha /* $OpenBSD: gptimer.c,v 1.23 2023/09/17 14:50:51 cheloha Exp $ */
28eda2d14Spatrick /*
38eda2d14Spatrick  * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
48eda2d14Spatrick  *
58eda2d14Spatrick  * Permission to use, copy, modify, and distribute this software for any
68eda2d14Spatrick  * purpose with or without fee is hereby granted, provided that the above
78eda2d14Spatrick  * copyright notice and this permission notice appear in all copies.
88eda2d14Spatrick  *
98eda2d14Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108eda2d14Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118eda2d14Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128eda2d14Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138eda2d14Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148eda2d14Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158eda2d14Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168eda2d14Spatrick  */
178eda2d14Spatrick 
188eda2d14Spatrick /*
1997db43afSjsg  *	WARNING - this timer initialization has not been checked
208eda2d14Spatrick  *	to see if it will do _ANYTHING_ sane if the omap enters
218eda2d14Spatrick  *	low power mode.
228eda2d14Spatrick  */
238eda2d14Spatrick 
248eda2d14Spatrick #include <sys/param.h>
258eda2d14Spatrick #include <sys/systm.h>
267c601e62Scheloha #include <sys/clockintr.h>
278eda2d14Spatrick #include <sys/kernel.h>
288eda2d14Spatrick #include <sys/evcount.h>
298eda2d14Spatrick #include <sys/device.h>
307c601e62Scheloha #include <sys/stdint.h>
318eda2d14Spatrick #include <sys/timetc.h>
328eda2d14Spatrick #include <machine/bus.h>
338d957e1aSsyl #include <armv7/armv7/armv7var.h>
348eda2d14Spatrick #include <armv7/omap/prcmvar.h>
358eda2d14Spatrick 
368eda2d14Spatrick #include <machine/intr.h>
378eda2d14Spatrick 
388eda2d14Spatrick /* registers */
398eda2d14Spatrick #define	GP_TIDR		0x000
408eda2d14Spatrick #define		GP_TIDR_REV	0xff
418eda2d14Spatrick #define GP_TIOCP_CFG	0x010
428eda2d14Spatrick #define 	GP_TIOCP_CFG_CLKA	0x000000300
438eda2d14Spatrick #define 	GP_TIOCP_CFG_EMUFREE	0x000000020
448eda2d14Spatrick #define 	GP_TIOCP_CFG_IDLEMODE	0x000000018
458eda2d14Spatrick #define 	GP_TIOCP_CFG_ENAPWAKEUP	0x000000004
468eda2d14Spatrick #define 	GP_TIOCP_CFG_SOFTRESET	0x000000002
478eda2d14Spatrick #define 	GP_TIOCP_CFG_AUTOIDLE	0x000000001
488eda2d14Spatrick #define	GP_TISTAT	0x014
498eda2d14Spatrick #define 	GP_TISTAT_RESETDONE	0x000000001
508eda2d14Spatrick #define	GP_TISR		0x018
518eda2d14Spatrick #define		GP_TISTAT_TCAR		0x00000004
528eda2d14Spatrick #define		GP_TISTAT_OVF		0x00000002
538eda2d14Spatrick #define		GP_TISTAT_MATCH		0x00000001
548eda2d14Spatrick #define GP_TIER		0x1c
558eda2d14Spatrick #define		GP_TIER_TCAR_EN		0x4
568eda2d14Spatrick #define		GP_TIER_OVF_EN		0x2
578eda2d14Spatrick #define		GP_TIER_MAT_EN		0x1
588eda2d14Spatrick #define	GP_TWER		0x020
598eda2d14Spatrick #define		GP_TWER_TCAR_EN		0x00000004
608eda2d14Spatrick #define		GP_TWER_OVF_EN		0x00000002
618eda2d14Spatrick #define		GP_TWER_MAT_EN		0x00000001
628eda2d14Spatrick #define	GP_TCLR		0x024
638eda2d14Spatrick #define		GP_TCLR_GPO		(1<<14)
648eda2d14Spatrick #define		GP_TCLR_CAPT		(1<<13)
658eda2d14Spatrick #define		GP_TCLR_PT		(1<<12)
668eda2d14Spatrick #define		GP_TCLR_TRG		(3<<10)
678eda2d14Spatrick #define		GP_TCLR_TRG_O		(1<<10)
688eda2d14Spatrick #define		GP_TCLR_TRG_OM		(2<<10)
698eda2d14Spatrick #define		GP_TCLR_TCM		(3<<8)
708eda2d14Spatrick #define		GP_TCLR_TCM_RISE	(1<<8)
718eda2d14Spatrick #define		GP_TCLR_TCM_FALL	(2<<8)
728eda2d14Spatrick #define		GP_TCLR_TCM_BOTH	(3<<8)
738eda2d14Spatrick #define		GP_TCLR_SCPWM		(1<<7)
748eda2d14Spatrick #define		GP_TCLR_CE		(1<<6)
758eda2d14Spatrick #define		GP_TCLR_PRE		(1<<5)
768eda2d14Spatrick #define		GP_TCLR_PTV		(7<<2)
778eda2d14Spatrick #define		GP_TCLR_AR		(1<<1)
788eda2d14Spatrick #define		GP_TCLR_ST		(1<<0)
798eda2d14Spatrick #define	GP_TCRR		0x028				/* counter */
808eda2d14Spatrick #define	GP_TLDR		0x02c				/* reload */
818eda2d14Spatrick #define	GP_TTGR		0x030
828eda2d14Spatrick #define	GP_TWPS		0x034
838eda2d14Spatrick #define		GP_TWPS_TCLR	0x01
848eda2d14Spatrick #define		GP_TWPS_TCRR	0x02
858eda2d14Spatrick #define		GP_TWPS_TLDR	0x04
868eda2d14Spatrick #define		GP_TWPS_TTGR	0x08
878eda2d14Spatrick #define		GP_TWPS_TMAR	0x10
888eda2d14Spatrick #define		GP_TWPS_ALL	0x1f
898eda2d14Spatrick #define	GP_TMAR		0x038
908eda2d14Spatrick #define	GP_TCAR		0x03C
918eda2d14Spatrick #define	GP_TSICR	0x040
928eda2d14Spatrick #define		GP_TSICR_POSTED		0x00000002
938eda2d14Spatrick #define		GP_TSICR_SFT		0x00000001
948eda2d14Spatrick #define	GP_TCAR2	0x044
958eda2d14Spatrick 
968eda2d14Spatrick #define TIMER_FREQUENCY			32768	/* 32kHz is used, selectable */
978eda2d14Spatrick 
988eda2d14Spatrick void gptimer_attach(struct device *parent, struct device *self, void *args);
998eda2d14Spatrick int gptimer_intr(void *frame);
1008eda2d14Spatrick void gptimer_wait(int reg);
1018eda2d14Spatrick void gptimer_cpu_initclocks(void);
10211d1f9b2Scheloha void gptimer_cpu_startclock(void);
1038eda2d14Spatrick void gptimer_delay(u_int);
1047c601e62Scheloha void gptimer_reset_tisr(void);
1058eda2d14Spatrick void gptimer_setstatclockrate(int newhz);
1068eda2d14Spatrick 
1078eda2d14Spatrick bus_space_tag_t gptimer_iot;
1088eda2d14Spatrick bus_space_handle_t gptimer_ioh0, gptimer_ioh1;
1098eda2d14Spatrick int gptimer_irq = 0;
1108eda2d14Spatrick 
1118eda2d14Spatrick u_int gptimer_get_timecount(struct timecounter *);
1128eda2d14Spatrick 
1138eda2d14Spatrick static struct timecounter gptimer_timecounter = {
1148611d3cdScheloha 	.tc_get_timecount = gptimer_get_timecount,
1158611d3cdScheloha 	.tc_counter_mask = 0xffffffff,
1168611d3cdScheloha 	.tc_frequency = 0,
1178611d3cdScheloha 	.tc_name = "gptimer",
1188611d3cdScheloha 	.tc_quality = 0,
1198611d3cdScheloha 	.tc_priv = NULL,
1208611d3cdScheloha 	.tc_user = 0,
1218eda2d14Spatrick };
1228eda2d14Spatrick 
1237c601e62Scheloha uint64_t gptimer_nsec_cycle_ratio;
1247c601e62Scheloha uint64_t gptimer_nsec_max;
1257c601e62Scheloha 
1267c601e62Scheloha void gptimer_rearm(void *, uint64_t);
1277c601e62Scheloha void gptimer_trigger(void *);
1287c601e62Scheloha 
1297c601e62Scheloha const struct intrclock gptimer_intrclock = {
1307c601e62Scheloha 	.ic_rearm = gptimer_rearm,
1317c601e62Scheloha 	.ic_trigger = gptimer_trigger
1327c601e62Scheloha };
1338eda2d14Spatrick 
1349fdf0c62Smpi const struct cfattach	gptimer_ca = {
1358eda2d14Spatrick 	sizeof (struct device), NULL, gptimer_attach
1368eda2d14Spatrick };
1378eda2d14Spatrick 
1388eda2d14Spatrick struct cfdriver gptimer_cd = {
1398eda2d14Spatrick 	NULL, "gptimer", DV_DULL
1408eda2d14Spatrick };
1418eda2d14Spatrick 
1428eda2d14Spatrick void
gptimer_attach(struct device * parent,struct device * self,void * args)1438eda2d14Spatrick gptimer_attach(struct device *parent, struct device *self, void *args)
1448eda2d14Spatrick {
1458d957e1aSsyl 	struct armv7_attach_args *aa = args;
1468eda2d14Spatrick 	bus_space_handle_t ioh;
1478eda2d14Spatrick 	u_int32_t rev;
1488eda2d14Spatrick 
1498d957e1aSsyl 	gptimer_iot = aa->aa_iot;
1508d957e1aSsyl 	if (bus_space_map(gptimer_iot, aa->aa_dev->mem[0].addr,
1518d957e1aSsyl 	    aa->aa_dev->mem[0].size, 0, &ioh))
1528eda2d14Spatrick 		panic("gptimer_attach: bus_space_map failed!");
1538eda2d14Spatrick 
1548eda2d14Spatrick 	rev = bus_space_read_4(gptimer_iot, ioh, GP_TIDR);
1558eda2d14Spatrick 
1568eda2d14Spatrick 	printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
1578eda2d14Spatrick 	if (self->dv_unit == 0) {
1588eda2d14Spatrick 		gptimer_ioh0 = ioh;
1598d957e1aSsyl 		gptimer_irq = aa->aa_dev->irq[0];
1608eda2d14Spatrick 		bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, 0);
1618eda2d14Spatrick 	} else if (self->dv_unit == 1) {
1628eda2d14Spatrick 		/* start timer because it is used in delay */
1638eda2d14Spatrick 		gptimer_ioh1 = ioh;
1648eda2d14Spatrick 		bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TCRR, 0);
1658eda2d14Spatrick 		gptimer_wait(GP_TWPS_ALL);
1668eda2d14Spatrick 		bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TLDR, 0);
1678eda2d14Spatrick 		gptimer_wait(GP_TWPS_ALL);
1688eda2d14Spatrick 		bus_space_write_4(gptimer_iot, gptimer_ioh1, GP_TCLR,
1698eda2d14Spatrick 		    GP_TCLR_AR | GP_TCLR_ST);
1708eda2d14Spatrick 		gptimer_wait(GP_TWPS_ALL);
1718eda2d14Spatrick 
1728eda2d14Spatrick 		gptimer_timecounter.tc_frequency = TIMER_FREQUENCY;
1738eda2d14Spatrick 		tc_init(&gptimer_timecounter);
1748eda2d14Spatrick 	}
1758eda2d14Spatrick 	else
176ddb53a58Smiod 		panic("attaching too many gptimers at 0x%lx",
1778d957e1aSsyl 		    aa->aa_dev->mem[0].addr);
1788eda2d14Spatrick 
1798eda2d14Spatrick 	arm_clock_register(gptimer_cpu_initclocks, gptimer_delay,
18011d1f9b2Scheloha 	    gptimer_setstatclockrate, gptimer_cpu_startclock);
1818eda2d14Spatrick }
1828eda2d14Spatrick 
1838eda2d14Spatrick int
gptimer_intr(void * frame)1848eda2d14Spatrick gptimer_intr(void *frame)
1858eda2d14Spatrick {
1867c601e62Scheloha 	clockintr_dispatch(frame);
1878eda2d14Spatrick 	return 1;
1888eda2d14Spatrick }
1898eda2d14Spatrick 
1908eda2d14Spatrick /*
1918eda2d14Spatrick  * would be interesting to play with trigger mode while having one timer
1922252d02cSkettenis  * in 32kHz mode, and the other timer running in sysclk mode and use
1938eda2d14Spatrick  * the high resolution speeds (matters more for delay than tick timer
1948eda2d14Spatrick  */
1958eda2d14Spatrick 
1968eda2d14Spatrick void
gptimer_cpu_initclocks(void)19737e35e27Sjsg gptimer_cpu_initclocks(void)
1988eda2d14Spatrick {
1997c601e62Scheloha 	stathz = hz;
2007c601e62Scheloha 	profhz = stathz * 10;
201b3ef18bdScheloha 	statclock_is_randomized = 1;
2028eda2d14Spatrick 
2037c601e62Scheloha 	gptimer_nsec_cycle_ratio = TIMER_FREQUENCY * (1ULL << 32) / 1000000000;
2047c601e62Scheloha 	gptimer_nsec_max = UINT64_MAX / gptimer_nsec_cycle_ratio;
2058eda2d14Spatrick 
2068eda2d14Spatrick 	prcm_setclock(1, PRCM_CLK_SPEED_32);
2078eda2d14Spatrick 	prcm_setclock(2, PRCM_CLK_SPEED_32);
2087c601e62Scheloha 
2098eda2d14Spatrick 	/* establish interrupts */
2108eda2d14Spatrick 	arm_intr_establish(gptimer_irq, IPL_CLOCK, gptimer_intr,
2118eda2d14Spatrick 	    NULL, "tick");
2128eda2d14Spatrick 
2138eda2d14Spatrick 	/* setup timer 0 (hardware timer 2) */
2148eda2d14Spatrick 	/* reset? - XXX */
2158eda2d14Spatrick 	gptimer_wait(GP_TWPS_ALL);
2168eda2d14Spatrick 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TIER, GP_TIER_OVF_EN);
2178eda2d14Spatrick 	gptimer_wait(GP_TWPS_ALL);
2188eda2d14Spatrick 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TWER, GP_TWER_OVF_EN);
2198eda2d14Spatrick 	gptimer_wait(GP_TWPS_ALL);
22011d1f9b2Scheloha }
2217c601e62Scheloha 
22211d1f9b2Scheloha void
gptimer_cpu_startclock(void)22311d1f9b2Scheloha gptimer_cpu_startclock(void)
22411d1f9b2Scheloha {
2257c601e62Scheloha 	/* start the clock interrupt cycle */
2267c601e62Scheloha 	clockintr_cpu_init(&gptimer_intrclock);
2277c601e62Scheloha 	clockintr_trigger();
2288eda2d14Spatrick }
2298eda2d14Spatrick 
2308eda2d14Spatrick void
gptimer_wait(int reg)2318eda2d14Spatrick gptimer_wait(int reg)
2328eda2d14Spatrick {
2338eda2d14Spatrick 	while (bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TWPS) & reg)
2348eda2d14Spatrick 		;
2358eda2d14Spatrick }
2368eda2d14Spatrick 
2377c601e62Scheloha /*
2387c601e62Scheloha  * Clear all interrupt status bits.
2397c601e62Scheloha  */
2407c601e62Scheloha void
gptimer_reset_tisr(void)2417c601e62Scheloha gptimer_reset_tisr(void)
2427c601e62Scheloha {
2437c601e62Scheloha 	u_int32_t tisr;
2447c601e62Scheloha 
2457c601e62Scheloha 	tisr = bus_space_read_4(gptimer_iot, gptimer_ioh0, GP_TISR);
2467c601e62Scheloha 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TISR, tisr);
2477c601e62Scheloha }
2487c601e62Scheloha 
2497c601e62Scheloha void
gptimer_rearm(void * unused,uint64_t nsecs)2507c601e62Scheloha gptimer_rearm(void *unused, uint64_t nsecs)
2517c601e62Scheloha {
2527c601e62Scheloha 	uint32_t cycles;
2537c601e62Scheloha 	u_long s;
2547c601e62Scheloha 
2557c601e62Scheloha 	if (nsecs > gptimer_nsec_max)
2567c601e62Scheloha 		nsecs = gptimer_nsec_max;
2577c601e62Scheloha 	cycles = (nsecs * gptimer_nsec_cycle_ratio) >> 32;
2587c601e62Scheloha 
2597c601e62Scheloha 	s = intr_disable();
2607c601e62Scheloha 	gptimer_reset_tisr();
2617c601e62Scheloha         bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCRR,
2627c601e62Scheloha 	    UINT32_MAX - cycles);
2637c601e62Scheloha 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, GP_TCLR_ST);
2647c601e62Scheloha 	gptimer_wait(GP_TWPS_ALL);
2657c601e62Scheloha 	intr_restore(s);
2667c601e62Scheloha }
2677c601e62Scheloha 
2687c601e62Scheloha void
gptimer_trigger(void * unused)2697c601e62Scheloha gptimer_trigger(void *unused)
2707c601e62Scheloha {
2717c601e62Scheloha 	u_long s;
2727c601e62Scheloha 
2737c601e62Scheloha 	s = intr_disable();
2747c601e62Scheloha 
2757c601e62Scheloha 	/* stop timer. */
2767c601e62Scheloha 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, 0);
2777c601e62Scheloha 	gptimer_wait(GP_TWPS_ALL);
2787c601e62Scheloha 
2797c601e62Scheloha 	/* clear interrupt status bits. */
2807c601e62Scheloha 	gptimer_reset_tisr();
2817c601e62Scheloha 
2827c601e62Scheloha 	/* set shortest possible timeout. */
2837c601e62Scheloha 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCRR, UINT32_MAX);
2847c601e62Scheloha 
2857c601e62Scheloha 	/* start timer, wait for writes to post. */
2867c601e62Scheloha 	bus_space_write_4(gptimer_iot, gptimer_ioh0, GP_TCLR, GP_TCLR_ST);
2877c601e62Scheloha 	gptimer_wait(GP_TWPS_ALL);
2887c601e62Scheloha 
2897c601e62Scheloha 	intr_restore(s);
2907c601e62Scheloha }
2917c601e62Scheloha 
2928eda2d14Spatrick void
gptimer_delay(u_int usecs)2938eda2d14Spatrick gptimer_delay(u_int usecs)
2948eda2d14Spatrick {
2958eda2d14Spatrick 	u_int32_t clock, oclock, delta, delaycnt;
2968eda2d14Spatrick 	volatile int j;
2978eda2d14Spatrick 	int csec, usec;
2988eda2d14Spatrick 
2998eda2d14Spatrick 	if (usecs > (0x80000000 / (TIMER_FREQUENCY))) {
3008eda2d14Spatrick 		csec = usecs / 10000;
3018eda2d14Spatrick 		usec = usecs % 10000;
3028eda2d14Spatrick 
3038eda2d14Spatrick 		delaycnt = (TIMER_FREQUENCY / 100) * csec +
3048eda2d14Spatrick 		    (TIMER_FREQUENCY / 100) * usec / 10000;
3058eda2d14Spatrick 	} else {
3068eda2d14Spatrick 		delaycnt = TIMER_FREQUENCY * usecs / 1000000;
3078eda2d14Spatrick 	}
3088eda2d14Spatrick 	if (delaycnt <= 1)
3098eda2d14Spatrick 		for (j = 100; j > 0; j--)
3108eda2d14Spatrick 			;
3118eda2d14Spatrick 
3128eda2d14Spatrick 	if (gptimer_ioh1 == 0) {
3138eda2d14Spatrick 		/* BAH */
3148eda2d14Spatrick 		for (; usecs > 0; usecs--)
3158eda2d14Spatrick 			for (j = 100; j > 0; j--)
3168eda2d14Spatrick 				;
3178eda2d14Spatrick 		return;
3188eda2d14Spatrick 	}
3198eda2d14Spatrick 	oclock = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR);
3208eda2d14Spatrick 	while (1) {
3218eda2d14Spatrick 		for (j = 100; j > 0; j--)
3228eda2d14Spatrick 			;
3238eda2d14Spatrick 		clock = bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR);
3248eda2d14Spatrick 		delta = clock - oclock;
3258eda2d14Spatrick 		if (delta > delaycnt)
3268eda2d14Spatrick 			break;
3278eda2d14Spatrick 	}
3288eda2d14Spatrick 
3298eda2d14Spatrick }
3308eda2d14Spatrick 
3318eda2d14Spatrick void
gptimer_setstatclockrate(int newhz)3328eda2d14Spatrick gptimer_setstatclockrate(int newhz)
3338eda2d14Spatrick {
3348eda2d14Spatrick }
3358eda2d14Spatrick 
3368eda2d14Spatrick 
3378eda2d14Spatrick u_int
gptimer_get_timecount(struct timecounter * tc)3388eda2d14Spatrick gptimer_get_timecount(struct timecounter *tc)
3398eda2d14Spatrick {
3408eda2d14Spatrick 	return bus_space_read_4(gptimer_iot, gptimer_ioh1, GP_TCRR);
3418eda2d14Spatrick }
342