1*482eef70Srin /* $NetBSD: ixp425_timer.c,v 1.20 2020/05/29 12:30:39 rin Exp $ */
200eb02e3Sichiro
300eb02e3Sichiro /*
400eb02e3Sichiro * Copyright (c) 2003
500eb02e3Sichiro * Ichiro FUKUHARA <ichiro@ichiro.org>.
600eb02e3Sichiro * All rights reserved.
700eb02e3Sichiro *
800eb02e3Sichiro * Redistribution and use in source and binary forms, with or without
900eb02e3Sichiro * modification, are permitted provided that the following conditions
1000eb02e3Sichiro * are met:
1100eb02e3Sichiro * 1. Redistributions of source code must retain the above copyright
1200eb02e3Sichiro * notice, this list of conditions and the following disclaimer.
1300eb02e3Sichiro * 2. Redistributions in binary form must reproduce the above copyright
1400eb02e3Sichiro * notice, this list of conditions and the following disclaimer in the
1500eb02e3Sichiro * documentation and/or other materials provided with the distribution.
1600eb02e3Sichiro *
1700eb02e3Sichiro * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
1800eb02e3Sichiro * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1900eb02e3Sichiro * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2000eb02e3Sichiro * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
2100eb02e3Sichiro * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2200eb02e3Sichiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2300eb02e3Sichiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2400eb02e3Sichiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2500eb02e3Sichiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2600eb02e3Sichiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2700eb02e3Sichiro * SUCH DAMAGE.
2800eb02e3Sichiro */
2900eb02e3Sichiro
3000eb02e3Sichiro #include <sys/cdefs.h>
31*482eef70Srin __KERNEL_RCSID(0, "$NetBSD: ixp425_timer.c,v 1.20 2020/05/29 12:30:39 rin Exp $");
3200eb02e3Sichiro
33c95cd915Sscw #include "opt_ixp425.h"
3400eb02e3Sichiro
3500eb02e3Sichiro #include <sys/types.h>
3600eb02e3Sichiro #include <sys/param.h>
3700eb02e3Sichiro #include <sys/systm.h>
3800eb02e3Sichiro #include <sys/kernel.h>
394e56cdd2Sjoerg #include <sys/atomic.h>
4000eb02e3Sichiro #include <sys/time.h>
414e56cdd2Sjoerg #include <sys/timetc.h>
4200eb02e3Sichiro #include <sys/device.h>
4300eb02e3Sichiro
44ca601a77Sthorpej #include <dev/clock_subr.h>
45ca601a77Sthorpej
46ed9977b1Sdyoung #include <sys/bus.h>
4700eb02e3Sichiro #include <machine/intr.h>
4800eb02e3Sichiro
4900eb02e3Sichiro #include <arm/cpufunc.h>
5000eb02e3Sichiro
5100eb02e3Sichiro #include <arm/xscale/ixp425reg.h>
5200eb02e3Sichiro #include <arm/xscale/ixp425var.h>
5300eb02e3Sichiro #include <arm/xscale/ixp425_sipvar.h>
5400eb02e3Sichiro
55a2b8c7fbSmsaitoh static int ixpclk_match(device_t, cfdata_t, void *);
56a2b8c7fbSmsaitoh static void ixpclk_attach(device_t, device_t, void *);
574e56cdd2Sjoerg static u_int ixpclk_get_timecount(struct timecounter *);
5800eb02e3Sichiro
5900eb02e3Sichiro static uint32_t counts_per_hz;
6000eb02e3Sichiro
6100eb02e3Sichiro static void *clock_ih;
6200eb02e3Sichiro
6300eb02e3Sichiro /* callback functions for intr_functions */
6400eb02e3Sichiro int ixpclk_intr(void *);
6500eb02e3Sichiro
6600eb02e3Sichiro struct ixpclk_softc {
6700eb02e3Sichiro bus_addr_t sc_baseaddr;
6800eb02e3Sichiro bus_space_tag_t sc_iot;
6900eb02e3Sichiro bus_space_handle_t sc_ioh;
7000eb02e3Sichiro };
7100eb02e3Sichiro
72c95cd915Sscw #ifndef IXP425_CLOCK_FREQ
73fb2c5211Sscw #define COUNTS_PER_SEC 66666600 /* 66MHz */
74c95cd915Sscw #else
75c95cd915Sscw #define COUNTS_PER_SEC IXP425_CLOCK_FREQ
76c95cd915Sscw #endif
77fb2c5211Sscw #define COUNTS_PER_USEC ((COUNTS_PER_SEC / 1000000) + 1)
7800eb02e3Sichiro
79fb2c5211Sscw static struct ixpclk_softc *ixpclk_sc;
8000eb02e3Sichiro
814e56cdd2Sjoerg static struct timecounter ixpclk_timecounter = {
82*482eef70Srin .tc_get_timecount = ixpclk_get_timecount,
83*482eef70Srin .tc_counter_mask = 0xffffffff,
84*482eef70Srin .tc_frequency = COUNTS_PER_SEC,
85*482eef70Srin .tc_name = "ixpclk",
86*482eef70Srin .tc_quality = 100,
874e56cdd2Sjoerg };
884e56cdd2Sjoerg
894e56cdd2Sjoerg static volatile uint32_t ixpclk_base;
904e56cdd2Sjoerg
91a2b8c7fbSmsaitoh CFATTACH_DECL_NEW(ixpclk, sizeof(struct ixpclk_softc),
9200eb02e3Sichiro ixpclk_match, ixpclk_attach, NULL, NULL);
9300eb02e3Sichiro
9400eb02e3Sichiro #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
9500eb02e3Sichiro (sc)->sc_ioh, \
9600eb02e3Sichiro IXP425_OST_TIM0))
9700eb02e3Sichiro
9808a4aba7Sskrll #define GET_TS_VALUE(sc) (*(volatile uint32_t *) \
995801c731Sscw (IXP425_TIMER_VBASE + IXP425_OST_TS))
1005801c731Sscw
10100eb02e3Sichiro static int
ixpclk_match(device_t parent,cfdata_t match,void * aux)102a2b8c7fbSmsaitoh ixpclk_match(device_t parent, cfdata_t match, void *aux)
10300eb02e3Sichiro {
10400eb02e3Sichiro return 2;
10500eb02e3Sichiro }
10600eb02e3Sichiro
10700eb02e3Sichiro static void
ixpclk_attach(device_t parent,device_t self,void * aux)108a2b8c7fbSmsaitoh ixpclk_attach(device_t parent, device_t self, void *aux)
10900eb02e3Sichiro {
110a2b8c7fbSmsaitoh struct ixpclk_softc *sc = device_private(self);
11100eb02e3Sichiro struct ixpsip_attach_args *sa = aux;
11200eb02e3Sichiro
11300eb02e3Sichiro printf("\n");
11400eb02e3Sichiro
1155801c731Sscw ixpclk_sc = sc;
1165801c731Sscw
11700eb02e3Sichiro sc->sc_iot = sa->sa_iot;
11800eb02e3Sichiro sc->sc_baseaddr = sa->sa_addr;
11900eb02e3Sichiro
12000eb02e3Sichiro if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0,
12100eb02e3Sichiro &sc->sc_ioh))
122a2b8c7fbSmsaitoh panic("%s: Cannot map registers", device_xname(self));
12300eb02e3Sichiro
124a2b8c7fbSmsaitoh aprint_normal_dev(self, "IXP425 Interval Timer\n");
12500eb02e3Sichiro }
12600eb02e3Sichiro
12700eb02e3Sichiro /*
12800eb02e3Sichiro * cpu_initclocks:
12900eb02e3Sichiro *
13000eb02e3Sichiro * Initialize the clock and get them going.
13100eb02e3Sichiro */
13200eb02e3Sichiro void
cpu_initclocks(void)13300eb02e3Sichiro cpu_initclocks(void)
13400eb02e3Sichiro {
13500eb02e3Sichiro struct ixpclk_softc *sc = ixpclk_sc;
13600eb02e3Sichiro u_int oldirqstate;
13700eb02e3Sichiro
13800eb02e3Sichiro if (hz < 50 || COUNTS_PER_SEC % hz) {
13900eb02e3Sichiro aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
14000eb02e3Sichiro hz = 100;
14100eb02e3Sichiro }
14200eb02e3Sichiro
14300eb02e3Sichiro /*
14400eb02e3Sichiro * We only have one timer available; stathz and profhz are
14500eb02e3Sichiro * always left as 0 (the upper-layer clock code deals with
14600eb02e3Sichiro * this situation).
14700eb02e3Sichiro */
14800eb02e3Sichiro if (stathz != 0)
14900eb02e3Sichiro aprint_error("Cannot get %d Hz statclock\n", stathz);
15000eb02e3Sichiro stathz = 0;
15100eb02e3Sichiro
15200eb02e3Sichiro if (profhz != 0)
15300eb02e3Sichiro aprint_error("Cannot get %d Hz profclock\n", profhz);
15400eb02e3Sichiro profhz = 0;
15500eb02e3Sichiro
15600eb02e3Sichiro /* Report the clock frequency. */
15700eb02e3Sichiro aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
15800eb02e3Sichiro
15900eb02e3Sichiro oldirqstate = disable_interrupts(I32_bit);
16000eb02e3Sichiro
16100eb02e3Sichiro /* Hook up the clock interrupt handler. */
16200eb02e3Sichiro clock_ih = ixp425_intr_establish(IXP425_INT_TMR0, IPL_CLOCK,
16300eb02e3Sichiro ixpclk_intr, NULL);
16400eb02e3Sichiro if (clock_ih == NULL)
16500eb02e3Sichiro panic("cpu_initclocks: unable to register timer interrupt");
16600eb02e3Sichiro
16700eb02e3Sichiro /* Set up the new clock parameters. */
16800eb02e3Sichiro
16900eb02e3Sichiro /* clear interrupt */
17000eb02e3Sichiro bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
17100eb02e3Sichiro OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT |
17200eb02e3Sichiro OST_TIM1_INT | OST_TIM0_INT);
17300eb02e3Sichiro
17400eb02e3Sichiro counts_per_hz = COUNTS_PER_SEC / hz;
17500eb02e3Sichiro
17600eb02e3Sichiro /* reload value & Timer enable */
17700eb02e3Sichiro bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD,
17800eb02e3Sichiro (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN);
17900eb02e3Sichiro
18000eb02e3Sichiro restore_interrupts(oldirqstate);
1814e56cdd2Sjoerg
1824e56cdd2Sjoerg tc_init(&ixpclk_timecounter);
18300eb02e3Sichiro }
18400eb02e3Sichiro
18500eb02e3Sichiro /*
18600eb02e3Sichiro * setstatclockrate:
18700eb02e3Sichiro *
18800eb02e3Sichiro * Set the rate of the statistics clock.
18900eb02e3Sichiro *
19000eb02e3Sichiro * We assume that hz is either stathz or profhz, and that neither
19100eb02e3Sichiro * will change after being set by cpu_initclocks(). We could
19200eb02e3Sichiro * recalculate the intervals here, but that would be a pain.
19300eb02e3Sichiro */
19400eb02e3Sichiro void
setstatclockrate(int newhz)195a816cb98She setstatclockrate(int newhz)
19600eb02e3Sichiro {
19700eb02e3Sichiro
19800eb02e3Sichiro /*
19900eb02e3Sichiro * XXX Use TMR1?
20000eb02e3Sichiro */
20100eb02e3Sichiro }
20200eb02e3Sichiro
2034e56cdd2Sjoerg static u_int
ixpclk_get_timecount(struct timecounter * tc)2044e56cdd2Sjoerg ixpclk_get_timecount(struct timecounter *tc)
20500eb02e3Sichiro {
2064e56cdd2Sjoerg u_int savedints, base, counter;
20700eb02e3Sichiro
2084e56cdd2Sjoerg savedints = disable_interrupts(I32_bit);
2094e56cdd2Sjoerg base = ixpclk_base;
2104e56cdd2Sjoerg counter = GET_TIMER_VALUE(ixpclk_sc);
2114e56cdd2Sjoerg restore_interrupts(savedints);
21200eb02e3Sichiro
2134e56cdd2Sjoerg return base - counter;
21400eb02e3Sichiro }
21500eb02e3Sichiro
21600eb02e3Sichiro /*
21700eb02e3Sichiro * delay:
21800eb02e3Sichiro *
21900eb02e3Sichiro * Delay for at least N microseconds.
22000eb02e3Sichiro */
22100eb02e3Sichiro void
delay(u_int n)22200eb02e3Sichiro delay(u_int n)
22300eb02e3Sichiro {
22408a4aba7Sskrll uint32_t first, last;
2255801c731Sscw int usecs;
2265801c731Sscw
2275801c731Sscw if (n == 0)
2285801c731Sscw return;
22900eb02e3Sichiro
23000eb02e3Sichiro /*
2315801c731Sscw * Clamp the timeout at a maximum value (about 32 seconds with
2325801c731Sscw * a 66MHz clock). *Nobody* should be delay()ing for anywhere
2335801c731Sscw * near that length of time and if they are, they should be hung
2345801c731Sscw * out to dry.
23500eb02e3Sichiro */
2365801c731Sscw if (n >= (0x80000000U / COUNTS_PER_USEC))
2375801c731Sscw usecs = (0x80000000U / COUNTS_PER_USEC) - 1;
23800eb02e3Sichiro else
2395801c731Sscw usecs = n * COUNTS_PER_USEC;
24000eb02e3Sichiro
2415801c731Sscw /* Note: Timestamp timer counts *up*, unlike the other timers */
2425801c731Sscw first = GET_TS_VALUE();
24300eb02e3Sichiro
2445801c731Sscw while (usecs > 0) {
2455801c731Sscw last = GET_TS_VALUE();
2465801c731Sscw usecs -= (int)(last - first);
2475801c731Sscw first = last;
24800eb02e3Sichiro }
24900eb02e3Sichiro }
25000eb02e3Sichiro
25100eb02e3Sichiro /*
25200eb02e3Sichiro * ixpclk_intr:
25300eb02e3Sichiro *
25400eb02e3Sichiro * Handle the hardclock interrupt.
25500eb02e3Sichiro */
25600eb02e3Sichiro int
ixpclk_intr(void * arg)25700eb02e3Sichiro ixpclk_intr(void *arg)
25800eb02e3Sichiro {
25900eb02e3Sichiro struct ixpclk_softc *sc = ixpclk_sc;
26000eb02e3Sichiro struct clockframe *frame = arg;
26100eb02e3Sichiro
26200eb02e3Sichiro bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
26300eb02e3Sichiro OST_TIM0_INT);
26400eb02e3Sichiro
2654e56cdd2Sjoerg atomic_add_32(&ixpclk_base, counts_per_hz);
2664e56cdd2Sjoerg
26700eb02e3Sichiro hardclock(frame);
26800eb02e3Sichiro
26900eb02e3Sichiro return (1);
27000eb02e3Sichiro }
271