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