diff options
Diffstat (limited to 'usr/src/uts/sun4u/io/sbbc.c')
| -rw-r--r-- | usr/src/uts/sun4u/io/sbbc.c | 1474 |
1 files changed, 1474 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/sbbc.c b/usr/src/uts/sun4u/io/sbbc.c new file mode 100644 index 0000000000..290798395a --- /dev/null +++ b/usr/src/uts/sun4u/io/sbbc.c @@ -0,0 +1,1474 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Starcat PCI SBBC Device Nexus Driver that provides interfaces into + * Console Bus, I2C, Error/Intr. EPLD, IOSRAM, and JTAG. + */ + +#include <sys/types.h> + +#include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/ddi_impldefs.h> +#include <sys/ddi_subrdefs.h> +#include <sys/pci.h> +#include <sys/nexusintr_impl.h> +#include <sys/pci/pci_nexus.h> +#include <sys/autoconf.h> +#include <sys/cmn_err.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/kmem.h> +#include <sys/debug.h> +#include <sys/sysmacros.h> +#include <sys/machsystm.h> +#include <sys/modctl.h> +#include <sys/stat.h> + + +#include <sys/sbbcreg.h> /* hw description */ +#include <sys/sbbcvar.h> /* driver description */ +#include <sys/sbbcio.h> /* ioctl description */ + + +#define getprop(dip, name, addr, intp) \ + ddi_getlongprop(DDI_DEV_T_NONE, (dip), DDI_PROP_DONTPASS, \ + (name), (caddr_t)(addr), (intp)) + +/* driver entry point fn definitions */ +static int sbbc_open(dev_t *, int, int, cred_t *); +static int sbbc_close(dev_t, int, int, cred_t *); +static int sbbc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); + +/* configuration entry point fn definitions */ +static int sbbc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); +static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t); +static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t); + +/* local utility routines */ +/* + * NOTE - sbbc_offset_valid contains detailed address information taken from + * the Serengeti Architecture Programmer's Reference Manual. If any + * changes are made to the SBBC registers, this routine may need to be + * updated. + */ +static int sbbc_offset_valid(uint32_t offset); + +/* + * function prototypes for bus ops routines: + */ +static int sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, + off_t offset, off_t len, caddr_t *addrp); +static int sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, + ddi_ctl_enum_t op, void *arg, void *result); + +static int sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); +static int sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); +static int sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); +static int sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, + ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); + +static int sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip, + sbbc_child_regspec_t *child_rp, pci_regspec_t *rp); + +static int sbbc_init(struct sbbcsoft *); + +static uint_t sbbc_intr_wrapper(caddr_t arg); + +static int sbbc_get_ranges(struct sbbcsoft *); +static int sbbc_config4pci(struct sbbcsoft *); +static int sbbc_initchild(dev_info_t *, dev_info_t *, dev_info_t *); +static int sbbc_uninitchild(dev_info_t *, dev_info_t *); +static void sbbc_remove_reg_maps(struct sbbcsoft *); + +/* debugging functions */ +#ifdef DEBUG +uint32_t sbbc_dbg_flags = 0x0; +static void sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt, + uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5); +#endif + +/* + * For tracing, allocate space for the trace buffer + */ +#if defined(SBBC_TRACE) +struct sbbctrace sbbctrace_buffer[NSBBCTRACE+1]; +struct sbbctrace *sbbctrace_ptr; +int sbbctrace_count; +#endif + +/* + * Local declarations and variables + */ + +static void *sbbcsoft_statep; +int sbbc_scmode = FALSE; + +/* + * ops stuff. + */ +static struct bus_ops sbbc_bus_ops = { + BUSO_REV, + sbbc_busmap, + 0, + 0, + 0, + NULL, /* (*bus_map_fault)() */ + ddi_no_dma_map, + ddi_no_dma_allochdl, + ddi_no_dma_freehdl, /* (*bus_dma_freehdl)() */ + ddi_no_dma_bindhdl, /* (*bus_dma_bindhdl)() */ + ddi_no_dma_unbindhdl, /* (*bus_dma_unbindhdl)() */ + ddi_no_dma_flush, /* (*bus_dma_flush)() */ + ddi_no_dma_win, /* (*bus_dma_win)() */ + ddi_no_dma_mctl, /* (*bus_dma_ctl)() */ + sbbc_ctlops, + ddi_bus_prop_op, + 0, /* (*bus_get_eventcookie)(); */ + 0, /* (*bus_add_eventcall)(); */ + 0, /* (*bus_remove_eventcall)(); */ + 0, /* (*bus_post_event)(); */ + 0, /* (*bus_intr_ctl)(); */ + 0, /* (*bus_config)(); */ + 0, /* (*bus_unconfig)(); */ + 0, /* (*bus_fm_init)(); */ + 0, /* (*bus_fm_fini)(); */ + 0, /* (*bus_fm_access_enter)(); */ + 0, /* (*bus_fm_access_exit)(); */ + 0, /* (*bus_power)(); */ + sbbc_intr_ops /* (*bus_intr_op)(); */ +}; + +/* + * cb_ops + */ +static struct cb_ops sbbc_cb_ops = { + sbbc_open, /* cb_open */ + sbbc_close, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + sbbc_ioctl, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + NULL, /* cb_stream */ + (int)(D_NEW | D_MP) /* cb_flag */ +}; + +/* + * Declare ops vectors for auto configuration. + */ +struct dev_ops sbbc_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + sbbc_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + sbbc_attach, /* devo_attach */ + sbbc_detach, /* devo_detach */ + nodev, /* devo_reset */ + &sbbc_cb_ops, /* devo_cb_ops */ + &sbbc_bus_ops, /* devo_bus_ops */ + nulldev /* devo_power */ +}; + +/* + * Loadable module support. + */ +extern struct mod_ops mod_driverops; + +static struct modldrv sbbcmodldrv = { + &mod_driverops, /* type of module - driver */ + "PCI Sbbc Nexus Driver v%I%", + &sbbc_ops, +}; + +static struct modlinkage sbbcmodlinkage = { + MODREV_1, + &sbbcmodldrv, + NULL +}; + +int +_init(void) +{ + int error; + + if ((error = ddi_soft_state_init(&sbbcsoft_statep, + sizeof (struct sbbcsoft), 1)) != 0) + return (error); + if ((error = mod_install(&sbbcmodlinkage)) != 0) + ddi_soft_state_fini(&sbbcsoft_statep); + + return (error); +} + +int +_fini(void) +{ + int error; + + if ((error = mod_remove(&sbbcmodlinkage)) == 0) + ddi_soft_state_fini(&sbbcsoft_statep); + + return (error); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&sbbcmodlinkage, modinfop)); +} + +static int +sbbc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int instance; + char name[32]; + struct sbbcsoft *sbbcsoftp; + struct ddi_device_acc_attr attr; + uint32_t sbbc_id_reg = 0; + uint16_t sbbc_id_reg_partid; + uint16_t sbbc_id_reg_manfid; + + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC; + + /* initialize tracing */ + SBBCTRACEINIT(); + + SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attaching\n"); + + instance = ddi_get_instance(dip); + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + if (!(sbbcsoftp = + ddi_get_soft_state(sbbcsoft_statep, instance))) { + cmn_err(CE_WARN, + "sbbc_attach:resume: unable to acquire sbbcsoftp for instance %d", + instance); + return (DDI_FAILURE); + } + mutex_enter(&sbbcsoftp->umutex); + if (!sbbcsoftp->suspended) { + mutex_exit(&sbbcsoftp->umutex); + return (DDI_FAILURE); + } + sbbcsoftp->suspended = 0; + mutex_exit(&sbbcsoftp->umutex); + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + if (ddi_soft_state_zalloc(sbbcsoft_statep, instance) != 0) { + cmn_err(CE_WARN, + "sbbc_attach: Unable to allocate statep for instance %d", + instance); + return (DDI_FAILURE); + } + + sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance); + + if (sbbcsoftp == NULL) { + cmn_err(CE_WARN, + "sbbc_attach: Unable to acquire sbbcsoftp for instance %d", + instance); + ddi_soft_state_free(sbbcsoft_statep, instance); + return (DDI_FAILURE); + } + + sbbcsoftp->instance = instance; + sbbcsoftp->dip = dip; + sbbcsoftp->oflag = FALSE; + + /* + * Read our ranges property from OBP to map children space. + * And setup the internal structure for a later use when + * a child gets initialized. + */ + if (sbbc_get_ranges(sbbcsoftp)) { + cmn_err(CE_WARN, + "sbbc_attach: Unable to read sbbc ranges from OBP %d", instance); + ddi_soft_state_free(sbbcsoft_statep, instance); + return (DDI_FAILURE); + } + + if (sbbc_config4pci(sbbcsoftp)) { + cmn_err(CE_WARN, + "sbbc_attach: Unable to configure sbbc on PCI %d", instance); + kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); + ddi_soft_state_free(sbbcsoft_statep, instance); + return (DDI_FAILURE); + } + + /* + * Map SBBC's internal registers used by hardware access daemon. + */ + + /* map the whole thing since OBP does not map individual devices */ + if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sbbcsoftp->pci_sbbc_map, + 0, 0, &attr, &sbbcsoftp->pci_sbbc_map_handle) != DDI_SUCCESS) { + cmn_err(CE_WARN, "(%d):sbbc_attach failed to map sbbc_reg", + instance); + goto failed; + } + + sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, + (uint32_t *) + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.device_conf); + + if (sbbc_id_reg & SBBC_SC_MODE) { + + SBBC_DBG5(SBBC_DBG_ATTACH, dip, + "Mapped sbbc %llx, regs %llx, eregs %llx, sram %llx, consbus %llx\n", + sbbcsoftp->pci_sbbc_map, + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs, + &sbbcsoftp->pci_sbbc_map->echip_regs, + &sbbcsoftp->pci_sbbc_map->sram[0], + &sbbcsoftp->pci_sbbc_map->consbus); + + sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, + (uint32_t *) + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.devid); + sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16); + sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21); + SBBC_DBG4(SBBC_DBG_ATTACH, dip, + "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n", + instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid, + sbbc_id_reg_manfid); + + sbbc_scmode = TRUE; + SBBC_DBG1(SBBC_DBG_ATTACH, dip, + "SBBC(%d) nexus running in System Controller Mode.\n", + instance); + + /* + * There will be only one SBBC instance on SC and no + * chosen node stuff to deal with :-) + */ + + } else { + /* The code below needs to be common with SC mode above */ + + SBBC_DBG4(SBBC_DBG_ATTACH, dip, + "Mapped sbbc %llx, regs %llx, eregs %llx, sram %llx\n", + sbbcsoftp->pci_sbbc_map, + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs, + &sbbcsoftp->pci_sbbc_map->echip_regs, + &sbbcsoftp->pci_sbbc_map->sram[0]); + + sbbc_id_reg = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, + (uint32_t *) + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.devid); + sbbc_id_reg_partid = ((sbbc_id_reg << 4) >> 16); + sbbc_id_reg_manfid = ((sbbc_id_reg << 20) >> 21); + SBBC_DBG4(SBBC_DBG_ATTACH, dip, + "FOUND SBBC(%d) Version %x, Partid %x, Manfid %x\n", + instance, (sbbc_id_reg >> 28), sbbc_id_reg_partid, + sbbc_id_reg_manfid); + + sbbc_scmode = FALSE; + SBBC_DBG1(SBBC_DBG_ATTACH, dip, + "SBBC(%d) nexus running in Domain Mode.\n", + instance); + + /* + * There will be only one SBBC instance on SC and no + * chosen node stuff to deal with :-) + */ + + } + + mutex_init(&sbbcsoftp->umutex, NULL, MUTEX_DRIVER, (void *)NULL); + mutex_init(&sbbcsoftp->sbbc_intr_mutex, NULL, + MUTEX_DRIVER, (void *)NULL); + + /* initialize sbbc */ + if (!sbbc_init(sbbcsoftp)) { + goto remlock; + } + + (void) sprintf(name, "sbbc%d", instance); + + if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, + NULL) == DDI_FAILURE) { + ddi_remove_minor_node(dip, NULL); + goto remlock; + } + + ddi_report_dev(dip); + + SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attached successfully\n"); + + return (DDI_SUCCESS); + +remlock: + mutex_destroy(&sbbcsoftp->sbbc_intr_mutex); + mutex_destroy(&sbbcsoftp->umutex); + +failed: + sbbc_remove_reg_maps(sbbcsoftp); + kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); + ddi_soft_state_free(sbbcsoft_statep, instance); + + SBBC_DBG0(SBBC_DBG_ATTACH, dip, "Attach failed\n"); + + return (DDI_FAILURE); +} + +static int +sbbc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + int instance; + struct sbbcsoft *sbbcsoftp; + + SBBCTRACE(sbbc_detach, 'DETA', dip); + + instance = ddi_get_instance(dip); + + switch (cmd) { + case DDI_DETACH: + break; + + case DDI_SUSPEND: + if (!(sbbcsoftp = + ddi_get_soft_state(sbbcsoft_statep, instance))) { + cmn_err(CE_WARN, + "sbbc_detach: unable to get softstate %p", + (void *)sbbcsoftp); + return (DDI_FAILURE); + } + mutex_enter(&sbbcsoftp->umutex); + if (sbbcsoftp->suspended) { + mutex_exit(&sbbcsoftp->umutex); + return (DDI_FAILURE); + } + sbbcsoftp->suspended = 1; + mutex_exit(&sbbcsoftp->umutex); + return (DDI_SUCCESS); + + default: + return (DDI_FAILURE); + } + + if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) { + cmn_err(CE_WARN, "sbbc_detach: unable to get softstate %p", + (void *)sbbcsoftp); + return (DDI_FAILURE); + } + + ddi_remove_minor_node(dip, NULL); + + mutex_destroy(&sbbcsoftp->sbbc_intr_mutex); + mutex_destroy(&sbbcsoftp->umutex); + + sbbc_remove_reg_maps(sbbcsoftp); + kmem_free(sbbcsoftp->rangep, sbbcsoftp->range_len); + + ddi_soft_state_free(sbbcsoft_statep, instance); + + return (DDI_SUCCESS); + +} + + +/* + * Translate child's address into parents. + */ +static int +sbbc_busmap(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, + off_t off, off_t len, caddr_t *addrp) +{ + struct sbbcsoft *sbbcsoftp; + sbbc_child_regspec_t *child_rp, *child_regs; + pci_regspec_t pci_reg; + ddi_map_req_t p_map_request; + int rnumber, i, n; + int rval = DDI_SUCCESS; + int instance; + + SBBC_DBG4(SBBC_DBG_BUSMAP, dip, + "mapping child %s, type %llx, off %llx, len %llx\n", + ddi_driver_name(rdip), mp->map_type, off, len); + + SBBCTRACE(sbbc_busmap, 'BMAP', mp); + + /* + * Handle the mapping according to its type. + */ + instance = ddi_get_instance(dip); + if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) + return (DDI_FAILURE); + + switch (mp->map_type) { + case DDI_MT_REGSPEC: + + /* + * We assume the register specification is in sbbc format. + * We must convert it into a PCI format regspec and pass + * the request to our parent. + */ + child_rp = (sbbc_child_regspec_t *)mp->map_obj.rp; + break; + + case DDI_MT_RNUMBER: + + /* + * map_type 0 + * Get the "reg" property from the device node and convert + * it to our parent's format. + */ + rnumber = mp->map_obj.rnumber; + + /* get the requester's reg property */ + if (ddi_getlongprop(DDI_DEV_T_NONE, rdip, DDI_PROP_DONTPASS, + "reg", (caddr_t)&child_regs, &i) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "SBBC: couldn't get %s ranges property %d", + ddi_get_name(sbbcsoftp->dip), instance); + return (DDI_ME_RNUMBER_RANGE); + } + n = i / sizeof (sbbc_child_regspec_t); + + if (rnumber < 0 || rnumber >= n) { + kmem_free(child_regs, i); + return (DDI_ME_RNUMBER_RANGE); + } + child_rp = &child_regs[rnumber]; + break; + + default: + return (DDI_ME_INVAL); + + } + + /* Adjust our reg property with offset and length */ + child_rp->addr_low += off; + + if (len) + child_rp->size = len; + + /* + * Combine this reg prop. into our parents PCI address using the ranges + * property. + */ + rval = sbbc_apply_range(sbbcsoftp, rdip, child_rp, &pci_reg); + + if (mp->map_type == DDI_MT_RNUMBER) + kmem_free(child_regs, i); + + if (rval != DDI_SUCCESS) + return (rval); + + p_map_request = *mp; + p_map_request.map_type = DDI_MT_REGSPEC; + p_map_request.map_obj.rp = (struct regspec *)&pci_reg; + + /* Send it to PCI nexus to map into the PCI space */ + rval = ddi_map(dip, &p_map_request, 0, 0, addrp); + + return (rval); + +} + + +/* new intr_ops structure */ +static int +sbbc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + ddi_ispec_t *ip = (ddi_ispec_t *)hdlp->ih_private; + int ret = DDI_SUCCESS; + + switch (intr_op) { + case DDI_INTROP_GETCAP: + *(int *)result = 0; + break; + case DDI_INTROP_ALLOC: + *(int *)result = hdlp->ih_scratch1; + break; + case DDI_INTROP_FREE: + break; + case DDI_INTROP_GETPRI: + if (ip->is_pil == 0) { + ip->is_pil = 0x1; + + cmn_err(CE_WARN, "%s%d assigning default interrupt " + "level %d for device %s%d", ddi_get_name(dip), + ddi_get_instance(dip), ip->is_pil, + ddi_get_name(rdip), ddi_get_instance(rdip)); + } + + *(int *)result = ip->is_pil; + break; + case DDI_INTROP_ADDISR: + ret = sbbc_add_intr_impl(dip, rdip, intr_op, hdlp, result); + break; + case DDI_INTROP_REMISR: + ret = sbbc_remove_intr_impl(dip, rdip, intr_op, hdlp, result); + break; + case DDI_INTROP_ENABLE: + ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result); + break; + case DDI_INTROP_DISABLE: + ret = sbbc_update_intr_state(dip, rdip, intr_op, hdlp, &result); + break; + case DDI_INTROP_NINTRS: + case DDI_INTROP_NAVAIL: + *(int *)result = i_ddi_get_nintrs(rdip); + break; + case DDI_INTROP_SUPPORTED_TYPES: + /* PCI nexus driver supports only fixed interrupts */ + *(int *)result = i_ddi_get_nintrs(rdip) ? + DDI_INTR_TYPE_FIXED : 0; + break; + default: + ret = DDI_ENOTSUP; + break; + } + + return (ret); +} + + +static int +sbbc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + sbbcsoft_t *sbbcsoftp; + sbbc_child_intr_t *childintr; + int instance, i, rval = DDI_SUCCESS; + + SBBC_DBG2(SBBC_DBG_INTR, dip, + "add: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp); + + /* insert the sbbc isr wrapper instead */ + instance = ddi_get_instance(dip); + if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) + return (DDI_FAILURE); + + childintr = kmem_zalloc(sizeof (struct sbbc_child_intr), KM_SLEEP); + + childintr->name = ddi_get_name(rdip); + childintr->inum = hdlp->ih_inum; + childintr->intr_handler = hdlp->ih_cb_func; + childintr->arg1 = hdlp->ih_cb_arg1; + childintr->arg2 = hdlp->ih_cb_arg2; + childintr->status = SBBC_INTR_STATE_DISABLE; + + for (i = 0; i < MAX_SBBC_DEVICES; i++) { + if (sbbcsoftp->child_intr[i] == 0) { + sbbcsoftp->child_intr[i] = childintr; + break; + } + } + + DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, + (ddi_intr_handler_t *)sbbc_intr_wrapper, + (caddr_t)sbbcsoftp, NULL); + + if ((rval = i_ddi_intr_ops(dip, rdip, intr_op, + hdlp, result)) != DDI_SUCCESS) { + cmn_err(CE_WARN, "sbbc%d: failed to add intr for %s", + instance, ddi_get_name(rdip)); + kmem_free(childintr, sizeof (struct sbbc_child_intr)); + sbbcsoftp->child_intr[i] = NULL; + } + + /* + * Restore original interrupt handler + * and arguments in interrupt handle. + */ + DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, childintr->intr_handler, + childintr->arg1, childintr->arg2); + + return (rval); +} + +static int +sbbc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + sbbcsoft_t *sbbcsoftp; + sbbc_child_intr_t *childintr; + int instance, i, rval = DDI_SUCCESS; + + SBBC_DBG2(SBBC_DBG_INTR, dip, + "remove: rdip 0x%llx hdlp 0x%llx\n", rdip, hdlp); + + instance = ddi_get_instance(dip); + if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) + return (DDI_FAILURE); + + /* remove the sbbc isr wrapper instead */ + for (i = 0; i < MAX_SBBC_DEVICES; i++) { + if (sbbcsoftp->child_intr[i]) { + childintr = sbbcsoftp->child_intr[i]; + if (childintr->status == SBBC_INTR_STATE_DISABLE && + childintr->name == ddi_get_name(rdip)) { + /* put back child's inum */ + hdlp->ih_inum = childintr->inum; + break; + } + } + } + + if (i >= MAX_SBBC_DEVICES) { + cmn_err(CE_WARN, "sbbc%d:obound failed to remove intr for %s", + instance, ddi_get_name(rdip)); + return (DDI_FAILURE); + } + + if ((rval = i_ddi_intr_ops(dip, rdip, intr_op, + hdlp, result)) != DDI_SUCCESS) { + cmn_err(CE_WARN, "sbbc%d: failed to remove intr for %s", + instance, ddi_get_name(rdip)); + return (rval); + } + + kmem_free(childintr, sizeof (struct sbbc_child_intr)); + sbbcsoftp->child_intr[i] = NULL; + + return (rval); +} + + +static int +sbbc_update_intr_state(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, + ddi_intr_handle_impl_t *hdlp, void *result) +{ + sbbcsoft_t *sbbcsoftp; + sbbc_child_intr_t *childintr; + int instance, i; + int ret = DDI_SUCCESS; + + SBBC_DBG2(SBBC_DBG_INTR, dip, "sbbc_update_intr_state: " + "rdip 0x%llx hdlp 0x%llx state 0x%x\n", rdip, hdlp); + + instance = ddi_get_instance(dip); + if (!(sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, instance))) + return (DDI_FAILURE); + + for (i = 0; i < MAX_SBBC_DEVICES; i++) { + if (sbbcsoftp->child_intr[i]) { + childintr = sbbcsoftp->child_intr[i]; + if (childintr->name == ddi_get_name(rdip)) + break; + } + } + + if (i >= MAX_SBBC_DEVICES) { + cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s", + instance, ddi_get_name(rdip)); + return (DDI_FAILURE); + } + + if ((ret = i_ddi_intr_ops(dip, rdip, intr_op, + hdlp, result)) != DDI_SUCCESS) { + cmn_err(CE_WARN, "sbbc%d: failed to update intr state for %s", + instance, ddi_get_name(rdip)); + return (ret); + } + + /* Update the interrupt state */ + childintr->status = (intr_op == DDI_INTROP_ENABLE) ? + SBBC_INTR_STATE_ENABLE : SBBC_INTR_STATE_DISABLE; + + return (ret); +} + + +/* + * This entry point is called before a child's probe or attach is called. + * The arg pointer points to child's dev_info_t structure. + */ +static int +sbbc_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, + void *arg, void *result) +{ + sbbc_child_regspec_t *child_rp; + int i, n; + + SBBC_DBG3(SBBC_DBG_CTLOPS, dip, + "Initializing %s, arg %x, op %x\n", + ddi_driver_name(rdip), arg, op); + + SBBCTRACE(sbbc_ctlops, 'CTLO', arg); + + switch (op) { + case DDI_CTLOPS_INITCHILD: { + return (sbbc_initchild(dip, rdip, (dev_info_t *)arg)); + } + + case DDI_CTLOPS_UNINITCHILD: { + return (sbbc_uninitchild(rdip, (dev_info_t *)arg)); + } + + case DDI_CTLOPS_REPORTDEV: + + cmn_err(CE_CONT, "?%s%d at %s%d: offset %s\n", + ddi_driver_name(rdip), ddi_get_instance(rdip), + ddi_driver_name(dip), ddi_get_instance(dip), + ddi_get_name_addr(rdip)); + return (DDI_SUCCESS); + + case DDI_CTLOPS_REGSIZE: + + if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) { + return (DDI_FAILURE); + } + n = i / sizeof (sbbc_child_regspec_t); + if (*(int *)arg < 0 || *(int *)arg >= n) { + kmem_free(child_rp, i); + return (DDI_FAILURE); + } + *((off_t *)result) = child_rp[*(int *)arg].size; + kmem_free(child_rp, i); + return (DDI_SUCCESS); + + case DDI_CTLOPS_NREGS: + + if (getprop(rdip, "reg", &child_rp, &i) != DDI_SUCCESS) { + return (DDI_FAILURE); + } + *((uint_t *)result) = i / sizeof (sbbc_child_regspec_t); + kmem_free(child_rp, i); + return (DDI_SUCCESS); + } + + /* + * Now pass the request up to our parent. + */ + SBBC_DBG0(SBBC_DBG_CTLOPS, dip, "Calling ddi_ctlops\n"); + + return (ddi_ctlops(dip, rdip, op, arg, result)); +} + + +/* + * The following routine uses ranges property, that was read earlier, and + * takes child's reg property, and computes the complete address and size + * for the PCI parent to map. + */ +static int +sbbc_apply_range(struct sbbcsoft *sbbc_p, dev_info_t *rdip, + sbbc_child_regspec_t *child_rp, pci_regspec_t *rp) +{ + int b; + int rval = DDI_SUCCESS; + struct sbbc_pci_rangespec *rangep = sbbc_p->rangep; + int nrange = sbbc_p->range_cnt; + + SBBC_DBG4(SBBC_DBG_MAPRANGES, rdip, + "Applying ranges for %s, rangep %llx, child_rp %llx, range %x\n", + ddi_driver_name(rdip), sbbc_p->rangep, child_rp, nrange); + + SBBCTRACE(sbbc_apply_range, 'APPL', sbbc_p); + + for (b = 0; b < nrange; ++b, ++rangep) { + + /* Make sure the correct range is being mapped */ + if (child_rp->addr_hi == rangep->sbbc_phys_hi) + /* See if we fit in this range */ + if ((child_rp->addr_low >= + rangep->sbbc_phys_low) && + ((child_rp->addr_low + child_rp->size - 1) + <= (rangep->sbbc_phys_low + + rangep->rng_size - 1))) { + uint_t addr_offset = child_rp->addr_low - + rangep->sbbc_phys_low; + /* + * Use the range entry to translate + * the SBBC physical address into the + * parents PCI space. + */ + rp->pci_phys_hi = + rangep->pci_phys_hi; + rp->pci_phys_mid = rangep->pci_phys_mid; + rp->pci_phys_low = + rangep->pci_phys_low + addr_offset; + rp->pci_size_hi = 0; + rp->pci_size_low = + min(child_rp->size, (rangep->rng_size - + addr_offset)); + + break; + } + } + + if (b == nrange) { + cmn_err(CE_WARN, "out_of_range %s", ddi_get_name(rdip)); + return (DDI_ME_REGSPEC_RANGE); + } + + return (rval); +} + + +/* + * The following routine reads sbbc's ranges property from OBP and sets up + * its soft structure with it. + */ +static int +sbbc_get_ranges(struct sbbcsoft *sbbcsoftp) +{ + struct sbbc_pci_rangespec *rangep; + int range_len, nrange; + + if (ddi_getlongprop(DDI_DEV_T_NONE, sbbcsoftp->dip, DDI_PROP_DONTPASS, + "ranges", (caddr_t)&rangep, &range_len) != DDI_SUCCESS) { + cmn_err(CE_WARN, "SBBC: couldn't get %s ranges property %d", + ddi_get_name(sbbcsoftp->dip), sbbcsoftp->instance); + return (DDI_ME_REGSPEC_RANGE); + } + + nrange = range_len / sizeof (struct sbbc_pci_rangespec); + + if (!nrange) { + kmem_free(rangep, range_len); + return (DDI_FAILURE); + } + + /* setup the soft structure with ranges info. */ + sbbcsoftp->rangep = rangep; + sbbcsoftp->range_cnt = nrange; + sbbcsoftp->range_len = range_len; + + return (DDI_SUCCESS); +} + + +/* + * Configure the SBBC for PCI + */ +static int +sbbc_config4pci(struct sbbcsoft *sbbcsoftp) +{ + ddi_acc_handle_t conf_handle; + uint16_t comm, vendid, devid, stat; + uint8_t revid; + +#ifdef DEBUG + if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { + cmn_err(CE_CONT, + "sbbc_config4pci: sbbcsoftp %p\n", (void *)sbbcsoftp); + } +#endif + if (pci_config_setup(sbbcsoftp->dip, &conf_handle) != DDI_SUCCESS) + return (1); + + vendid = pci_config_get16(conf_handle, PCI_CONF_VENID); + devid = pci_config_get16(conf_handle, PCI_CONF_DEVID); + comm = pci_config_get16(conf_handle, PCI_CONF_COMM); + stat = pci_config_get16(conf_handle, PCI_CONF_STAT); + revid = pci_config_get8(conf_handle, PCI_CONF_REVID); + +#ifdef DEBUG + if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { + cmn_err(CE_CONT, + "SBBC vendid %x, devid %x, comm %x, stat %x, revid %x\n", + vendid, devid, comm, stat, revid); + } +#endif + comm = (PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_SERR_ENABLE | + PCI_COMM_PARITY_DETECT); + + pci_config_put16(conf_handle, PCI_CONF_COMM, comm); + + comm = pci_config_get16(conf_handle, PCI_CONF_COMM); + +#ifdef DEBUG + if (sbbc_dbg_flags & SBBC_DBG_PCICONF) { + cmn_err(CE_CONT, "comm %x\n", comm); + } +#endif + pci_config_teardown(&conf_handle); + + return (0); +} + + +/* ARGSUSED0 */ +int +sbbc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + dev_t dev = (dev_t)arg; + struct sbbcsoft *sbbcsoftp; + int instance, ret; + + instance = getminor(dev); + + SBBCTRACE(sbbc_getinfo, 'GINF', instance); + + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + sbbcsoftp = (struct sbbcsoft *) + ddi_get_soft_state(sbbcsoft_statep, instance); + *result = sbbcsoftp->dip; + ret = DDI_SUCCESS; + break; + case DDI_INFO_DEVT2INSTANCE: + *result = (void *)instance; + ret = DDI_SUCCESS; + break; + default: + ret = DDI_FAILURE; + break; + } + + return (ret); +} + +/*ARGSUSED1*/ +static int +sbbc_open(dev_t *dev, int flag, int otype, cred_t *credp) +{ + struct sbbcsoft *sbbcsoftp; + int instance; + + /* check privilege of caller process */ + if (drv_priv(credp)) { + return (EPERM); + } + + instance = getminor(*dev); + if (instance < 0) + return (ENXIO); + sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep, + instance); + SBBCTRACE(sbbc_open, 'OPEN', sbbcsoftp); + + if (sbbcsoftp == NULL) + return (ENXIO); + + mutex_enter(&sbbcsoftp->umutex); + + /* check for exclusive access */ + if ((sbbcsoftp->oflag == TRUE)) { + mutex_exit(&sbbcsoftp->umutex); + return (EBUSY); + } + sbbcsoftp->oflag = TRUE; + + mutex_exit(&sbbcsoftp->umutex); + + return (0); +} + +/*ARGSUSED1*/ +static int +sbbc_close(dev_t dev, int flag, int otype, cred_t *credp) +{ + struct sbbcsoft *sbbcsoftp; + int instance; + + instance = getminor(dev); + if (instance < 0) + return (ENXIO); + sbbcsoftp = (struct sbbcsoft *)ddi_get_soft_state(sbbcsoft_statep, + instance); + /* wait till all output activity has ceased */ + + mutex_enter(&sbbcsoftp->umutex); + + SBBCTRACE(sbbc_close, 'CLOS', sbbcsoftp); + + sbbcsoftp->oflag = FALSE; + + mutex_exit(&sbbcsoftp->umutex); + + return (0); +} + +/*ARGSUSED2*/ +static int +sbbc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + struct sbbcsoft *sbbcsoftp; + + SBBCTRACE(sbbc_ioctl, 'IOCT', arg); + + sbbcsoftp = ddi_get_soft_state(sbbcsoft_statep, getminor(dev)); + + if (sbbcsoftp == NULL) { + return (ENXIO); + } + + switch (cmd) { + case SBBC_SBBCREG_WR: + { + struct ssc_sbbc_regio sbbcregs; + uint64_t offset; + + if (arg == NULL) { + return (ENXIO); + } + + if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs, + sizeof (struct ssc_sbbc_regio), mode)) { + cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p", + (void *)arg); + return (EFAULT); + } + + /* + * Bug #4287186: SBBC driver on cp1500 doesn't check length for + * reads or writes + * Note that I've also added a check to make sure the offset is + * valid, since misaligned (i.e. not on 16-byte boundary) + * accesses or accesses to "Reserved" register offsets are + * treated as unmapped by the SBBC. + */ + if ((sbbcregs.len != 4) || + !sbbc_offset_valid(sbbcregs.offset)) { + return (EINVAL); + } + + offset = (uint64_t)&sbbcsoftp->pci_sbbc_map->sbbc_internal_regs; + offset += sbbcregs.offset; + ddi_put32(sbbcsoftp->pci_sbbc_map_handle, (uint32_t *)offset, + sbbcregs.value); + } + break; + case SBBC_SBBCREG_RD: + { + struct ssc_sbbc_regio sbbcregs; + uint64_t offset; + + if (arg == NULL) { + return (ENXIO); + } + + if (ddi_copyin((caddr_t)arg, (caddr_t)&sbbcregs, + sizeof (struct ssc_sbbc_regio), mode)) { + cmn_err(CE_WARN, "sbbc_ioctl: copyin failed arg %p", + (void *)arg); + return (EFAULT); + } + + /* + * Bug #4287186: SBBC driver on cp1500 doesn't check length for + * reads or writes + * Note that I've also added a check to make sure the offset is + * valid, since misaligned (i.e. not on 16-byte boundary) + * accesses or accesses to "Reserved" register offsets are + * treated as unmapped by the SBBC. + */ + if ((sbbcregs.len != 4) || + !sbbc_offset_valid(sbbcregs.offset)) { + return (EINVAL); + } + + offset = (uint64_t)&sbbcsoftp->pci_sbbc_map->sbbc_internal_regs; + offset += sbbcregs.offset; + + sbbcregs.value = ddi_get32(sbbcsoftp->pci_sbbc_map_handle, + (uint32_t *)offset); + + if (ddi_copyout((caddr_t)&sbbcregs.value, + &((struct ssc_sbbc_regio *)arg)->value, sbbcregs.len, mode)) { + cmn_err(CE_WARN, "sbbc_ioctl:copyout failed arg %p", + (void *)arg); + return (EFAULT); + } + } + break; + default: + cmn_err(CE_WARN, "sbbc_ioctl:Illegal command 0x%08x", cmd); + return (ENOTTY); + } + + return (DDI_SUCCESS); +} + +static void +sbbc_remove_reg_maps(struct sbbcsoft *sbbcsoftp) +{ + SBBCTRACE(sbbc_remove_reg_maps, 'RMAP', sbbcsoftp); + if (sbbcsoftp->pci_sbbc_map_handle) + ddi_regs_map_free(&sbbcsoftp->pci_sbbc_map_handle); + + /* need to unmap other registers as well */ +} + + +static int +sbbc_init(struct sbbcsoft *sbbcsoftp) +{ + + /* + * setup the regs. and mask all the interrupts + * till we are ready. + */ + ddi_put32(sbbcsoftp->pci_sbbc_map_handle, + &sbbcsoftp->pci_sbbc_map->sbbc_internal_regs.sys_intr_enable, + 0x00000000); + + return (1); +} + +/* + * The following routine is a generic routine to initialize any child of + * sbbc nexus driver information into parent private data structure. + */ +/* ARGSUSED0 */ +static int +sbbc_initchild(dev_info_t *dip, dev_info_t *rdip, dev_info_t *child) +{ + sbbc_child_regspec_t *child_rp; + int reglen, slot; + char name[10]; + + SBBC_DBG1(SBBC_DBG_INITCHILD, dip, "Initializing %s\n", + ddi_driver_name(rdip)); + + /* + * Initialize a child + * Set the address portion of the node name based on the + * address/offset. + */ + if (ddi_getlongprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, + "reg", (caddr_t)&child_rp, ®len) != DDI_SUCCESS) { + if (strcmp(ddi_node_name(child), "hotplug-controller") == 0) { + slot = 1; + (void) sprintf(name, "%x", slot); + ddi_set_name_addr(child, name); + return (DDI_SUCCESS); + } + return (DDI_FAILURE); + } + + SBBC_DBG3(SBBC_DBG_INITCHILD, dip, "hi 0x%x, low 0x%x, size 0x%x\n", + child_rp->addr_hi, child_rp->addr_low, child_rp->size); + + (void) sprintf(name, "%x,%x", child_rp->addr_hi, child_rp->addr_low); + + /* + * set child's addresses from the reg property into parent private + * data structure. + */ + ddi_set_name_addr(child, name); + kmem_free(child_rp, reglen); + + ddi_set_parent_data(child, NULL); + + return (DDI_SUCCESS); +} + + +/* ARGSUSED0 */ +static int +sbbc_uninitchild(dev_info_t *rdip, dev_info_t *child) +{ + + SBBC_DBG1(SBBC_DBG_UNINITCHILD, rdip, "Uninitializing %s\n", + ddi_driver_name(rdip)); + + ddi_set_name_addr(child, NULL); + ddi_remove_minor_node(child, NULL); + impl_rem_dev_props(child); + + return (DDI_SUCCESS); + +} + + +/* + * The following routine is an interrupt service routine that is used + * as a wrapper to all the children requiring interrupt services. + */ +static uint_t +sbbc_intr_wrapper(caddr_t arg) +{ + + struct sbbcsoft *sbbcsoftp = (struct sbbcsoft *)arg; + int i, rval; + + SBBC_DBG1(SBBC_DBG_INTR, sbbcsoftp->dip, "Isr arg 0x%llx\n", arg); + + mutex_enter(&sbbcsoftp->sbbc_intr_mutex); + + for (i = 0; i < MAX_SBBC_DEVICES; i++) { + /* + * Check the interrupt status reg. to determine the cause. + */ + /* + * Check the error status reg. to determine the cause. + */ + if (sbbcsoftp->child_intr[i] && + sbbcsoftp->child_intr[i]->status == + SBBC_INTR_STATE_ENABLE) { + /* + * Dispatch the children interrupt service routines and + * look for someone to claim. + */ + rval = sbbcsoftp->child_intr[i]->intr_handler( + sbbcsoftp->child_intr[i]->arg1, + sbbcsoftp->child_intr[i]->arg2); + + if (rval == DDI_INTR_CLAIMED) { + mutex_exit(&sbbcsoftp->sbbc_intr_mutex); + return (rval); + } + } + } + + mutex_exit(&sbbcsoftp->sbbc_intr_mutex); + + /* for now do not claim since we know its not enabled */ + return (DDI_INTR_UNCLAIMED); +} + + +/* + * This function checks an SBBC register offset to make sure that it is properly + * aligned (i.e. on a 16-byte boundary) and that it corresponds to an accessible + * register. Since the SBBC treates accesses to unaligned or reserved addresses + * as unmapped, failing to check for these would leave a loophole that could be + * used to crash the system. + */ +static int +sbbc_offset_valid(uint32_t offset) { + /* + * Check for proper alignment first. + */ + if ((offset % 16) != 0) { + return (0); + } + + /* + * Now start checking for the various reserved ranges. + * While sticking a bunch of constants in the code (rather than + * #define'd values) is usually best avoided, it would probably + * do more harm than good here. These values were taken from the + * Serengeti Architecture Programmer's Reference Manual dated + * August 10, 1999, pages 2-99 through 2-103. While there are + * various "clever" ways this check could be performed that would + * be slightly more efficient, arranging the code in this fashion + * should maximize maintainability. + */ + if (((offset >= 0x001a0) && (offset <= 0x001ff)) || + ((offset >= 0x002a0) && (offset <= 0x002ff)) || + ((offset >= 0x00350) && (offset <= 0x003ff)) || + ((offset >= 0x00500) && (offset <= 0x00fff)) || + ((offset >= 0x01160) && (offset <= 0x011ff)) || + ((offset >= 0x01210) && (offset <= 0x017ff)) || + ((offset >= 0x01810) && (offset <= 0x01fff)) || + ((offset >= 0x02030) && (offset <= 0x022ff)) || + ((offset >= 0x02340) && (offset <= 0x03fff)) || + ((offset >= 0x04030) && (offset <= 0x05fff)) || + ((offset >= 0x060a0) && (offset <= 0x060ff)) || + (offset == 0x06120) || + ((offset >= 0x06190) && (offset <= 0x061ff)) || + ((offset >= 0x06230) && (offset <= 0x062f0)) || + (offset > 0x06320)) { + return (0); + } + + return (1); +} + +#ifdef DEBUG +void +sbbc_dbg(uint32_t flag, dev_info_t *dip, char *fmt, + uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) +{ + char *s = NULL; + + if (sbbc_dbg_flags && ((sbbc_dbg_flags & flag) == flag)) { + switch (flag) { + case SBBC_DBG_ATTACH: + s = "attach"; + break; + case SBBC_DBG_DETACH: + s = "detach"; + break; + case SBBC_DBG_CTLOPS: + s = "ctlops"; + break; + case SBBC_DBG_INITCHILD: + s = "initchild"; + break; + case SBBC_DBG_UNINITCHILD: + s = "uninitchild"; + break; + case SBBC_DBG_BUSMAP: + s = "busmap"; + break; + case SBBC_DBG_INTR: + s = "intr"; + break; + case SBBC_DBG_INTROPS: + s = "intr_ops"; + break; + case SBBC_DBG_PCICONF: + s = "pciconfig"; + break; + case SBBC_DBG_MAPRANGES: + s = "mapranges"; + break; + case SBBC_DBG_PROPERTIES: + s = "properties"; + break; + case SBBC_DBG_OPEN: + s = "open"; + break; + case SBBC_DBG_CLOSE: + s = "close"; + break; + case SBBC_DBG_IOCTL: + s = "ioctl"; + break; + default: + s = "Unknown debug flag"; + break; + } + + cmn_err(CE_CONT, "%s_%s(%d): ", ddi_driver_name(dip), s, + ddi_get_instance(dip)); + cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); + } +} + +#endif /* DEBUG */ |
