pwmclock.c revision 1.1 1 /* $NetBSD: pwmclock.c,v 1.1 2011/12/13 14:39:37 macallan Exp $ */
2
3 /*
4 * Copyright (c) 2011 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: pwmclock.c,v 1.1 2011/12/13 14:39:37 macallan Exp $");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/device.h>
35 #include <sys/cpu.h>
36 #include <sys/timetc.h>
37 #include <sys/sysctl.h>
38
39 #include <dev/pci/voyagervar.h>
40 #include <dev/ic/sm502reg.h>
41
42 #include <mips/mips3_clock.h>
43 #include <mips/locore.h>
44 #include <mips/bonito/bonitoreg.h>
45 #include <mips/bonito/bonitovar.h>
46
47 #ifdef PWMCLOCK_DEBUG
48 #define DPRINTF aprint_error
49 #else
50 #define DPRINTF while (0) printf
51 #endif
52
53 int pwmclock_intr(void *);
54
55 struct pwmclock_softc {
56 device_t sc_dev;
57 bus_space_tag_t sc_memt;
58 bus_space_handle_t sc_regh;
59 uint32_t sc_reg, sc_last;
60 uint32_t sc_scale[8];
61 uint32_t sc_count; /* should probably be 64 bit */
62 int sc_step;
63 int sc_step_wanted;
64 };
65
66 static int pwmclock_match(device_t, cfdata_t, void *);
67 static void pwmclock_attach(device_t, device_t, void *);
68
69 CFATTACH_DECL_NEW(pwmclock, sizeof(struct pwmclock_softc),
70 pwmclock_match, pwmclock_attach, NULL, NULL);
71
72 static void pwmclock_start(void);
73 static u_int get_pwmclock_timecount(struct timecounter *);
74
75 struct pwmclock_softc *pwmclock;
76 extern void (*initclocks_ptr)(void);
77 extern struct clockframe cf;
78
79 /* 0, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, 1 */
80 static int scale_m[] = {1, 1, 3, 1, 5, 3, 7, 1};
81 static int scale_d[] = {0, 4, 8, 2, 8, 4, 8, 1};
82
83 #define scale(x, f) (x * scale_d[f] / scale_m[f])
84
85 void pwmclock_set_speed(struct pwmclock_softc *, int);
86 static int pwmclock_cpuspeed_temp(SYSCTLFN_ARGS);
87 static int pwmclock_cpuspeed_cur(SYSCTLFN_ARGS);
88 static int pwmclock_cpuspeed_available(SYSCTLFN_ARGS);
89
90 static struct timecounter pwmclock_timecounter = {
91 get_pwmclock_timecount, /* get_timecount */
92 0, /* no poll_pps */
93 0xffffffff, /* counter_mask */
94 0, /* frequency */
95 "pwm", /* name */
96 100, /* quality */
97 NULL, /* tc_priv */
98 NULL /* tc_next */
99 };
100
101 static int
102 pwmclock_match(device_t parent, cfdata_t match, void *aux)
103 {
104 struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
105
106 if (strcmp(vaa->vaa_name, "pwmclock") == 0) return 100;
107 return 0;
108 }
109
110 static uint32_t
111 pwmclock_wait_edge(struct pwmclock_softc *sc)
112 {
113 /* clear interrupt */
114 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM1, sc->sc_reg);
115 while ((bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PWM1) & SM502_PWM_INTR_PENDING) == 0);
116 return mips3_cp0_count_read();
117 }
118
119 static void
120 pwmclock_attach(device_t parent, device_t self, void *aux)
121 {
122 struct pwmclock_softc *sc = device_private(self);
123 struct voyager_attach_args *vaa = aux;
124 const struct sysctlnode *sysctl_node, *me, *freq;
125 uint32_t reg, last, curr, diff, acc;
126 int i, clk;
127
128 sc->sc_dev = self;
129 sc->sc_memt = vaa->vaa_tag;
130 sc->sc_regh = vaa->vaa_regh;
131
132 aprint_normal("\n");
133
134 voyager_establish_intr(parent, 22, pwmclock_intr, sc);
135 reg = voyager_set_pwm(100, 100); /* 100Hz, 10% duty cycle */
136 reg |= SM502_PWM_ENABLE | SM502_PWM_ENABLE_INTR | SM502_PWM_INTR_PENDING;
137 sc->sc_reg = reg;
138 pwmclock = sc;
139 initclocks_ptr = pwmclock_start;
140
141 /* ok, let's see how far the cycle counter gets between interrupts */
142 aprint_normal_dev(sc->sc_dev, "calibrating CPU timer...\n");
143 for (clk = 1; clk < 8; clk++) {
144 REGVAL(LS2F_CHIPCFG0) = (REGVAL(LS2F_CHIPCFG0) & ~LS2FCFG_FREQSCALE_MASK) | clk;
145 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM1, sc->sc_reg);
146 acc = 0;
147 last = pwmclock_wait_edge(sc);
148 for (i = 0; i < 16; i++) {
149 curr = pwmclock_wait_edge(sc);
150 diff = curr - last;
151 acc += diff;
152 last = curr;
153 }
154 sc->sc_scale[clk] = (acc >> 4) / 5000;
155 }
156 for (clk = 1; clk < 8; clk++) {
157 aprint_normal_dev(sc->sc_dev, "%d/8: %d\n", clk + 1, sc->sc_scale[clk]);
158 }
159 sc->sc_step = 7;
160 sc->sc_step_wanted = 7;
161
162 /* now setup sysctl */
163 if (sysctl_createv(NULL, 0, NULL,
164 &me,
165 CTLFLAG_READWRITE, CTLTYPE_NODE, "loongson", NULL, NULL,
166 0, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL) != 0)
167 aprint_error_dev(sc->sc_dev, "couldn't create 'loongson' node\n");
168
169 if (sysctl_createv(NULL, 0, NULL,
170 &freq,
171 CTLFLAG_READWRITE, CTLTYPE_NODE, "frequency", NULL, NULL,
172 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL) != 0)
173 aprint_error_dev(sc->sc_dev, "couldn't create 'frequency' node\n");
174
175 if (sysctl_createv(NULL, 0, NULL,
176 &sysctl_node,
177 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
178 CTLTYPE_INT, "target", "CPU speed", pwmclock_cpuspeed_temp,
179 0, sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
180 CTL_CREATE, CTL_EOL) == 0) {
181 } else
182 aprint_error_dev(sc->sc_dev, "couldn't create 'target' node\n");
183
184 if (sysctl_createv(NULL, 0, NULL,
185 &sysctl_node,
186 CTLFLAG_READWRITE,
187 CTLTYPE_INT, "current", NULL, pwmclock_cpuspeed_cur,
188 1, sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
189 CTL_CREATE, CTL_EOL) == 0) {
190 } else
191 aprint_error_dev(sc->sc_dev, "couldn't create 'current' node\n");
192
193 if (sysctl_createv(NULL, 0, NULL,
194 &sysctl_node,
195 CTLFLAG_READWRITE,
196 CTLTYPE_STRING, "available", NULL, pwmclock_cpuspeed_available,
197 2, sc, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
198 CTL_CREATE, CTL_EOL) == 0) {
199 } else
200 aprint_error_dev(sc->sc_dev, "couldn't create 'available' node\n");
201 }
202
203 void
204 pwmclock_set_speed(struct pwmclock_softc *sc, int speed)
205 {
206
207 if ((speed < 1) || (speed > 7))
208 return;
209 sc->sc_step_wanted = speed;
210 DPRINTF("%s: %d\n", __func__, speed);
211 }
212
213 /*
214 * the PWM interrupt handler
215 * we don't have a CPU clock independent, high resolution counter so we're
216 * stuck with a PWM that can't count and a CP0 counter that slows down or
217 * speeds up with the actual CPU speed. In order to still get halfway
218 * accurate time we do the following:
219 * - only change CPU speed in the timer interrupt
220 * - each timer interrupt we measure how many CP0 cycles passed since last
221 * time, adjust for CPU speed since we can be sure it didn't change, use
222 * that to update a separate counter
223 * - when reading the time counter we take the number of CP0 ticks since
224 * the last timer interrupt, scale it to CPU clock, return that plus the
225 * interrupt updated counter mentioned above to get something close to
226 * CP0 running at full speed
227 * - when changing CPU speed do it as close to taking the time from CP0 as
228 * possible to keep the period of time we spend with CP0 running at the
229 * wrong frequency as short as possible - hopefully short enough to stay
230 * insignificant compared to other noise since switching speeds isn't
231 * going to happen all that often
232 */
233
234 int
235 pwmclock_intr(void *cookie)
236 {
237 struct pwmclock_softc *sc = cookie;
238 uint32_t reg, now, diff;
239
240 /* is it us? */
241 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PWM1);
242 if ((reg & SM502_PWM_INTR_PENDING) == 0)
243 return 0;
244
245 /* yes, it's us, so clear the interrupt */
246 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM1, sc->sc_reg);
247
248 /*
249 * this looks kinda funny but what we want here is this:
250 * - reading the counter and changing the CPU clock should be as
251 * close together as possible in order to remain halfway accurate
252 * - we need to use the previous sc_step in order to scale the
253 * interval passed since the last clock interrupt correctly, so
254 * we only change sc_step after doing that
255 */
256 if (sc->sc_step_wanted != sc->sc_step) {
257 REGVAL(LS2F_CHIPCFG0) =
258 (REGVAL(LS2F_CHIPCFG0) & ~LS2FCFG_FREQSCALE_MASK) |
259 sc->sc_step_wanted;
260 }
261
262 now = mips3_cp0_count_read();
263 diff = now - sc->sc_last;
264 sc->sc_count += scale(diff, sc->sc_step);
265 sc->sc_last = now;
266 if (sc->sc_step_wanted != sc->sc_step) {
267 sc->sc_step = sc->sc_step_wanted;
268 }
269 hardclock(&cf);
270
271 return 1;
272 }
273
274 static void
275 pwmclock_start()
276 {
277 struct pwmclock_softc *sc = pwmclock;
278 sc->sc_count = 0;
279 sc->sc_last = mips3_cp0_count_read();
280 pwmclock_timecounter.tc_frequency = curcpu()->ci_cpu_freq / 2;
281 tc_init(&pwmclock_timecounter);
282 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM1, sc->sc_reg);
283 }
284
285 static u_int
286 get_pwmclock_timecount(struct timecounter *tc)
287 {
288 struct pwmclock_softc *sc = pwmclock;
289 uint32_t now, diff;
290
291 now = mips3_cp0_count_read();
292 diff = now - sc->sc_last;
293 return sc->sc_count + scale(diff, sc->sc_step);
294 }
295
296 static int
297 pwmclock_cpuspeed_temp(SYSCTLFN_ARGS)
298 {
299 struct sysctlnode node = *rnode;
300 struct pwmclock_softc *sc = node.sysctl_data;
301 int mhz, i;
302
303 mhz = sc->sc_scale[sc->sc_step_wanted];
304
305 node.sysctl_data = &mhz;
306 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
307 int new_reg;
308
309 new_reg = *(int *)node.sysctl_data;
310 i = 1;
311 while ((i < 8) && (sc->sc_scale[i] != new_reg))
312 i++;
313 if (i > 7)
314 return EINVAL;
315 pwmclock_set_speed(sc, i);
316 return 0;
317 }
318 return EINVAL;
319 }
320
321 static int
322 pwmclock_cpuspeed_cur(SYSCTLFN_ARGS)
323 {
324 struct sysctlnode node = *rnode;
325 struct pwmclock_softc *sc = node.sysctl_data;
326 int mhz;
327
328 mhz = sc->sc_scale[sc->sc_step];
329 node.sysctl_data = &mhz;
330 return sysctl_lookup(SYSCTLFN_CALL(&node));
331 }
332
333 static int
334 pwmclock_cpuspeed_available(SYSCTLFN_ARGS)
335 {
336 struct sysctlnode node = *rnode;
337 struct pwmclock_softc *sc = node.sysctl_data;
338 char buf[128];
339
340 snprintf(buf, 128, "%d %d %d %d %d %d %d", sc->sc_scale[1],
341 sc->sc_scale[2], sc->sc_scale[3], sc->sc_scale[4],
342 sc->sc_scale[5], sc->sc_scale[6], sc->sc_scale[7]);
343 node.sysctl_data = buf;
344 return(sysctl_lookup(SYSCTLFN_CALL(&node)));
345 }
346
347 SYSCTL_SETUP(sysctl_ams_setup, "sysctl obio subtree setup")
348 {
349
350 sysctl_createv(NULL, 0, NULL, NULL,
351 CTLFLAG_PERMANENT,
352 CTLTYPE_NODE, "machdep", NULL,
353 NULL, 0, NULL, 0,
354 CTL_MACHDEP, CTL_EOL);
355 }
356