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