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