1/*	$NetBSD: mpcore_clock.c,v 1.1 2011/03/10 07:47:15 bsh Exp $ */
2/*
3 * Copyright (c) 2009, 2010, 2011  Genetec corp.  All rights reserved.
4 * Written by Hashimoto Kenichi for Genetec corp.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY GENETEC CORP. ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORP.
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: mpcore_clock.c,v 1.1 2011/03/10 07:47:15 bsh Exp $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/evcnt.h>
34#include <sys/atomic.h>
35#include <sys/time.h>
36#include <sys/timetc.h>
37
38#include <sys/types.h>
39#include <sys/device.h>
40
41#include <dev/clock_subr.h>
42
43#include <machine/intr.h>
44#include <sys/bus.h>
45
46#include <arm/cpu.h>
47#include <arm/armreg.h>
48#include <arm/cpufunc.h>
49
50#include <arm/mpcore/mpcorereg.h>
51#include <arm/mpcore/mpcorevar.h>
52
53#ifndef	MPCORE_CPU_FREQ
54#error	define MPCORE_CPU_FREQ
55#endif
56
57static volatile uint32_t mpcoreclk_base;
58
59static u_int mpcore_get_timecount(struct timecounter *);
60static int mpcoreclk_intr(void *);
61
62static struct timecounter mpcore_timecounter = {
63	mpcore_get_timecount,	/* get_timecount */
64	0,			/* no poll_pps */
65	0xffffffff,		/* counter_mask */
66	0,			/* frequency */
67	"cpuclock",		/* name */
68	100,			/* quality */
69	NULL,			/* prev */
70	NULL,			/* next */
71};
72
73struct mpcoreclk_softc {
74	device_t sc_dev;
75	bus_space_tag_t sc_iot;
76	bus_space_handle_t sc_ioh;
77	int sc_intr;
78
79	int sc_reload_value;
80
81	void *sc_ih;			/* interrupt handler */
82};
83
84static struct mpcoreclk_softc *mpcoreclk;
85
86static int mpcoreclk_match(device_t, struct cfdata *, void *);
87static void mpcoreclk_attach(device_t, device_t, void *);
88
89CFATTACH_DECL_NEW(mpcoreclk, sizeof(struct mpcoreclk_softc),
90    mpcoreclk_match, mpcoreclk_attach, NULL, NULL);
91
92static int
93mpcoreclk_match(device_t parent, struct cfdata *match, void *aux)
94{
95	if (strcmp(match->cf_name, "mpcoreclk") == 0)
96		return 1;
97
98	return 0;
99}
100
101static void
102mpcoreclk_attach(device_t parent, device_t self, void *aux)
103{
104	struct mpcoreclk_softc *sc = device_private(self);
105	struct pmr_attach_args * const pa = aux;
106
107	aprint_normal(": internal timer\n");
108	aprint_naive("\n");
109
110	sc->sc_dev = self;
111	sc->sc_iot = pa->pa_iot;
112	sc->sc_intr = pa->pa_irq;
113
114	mpcoreclk = sc;
115
116	if (bus_space_subregion(sc->sc_iot, pa->pa_ioh,
117		MPCORE_PMR_TIMER, MPCORE_PMR_TIMER_SIZE,
118		&sc->sc_ioh)){
119		aprint_error_dev(self, "can't subregion\n");
120		return;
121	}
122
123}
124
125void
126cpu_initclocks(void)
127{
128	int freq;
129
130	if (!mpcoreclk) {
131		panic("%s: driver has not been initialized!", __func__);
132	}
133
134	freq = MPCORE_CPU_FREQ / 2;
135	mpcore_timecounter.tc_frequency = freq;
136	mpcoreclk->sc_reload_value = freq / hz;
137
138	/* stop all timers */
139	bus_space_write_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh,
140	    PMR_CLK_CONTROL, 0);
141
142	aprint_normal("clock: hz=%d stathz = %d\n", hz, stathz);
143
144
145	bus_space_write_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh, PMR_CLK_LOAD,
146	    mpcoreclk->sc_reload_value);
147	bus_space_write_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh, PMR_CLK_COUNTER,
148	    mpcoreclk->sc_reload_value);
149	bus_space_write_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh, PMR_CLK_CONTROL,
150	    (0 << CLK_CONTROL_PRESCALER_SHIFT) |
151	    CLK_CONTROL_ENABLE | CLK_CONTROL_AUTOLOAD |
152	    CLK_CONTROL_ITENABLE);
153
154	mpcoreclk->sc_ih = intr_establish(mpcoreclk->sc_intr, IPL_CLOCK,
155	    IST_LEVEL, mpcoreclk_intr, NULL);
156
157	tc_init(&mpcore_timecounter);
158
159}
160
161#if 0
162void
163microtime(struct timeval *tvp)
164{
165}
166#endif
167
168void
169setstatclockrate(int schz)
170{
171}
172
173static int
174mpcoreclk_intr(void *arg)
175{
176	uint32_t reg;
177
178	reg = bus_space_read_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh,
179	    PMR_CLK_INTR);
180	if (reg) {
181		/* clear status */
182		bus_space_write_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh,
183		    PMR_CLK_INTR, reg);
184
185		atomic_add_32(&mpcoreclk_base, mpcoreclk->sc_reload_value);
186
187	}
188
189	hardclock((struct clockframe *)arg);
190
191	return 1;
192}
193
194u_int
195mpcore_get_timecount(struct timecounter *tc)
196{
197	uint32_t counter;
198	uint32_t base;
199	u_int oldirqstate;
200
201	oldirqstate = disable_interrupts(I32_bit);
202	counter = bus_space_read_4(mpcoreclk->sc_iot, mpcoreclk->sc_ioh,
203	    PMR_CLK_COUNTER);
204	base = mpcoreclk_base;
205	restore_interrupts(oldirqstate);
206
207	return base - counter;
208}
209