summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/io/grbeep.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/io/grbeep.c')
-rw-r--r--usr/src/uts/sun4u/io/grbeep.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/grbeep.c b/usr/src/uts/sun4u/io/grbeep.c
new file mode 100644
index 0000000000..c0a393e073
--- /dev/null
+++ b/usr/src/uts/sun4u/io/grbeep.c
@@ -0,0 +1,502 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This is the Beep driver for SMBUS based beep mechanism.
+ * The driver exports the interfaces to set frequency,
+ * turn on beeper and turn off beeper to the generic beep
+ * module. If a beep is in progress, the driver discards a
+ * second beep. This driver uses the 8254 timer to program
+ * the beeper ports.
+ */
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/kmem.h>
+#include <sys/devops.h>
+#include <sys/grbeep.h>
+#include <sys/beep_driver.h>
+
+
+/* Pointer to the state structure */
+static void *grbeep_statep;
+
+
+/*
+ * Debug stuff
+ */
+#ifdef DEBUG
+int grbeep_debug = 0;
+#define GRBEEP_DEBUG(args) if (grbeep_debug) cmn_err args
+#define GRBEEP_DEBUG1(args) if (grbeep_debug > 1) cmn_err args
+#else
+#define GRBEEP_DEBUG(args)
+#define GRBEEP_DEBUG1(args)
+#endif
+
+
+/*
+ * Prototypes
+ */
+static int grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result);
+static void grbeep_freq(dev_info_t *, int);
+static void grbeep_on(dev_info_t *);
+static void grbeep_off(dev_info_t *);
+static void grbeep_cleanup(grbeep_state_t *);
+static int grbeep_map_regs(dev_info_t *, grbeep_state_t *);
+static grbeep_state_t *grbeep_obtain_state(dev_info_t *);
+
+
+struct cb_ops grbeep_cb_ops = {
+ nulldev, /* open */
+ nulldev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nulldev, /* dump */
+ nulldev, /* read */
+ nulldev, /* write */
+ nulldev, /* ioctl */
+ nulldev, /* devmap */
+ nulldev, /* mmap */
+ nulldev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_MP | D_NEW
+};
+
+
+static struct dev_ops grbeep_ops = {
+ DEVO_REV, /* Devo_rev */
+ 0, /* Refcnt */
+ grbeep_info, /* Info */
+ nulldev, /* Identify */
+ nulldev, /* Probe */
+ grbeep_attach, /* Attach */
+ grbeep_detach, /* Detach */
+ nodev, /* Reset */
+ &grbeep_cb_ops, /* Driver operations */
+ 0, /* Bus operations */
+ NULL /* Power */
+};
+
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* This one is a driver */
+ "SMBUS Beep Driver %I%", /* Name of the module. */
+ &grbeep_ops, /* Driver ops */
+};
+
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modldrv, NULL
+};
+
+
+int
+_init(void)
+{
+ int error;
+
+ /* Initialize the soft state structures */
+ if ((error = ddi_soft_state_init(&grbeep_statep,
+ sizeof (grbeep_state_t), 1)) != 0) {
+
+ return (error);
+ }
+
+ /* Install the loadable module */
+ if ((error = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&grbeep_statep);
+ }
+
+ return (error);
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+
+ if (error == 0) {
+ /* Release per module resources */
+ ddi_soft_state_fini(&grbeep_statep);
+ }
+
+ return (error);
+}
+
+
+/*
+ * Beep entry points
+ */
+
+/*
+ * grbeep_attach:
+ */
+static int
+grbeep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance;
+
+ /* Pointer to soft state */
+ grbeep_state_t *grbeeptr = NULL;
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: Start"));
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+
+ return (DDI_SUCCESS);
+ default:
+
+ return (DDI_FAILURE);
+ }
+
+ /* Get the instance and create soft state */
+ instance = ddi_get_instance(dip);
+
+ if (ddi_soft_state_zalloc(grbeep_statep, instance) != 0) {
+
+ return (DDI_FAILURE);
+ }
+
+ grbeeptr = ddi_get_soft_state(grbeep_statep, instance);
+
+ if (grbeeptr == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeeptr = 0x%p, instance %x",
+ (void *)grbeeptr, instance));
+
+ /* Save the dip */
+ grbeeptr->grbeep_dip = dip;
+
+ /* Initialize beeper mode */
+ grbeeptr->grbeep_mode = GRBEEP_OFF;
+
+ /* Map the Beep Control and Beep counter Registers */
+ if (grbeep_map_regs(dip, grbeeptr) != DDI_SUCCESS) {
+
+ GRBEEP_DEBUG((CE_WARN,
+ "grbeep_attach: Mapping of beep registers failed."));
+
+ grbeep_cleanup(grbeeptr);
+
+ return (DDI_FAILURE);
+ }
+
+ (void) beep_init(dip, grbeep_on, grbeep_off, grbeep_freq);
+
+ /* Display information in the banner */
+ ddi_report_dev(dip);
+
+ mutex_init(&grbeeptr->grbeep_mutex, NULL, MUTEX_DRIVER, NULL);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_attach: dip = 0x%p done",
+ (void *)dip));
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * grbeep_detach:
+ */
+/* ARGSUSED */
+static int
+grbeep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ /* Pointer to soft state */
+ grbeep_state_t *grbeeptr = NULL;
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_detach: Start"));
+
+ switch (cmd) {
+ case DDI_SUSPEND:
+ grbeeptr = grbeep_obtain_state(dip);
+
+ if (grbeeptr == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * If a beep is in progress; fail suspend
+ */
+ if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
+
+ return (DDI_SUCCESS);
+ } else {
+
+ return (DDI_FAILURE);
+ }
+ default:
+
+ return (DDI_FAILURE);
+ }
+}
+
+
+/*
+ * grbeep_info:
+ */
+/* ARGSUSED */
+static int
+grbeep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
+ void *arg, void **result)
+{
+ dev_t dev;
+ grbeep_state_t *grbeeptr;
+ int instance, error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ dev = (dev_t)arg;
+ instance = GRBEEP_UNIT(dev);
+
+ if ((grbeeptr = ddi_get_soft_state(grbeep_statep,
+ instance)) == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ *result = (void *)grbeeptr->grbeep_dip;
+
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ dev = (dev_t)arg;
+ instance = GRBEEP_UNIT(dev);
+
+ *result = (void *)(uintptr_t)instance;
+
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+
+ }
+
+ return (error);
+}
+
+
+/*
+ * grbeep_freq() :
+ * Set beep frequency
+ */
+/*ARGSUSED*/
+static void
+grbeep_freq(dev_info_t *dip, int freq)
+{
+ grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
+ int divisor = 0;
+
+ ASSERT(freq != 0);
+
+ mutex_enter(&grbeeptr->grbeep_mutex);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: dip=0x%p freq=%d mode=%d",
+ (void *)dip, freq, grbeeptr->grbeep_mode));
+
+ GRBEEP_WRITE_FREQ_CONTROL_REG(GRBEEP_CONTROL);
+
+ divisor = GRBEEP_INPUT_FREQ / freq;
+
+ if (divisor > GRBEEP_DIVISOR_MAX) {
+ divisor = GRBEEP_DIVISOR_MAX;
+ } else if (divisor < GRBEEP_DIVISOR_MIN) {
+ divisor = GRBEEP_DIVISOR_MIN;
+ }
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_freq: first=0x%x second=0x%x",
+ (divisor & 0xff), ((divisor & 0xff00) >> 8)));
+
+ GRBEEP_WRITE_FREQ_DIVISOR_REG(divisor & 0xff);
+ GRBEEP_WRITE_FREQ_DIVISOR_REG((divisor & 0xff00) >> 8);
+
+ mutex_exit(&grbeeptr->grbeep_mutex);
+}
+
+
+/*
+ * grbeep_on() :
+ * Turn the beeper on
+ */
+static void
+grbeep_on(dev_info_t *dip)
+{
+ grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
+
+ mutex_enter(&grbeeptr->grbeep_mutex);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p mode=%d",
+ (void *)dip, grbeeptr->grbeep_mode));
+
+ if (grbeeptr->grbeep_mode == GRBEEP_OFF) {
+
+ grbeeptr->grbeep_mode = GRBEEP_ON;
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_on: Starting beep"));
+ GRBEEP_WRITE_START_STOP_REG(GRBEEP_START);
+
+ }
+
+ mutex_exit(&grbeeptr->grbeep_mutex);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_on: dip = 0x%p done", (void *)dip));
+}
+
+
+/*
+ * grbeep_off() :
+ * Turn the beeper off
+ */
+/*ARGSUSED*/
+static void
+grbeep_off(dev_info_t *dip)
+{
+ grbeep_state_t *grbeeptr = grbeep_obtain_state(dip);
+
+ mutex_enter(&grbeeptr->grbeep_mutex);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p mode=%d",
+ (void *)dip, grbeeptr->grbeep_mode));
+
+ if (grbeeptr->grbeep_mode == GRBEEP_ON) {
+
+ grbeeptr->grbeep_mode = GRBEEP_OFF;
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_off: Stopping beep"));
+ GRBEEP_WRITE_START_STOP_REG(GRBEEP_STOP);
+
+ }
+
+ mutex_exit(&grbeeptr->grbeep_mutex);
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_off: dip = 0x%p done", (void *)dip));
+}
+
+/*
+ * grbeep_map_regs() :
+ *
+ * The write beep port register and spkr control register
+ * should be mapped into a non-cacheable portion of the system
+ * addressable space.
+ */
+static int
+grbeep_map_regs(dev_info_t *dip, grbeep_state_t *grbeeptr)
+{
+ ddi_device_acc_attr_t attr;
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: Start"));
+
+ /* The host controller will be little endian */
+ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ /* Map in operational registers */
+ if (ddi_regs_map_setup(dip, 2,
+ (caddr_t *)&grbeeptr->grbeep_freq_regs,
+ 0,
+ sizeof (grbeep_freq_regs_t),
+ &attr,
+ &grbeeptr->grbeep_freq_regs_handle)
+ != DDI_SUCCESS) {
+
+ GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
+ return (DDI_FAILURE);
+ }
+
+ /* Map in operational registers */
+ if (ddi_regs_map_setup(dip, 3,
+ (caddr_t *)&grbeeptr->grbeep_start_stop_reg,
+ 0,
+ 1,
+ &attr,
+ &grbeeptr->grbeep_start_stop_reg_handle)
+ != DDI_SUCCESS) {
+
+ GRBEEP_DEBUG((CE_CONT, "grbeep_map_regs: Failed to map"));
+ ddi_regs_map_free((void *)&grbeeptr->grbeep_freq_regs_handle);
+
+ return (DDI_FAILURE);
+ }
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_map_regs: done"));
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * grbeep_obtain_state:
+ */
+static grbeep_state_t *
+grbeep_obtain_state(dev_info_t *dip)
+{
+ int instance = ddi_get_instance(dip);
+
+ grbeep_state_t *state = ddi_get_soft_state(grbeep_statep, instance);
+
+ ASSERT(state != NULL);
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_obtain_state: done"));
+
+ return (state);
+}
+
+
+/*
+ * grbeep_cleanup :
+ * Cleanup soft state
+ */
+static void
+grbeep_cleanup(grbeep_state_t *grbeeptr)
+{
+ int instance = ddi_get_instance(grbeeptr->grbeep_dip);
+
+ mutex_destroy(&grbeeptr->grbeep_mutex);
+ ddi_soft_state_free(grbeep_statep, instance);
+
+ GRBEEP_DEBUG1((CE_CONT, "grbeep_cleanup: done"));
+}