1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Schizo Power Management Driver
*
* This driver deals with Safari bus interface and it is used
* as part of the protocol to change the clock speed on Safari bus.
*
* The routine on this driver is referenced by Platform Power
* Management driver of systems like Excalibur. Driver is
* loaded because of an explicit dependency defined in PPM driver.
* PPM driver also attaches the driver.
*/
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
/*
* Function prototypes
*/
static int spm_attach(dev_info_t *, ddi_attach_cmd_t);
static int spm_detach(dev_info_t *, ddi_detach_cmd_t);
/*
* Private data for schizo_pm driver
*/
struct spm_soft_state {
dev_info_t *dip;
};
/*
* Configuration data structures
*/
static struct dev_ops spm_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
nodev, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
spm_attach, /* attach */
spm_detach, /* detach */
nodev, /* reset */
(struct cb_ops *)0, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* Driver globals
*/
static void *spm_state;
static int spm_inst = -1;
static struct modldrv modldrv = {
&mod_driverops, /* Type of module = driver */
"schizo pm driver", /* name of module */
&spm_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
/*
* Schizo CSR E* bit masks
*/
#define SCHIZO_SAFARI_ECLK_32 0x20ULL
#define SCHIZO_SAFARI_ECLK_2 0x2ULL
#define SCHIZO_SAFARI_ECLK_1 0x1ULL
#define SCHIZO_SAFARI_ECLK_MASK (SCHIZO_SAFARI_ECLK_32 | \
SCHIZO_SAFARI_ECLK_2 | SCHIZO_SAFARI_ECLK_1)
/*
* bit masks to set schizo clock in parallel with setting cpu clock.
* Used when changing cpu speeds.
*
* NOTE: The order of entries must be from slowest to fastest.
*/
static const uint64_t schizo_safari_masks[] = {
SCHIZO_SAFARI_ECLK_32,
SCHIZO_SAFARI_ECLK_2,
SCHIZO_SAFARI_ECLK_1
};
/*
* Normally, the address of the registers we use would be accessed from
* our "official" private data. However, since the dip is not passed
* in when spm_change_speed (see below) is called, and since there is
* only one unit of the spm "device", we keep it here as a static.
*/
static volatile uint64_t *spm_schizo_csr;
ddi_acc_handle_t spm_schizo_handle;
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&spm_state,
sizeof (struct spm_soft_state), 0)) != 0)
return (error);
if ((error = mod_install(&modlinkage)) != 0)
ddi_soft_state_fini(&spm_state);
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) == 0)
ddi_soft_state_fini(&spm_state);
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
spm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rv;
struct spm_soft_state *softsp;
ddi_device_acc_attr_t attr;
switch (cmd) {
case DDI_ATTACH:
if (spm_inst != -1) {
cmn_err(CE_WARN, "spm_attach: "
"only one instance is allowed.");
return (DDI_FAILURE);
}
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
spm_inst = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(spm_state, spm_inst) != DDI_SUCCESS) {
cmn_err(CE_WARN, "spm_attach: can't allocate state.");
return (DDI_FAILURE);
}
if ((softsp = ddi_get_soft_state(spm_state, spm_inst)) == NULL) {
cmn_err(CE_WARN, "spm_attach: can't get state.");
return (DDI_FAILURE);
}
softsp->dip = dip;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
/*
* Map the Safari E* Control register.
*/
rv = ddi_regs_map_setup(dip, 0,
(caddr_t *)&spm_schizo_csr, 0, 8, &attr, &spm_schizo_handle);
if (rv != DDI_SUCCESS) {
cmn_err(CE_WARN, "spm_attach: can't map the register.");
ddi_soft_state_free(spm_state, spm_inst);
return (rv);
}
ddi_report_dev(dip);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
spm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
/*
* This globally visible function is the main reason this driver exists.
* It will be called by a platform power management driver to write to
* the schizo ASIC csr which changes schizo's clock rate. This is a
* required step when changing the clock of the cpus.
*
* NOTE - The caller should enter this routine sequentially.
*/
void
spm_change_schizo_speed(int lvl_index)
{
uint64_t contents;
ASSERT(lvl_index >= 0 && lvl_index <= 2);
contents = ddi_get64(spm_schizo_handle, (uint64_t *)spm_schizo_csr);
contents &= ~SCHIZO_SAFARI_ECLK_MASK;
contents |= schizo_safari_masks[ lvl_index ];
ddi_put64(spm_schizo_handle, (uint64_t *)spm_schizo_csr, contents);
}
|