xref: /illumos-gate/usr/src/uts/sun4u/io/ppm/jbusppm.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 #include <sys/types.h>
30 #include <sys/conf.h>
31 #include <sys/open.h>
32 #include <sys/modctl.h>
33 #include <sys/promif.h>
34 #include <sys/stat.h>
35 #include <sys/ddi_impldefs.h>
36 #include <sys/jbusppm.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 
40 /*
41  *	JBus Power Management Driver
42  *
43  *	jbusppm driver initiates the JBus clock speed change
44  *	as part of the protocol to adjust the clock speed on
45  *	all JBus resident devices.
46  *
47  *	jbusppm driver is loaded because of the explicit dependency
48  *	defined in PPM driver.
49  */
50 
51 /*
52  * Configuration Function prototypes and data structures
53  */
54 static int	jbppm_attach(dev_info_t *, ddi_attach_cmd_t);
55 static int	jbppm_detach(dev_info_t *, ddi_detach_cmd_t);
56 static int	jbppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
57 static int	jbppm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
58 static int	jbppm_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
59 static int	jbppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
60 
61 /*
62  * Configuration data structures
63  */
64 static struct cb_ops jbppm_cbops = {
65 	jbppm_open,		/* open */
66 	jbppm_close,		/* close */
67 	nodev,			/* strategy */
68 	nodev,			/* print */
69 	nodev,			/* dump */
70 	nodev,			/* read */
71 	nodev,			/* write */
72 	jbppm_ioctl,		/* ioctl */
73 	nodev,			/* devmap */
74 	nodev,			/* mmap */
75 	nodev,			/* segmap */
76 	nochpoll,		/* chpoll */
77 	ddi_prop_op,		/* prop_op */
78 	NULL,			/* stream */
79 	D_MP | D_NEW,		/* flag */
80 	CB_REV,			/* rev */
81 	nodev,			/* aread */
82 	nodev,			/* awrite */
83 };
84 
85 static struct dev_ops jbppm_ops = {
86 	DEVO_REV,		/* devo_rev */
87 	0,			/* refcnt */
88 	jbppm_getinfo,		/* getinfo */
89 	nulldev,		/* identify */
90 	nulldev,		/* probe */
91 	jbppm_attach,		/* attach */
92 	jbppm_detach,		/* detach */
93 	nodev,			/* reset */
94 	&jbppm_cbops,		/* cb_ops */
95 	NULL,			/* bus_ops */
96 	NULL			/* power */
97 };
98 
99 extern struct mod_ops mod_driverops;
100 
101 static struct modldrv modldrv = {
102 	&mod_driverops,
103 	"JBus ppm driver v%I%",
104 	&jbppm_ops,
105 };
106 
107 static struct modlinkage modlinkage = {
108 	MODREV_1,
109 	&modldrv,
110 	NULL
111 };
112 
113 /*
114  * Local functions
115  */
116 static void jbppm_next_speed(dev_info_t *, uint_t);
117 static int jbppm_start_next(dev_info_t *, int);
118 
119 /*
120  * Driver global variables
121  *
122  * jbppm_lock synchronize the access of lyr handle to each jbppm
123  * minor device, therefore write to tomatillo device is
124  * sequentialized.  Lyr protocol requires pairing up lyr open
125  * and close, so only a single reference is allowed per minor node.
126  */
127 static void	*jbppm_statep;
128 static kmutex_t  jbppm_lock;
129 
130 /*
131  * bit masks to scale the IO bridge clock in sync with and only with
132  * scaling CPU clock.
133  *
134  * The array index indicates power level (from lowest to highest).
135  */
136 static const uint64_t jbus_clock_masks[] = {
137 	JBUS_ESTAR_CNTL_32,
138 	JBUS_ESTAR_CNTL_2,
139 	JBUS_ESTAR_CNTL_1
140 };
141 
142 int
143 _init(void)
144 {
145 	int	error;
146 
147 	if ((error = ddi_soft_state_init(&jbppm_statep,
148 	    sizeof (jbppm_unit), 0)) != DDI_SUCCESS) {
149 		return (error);
150 	}
151 
152 	mutex_init(&jbppm_lock, NULL, MUTEX_DRIVER, NULL);
153 
154 	if ((error = mod_install(&modlinkage)) != DDI_SUCCESS) {
155 		mutex_destroy(&jbppm_lock);
156 		ddi_soft_state_fini(&jbppm_statep);
157 		return (error);
158 	}
159 
160 	return (error);
161 }
162 
163 int
164 _fini(void)
165 {
166 	int	error;
167 
168 	if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) {
169 		mutex_destroy(&jbppm_lock);
170 		ddi_soft_state_fini(&jbppm_statep);
171 	}
172 
173 	return (error);
174 
175 }
176 
177 int
178 _info(struct modinfo *modinfop)
179 {
180 	return (mod_info(&modlinkage, modinfop));
181 }
182 
183 
184 
185 /*
186  * Driver attach(9e) entry point
187  */
188 static int
189 jbppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
190 {
191 	char	*str = "jbppm_attach";
192 	int		instance;
193 	jbppm_unit	*unitp;
194 	uint64_t	data64;
195 	ddi_device_acc_attr_t	attr;
196 	int		rv = DDI_SUCCESS;
197 
198 	switch (cmd) {
199 	case DDI_ATTACH:
200 		break;
201 	case DDI_RESUME:
202 		return (DDI_SUCCESS);
203 	default:
204 		cmn_err(CE_WARN, "%s: cmd %d unsupported.\n", str, cmd);
205 		return (DDI_FAILURE);
206 	}
207 
208 	instance = ddi_get_instance(dip);
209 	rv = ddi_soft_state_zalloc(jbppm_statep, instance);
210 	if (rv != DDI_SUCCESS) {
211 		cmn_err(CE_WARN, "%s: failed alloc for dev(%s@%s)",
212 		    str, ddi_binding_name(dip),
213 		    ddi_get_name_addr(dip) ? ddi_get_name_addr(dip) : " ");
214 		return (rv);
215 	}
216 
217 	if ((unitp = ddi_get_soft_state(jbppm_statep, instance)) == NULL) {
218 		rv = DDI_FAILURE;
219 		goto doerrs;
220 	}
221 
222 	/*
223 	 * Export "ddi-kernel-ioctl" property - prepared to support
224 	 * kernel ioctls (driver layering).
225 	 */
226 	rv = ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
227 	    DDI_KERNEL_IOCTL, NULL, 0);
228 	if (rv != DDI_PROP_SUCCESS)
229 		goto doerrs;
230 
231 	ddi_report_dev(dip);
232 	unitp->dip = dip;
233 
234 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
235 	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
236 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
237 
238 	rv = ddi_regs_map_setup(dip, 0, (caddr_t *)&unitp->devid_csr, 0, 8,
239 	    &attr, &unitp->devid_hndl);
240 	if (rv != DDI_SUCCESS)
241 		goto doerrs;
242 
243 	rv = ddi_regs_map_setup(dip, 1, (caddr_t *)&unitp->estar_csr, 0, 16,
244 	    &attr, &unitp->estar_hndl);
245 	if (rv != DDI_SUCCESS)
246 		goto doerrs;
247 	unitp->j_chng_csr = (uint64_t *)((caddr_t)unitp->estar_csr +
248 	    J_CHNG_INITIATION_OFFSET);
249 
250 	data64 = ddi_get64(unitp->devid_hndl, (uint64_t *)unitp->devid_csr);
251 	unitp->is_master = (data64 & MASTER_IOBRIDGE_BIT) ? 1 : 0;
252 	unitp->lyropen = 0;
253 
254 	/*
255 	 * create minor node for kernel_ioctl calls
256 	 */
257 	rv = ddi_create_minor_node(dip, "jbus-ppm", S_IFCHR, instance, 0, 0);
258 	if (rv != DDI_SUCCESS)
259 		goto doerrs;
260 
261 	return (rv);
262 
263 doerrs:
264 	if (unitp->devid_hndl != NULL)
265 		ddi_regs_map_free(&unitp->devid_hndl);
266 
267 	if (unitp->estar_csr != NULL)
268 		ddi_regs_map_free(&unitp->estar_hndl);
269 
270 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS |
271 	    DDI_PROP_NOTPROM, DDI_KERNEL_IOCTL))
272 		ddi_prop_remove_all(dip);
273 
274 	ddi_soft_state_free(jbppm_statep, instance);
275 
276 	return (rv);
277 }
278 
279 
280 /*
281  * Driver getinfo(9e) entry routine
282  */
283 /* ARGSUSED */
284 static int
285 jbppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
286 {
287 	jbppm_unit	*unitp;
288 	int		instance;
289 
290 	switch (cmd) {
291 	case DDI_INFO_DEVT2DEVINFO:
292 		instance = getminor((dev_t)arg);
293 		unitp = ddi_get_soft_state(jbppm_statep, instance);
294 		if (unitp == NULL) {
295 			return (DDI_FAILURE);
296 		}
297 		*result = (void *) unitp->dip;
298 		return (DDI_SUCCESS);
299 
300 	case DDI_INFO_DEVT2INSTANCE:
301 		instance = getminor((dev_t)arg);
302 		*result = (void *)(uintptr_t)instance;
303 		return (DDI_SUCCESS);
304 
305 	default:
306 		return (DDI_FAILURE);
307 	}
308 }
309 
310 
311 /*
312  * detach(9e)
313  */
314 /* ARGSUSED */
315 static int
316 jbppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
317 {
318 	char *str = "jbppm_detach";
319 
320 	switch (cmd) {
321 	case DDI_DETACH:
322 		return (DDI_FAILURE);
323 	case DDI_SUSPEND:
324 		return (DDI_SUCCESS);
325 	default:
326 		cmn_err(CE_WARN, "%s: cmd %d unsupported", str, cmd);
327 		return (DDI_FAILURE);
328 	}
329 }
330 
331 
332 /* ARGSUSED */
333 static int
334 jbppm_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
335 {
336 	jbppm_unit	*unitp;
337 
338 	/* not intended to allow sysadmin level root process to open it */
339 	if (drv_priv(cred_p) != DDI_SUCCESS)
340 		return (EPERM);
341 
342 	if ((unitp = ddi_get_soft_state(
343 	    jbppm_statep, getminor(*dev_p))) == NULL) {
344 		cmn_err(CE_WARN, "jbppm_open: failed to get soft state!");
345 		return (DDI_FAILURE);
346 	}
347 
348 	mutex_enter(&jbppm_lock);
349 	if (unitp->lyropen != 0) {
350 		mutex_exit(&jbppm_lock);
351 		return (EBUSY);
352 	}
353 	unitp->lyropen++;
354 	mutex_exit(&jbppm_lock);
355 
356 	return (DDI_SUCCESS);
357 }
358 
359 
360 /* ARGSUSED */
361 static int
362 jbppm_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
363 {
364 	jbppm_unit	*unitp;
365 
366 	if ((unitp =
367 	    ddi_get_soft_state(jbppm_statep, getminor(dev))) == NULL)
368 		return (DDI_FAILURE);
369 
370 	mutex_enter(&jbppm_lock);
371 	unitp->lyropen = 0;
372 	mutex_exit(&jbppm_lock);
373 
374 	return (DDI_SUCCESS);
375 }
376 
377 
378 #define	JBPPMIOC		('j' << 8)
379 #define	JBPPMIOC_ISMASTER	(JBPPMIOC | 1)	/* no 'arg' */
380 #define	JBPPMIOC_NEXT		(JBPPMIOC | 2)	/* 'arg': next speed level */
381 #define	JBPPMIOC_GO		(JBPPMIOC | 3)	/* 'arg': jbus chng_delay */
382 
383 /* ARGSUSED3 */
384 static int
385 jbppm_ioctl(dev_t dev, int cmd, intptr_t arg, int flag,
386     cred_t *cred_p, int *rval_p)
387 {
388 	jbppm_unit	*unitp;
389 
390 	if (drv_priv(cred_p) != 0)
391 		return (EPERM);
392 
393 	if ((unitp =
394 	    ddi_get_soft_state(jbppm_statep, getminor(dev))) == NULL)
395 		return (EIO);
396 
397 	switch (cmd) {
398 	case JBPPMIOC_ISMASTER:
399 		if (unitp->is_master)
400 			return (0);
401 		else
402 			return (-1);
403 
404 	case  JBPPMIOC_NEXT:
405 		jbppm_next_speed(unitp->dip, (uint_t)arg);
406 		return (0);
407 
408 	case  JBPPMIOC_GO:
409 		if (!unitp->is_master)
410 			return (EINVAL);
411 		return (jbppm_start_next(unitp->dip, (int)arg));
412 
413 	default:
414 		return (ENOTTY);
415 	}
416 }
417 
418 
419 /*
420  * jbppm_next_speed - program a new speed into IO bridge device prior to
421  * actual speed transition.
422  */
423 static void
424 jbppm_next_speed(dev_info_t *dip, uint_t lvl_index)
425 {
426 	volatile uint64_t	data64;
427 	static jbppm_unit	*unitp;
428 
429 	unitp = ddi_get_soft_state(jbppm_statep, ddi_get_instance(dip));
430 	ASSERT(unitp);
431 
432 	mutex_enter(&jbppm_lock);
433 	data64 = ddi_get64(unitp->estar_hndl, unitp->estar_csr);
434 	data64 &= ~JBUS_ESTAR_CNTL_MASK;
435 	data64 |= jbus_clock_masks[lvl_index];
436 
437 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->estar_csr, data64);
438 	data64 = ddi_get64(unitp->estar_hndl, unitp->estar_csr);
439 	mutex_exit(&jbppm_lock);
440 }
441 
442 
443 /*
444  * jbppm_start_next - Initiate JBus speed change on all JBus devices.
445  * chng_delay indicates after master deassert j_chng signal the number of
446  * jbus clock delay before all jbus device start to transit to the new
447  * speed.
448  * Trigger sequence:
449  *      wait while j_chng[1:0] == 10
450  *	write 00 to j_chng
451  *	trigger by writing 10 to j_chng[1:0]
452  *	wait while j_chng[1:0] == 10
453  *	write 00 to j_chng[1:0]
454  * Note: this sequence is not the same as Enchilada spec described, chiefly
455  * because else where (e.g. flush E$ code) may have speed change code. If sw
456  * wait upon j_chng[1:0] == 11 in both places, we'll have problem.  That spec
457  * requires wait on 11 to ensure that trigger has completed. An alternative
458  * way to ensure that is to check and wait upon 10. J_chng[1:0] stays as 10
459  * for only a short period of time that is under HW control, unlike 11 signals
460  * which has to be cleared by sw.
461  */
462 /* ARGSUSED */
463 static int
464 jbppm_start_next(dev_info_t *dip, int chng_delay)
465 {
466 	volatile uint64_t	data64;
467 	static jbppm_unit	*unitp;
468 
469 	unitp = ddi_get_soft_state(jbppm_statep, ddi_get_instance(dip));
470 	ASSERT(unitp && unitp->is_master);
471 
472 	mutex_enter(&jbppm_lock);
473 
474 	/* wait while trigger is incomplete */
475 	do {
476 		data64 = ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
477 	} while ((J_CHNG_INITIATION_MASK & data64) == J_CHNG_START);
478 
479 	/* clear(reset) */
480 	data64 &= ~J_CHNG_INITIATION_MASK;
481 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
482 
483 	/* trigger */
484 	data64 &= ~J_CHNG_DELAY_MASK;
485 	data64 |= (J_CHNG_START | chng_delay);
486 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
487 
488 	/* wait while trigger is incomplete */
489 	do {
490 		data64 = ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
491 	} while ((J_CHNG_INITIATION_MASK & data64) == J_CHNG_START);
492 
493 	/* clear(reset) */
494 	data64 &= ~J_CHNG_INITIATION_MASK;
495 	ddi_put64(unitp->estar_hndl, (uint64_t *)unitp->j_chng_csr, data64);
496 	(void) ddi_get64(unitp->estar_hndl, unitp->j_chng_csr);
497 
498 	mutex_exit(&jbppm_lock);
499 	return (DDI_SUCCESS);
500 }
501