xref: /openbsd/sys/arch/macppc/dev/dfs.c (revision 09467b48)
1 /*	$OpenBSD: dfs.c,v 1.3 2014/07/08 13:06:58 deraadt 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 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
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
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", &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
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
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