xref: /illumos-gate/usr/src/uts/sun4u/io/ppm/schppm.c (revision 19397407)
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