diff options
Diffstat (limited to 'usr/src/uts/sun4u/io/bbc_beep.c')
| -rw-r--r-- | usr/src/uts/sun4u/io/bbc_beep.c | 560 |
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); +} |
