Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/cpufreq/dfs.c
39534 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2009 Nathan Whitehorn
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/bus.h>
32
#include <sys/cpu.h>
33
#include <sys/kernel.h>
34
#include <sys/module.h>
35
36
#include "cpufreq_if.h"
37
38
struct dfs_softc {
39
device_t dev;
40
int dfs4;
41
};
42
43
static void dfs_identify(driver_t *driver, device_t parent);
44
static int dfs_probe(device_t dev);
45
static int dfs_attach(device_t dev);
46
static int dfs_settings(device_t dev, struct cf_setting *sets, int *count);
47
static int dfs_set(device_t dev, const struct cf_setting *set);
48
static int dfs_get(device_t dev, struct cf_setting *set);
49
static int dfs_type(device_t dev, int *type);
50
51
static device_method_t dfs_methods[] = {
52
/* Device interface */
53
DEVMETHOD(device_identify, dfs_identify),
54
DEVMETHOD(device_probe, dfs_probe),
55
DEVMETHOD(device_attach, dfs_attach),
56
57
/* cpufreq interface */
58
DEVMETHOD(cpufreq_drv_set, dfs_set),
59
DEVMETHOD(cpufreq_drv_get, dfs_get),
60
DEVMETHOD(cpufreq_drv_type, dfs_type),
61
DEVMETHOD(cpufreq_drv_settings, dfs_settings),
62
{0, 0}
63
};
64
65
static driver_t dfs_driver = {
66
"dfs",
67
dfs_methods,
68
sizeof(struct dfs_softc)
69
};
70
71
DRIVER_MODULE(dfs, cpu, dfs_driver, 0, 0);
72
73
/*
74
* Bits of the HID1 register to enable DFS. See page 2-24 of "MPC7450
75
* RISC Microprocessor Family Reference Manual", rev. 5.
76
*/
77
78
#define HID1_DFS2 (1UL << 22)
79
#define HID1_DFS4 (1UL << 23)
80
81
static void
82
dfs_identify(driver_t *driver, device_t parent)
83
{
84
uint16_t vers;
85
vers = mfpvr() >> 16;
86
87
/* Check for an MPC 7447A or 7448 CPU */
88
switch (vers) {
89
case MPC7447A:
90
case MPC7448:
91
break;
92
default:
93
return;
94
}
95
96
/* Make sure we're not being doubly invoked. */
97
if (device_find_child(parent, "dfs", DEVICE_UNIT_ANY) != NULL)
98
return;
99
100
/*
101
* We attach a child for every CPU since settings need to
102
* be performed on every CPU in the SMP case.
103
*/
104
if (BUS_ADD_CHILD(parent, 10, "dfs", DEVICE_UNIT_ANY) == NULL)
105
device_printf(parent, "add dfs child failed\n");
106
}
107
108
static int
109
dfs_probe(device_t dev)
110
{
111
if (resource_disabled("dfs", 0))
112
return (ENXIO);
113
114
device_set_desc(dev, "Dynamic Frequency Switching");
115
return (0);
116
}
117
118
static int
119
dfs_attach(device_t dev)
120
{
121
struct dfs_softc *sc;
122
uint16_t vers;
123
124
sc = device_get_softc(dev);
125
sc->dev = dev;
126
sc->dfs4 = 0;
127
vers = mfpvr() >> 16;
128
129
/* The 7448 supports divide-by-four as well */
130
if (vers == MPC7448)
131
sc->dfs4 = 1;
132
133
cpufreq_register(dev);
134
return (0);
135
}
136
137
static int
138
dfs_settings(device_t dev, struct cf_setting *sets, int *count)
139
{
140
struct dfs_softc *sc;
141
int states;
142
143
sc = device_get_softc(dev);
144
states = sc->dfs4 ? 3 : 2;
145
if (sets == NULL || count == NULL)
146
return (EINVAL);
147
if (*count < states)
148
return (E2BIG);
149
150
/* Return a list of valid settings for this driver. */
151
memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * states);
152
153
sets[0].freq = 10000; sets[0].dev = dev;
154
sets[1].freq = 5000; sets[1].dev = dev;
155
if (sc->dfs4) {
156
sets[2].freq = 2500;
157
sets[2].dev = dev;
158
}
159
*count = states;
160
161
return (0);
162
}
163
164
static int
165
dfs_set(device_t dev, const struct cf_setting *set)
166
{
167
register_t hid1;
168
169
if (set == NULL)
170
return (EINVAL);
171
172
hid1 = mfspr(SPR_HID1);
173
hid1 &= ~(HID1_DFS2 | HID1_DFS4);
174
175
if (set->freq == 5000)
176
hid1 |= HID1_DFS2;
177
else if (set->freq == 2500)
178
hid1 |= HID1_DFS4;
179
180
/*
181
* Now set the HID1 register with new values. Calling sequence
182
* taken from page 2-26 of the MPC7450 family CPU manual.
183
*/
184
185
powerpc_sync();
186
mtspr(SPR_HID1, hid1);
187
powerpc_sync(); isync();
188
189
return (0);
190
}
191
192
static int
193
dfs_get(device_t dev, struct cf_setting *set)
194
{
195
struct dfs_softc *sc;
196
register_t hid1;
197
198
if (set == NULL)
199
return (EINVAL);
200
sc = device_get_softc(dev);
201
202
memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
203
204
hid1 = mfspr(SPR_HID1);
205
206
set->freq = 10000;
207
if (hid1 & HID1_DFS2)
208
set->freq = 5000;
209
else if (sc->dfs4 && (hid1 & HID1_DFS4))
210
set->freq = 2500;
211
212
set->dev = dev;
213
214
return (0);
215
}
216
217
static int
218
dfs_type(device_t dev, int *type)
219
{
220
221
if (type == NULL)
222
return (EINVAL);
223
224
*type = CPUFREQ_TYPE_RELATIVE;
225
return (0);
226
}
227
228