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