xref: /titanic_51/usr/src/uts/sun4u/io/grbeep.c (revision a776e90690d98f629ab57f6493ff5949af9d65bd)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This is the Beep driver for SMBUS based beep mechanism.
30  * The driver exports the interfaces to set frequency,
31  * turn on beeper and turn off beeper to the generic beep
32  * module. If a beep is in progress, the driver discards a
33  * second beep. This driver uses the 8254 timer to program
34  * the beeper ports.
35  */
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/modctl.h>
41 #include <sys/ddi_impldefs.h>
42 #include <sys/kmem.h>
43 #include <sys/devops.h>
44 #include <sys/grbeep.h>
45 #include <sys/beep.h>
46 
47 
48 /* Pointer to the state structure */
49 static void *grbeep_statep;
50 
51 
52 /*
53  * Debug stuff
54  */
55 #ifdef DEBUG
56 int grbeep_debug = 0;
57 #define	GRBEEP_DEBUG(args)  if (grbeep_debug) cmn_err args
58 #define	GRBEEP_DEBUG1(args)  if (grbeep_debug > 1) cmn_err args
59 #else
60 #define	GRBEEP_DEBUG(args)
61 #define	GRBEEP_DEBUG1(args)
62 #endif
63 
64 
65 /*
66  * Prototypes
67  */
68 static int grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
69 static int grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
70 static int grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
71 		void **result);
72 static void grbeep_freq(void *arg, int freq);
73 static void grbeep_on(void *arg);
74 static void grbeep_off(void *arg);
75 static void grbeep_cleanup(grbeep_state_t *);
76 static int grbeep_map_regs(dev_info_t *, grbeep_state_t *);
77 static grbeep_state_t *grbeep_obtain_state(dev_info_t *);
78 
79 
80 struct cb_ops grbeep_cb_ops = {
81 	nulldev,	/* open  */
82 	nulldev,	/* close */
83 	nulldev,	/* strategy */
84 	nulldev,	/* print */
85 	nulldev,	/* dump */
86 	nulldev,	/* read */
87 	nulldev,	/* write */
88 	nulldev,	/* ioctl */
89 	nulldev,	/* devmap */
90 	nulldev,	/* mmap */
91 	nulldev,	/* segmap */
92 	nochpoll,	/* poll */
93 	ddi_prop_op,	/* cb_prop_op */
94 	NULL,		/* streamtab  */
95 	D_MP | D_NEW
96 };
97 
98 
99 static struct dev_ops grbeep_ops = {
100 	DEVO_REV,		/* Devo_rev */
101 	0,			/* Refcnt */
102 	grbeep_info,		/* Info */
103 	nulldev,		/* Identify */
104 	nulldev,		/* Probe */
105 	grbeep_attach,		/* Attach */
106 	grbeep_detach,		/* Detach */
107 	nodev,			/* Reset */
108 	&grbeep_cb_ops,		/* Driver operations */
109 	0,			/* Bus operations */
110 	NULL			/* Power */
111 };
112 
113 
114 static struct modldrv modldrv = {
115 	&mod_driverops, 		/* This one is a driver */
116 	"SMBUS Beep Driver %I%", 	/* Name of the module. */
117 	&grbeep_ops,			/* Driver ops */
118 };
119 
120 
121 static struct modlinkage modlinkage = {
122 	MODREV_1, (void *)&modldrv, NULL
123 };
124 
125 
126 int
127 _init(void)
128 {
129 	int error;
130 
131 	/* Initialize the soft state structures */
132 	if ((error = ddi_soft_state_init(&grbeep_statep,
133 	    sizeof (grbeep_state_t), 1)) != 0) {
134 
135 		return (error);
136 	}
137 
138 	/* Install the loadable module */
139 	if ((error = mod_install(&modlinkage)) != 0) {
140 		ddi_soft_state_fini(&grbeep_statep);
141 	}
142 
143 	return (error);
144 }
145 
146 
147 int
148 _info(struct modinfo *modinfop)
149 {
150 	return (mod_info(&modlinkage, modinfop));
151 }
152 
153 
154 int
155 _fini(void)
156 {
157 	int error;
158 
159 	error = mod_remove(&modlinkage);
160 
161 	if (error == 0) {
162 		/* Release per module resources */
163 		ddi_soft_state_fini(&grbeep_statep);
164 	}
165 
166 	return (error);
167 }
168 
169 
170 /*
171  * Beep entry points
172  */
173 
174 /*
175  * grbeep_attach:
176  */
177 static int
178 grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
179 {
180 	int		instance;
181 
182 	/* Pointer to soft state */
183 	grbeep_state_t	*grbeeptr = NULL;
184 
185 	GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: Start"));
186 
187 	switch (cmd) {
188 		case DDI_ATTACH:
189 			break;
190 		case DDI_RESUME:
191 
192 			return (DDI_SUCCESS);
193 		default:
194 
195 			return (DDI_FAILURE);
196 	}
197 
198 	/* Get the instance and create soft state */
199 	instance = ddi_get_instance(dip);
200 
201 	if (ddi_soft_state_zalloc(grbeep_statep, instance) != 0) {
202 
203 		return (DDI_FAILURE);
204 	}
205 
206 	grbeeptr = ddi_get_soft_state(grbeep_statep, instance);
207 
208 	if (grbeeptr == NULL) {
209 
210 		return (DDI_FAILURE);
211 	}
212 
213 	GRBEEP_DEBUG1((CE_CONT, "grbeeptr = 0x%p, instance %x",
214 	    (void *)grbeeptr, instance));
215 
216 	/* Save the dip */
217 	grbeeptr->grbeep_dip = dip;
218 
219 	/* Initialize beeper mode */
220 	grbeeptr->grbeep_mode = GRBEEP_OFF;
221 
222 	/* Map the Beep Control and Beep counter Registers */
223 	if (grbeep_map_regs(dip, grbeeptr) != DDI_SUCCESS) {
224 
225 		GRBEEP_DEBUG((CE_WARN,
226 		    "grbeep_attach: Mapping of beep registers failed."));
227 
228 		grbeep_cleanup(grbeeptr);
229 
230 		return (DDI_FAILURE);
231 	}
232 
233 	(void) beep_init((void *)dip, grbeep_on, grbeep_off, grbeep_freq);
234 
235 	/* Display information in the banner */
236 	ddi_report_dev(dip);
237 
238 	GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: dip = 0x%p done",
239 	    (void *)dip));
240 
241 	return (DDI_SUCCESS);
242 }
243 
244 
245 /*
246  * grbeep_detach:
247  */
248 static int
249 grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
250 {
251 	/* Pointer to soft state */
252 	grbeep_state_t	*grbeeptr = NULL;
253 
254 	GRBEEP_DEBUG1((CE_CONT, "grbeep_detach: Start"));
255 
256 	switch (cmd) {
257 		case DDI_SUSPEND:
258 			grbeeptr = grbeep_obtain_state(dip);
259 
260 			if (grbeeptr == NULL) {
261 
262 				return (DDI_FAILURE);
263 			}
264 
265 			/*
266 			 * If a beep is in progress; fail suspend
267 			 */
268 			if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
269 
270 				return (DDI_SUCCESS);
271 			} else {
272 
273 				return (DDI_FAILURE);
274 			}
275 		default:
276 
277 			return (DDI_FAILURE);
278 	}
279 }
280 
281 
282 /*
283  * grbeep_info:
284  */
285 /* ARGSUSED */
286 static int
287 grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
288 		void *arg, void **result)
289 {
290 	dev_t dev;
291 	grbeep_state_t  *grbeeptr;
292 	int instance, error;
293 
294 	switch (infocmd) {
295 	case DDI_INFO_DEVT2DEVINFO:
296 		dev = (dev_t)arg;
297 		instance = GRBEEP_UNIT(dev);
298 
299 		if ((grbeeptr = ddi_get_soft_state(grbeep_statep,
300 		    instance)) == NULL) {
301 
302 			return (DDI_FAILURE);
303 		}
304 
305 		*result = (void *)grbeeptr->grbeep_dip;
306 
307 		error = DDI_SUCCESS;
308 		break;
309 	case DDI_INFO_DEVT2INSTANCE:
310 		dev = (dev_t)arg;
311 		instance = GRBEEP_UNIT(dev);
312 
313 		*result = (void *)(uintptr_t)instance;
314 
315 		error = DDI_SUCCESS;
316 		break;
317 	default:
318 		error = DDI_FAILURE;
319 
320 	}
321 
322 	return (error);
323 }
324 
325 
326 /*
327  * grbeep_freq() :
328  * 	Set beep frequency
329  */
330 static void
331 grbeep_freq(void *arg, int freq)
332 {
333 	dev_info_t *dip = (dev_info_t *)arg;
334 	grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
335 	int divisor = 0;
336 
337 	ASSERT(freq != 0);
338 
339 	GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: dip=0x%p freq=%d mode=%d",
340 	    (void *)dip, freq, grbeeptr->grbeep_mode));
341 
342 	GRBEEP_WRITE_FREQ_CONTROL_REG(GRBEEP_CONTROL);
343 
344 	divisor = GRBEEP_INPUT_FREQ / freq;
345 
346 	if (divisor > GRBEEP_DIVISOR_MAX) {
347 		divisor = GRBEEP_DIVISOR_MAX;
348 	} else if (divisor < GRBEEP_DIVISOR_MIN) {
349 		divisor = GRBEEP_DIVISOR_MIN;
350 	}
351 
352 	GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: first=0x%x second=0x%x",
353 	    (divisor & 0xff), ((divisor & 0xff00) >> 8)));
354 
355 	GRBEEP_WRITE_FREQ_DIVISOR_REG(divisor & 0xff);
356 	GRBEEP_WRITE_FREQ_DIVISOR_REG((divisor & 0xff00) >> 8);
357 }
358 
359 
360 /*
361  * grbeep_on() :
362  *	Turn the beeper on
363  */
364 static void
365 grbeep_on(void *arg)
366 {
367 	dev_info_t *dip = (dev_info_t *)arg;
368 	grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
369 
370 	GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p mode=%d",
371 	    (void *)dip, grbeeptr->grbeep_mode));
372 
373 	if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
374 
375 		grbeeptr->grbeep_mode = GRBEEP_ON;
376 		GRBEEP_DEBUG1((CE_CONT, "grbeep_on: Starting beep"));
377 		GRBEEP_WRITE_START_STOP_REG(GRBEEP_START);
378 
379 	}
380 
381 	GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p done", (void *)dip));
382 }
383 
384 
385 /*
386  * grbeep_off() :
387  * 	Turn the beeper off
388  */
389 static void
390 grbeep_off(void *arg)
391 {
392 	dev_info_t *dip = (dev_info_t *)arg;
393 	grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
394 
395 	GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p mode=%d",
396 	    (void *)dip, grbeeptr->grbeep_mode));
397 
398 	if (grbeeptr->grbeep_mode == GRBEEP_ON) {
399 
400 		grbeeptr->grbeep_mode = GRBEEP_OFF;
401 		GRBEEP_DEBUG1((CE_CONT, "grbeep_off: Stopping beep"));
402 		GRBEEP_WRITE_START_STOP_REG(GRBEEP_STOP);
403 
404 	}
405 
406 	GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p done", (void *)dip));
407 }
408 
409 /*
410  * grbeep_map_regs() :
411  *
412  *	The write beep port register and spkr control register
413  *	should be mapped into a non-cacheable portion of the  system
414  *	addressable space.
415  */
416 static int
417 grbeep_map_regs(dev_info_t *dip, grbeep_state_t *grbeeptr)
418 {
419 	ddi_device_acc_attr_t attr;
420 
421 	GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: Start"));
422 
423 	/* The host controller will be little endian */
424 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
425 	attr.devacc_attr_endian_flags  = DDI_STRUCTURE_LE_ACC;
426 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
427 
428 	/* Map in operational registers */
429 	if (ddi_regs_map_setup(dip, 2,
430 	    (caddr_t *)&grbeeptr->grbeep_freq_regs,
431 	    0,
432 	    sizeof (grbeep_freq_regs_t),
433 	    &attr,
434 	    &grbeeptr->grbeep_freq_regs_handle)
435 	    != DDI_SUCCESS) {
436 
437 		GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
438 		return (DDI_FAILURE);
439 	}
440 
441 	/* Map in operational registers */
442 	if (ddi_regs_map_setup(dip, 3,
443 	    (caddr_t *)&grbeeptr->grbeep_start_stop_reg,
444 	    0,
445 	    1,
446 	    &attr,
447 	    &grbeeptr->grbeep_start_stop_reg_handle)
448 	    != DDI_SUCCESS) {
449 
450 		GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
451 		ddi_regs_map_free((void *)&grbeeptr->grbeep_freq_regs_handle);
452 
453 		return (DDI_FAILURE);
454 	}
455 
456 	GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: done"));
457 
458 	return (DDI_SUCCESS);
459 }
460 
461 
462 /*
463  * grbeep_obtain_state:
464  */
465 static grbeep_state_t *
466 grbeep_obtain_state(dev_info_t *dip)
467 {
468 	int instance = ddi_get_instance(dip);
469 
470 	grbeep_state_t *state = ddi_get_soft_state(grbeep_statep, instance);
471 
472 	ASSERT(state != NULL);
473 
474 	GRBEEP_DEBUG1((CE_CONT, "grbeep_obtain_state: done"));
475 
476 	return (state);
477 }
478 
479 
480 /*
481  * grbeep_cleanup :
482  *	Cleanup soft state
483  */
484 static void
485 grbeep_cleanup(grbeep_state_t *grbeeptr)
486 {
487 	int instance = ddi_get_instance(grbeeptr->grbeep_dip);
488 
489 	ddi_soft_state_free(grbeep_statep, instance);
490 
491 	GRBEEP_DEBUG1((CE_CONT, "grbeep_cleanup: done"));
492 }
493