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