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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * This is the Beep driver for SMBUS based beep mechanism.
29 * The driver exports the interfaces to set frequency,
30 * turn on beeper and turn off beeper to the generic beep
31 * module. If a beep is in progress, the driver discards a
32 * second beep. This driver uses the 8254 timer to program
35 #include <sys/types.h>
38 #include <sys/sunddi.h>
39 #include <sys/modctl.h>
40 #include <sys/ddi_impldefs.h>
42 #include <sys/devops.h>
43 #include <sys/grbeep.h>
47 /* Pointer to the state structure */
48 static void *grbeep_statep
;
56 #define GRBEEP_DEBUG(args) if (grbeep_debug) cmn_err args
57 #define GRBEEP_DEBUG1(args) if (grbeep_debug > 1) cmn_err args
59 #define GRBEEP_DEBUG(args)
60 #define GRBEEP_DEBUG1(args)
67 static int grbeep_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
);
68 static int grbeep_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
);
69 static int grbeep_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
,
71 static void grbeep_freq(void *arg
, int freq
);
72 static void grbeep_on(void *arg
);
73 static void grbeep_off(void *arg
);
74 static void grbeep_cleanup(grbeep_state_t
*);
75 static int grbeep_map_regs(dev_info_t
*, grbeep_state_t
*);
76 static grbeep_state_t
*grbeep_obtain_state(dev_info_t
*);
79 struct cb_ops grbeep_cb_ops
= {
82 nulldev
, /* strategy */
92 ddi_prop_op
, /* cb_prop_op */
98 static struct dev_ops grbeep_ops
= {
99 DEVO_REV
, /* Devo_rev */
101 grbeep_info
, /* Info */
102 nulldev
, /* Identify */
104 grbeep_attach
, /* Attach */
105 grbeep_detach
, /* Detach */
107 &grbeep_cb_ops
, /* Driver operations */
108 0, /* Bus operations */
110 ddi_quiesce_not_supported
, /* devo_quiesce */
114 static struct modldrv modldrv
= {
115 &mod_driverops
, /* This one is a driver */
116 "SMBUS Beep Driver", /* Name of the module. */
117 &grbeep_ops
, /* Driver ops */
121 static struct modlinkage modlinkage
= {
122 MODREV_1
, (void *)&modldrv
, NULL
131 /* Initialize the soft state structures */
132 if ((error
= ddi_soft_state_init(&grbeep_statep
,
133 sizeof (grbeep_state_t
), 1)) != 0) {
138 /* Install the loadable module */
139 if ((error
= mod_install(&modlinkage
)) != 0) {
140 ddi_soft_state_fini(&grbeep_statep
);
148 _info(struct modinfo
*modinfop
)
150 return (mod_info(&modlinkage
, modinfop
));
159 error
= mod_remove(&modlinkage
);
162 /* Release per module resources */
163 ddi_soft_state_fini(&grbeep_statep
);
178 grbeep_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
182 /* Pointer to soft state */
183 grbeep_state_t
*grbeeptr
= NULL
;
185 GRBEEP_DEBUG1((CE_CONT
, "grbeep_attach: Start"));
192 return (DDI_SUCCESS
);
195 return (DDI_FAILURE
);
198 /* Get the instance and create soft state */
199 instance
= ddi_get_instance(dip
);
201 if (ddi_soft_state_zalloc(grbeep_statep
, instance
) != 0) {
203 return (DDI_FAILURE
);
206 grbeeptr
= ddi_get_soft_state(grbeep_statep
, instance
);
208 if (grbeeptr
== NULL
) {
210 return (DDI_FAILURE
);
213 GRBEEP_DEBUG1((CE_CONT
, "grbeeptr = 0x%p, instance %x",
214 (void *)grbeeptr
, instance
));
217 grbeeptr
->grbeep_dip
= dip
;
219 /* Initialize beeper mode */
220 grbeeptr
->grbeep_mode
= GRBEEP_OFF
;
222 /* Map the Beep Control and Beep counter Registers */
223 if (grbeep_map_regs(dip
, grbeeptr
) != DDI_SUCCESS
) {
225 GRBEEP_DEBUG((CE_WARN
,
226 "grbeep_attach: Mapping of beep registers failed."));
228 grbeep_cleanup(grbeeptr
);
230 return (DDI_FAILURE
);
233 (void) beep_init((void *)dip
, grbeep_on
, grbeep_off
, grbeep_freq
);
235 /* Display information in the banner */
238 GRBEEP_DEBUG1((CE_CONT
, "grbeep_attach: dip = 0x%p done",
241 return (DDI_SUCCESS
);
249 grbeep_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
251 /* Pointer to soft state */
252 grbeep_state_t
*grbeeptr
= NULL
;
254 GRBEEP_DEBUG1((CE_CONT
, "grbeep_detach: Start"));
258 grbeeptr
= grbeep_obtain_state(dip
);
260 if (grbeeptr
== NULL
) {
262 return (DDI_FAILURE
);
266 * If a beep is in progress; fail suspend
268 if (grbeeptr
->grbeep_mode
== GRBEEP_OFF
) {
270 return (DDI_SUCCESS
);
273 return (DDI_FAILURE
);
277 return (DDI_FAILURE
);
287 grbeep_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
,
288 void *arg
, void **result
)
291 grbeep_state_t
*grbeeptr
;
295 case DDI_INFO_DEVT2DEVINFO
:
297 instance
= GRBEEP_UNIT(dev
);
299 if ((grbeeptr
= ddi_get_soft_state(grbeep_statep
,
300 instance
)) == NULL
) {
302 return (DDI_FAILURE
);
305 *result
= (void *)grbeeptr
->grbeep_dip
;
309 case DDI_INFO_DEVT2INSTANCE
:
311 instance
= GRBEEP_UNIT(dev
);
313 *result
= (void *)(uintptr_t)instance
;
331 grbeep_freq(void *arg
, int freq
)
333 dev_info_t
*dip
= (dev_info_t
*)arg
;
334 grbeep_state_t
*grbeeptr
= grbeep_obtain_state(dip
);
339 GRBEEP_DEBUG1((CE_CONT
, "grbeep_freq: dip=0x%p freq=%d mode=%d",
340 (void *)dip
, freq
, grbeeptr
->grbeep_mode
));
342 GRBEEP_WRITE_FREQ_CONTROL_REG(GRBEEP_CONTROL
);
344 divisor
= GRBEEP_INPUT_FREQ
/ freq
;
346 if (divisor
> GRBEEP_DIVISOR_MAX
) {
347 divisor
= GRBEEP_DIVISOR_MAX
;
348 } else if (divisor
< GRBEEP_DIVISOR_MIN
) {
349 divisor
= GRBEEP_DIVISOR_MIN
;
352 GRBEEP_DEBUG1((CE_CONT
, "grbeep_freq: first=0x%x second=0x%x",
353 (divisor
& 0xff), ((divisor
& 0xff00) >> 8)));
355 GRBEEP_WRITE_FREQ_DIVISOR_REG(divisor
& 0xff);
356 GRBEEP_WRITE_FREQ_DIVISOR_REG((divisor
& 0xff00) >> 8);
367 dev_info_t
*dip
= (dev_info_t
*)arg
;
368 grbeep_state_t
*grbeeptr
= grbeep_obtain_state(dip
);
370 GRBEEP_DEBUG1((CE_CONT
, "grbeep_on: dip = 0x%p mode=%d",
371 (void *)dip
, grbeeptr
->grbeep_mode
));
373 if (grbeeptr
->grbeep_mode
== GRBEEP_OFF
) {
375 grbeeptr
->grbeep_mode
= GRBEEP_ON
;
376 GRBEEP_DEBUG1((CE_CONT
, "grbeep_on: Starting beep"));
377 GRBEEP_WRITE_START_STOP_REG(GRBEEP_START
);
381 GRBEEP_DEBUG1((CE_CONT
, "grbeep_on: dip = 0x%p done", (void *)dip
));
387 * Turn the beeper off
390 grbeep_off(void *arg
)
392 dev_info_t
*dip
= (dev_info_t
*)arg
;
393 grbeep_state_t
*grbeeptr
= grbeep_obtain_state(dip
);
395 GRBEEP_DEBUG1((CE_CONT
, "grbeep_off: dip = 0x%p mode=%d",
396 (void *)dip
, grbeeptr
->grbeep_mode
));
398 if (grbeeptr
->grbeep_mode
== GRBEEP_ON
) {
400 grbeeptr
->grbeep_mode
= GRBEEP_OFF
;
401 GRBEEP_DEBUG1((CE_CONT
, "grbeep_off: Stopping beep"));
402 GRBEEP_WRITE_START_STOP_REG(GRBEEP_STOP
);
406 GRBEEP_DEBUG1((CE_CONT
, "grbeep_off: dip = 0x%p done", (void *)dip
));
410 * grbeep_map_regs() :
412 * The write beep port register and spkr control register
413 * should be mapped into a non-cacheable portion of the system
417 grbeep_map_regs(dev_info_t
*dip
, grbeep_state_t
*grbeeptr
)
419 ddi_device_acc_attr_t attr
;
421 GRBEEP_DEBUG1((CE_CONT
, "grbeep_map_regs: Start"));
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
;
428 /* Map in operational registers */
429 if (ddi_regs_map_setup(dip
, 2,
430 (caddr_t
*)&grbeeptr
->grbeep_freq_regs
,
432 sizeof (grbeep_freq_regs_t
),
434 &grbeeptr
->grbeep_freq_regs_handle
)
437 GRBEEP_DEBUG((CE_CONT
, "grbeep_map_regs: Failed to map"));
438 return (DDI_FAILURE
);
441 /* Map in operational registers */
442 if (ddi_regs_map_setup(dip
, 3,
443 (caddr_t
*)&grbeeptr
->grbeep_start_stop_reg
,
447 &grbeeptr
->grbeep_start_stop_reg_handle
)
450 GRBEEP_DEBUG((CE_CONT
, "grbeep_map_regs: Failed to map"));
451 ddi_regs_map_free((void *)&grbeeptr
->grbeep_freq_regs_handle
);
453 return (DDI_FAILURE
);
456 GRBEEP_DEBUG1((CE_CONT
, "grbeep_map_regs: done"));
458 return (DDI_SUCCESS
);
463 * grbeep_obtain_state:
465 static grbeep_state_t
*
466 grbeep_obtain_state(dev_info_t
*dip
)
468 int instance
= ddi_get_instance(dip
);
470 grbeep_state_t
*state
= ddi_get_soft_state(grbeep_statep
, instance
);
472 ASSERT(state
!= NULL
);
474 GRBEEP_DEBUG1((CE_CONT
, "grbeep_obtain_state: done"));
485 grbeep_cleanup(grbeep_state_t
*grbeeptr
)
487 int instance
= ddi_get_instance(grbeeptr
->grbeep_dip
);
489 ddi_soft_state_free(grbeep_statep
, instance
);
491 GRBEEP_DEBUG1((CE_CONT
, "grbeep_cleanup: done"));