summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/io/bbc_beep.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/io/bbc_beep.c')
-rw-r--r--usr/src/uts/sun4u/io/bbc_beep.c560
1 files changed, 560 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/bbc_beep.c b/usr/src/uts/sun4u/io/bbc_beep.c
new file mode 100644
index 0000000000..c034ffcdf8
--- /dev/null
+++ b/usr/src/uts/sun4u/io/bbc_beep.c
@@ -0,0 +1,560 @@
+/*
+ * 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 1999-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 bbc based beep mechanism.
+ *
+ */
+#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/bbc_beep.h>
+#include <sys/beep_driver.h>
+
+
+/* Pointer to the state structure */
+static void *bbc_beep_statep;
+
+
+/*
+ * Debug stuff
+ */
+#ifdef DEBUG
+int bbc_beep_debug = 0;
+#define BBC_BEEP_DEBUG(args) if (bbc_beep_debug) cmn_err args
+#define BBC_BEEP_DEBUG1(args) if (bbc_beep_debug > 1) cmn_err args
+#else
+#define BBC_BEEP_DEBUG(args)
+#define BBC_BEEP_DEBUG1(args)
+#endif
+
+
+/*
+ * Prototypes
+ */
+static int bbc_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int bbc_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int bbc_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **result);
+static void bbc_beep_freq(dev_info_t *, int);
+static void bbc_beep_on(dev_info_t *);
+static void bbc_beep_off(dev_info_t *);
+static void bbc_beep_cleanup(bbc_beep_state_t *);
+static int bbc_beep_map_regs(dev_info_t *, bbc_beep_state_t *);
+static bbc_beep_state_t *bbc_beep_obtain_state(dev_info_t *);
+static unsigned long bbc_beep_hztocounter(int);
+
+
+struct cb_ops bbc_beep_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_64BIT | D_MP | D_NEW| D_HOTPLUG
+};
+
+
+static struct dev_ops bbc_beep_ops = {
+ DEVO_REV, /* Devo_rev */
+ 0, /* Refcnt */
+ bbc_beep_info, /* Info */
+ nulldev, /* Identify */
+ nulldev, /* Probe */
+ bbc_beep_attach, /* Attach */
+ bbc_beep_detach, /* Detach */
+ nodev, /* Reset */
+ &bbc_beep_cb_ops, /* Driver operations */
+ 0, /* Bus operations */
+ ddi_power /* Power */
+};
+
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* This one is a driver */
+ "BBC Beep Driver %I%", /* Name of the module. */
+ &bbc_beep_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(&bbc_beep_statep,
+ sizeof (bbc_beep_state_t), 1)) != 0) {
+
+ return (error);
+ }
+
+ /* Install the loadable module */
+ if ((error = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini(&bbc_beep_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(&bbc_beep_statep);
+ }
+
+ return (error);
+}
+
+
+/*
+ * Beep entry points
+ */
+
+/*
+ * bbc_beep_attach:
+ */
+static int
+bbc_beep_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance; /* Instance number */
+
+ /* Pointer to soft state */
+ bbc_beep_state_t *bbc_beeptr = NULL;
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_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(bbc_beep_statep, instance) != 0) {
+
+ return (DDI_FAILURE);
+ }
+
+ bbc_beeptr = ddi_get_soft_state(bbc_beep_statep, instance);
+
+ if (bbc_beeptr == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beeptr = 0x%p, instance %x",
+ (void *)bbc_beeptr, instance));
+
+ /* Save the dip */
+ bbc_beeptr->bbc_beep_dip = dip;
+
+ /* Initialize beeper mode */
+ bbc_beeptr->bbc_beep_mode = BBC_BEEP_OFF;
+
+ /* Map the Beep Control and Beep counter Registers */
+ if (bbc_beep_map_regs(dip, bbc_beeptr) != DDI_SUCCESS) {
+
+ BBC_BEEP_DEBUG((CE_WARN, \
+ "bbc_beep_attach: Mapping of bbc registers failed."));
+
+ bbc_beep_cleanup(bbc_beeptr);
+
+ return (DDI_FAILURE);
+ }
+
+ (void) beep_init(dip, bbc_beep_on, bbc_beep_off, bbc_beep_freq);
+
+ /* Display information in the banner */
+ ddi_report_dev(dip);
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_attach: dip = 0x%p done",
+ (void *)dip));
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * bbc_beep_detach:
+ */
+/* ARGSUSED */
+static int
+bbc_beep_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ /* Pointer to soft state */
+ bbc_beep_state_t *bbc_beeptr = NULL;
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_detach: Start"));
+
+ switch (cmd) {
+ case DDI_SUSPEND:
+ bbc_beeptr = bbc_beep_obtain_state(dip);
+
+ if (bbc_beeptr == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * If a beep is in progress; fail suspend
+ */
+ if (bbc_beeptr->bbc_beep_mode == BBC_BEEP_OFF) {
+ return (DDI_SUCCESS);
+ } else {
+ return (DDI_FAILURE);
+ }
+ default:
+
+ return (DDI_FAILURE);
+ }
+}
+
+
+/*
+ * bbc_beep_info:
+ */
+/* ARGSUSED */
+static int
+bbc_beep_info(dev_info_t *dip, ddi_info_cmd_t infocmd,
+ void *arg, void **result)
+{
+ dev_t dev;
+ bbc_beep_state_t *bbc_beeptr;
+ int instance, error;
+
+ switch (infocmd) {
+
+ case DDI_INFO_DEVT2DEVINFO:
+ dev = (dev_t)arg;
+ instance = BEEP_UNIT(dev);
+
+ if ((bbc_beeptr = ddi_get_soft_state(bbc_beep_statep,
+ instance)) == NULL) {
+
+ return (DDI_FAILURE);
+ }
+
+ *result = (void *)bbc_beeptr->bbc_beep_dip;
+
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ dev = (dev_t)arg;
+ instance = BEEP_UNIT(dev);
+
+ *result = (void *)(uintptr_t)instance;
+
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+
+ }
+
+ return (error);
+}
+
+
+/*
+ * bbc_beep_freq() :
+ * Set the frequency
+ */
+static void
+bbc_beep_freq(dev_info_t *dip, int freq)
+{
+ unsigned long counter;
+ int8_t beep_c2 = 0;
+ int8_t beep_c3 = 0;
+
+ bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip);
+
+ /* Convert the frequency in hz to the bbc counter value */
+ counter = bbc_beep_hztocounter(freq);
+
+ /* Extract relevant second and third byte of counter value */
+ beep_c2 = (counter & 0xff00) >> 8;
+ beep_c3 = (counter & 0xff0000) >> 16;
+
+ /*
+ * We need to write individual bytes instead of writing
+ * all of 32 bits to take care of allignment problem.
+ * Write 0 to LS 8 bits and MS 8 bits
+ * Write beep_c3 to bit 8..15 and beep_c2 to bit 16..24
+ * Little Endian format
+ */
+ BEEP_WRITE_COUNTER_REG(0, 0);
+ BEEP_WRITE_COUNTER_REG(1, beep_c3);
+ BEEP_WRITE_COUNTER_REG(2, beep_c2);
+ BEEP_WRITE_COUNTER_REG(3, 0);
+
+ BBC_BEEP_DEBUG1((CE_CONT,
+ "bbc_beep_freq: dip = 0x%p, freq = %d, counter = 0x%x : Done",
+ (void *)dip, freq, (int)counter));
+}
+
+
+/*
+ * bbc_beep_on() :
+ * Turn the beeper on
+ */
+static void
+bbc_beep_on(dev_info_t *dip)
+{
+ bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip);
+
+ BEEP_WRITE_CTRL_REG(BBC_BEEP_ON);
+
+ bbc_beeptr->bbc_beep_mode = BBC_BEEP_ON;
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_on: dip = 0x%p done",
+ (void *)dip));
+}
+
+
+/*
+ * bbc_beep_off() :
+ * Turn the beeper off
+ */
+static void
+bbc_beep_off(dev_info_t *dip)
+{
+ bbc_beep_state_t *bbc_beeptr = bbc_beep_obtain_state(dip);
+
+ BEEP_WRITE_CTRL_REG(BBC_BEEP_OFF);
+
+ bbc_beeptr->bbc_beep_mode = BBC_BEEP_OFF;
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_off: dip = 0x%p done",
+ (void *)dip));
+}
+
+
+/*
+ * bbc_beep_map_regs() :
+ *
+ * The Keyboard Beep Control Register and Keyboard Beep Counter Register
+ * should be mapped into a non-cacheable portion of the system
+ * addressable space.
+ */
+static int
+bbc_beep_map_regs(dev_info_t *dip, bbc_beep_state_t *bbc_beeptr)
+{
+ ddi_device_acc_attr_t attr;
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_map_regs: Start\n"));
+
+ /* 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, 0,
+ (caddr_t *)&bbc_beeptr->bbc_beep_regsp,
+ 0,
+ sizeof (bbc_beep_regs_t),
+ &attr,
+ &bbc_beeptr->bbc_beep_regs_handle) != DDI_SUCCESS) {
+
+ return (DDI_FAILURE);
+ }
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_map_regs: done\n"));
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * bbc_beep_obtain_state:
+ */
+static bbc_beep_state_t *
+bbc_beep_obtain_state(dev_info_t *dip)
+{
+ int instance = ddi_get_instance(dip);
+
+ bbc_beep_state_t *state = ddi_get_soft_state(bbc_beep_statep, instance);
+
+ ASSERT(state != NULL);
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_obtain_state: done"));
+
+ return (state);
+}
+
+
+/*
+ * bbc_beep_cleanup :
+ * Cleanup soft state
+ */
+static void
+bbc_beep_cleanup(bbc_beep_state_t *bbc_beeptr)
+{
+ int instance = ddi_get_instance(bbc_beeptr->bbc_beep_dip);
+
+ ddi_soft_state_free(bbc_beep_statep, instance);
+
+ BBC_BEEP_DEBUG1((CE_CONT, "bbc_beep_cleanup: done"));
+}
+
+
+/*
+ * bbc_beep_hztocounter() :
+ * Given a frequency in hz, find out the value to
+ * be set in the Keyboard Beep Counter register
+ * BBC beeper uses the following formula to calculate
+ * frequency. The formulae is :
+ * frequency generated = system freq /2^(n+2)
+ * Where n = position of the bit of counter register
+ * that is turned on and can range between 10 to 18.
+ * So in this function, the inputs are frequency generated
+ * and system frequency and we need to find out n, i.e, which
+ * bit to turn on.(Ref. to Section 4.2.22 of the BBC programming
+ * manual).
+ */
+unsigned long
+bbc_beep_hztocounter(int freq)
+{
+ int i;
+ unsigned long counter;
+ int newfreq, oldfreq;
+
+ int system_freq;
+
+ /*
+ * Get system frequency for the root dev_info properties
+ */
+ system_freq = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(),
+ 0, "clock-frequency", 0);
+
+ oldfreq = 0;
+
+ /*
+ * Calculate frequency by turning on ith bit and
+ * matching it with the passed frequency and we do this
+ * in a loop for all the relevant bits
+ */
+ for (i = BBC_BEEP_MIN_SHIFT, counter = 1 << BBC_BEEP_MSBIT;
+ i >= BBC_BEEP_MAX_SHIFT; i--, counter >>= 1) {
+
+ /*
+ * Calculate the frequency by dividing the system
+ * frequency by 2^i
+ */
+ newfreq = system_freq >> i;
+
+ /*
+ * Check if we turn on the ith bit, the
+ * frequency matches exactly or not
+ */
+ if (newfreq == freq) {
+ /*
+ * Exact match of passed frequency with the
+ * counter value
+ */
+
+ return (counter);
+ }
+
+ /*
+ * If calculated frequency is bigger
+ * return the passed frequency
+ */
+ if (newfreq > freq) {
+
+ if (i == BBC_BEEP_MIN_SHIFT) {
+ /* Input freq is less than the possible min */
+
+ return (counter);
+ }
+
+ /*
+ * Find out the nearest frequency to the passed
+ * frequency by comparing the difference between
+ * the calculated frequency and the passed frequency
+ */
+ if ((freq - oldfreq) > (newfreq - freq)) {
+ /* Return new counter corres. to newfreq */
+
+ return (counter);
+ }
+
+ /* Return old counter corresponding to oldfreq */
+
+ return (counter << 1);
+ }
+
+ oldfreq = newfreq;
+ }
+
+ /*
+ * Input freq is greater than the possible max;
+ * Back off the counter value and return max counter
+ * value possible in the register
+ */
+ return (counter << 1);
+}