1*feee9657Srin /* $NetBSD: cpu_speedctl.c,v 1.3 2020/07/06 10:58:06 rin Exp $ */
20e0166f0Smacallan
30e0166f0Smacallan /*-
40e0166f0Smacallan * Copyright (c) 2006 Michael Lorenz
50e0166f0Smacallan * All rights reserved.
60e0166f0Smacallan *
70e0166f0Smacallan * Redistribution and use in source and binary forms, with or without
80e0166f0Smacallan * modification, are permitted provided that the following conditions
90e0166f0Smacallan * are met:
100e0166f0Smacallan * 1. Redistributions of source code must retain the above copyright
110e0166f0Smacallan * notice, this list of conditions and the following disclaimer.
120e0166f0Smacallan * 2. Redistributions in binary form must reproduce the above copyright
130e0166f0Smacallan * notice, this list of conditions and the following disclaimer in the
140e0166f0Smacallan * documentation and/or other materials provided with the distribution.
150e0166f0Smacallan *
160e0166f0Smacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
170e0166f0Smacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
180e0166f0Smacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
190e0166f0Smacallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
200e0166f0Smacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
210e0166f0Smacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220e0166f0Smacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
230e0166f0Smacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
240e0166f0Smacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
250e0166f0Smacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
260e0166f0Smacallan * POSSIBILITY OF SUCH DAMAGE.
270e0166f0Smacallan */
280e0166f0Smacallan
2916031f7dSrin #include <sys/cdefs.h>
30*feee9657Srin __KERNEL_RCSID(0, "$NetBSD: cpu_speedctl.c,v 1.3 2020/07/06 10:58:06 rin Exp $");
310e0166f0Smacallan
320e0166f0Smacallan #include <sys/param.h>
330e0166f0Smacallan #include <sys/systm.h>
340e0166f0Smacallan #include <sys/device.h>
350e0166f0Smacallan #include <sys/types.h>
360e0166f0Smacallan
370e0166f0Smacallan #include <powerpc/psl.h>
380e0166f0Smacallan #include <powerpc/spr.h>
390e0166f0Smacallan #include <powerpc/oea/cpufeat.h>
400e0166f0Smacallan #include <powerpc/oea/spr.h>
410e0166f0Smacallan
420e0166f0Smacallan #include <sys/sysctl.h>
430e0166f0Smacallan #include <dev/ofw/openfirm.h>
440e0166f0Smacallan
450e0166f0Smacallan void init_scom_speedctl(void);
460e0166f0Smacallan static int scom_speedctl = 0;
470e0166f0Smacallan static uint32_t scom_speeds[2];
480e0166f0Smacallan static uint32_t scom_reg[2];
490e0166f0Smacallan static int sysctl_cpuspeed_temp(SYSCTLFN_ARGS);
500e0166f0Smacallan static int sysctl_cpuspeed_cur(SYSCTLFN_ARGS);
510e0166f0Smacallan static int sysctl_cpuspeed_available(SYSCTLFN_ARGS);
520e0166f0Smacallan
530e0166f0Smacallan void
init_scom_speedctl(void)540e0166f0Smacallan init_scom_speedctl(void)
550e0166f0Smacallan {
560e0166f0Smacallan int node;
570e0166f0Smacallan const struct sysctlnode *sysctl_node, *me, *freq;
580e0166f0Smacallan
590e0166f0Smacallan /* do this only once */
600e0166f0Smacallan if (scom_speedctl != 0) return;
610e0166f0Smacallan scom_speedctl = 1;
620e0166f0Smacallan
630e0166f0Smacallan node = OF_finddevice("/cpus/@0");
640e0166f0Smacallan if (OF_getprop(node, "power-mode-data", scom_reg, 8) != 8)
650e0166f0Smacallan return;
660e0166f0Smacallan if (OF_getprop(node, "clock-frequency", scom_speeds, 4) != 4)
670e0166f0Smacallan return;
680e0166f0Smacallan scom_speeds[0] = scom_speeds[0] / 1000000; /* Hz -> MHz */
690e0166f0Smacallan scom_speeds[1] = scom_speeds[0] / 2;
700e0166f0Smacallan
710e0166f0Smacallan sysctl_node = NULL;
720e0166f0Smacallan
730e0166f0Smacallan if (sysctl_createv(NULL, 0, NULL,
740e0166f0Smacallan &me,
750e0166f0Smacallan CTLFLAG_READWRITE, CTLTYPE_NODE, "cpu", NULL, NULL,
760e0166f0Smacallan 0, NULL, 0, CTL_MACHDEP, CTL_CREATE, CTL_EOL) != 0)
770e0166f0Smacallan printf("couldn't create 'cpu' node\n");
780e0166f0Smacallan
790e0166f0Smacallan if (sysctl_createv(NULL, 0, NULL,
800e0166f0Smacallan &freq,
810e0166f0Smacallan CTLFLAG_READWRITE, CTLTYPE_NODE, "frequency", NULL, NULL,
820e0166f0Smacallan 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL) != 0)
830e0166f0Smacallan printf("couldn't create 'frequency' node\n");
840e0166f0Smacallan
850e0166f0Smacallan if (sysctl_createv(NULL, 0, NULL,
860e0166f0Smacallan &sysctl_node,
870e0166f0Smacallan CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
880e0166f0Smacallan CTLTYPE_INT, "target", "CPU speed", sysctl_cpuspeed_temp,
890e0166f0Smacallan 0, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
900e0166f0Smacallan CTL_CREATE, CTL_EOL) == 0) {
910e0166f0Smacallan } else
920e0166f0Smacallan printf("couldn't create 'target' node\n");
930e0166f0Smacallan
940e0166f0Smacallan if (sysctl_createv(NULL, 0, NULL,
950e0166f0Smacallan &sysctl_node,
960e0166f0Smacallan CTLFLAG_READWRITE,
970e0166f0Smacallan CTLTYPE_INT, "current", NULL, sysctl_cpuspeed_cur,
980e0166f0Smacallan 1, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
990e0166f0Smacallan CTL_CREATE, CTL_EOL) == 0) {
1000e0166f0Smacallan } else
1010e0166f0Smacallan printf("couldn't create 'current' node\n");
1020e0166f0Smacallan
1030e0166f0Smacallan if (sysctl_createv(NULL, 0, NULL,
1040e0166f0Smacallan &sysctl_node,
1050e0166f0Smacallan CTLFLAG_READWRITE,
1060e0166f0Smacallan CTLTYPE_STRING, "available", NULL, sysctl_cpuspeed_available,
1070e0166f0Smacallan 2, NULL, 0, CTL_MACHDEP, me->sysctl_num, freq->sysctl_num,
1080e0166f0Smacallan CTL_CREATE, CTL_EOL) == 0) {
1090e0166f0Smacallan } else
1100e0166f0Smacallan printf("couldn't create 'available' node\n");
1110e0166f0Smacallan }
1120e0166f0Smacallan
1130e0166f0Smacallan static int
sysctl_cpuspeed_temp(SYSCTLFN_ARGS)1140e0166f0Smacallan sysctl_cpuspeed_temp(SYSCTLFN_ARGS)
1150e0166f0Smacallan {
1160e0166f0Smacallan struct sysctlnode node = *rnode;
1170e0166f0Smacallan int speed, nspeed = -1, mhz;
1180e0166f0Smacallan
1190e0166f0Smacallan speed = (scom_read(SCOM_PSR) >> 56) & 3;
1200e0166f0Smacallan if (speed > 1) speed = 1;
1210e0166f0Smacallan mhz = scom_speeds[speed];
1220e0166f0Smacallan node.sysctl_data = &mhz;
1230e0166f0Smacallan
1240e0166f0Smacallan if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
1250e0166f0Smacallan int new_reg;
1260e0166f0Smacallan
1270e0166f0Smacallan new_reg = *(int *)node.sysctl_data;
1280e0166f0Smacallan if (new_reg == scom_speeds[0]) {
1290e0166f0Smacallan nspeed = 0;
1300e0166f0Smacallan } else if (new_reg == scom_speeds[1]) {
1310e0166f0Smacallan nspeed = 1;
1320e0166f0Smacallan } else {
1330e0166f0Smacallan printf("%s: new_reg %d\n", __func__, new_reg);
1340e0166f0Smacallan return EINVAL;
1350e0166f0Smacallan }
1360e0166f0Smacallan if (nspeed != speed) {
1370e0166f0Smacallan volatile uint64_t junk;
1380e0166f0Smacallan scom_write(SCOM_PCR, 0x80000000);
1390e0166f0Smacallan scom_write(SCOM_PCR, scom_reg[nspeed]);
1400e0166f0Smacallan junk = scom_read(SCOM_PSR);
1410e0166f0Smacallan __USE(junk);
1420e0166f0Smacallan }
1430e0166f0Smacallan return 0;
1440e0166f0Smacallan }
1450e0166f0Smacallan return EINVAL;
1460e0166f0Smacallan }
1470e0166f0Smacallan
1480e0166f0Smacallan static int
sysctl_cpuspeed_cur(SYSCTLFN_ARGS)1490e0166f0Smacallan sysctl_cpuspeed_cur(SYSCTLFN_ARGS)
1500e0166f0Smacallan {
1510e0166f0Smacallan struct sysctlnode node = *rnode;
1520e0166f0Smacallan uint64_t speed;
1530e0166f0Smacallan int mhz;
1540e0166f0Smacallan
1550e0166f0Smacallan speed = (scom_read(SCOM_PSR) >> 56) & 3;
1560e0166f0Smacallan if (speed > 1) speed = 1;
1570e0166f0Smacallan
1580e0166f0Smacallan mhz = scom_speeds[speed];
1590e0166f0Smacallan node.sysctl_data = &mhz;
1600e0166f0Smacallan return sysctl_lookup(SYSCTLFN_CALL(&node));
1610e0166f0Smacallan }
1620e0166f0Smacallan
1630e0166f0Smacallan static int
sysctl_cpuspeed_available(SYSCTLFN_ARGS)1640e0166f0Smacallan sysctl_cpuspeed_available(SYSCTLFN_ARGS)
1650e0166f0Smacallan {
1660e0166f0Smacallan struct sysctlnode node = *rnode;
1670e0166f0Smacallan char buf[128];
1680e0166f0Smacallan
1690e0166f0Smacallan snprintf(buf, 128, "%d %d", scom_speeds[0], scom_speeds[1]);
1700e0166f0Smacallan node.sysctl_data = buf;
1710e0166f0Smacallan return(sysctl_lookup(SYSCTLFN_CALL(&node)));
1720e0166f0Smacallan }
1730e0166f0Smacallan
1740e0166f0Smacallan SYSCTL_SETUP(sysctl_ams_setup, "sysctl obio subtree setup")
1750e0166f0Smacallan {
1760e0166f0Smacallan
1770e0166f0Smacallan sysctl_createv(NULL, 0, NULL, NULL,
1780e0166f0Smacallan CTLFLAG_PERMANENT,
1790e0166f0Smacallan CTLTYPE_NODE, "machdep", NULL,
1800e0166f0Smacallan NULL, 0, NULL, 0,
1810e0166f0Smacallan CTL_MACHDEP, CTL_EOL);
1820e0166f0Smacallan }
183