1 /* $OpenBSD: dfs.c,v 1.4 2022/03/13 12:33:01 mpi Exp $ */
2 /*
3 * Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/proc.h>
21 #include <sys/sysctl.h>
22
23 #include <dev/ofw/openfirm.h>
24
25 #include <machine/cpu.h>
26 #include <machine/autoconf.h>
27 #include <macppc/pci/macobio.h>
28 #include <powerpc/hid.h>
29
30 extern int perflevel;
31
32 struct dfs_softc {
33 struct device sc_dev;
34 int sc_voltage;
35 };
36
37 int dfs_match(struct device *, void *, void *);
38 void dfs_attach(struct device *, struct device *, void *);
39 void dfs_setperf(int);
40 void dfs_scale_frequency(u_int);
41
42 const struct cfattach dfs_ca = {
43 sizeof(struct dfs_softc), dfs_match, dfs_attach
44 };
45
46 struct cfdriver dfs_cd = {
47 NULL, "dfs", DV_DULL
48 };
49
50 int
dfs_match(struct device * parent,void * arg,void * aux)51 dfs_match(struct device *parent, void *arg, void *aux)
52 {
53 struct confargs *ca = aux;
54 uint16_t cpu;
55
56 if (strcmp(ca->ca_name, "cpu-vcore-select") != 0)
57 return (0);
58
59 cpu = ppc_mfpvr() >> 16;
60 if (cpu == PPC_CPU_MPC7447A || cpu == PPC_CPU_MPC7448)
61 return (1);
62
63 return (0);
64 }
65
66 void
dfs_attach(struct device * parent,struct device * self,void * aux)67 dfs_attach(struct device *parent, struct device *self, void *aux)
68 {
69 struct dfs_softc *sc = (struct dfs_softc *)self;
70 struct confargs *ca = aux;
71 uint32_t hid1, reg;
72 uint16_t cpu;
73
74 /*
75 * On some models the vcore-select offset is relative to
76 * its parent offset and not to the bus base address.
77 */
78 OF_getprop(OF_parent(ca->ca_node), "reg", ®, sizeof(reg));
79 if (reg > ca->ca_reg[0])
80 sc->sc_voltage = reg + ca->ca_reg[0];
81 else
82 sc->sc_voltage = ca->ca_reg[0];
83
84 hid1 = ppc_mfhid1();
85
86 if (hid1 & HID1_DFS4) {
87 ppc_curfreq = ppc_maxfreq / 4;
88 perflevel = 25;
89 } else if (hid1 & HID1_DFS2) {
90 ppc_curfreq = ppc_maxfreq / 2;
91 perflevel = 50;
92 }
93
94 cpu_setperf = dfs_setperf;
95
96 printf(": speeds: %d, %d", ppc_maxfreq, ppc_maxfreq / 2);
97
98 cpu = ppc_mfpvr() >> 16;
99 if (cpu == PPC_CPU_MPC7448)
100 printf(", %d", ppc_maxfreq / 4);
101 printf(" MHz\n");
102 }
103
104 void
dfs_setperf(int perflevel)105 dfs_setperf(int perflevel)
106 {
107 struct dfs_softc *sc = dfs_cd.cd_devs[0];
108
109 if (perflevel > 50) {
110 if (ppc_curfreq != ppc_maxfreq) {
111 macobio_write(sc->sc_voltage, GPIO_DDR_OUTPUT | 1);
112 delay(1000);
113 dfs_scale_frequency(FREQ_FULL);
114 }
115 } else {
116 uint16_t cpu;
117
118 cpu = ppc_mfpvr() >> 16;
119 if (cpu == PPC_CPU_MPC7448 && perflevel <= 25) {
120 if (ppc_curfreq != ppc_maxfreq / 4) {
121 dfs_scale_frequency(FREQ_QUARTER);
122 macobio_write(sc->sc_voltage,
123 GPIO_DDR_OUTPUT | 0);
124 delay(1000);
125 }
126 } else {
127 if (ppc_curfreq != ppc_maxfreq / 2) {
128 dfs_scale_frequency(FREQ_HALF);
129 macobio_write(sc->sc_voltage,
130 GPIO_DDR_OUTPUT | 0);
131 delay(1000);
132 }
133 }
134 }
135 }
136
137 void
dfs_scale_frequency(u_int freq_scale)138 dfs_scale_frequency(u_int freq_scale)
139 {
140 uint32_t hid1;
141 int s;
142
143 s = splhigh();
144 hid1 = ppc_mfhid1();
145
146 hid1 &= ~(HID1_DFS2 | HID1_DFS4);
147 switch (freq_scale) {
148 case FREQ_QUARTER:
149 hid1 |= HID1_DFS4;
150 ppc_curfreq = ppc_maxfreq / 4;
151 break;
152 case FREQ_HALF:
153 hid1 |= HID1_DFS2;
154 ppc_curfreq = ppc_maxfreq / 2;
155 break;
156 case FREQ_FULL: /* FALLTHROUGH */
157 default:
158 ppc_curfreq = ppc_maxfreq;
159 }
160
161 asm volatile ("sync");
162 ppc_mthid1(hid1);
163 asm volatile ("sync; isync");
164
165 splx(s);
166 }
167