xref: /openbsd-src/sys/arch/macppc/dev/dfs.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: dfs.c,v 1.1 2011/05/25 07:42:15 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/proc.h>
20 #include <sys/sysctl.h>
21 
22 #include <dev/ofw/openfirm.h>
23 
24 #include <machine/cpu.h>
25 #include <machine/autoconf.h>
26 #include <macppc/pci/macobio.h>
27 
28 #define DFS2	(1 << 22)	/* Divide-by-Two */
29 #define DFS4	(1 << 23)	/* Divide-by-Four (MPC7448 Specific) */
30 
31 extern int perflevel;
32 
33 struct dfs_softc {
34 	struct device	sc_dev;
35 	int		sc_voltage;
36 };
37 
38 int	dfs_match(struct device *, void *, void *);
39 void	dfs_attach(struct device *, struct device *, void *);
40 void	dfs_setperf(int);
41 void	dfs_scale_frequency(u_int);
42 
43 struct cfattach dfs_ca = {
44 	sizeof(struct dfs_softc), dfs_match, dfs_attach
45 };
46 
47 struct cfdriver dfs_cd = {
48 	NULL, "dfs", DV_DULL
49 };
50 
51 int
52 dfs_match(struct device *parent, void *arg, void *aux)
53 {
54 	struct confargs *ca = aux;
55 	uint16_t cpu;
56 
57 	if (strcmp(ca->ca_name, "cpu-vcore-select") != 0)
58 		return (0);
59 
60 	cpu = ppc_mfpvr() >> 16;
61 	if (cpu == PPC_CPU_MPC7447A || cpu == PPC_CPU_MPC7448)
62 			return (1);
63 
64 	return (0);
65 }
66 
67 void
68 dfs_attach(struct device *parent, struct device *self, void *aux)
69 {
70 	struct dfs_softc *sc = (struct dfs_softc *)self;
71 	struct confargs *ca = aux;
72 	uint32_t hid1, reg;
73 	uint16_t cpu;
74 
75 	/*
76 	 * On some models the vcore-select offset is relative to
77 	 * its parent offset and not to the bus base address.
78 	 */
79 	OF_getprop(OF_parent(ca->ca_node), "reg", &reg, sizeof(reg));
80 	if (reg > ca->ca_reg[0])
81 		sc->sc_voltage = reg + ca->ca_reg[0];
82 	else
83 		sc->sc_voltage = ca->ca_reg[0];
84 
85 	hid1 = ppc_mfhid1();
86 
87 	if (hid1 & DFS4) {
88 		ppc_curfreq = ppc_maxfreq / 4;
89 		perflevel = 25;
90 	} else if (hid1 & DFS2) {
91 		ppc_curfreq = ppc_maxfreq / 2;
92 		perflevel = 50;
93 	}
94 
95 	cpu_setperf = dfs_setperf;
96 
97 	printf(": speeds: %d, %d", ppc_maxfreq, ppc_maxfreq / 2);
98 
99 	cpu = ppc_mfpvr() >> 16;
100 	if (cpu == PPC_CPU_MPC7448)
101 		printf(", %d", ppc_maxfreq / 4);
102 	printf(" MHz\n");
103 }
104 
105 void
106 dfs_setperf(int perflevel)
107 {
108 	struct dfs_softc *sc = dfs_cd.cd_devs[0];
109 
110 	if (perflevel > 50) {
111 		if (ppc_curfreq != ppc_maxfreq) {
112 			macobio_write(sc->sc_voltage, GPIO_DDR_OUTPUT | 1);
113 			delay(1000);
114 			dfs_scale_frequency(FREQ_FULL);
115 		}
116 	} else {
117 		uint16_t cpu;
118 
119 		cpu = ppc_mfpvr() >> 16;
120 		if (cpu == PPC_CPU_MPC7448 && perflevel <= 25)  {
121 			if (ppc_curfreq != ppc_maxfreq / 4) {
122 				dfs_scale_frequency(FREQ_QUARTER);
123 				macobio_write(sc->sc_voltage,
124 				    GPIO_DDR_OUTPUT | 0);
125 				delay(1000);
126 			}
127 		} else {
128 			if (ppc_curfreq != ppc_maxfreq / 2) {
129 				dfs_scale_frequency(FREQ_HALF);
130 				macobio_write(sc->sc_voltage,
131 				    GPIO_DDR_OUTPUT | 0);
132 				delay(1000);
133 			}
134 		}
135 	}
136 }
137 
138 void
139 dfs_scale_frequency(u_int freq_scale)
140 {
141 	uint32_t hid1;
142 	int s;
143 
144 	s = splhigh();
145 	hid1 = ppc_mfhid1();
146 
147 	hid1 &= ~(DFS2 | DFS4);
148 	switch (freq_scale) {
149 	case FREQ_QUARTER:
150 		hid1 |= DFS4;
151 		ppc_curfreq = ppc_maxfreq / 4;
152 		break;
153 	case FREQ_HALF:
154 		hid1 |= DFS2;
155 		ppc_curfreq = ppc_maxfreq / 2;
156 		break;
157 	case FREQ_FULL: /* FALLTHROUGH */
158 	default:
159 		ppc_curfreq = ppc_maxfreq;
160 	}
161 
162 	asm volatile ("sync");
163 	ppc_mthid1(hid1);
164 	asm volatile ("sync; isync");
165 
166 	splx(s);
167 }
168