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