sgsbbc.c revision 03831d35f7499c87d51205817c93e9a8d42c4bae
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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"
/*
* PCI SBBC Device Driver that provides interfaces into
* EPLD and IO-SRAM
*
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/kmem.h>
#include <sys/sunndi.h>
#include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
#include <sys/modctl.h> /* for modldrv */
#include <sys/promif.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/serengeti.h>
#include <sys/sgsbbc_priv.h>
#include <sys/sgsbbc_iosram_priv.h>
#include <sys/sgsbbc_mailbox_priv.h>
#ifdef DEBUG
/* debug flag */
uint_t sgsbbc_debug = 0;
#endif /* DEBUG */
/* driver entry point fn definitions */
static int sbbc_attach(dev_info_t *, ddi_attach_cmd_t);
static int sbbc_detach(dev_info_t *, ddi_detach_cmd_t);
/*
* SBBC soft state hook
*/
static void *sbbcp;
/*
* Chosen IOSRAM
*/
struct chosen_iosram *master_iosram = NULL;
/*
* define new iosram's sbbc and liked list of sbbc.
*/
struct sbbc_softstate *sgsbbc_instances = NULL;
/*
* At attach time, check if the device is the 'chosen' node
* if it is, set up the IOSRAM Solaris<->SC Comm tunnel
* Its like 'Highlander' - there can be only one !
*/
static int master_chosen = FALSE;
kmutex_t chosen_lock;
/*
* Local variable to save intr_in_enabled when the driver is suspended
*/
static uint32_t intr_in_enabled;
/*
* Local declarations
*/
static void softsp_init(sbbc_softstate_t *, dev_info_t *);
static void sbbc_chosen_init(sbbc_softstate_t *);
static void sbbc_add_instance(sbbc_softstate_t *);
static void sbbc_remove_instance(sbbc_softstate_t *);
static int sbbc_find_dip(dev_info_t *, void *);
static void sbbc_unmap_regs(sbbc_softstate_t *);
/*
* ops stuff.
*/
static struct cb_ops sbbc_cb_ops = {
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
D_NEW | D_MP /* cb_flag */
};
/*
* Declare ops vectors for auto configuration.
*/
struct dev_ops sbbc_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
ddi_getinfo_1to1, /* 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 */
(struct bus_ops *)NULL, /* devo_bus_ops */
nulldev /* devo_power */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops, /* type of module - driver */
"PCI SBBC %I%",
&sbbc_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&sbbcp,
sizeof (sbbc_softstate_t), 1)) != 0)
return (error);
if ((error = mod_install(&modlinkage)) != 0) {
ddi_soft_state_fini(&sbbcp);
return (error);
}
/*
* Initialise the global 'chosen' IOSRAM mutex
*/
mutex_init(&chosen_lock, NULL, MUTEX_DEFAULT, NULL);
/*
* Initialise the iosram driver
*/
iosram_init();
/*
* Initialize the mailbox
*/
sbbc_mbox_init();
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) == 0)
ddi_soft_state_fini(&sbbcp);
master_chosen = FALSE;
mutex_destroy(&chosen_lock);
/*
* remove the mailbox
*/
sbbc_mbox_fini();
/*
* remove the iosram driver
*/
iosram_fini();
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int
sbbc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
int instance;
sbbc_softstate_t *softsp;
uint32_t *pci_intr_enable_reg;
int len;
#ifdef DEBUG
char name[8];
#endif /* DEBUG */
instance = ddi_get_instance(devi);
switch (cmd) {
case DDI_ATTACH:
if (ddi_soft_state_zalloc(sbbcp, instance) != 0)
return (DDI_FAILURE);
softsp = ddi_get_soft_state(sbbcp, instance);
softsp->sbbc_instance = instance;
/*
* Set the dip in the soft state
* And get interrupt cookies and initialize the
* per instance mutex.
*/
softsp_init(softsp, devi);
/*
* Verify that an 'interrupts' property exists for
* this device. If not, this instance will be ignored.
*/
if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "interrupts",
&len) != DDI_PROP_SUCCESS) {
SBBC_ERR1(CE_WARN, "No 'interrupts' property for the "
"SBBC instance %d\n", instance);
return (DDI_FAILURE);
}
/*
* Add this instance to the sbbc chosen iosram list
* so that it can be used for tunnel switch.
*/
mutex_enter(&chosen_lock);
softsp->sbbc_state = SBBC_STATE_INIT;
sbbc_add_instance(softsp);
/*
* If this is the chosen IOSRAM and there is no master IOSRAM
* yet, then let's set this instance as the master.
* if there is a master alreay due to the previous tunnel switch
* then keep as is even though this is the chosen.
*/
if (sgsbbc_iosram_is_chosen(softsp)) {
ASSERT(master_iosram);
softsp->iosram = master_iosram;
master_iosram->sgsbbc = softsp;
/* Do 'chosen' init only */
sbbc_chosen_init(softsp);
}
mutex_exit(&chosen_lock);
#ifdef DEBUG
(void) sprintf(name, "sbbc%d", instance);
if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
NULL, NULL) == DDI_FAILURE) {
mutex_destroy(&softsp->sbbc_lock);
ddi_remove_minor_node(devi, NULL);
ddi_soft_state_free(sbbcp, instance);
return (DDI_FAILURE);
}
#endif /* DEBUG */
ddi_report_dev(devi);
return (DDI_SUCCESS);
case DDI_RESUME:
if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
return (DDI_FAILURE);
mutex_enter(&softsp->sbbc_lock);
if ((softsp->suspended == TRUE) && (softsp->chosen == TRUE)) {
/*
* Enable Interrupts now, turn on both INT#A lines
*/
pci_intr_enable_reg = (uint32_t *)
((char *)softsp->sbbc_regs +
SBBC_PCI_INT_ENABLE);
ddi_put32(softsp->sbbc_reg_handle1,
pci_intr_enable_reg,
(uint32_t)SBBC_PCI_ENABLE_INT_A);
/*
* Reset intr_in_enabled to the original value
* so the SC can send us interrupt.
*/
if (iosram_write(SBBC_SC_INTR_ENABLED_KEY,
0, (caddr_t)&intr_in_enabled,
sizeof (intr_in_enabled))) {
mutex_exit(&softsp->sbbc_lock);
return (DDI_FAILURE);
}
}
softsp->suspended = FALSE;
mutex_exit(&softsp->sbbc_lock);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
sbbc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
sbbc_softstate_t *softsp;
int instance;
uint32_t *pci_intr_enable_reg;
int rc = DDI_SUCCESS;
instance = ddi_get_instance(devi);
if (!(softsp = ddi_get_soft_state(sbbcp, instance)))
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
mutex_enter(&chosen_lock);
softsp->sbbc_state |= SBBC_STATE_DETACH;
mutex_exit(&chosen_lock);
/* only tunnel switch the instance with iosram chosen */
if (softsp->chosen == TRUE) {
if (sgsbbc_iosram_switchfrom(softsp) == DDI_FAILURE) {
SBBC_ERR(CE_WARN, "Cannot unconfigure: "
"tunnel switch failed\n");
return (DDI_FAILURE);
}
}
/* Adjust linked list */
mutex_enter(&chosen_lock);
sbbc_remove_instance(softsp);
mutex_exit(&chosen_lock);
sbbc_unmap_regs(softsp);
mutex_destroy(&softsp->sbbc_lock);
ddi_soft_state_free(sbbcp, instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
mutex_enter(&softsp->sbbc_lock);
if ((softsp->suspended == FALSE) && (softsp->chosen == TRUE)) {
uint32_t tmp_intr_enabled = 0;
/*
* Disable Interrupts now, turn OFF both INT#A lines
*/
pci_intr_enable_reg = (uint32_t *)
((char *)softsp->sbbc_regs +
SBBC_PCI_INT_ENABLE);
ddi_put32(softsp->sbbc_reg_handle1,
pci_intr_enable_reg, 0);
/*
* Set intr_in_enabled to 0 so the SC won't send
* us interrupt.
*/
rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY,
0, (caddr_t)&intr_in_enabled,
sizeof (intr_in_enabled));
if (rc) {
mutex_exit(&softsp->sbbc_lock);
return (DDI_FAILURE);
}
rc = iosram_write(SBBC_SC_INTR_ENABLED_KEY,
0, (caddr_t)&tmp_intr_enabled,
sizeof (tmp_intr_enabled));
if (rc) {
mutex_exit(&softsp->sbbc_lock);
return (DDI_FAILURE);
}
}
softsp->suspended = TRUE;
mutex_exit(&softsp->sbbc_lock);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static void
softsp_init(sbbc_softstate_t *softsp, dev_info_t *devi)
{
softsp->dip = devi;
/*
* XXXX
* ddi_get_iblock_cookie() here because we need
* to initialise the mutex regardless of whether
* or not this SBBC will eventually
* register an interrupt handler
*/
(void) ddi_get_iblock_cookie(devi, 0, &softsp->iblock);
mutex_init(&softsp->sbbc_lock, NULL, MUTEX_DRIVER,
(void *)softsp->iblock);
softsp->suspended = FALSE;
softsp->chosen = FALSE;
}
static int
sbbc_find_dip(dev_info_t *dip, void *arg)
{
char *node_name;
sbbc_find_dip_t *dip_struct = (sbbc_find_dip_t *)arg;
char status[OBP_MAXPROPNAME];
/*
* Need to find a node named "bootbus-controller" that is neither
* disabled nor failed. If a node is not ok, there will be an
* OBP status property. Therefore, we will look for a node
* without the status property.
*/
node_name = ddi_node_name(dip);
if (strcmp(node_name, "bootbus-controller") == 0 && DDI_CF2(dip) &&
(prom_getprop(ddi_get_nodeid(dip),
"status", (caddr_t)status) == -1) &&
(prom_getprop(ddi_get_nodeid(ddi_get_parent(dip)),
"status", (caddr_t)status) == -1)) {
if (dip != dip_struct->cur_dip) {
dip_struct->new_dip = (void *)dip;
return (DDI_WALK_TERMINATE);
}
}
return (DDI_WALK_CONTINUE);
}
/*
* SBBC Interrupt Handler
*
* Check the SBBC Port Interrupt Status
* register to verify that its our interrupt.
* If yes, clear the register.
*
* Then read the 'interrupt reason' field from SRAM,
* this triggers the appropriate soft_intr handler
*/
uint_t
sbbc_intr_handler(caddr_t arg)
{
sbbc_softstate_t *softsp = (sbbc_softstate_t *)arg;
uint32_t *port_int_reg;
volatile uint32_t port_int_status;
volatile uint32_t intr_reason;
uint32_t intr_enabled;
sbbc_intrs_t *intr;
int i, intr_mask;
struct tunnel_key tunnel_key;
ddi_acc_handle_t intr_in_handle;
uint32_t *intr_in_reason;
if (softsp == (sbbc_softstate_t *)NULL) {
return (DDI_INTR_UNCLAIMED);
}
mutex_enter(&softsp->sbbc_lock);
if (softsp->port_int_regs == NULL) {
mutex_exit(&softsp->sbbc_lock);
return (DDI_INTR_UNCLAIMED);
}
/*
* Normally if port_int_status is 0, we assume it is not
* our interrupt. However, we don't want to miss the
* ones that come in during tunnel switch. Therefore,
* we always check the interrupt reason bits in IOSRAM
* to be sure.
*/
port_int_reg = softsp->port_int_regs;
port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
/*
* Generate a softint for each interrupt
* bit set in the intr_in_reason field in SRAM
* that has a corresponding bit set in the
* intr_in_enabled field in SRAM
*/
if (iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
(caddr_t)&intr_enabled, sizeof (intr_enabled))) {
goto intr_handler_exit;
}
tunnel_key = master_iosram->tunnel->tunnel_keys[SBBC_SC_INTR_KEY];
intr_in_reason = (uint32_t *)tunnel_key.base;
intr_in_handle = tunnel_key.reg_handle;
intr_reason = ddi_get32(intr_in_handle, intr_in_reason);
SGSBBC_DBG_INTR(CE_CONT, "intr_reason = %x\n", intr_reason);
intr_reason &= intr_enabled;
for (i = 0; i < SBBC_MAX_INTRS; i++) {
intr_mask = (1 << i);
if (intr_reason & intr_mask) {
intr = &softsp->intr_hdlrs[i];
if ((intr != NULL) &&
(intr->sbbc_intr_id != 0)) {
/*
* XXXX
* The model we agree with a handler
* is that they run until they have
* exhausted all work. To avoid
* triggering them again, they pass
* a state flag and lock when registering.
* We check the flag, if they are idle,
* we trigger.
* The interrupt handler should so
* intr_func()
* mutex_enter(sbbc_intr_lock);
* sbbc_intr_state = RUNNING;
* mutex_exit(sbbc_intr_lock);
* ..........
* ..........
* ..........
* mutex_enter(sbbc_intr_lock);
* sbbc_intr_state = IDLE;
* mutex_exit(sbbc_intr_lock);
*
* XXXX
*/
mutex_enter(intr->sbbc_intr_lock);
if (*(intr->sbbc_intr_state) ==
SBBC_INTR_IDLE) {
mutex_exit(intr->sbbc_intr_lock);
ddi_trigger_softintr(
intr->sbbc_intr_id);
} else {
/*
* The handler is running
*/
mutex_exit(intr->sbbc_intr_lock);
}
intr_reason &= ~intr_mask;
/*
* Clear the corresponding reason bit in SRAM
*
* Since there is no interlocking between
* Solaris and the SC when writing to SRAM,
* it is possible for the SC to set another
* bit in the interrupt reason field while
* we are handling the current interrupt.
* To minimize the window in which an
* additional bit can be set, reading
* and writing the interrupt reason
* in SRAM must be as close as possible.
*/
ddi_put32(intr_in_handle, intr_in_reason,
ddi_get32(intr_in_handle,
intr_in_reason) & ~intr_mask);
}
}
if (intr_reason == 0) /* No more interrupts to be processed */
break;
}
/*
* Clear the Interrupt Status Register (RW1C)
*/
ddi_put32(softsp->sbbc_reg_handle1, port_int_reg, port_int_status);
port_int_status = ddi_get32(softsp->sbbc_reg_handle1, port_int_reg);
intr_handler_exit:
mutex_exit(&softsp->sbbc_lock);
return (DDI_INTR_CLAIMED);
}
/*
* If we don't already have a master SBBC selected,
* get the <sbbc> property from the /chosen node. If
* the pathname matches, this is the master SBBC and
* we set up the console/TOD SRAM mapping here.
*/
static void
sbbc_chosen_init(sbbc_softstate_t *softsp)
{
char master_sbbc[MAXNAMELEN];
char pn[MAXNAMELEN];
int nodeid, len;
pnode_t dnode;
if (master_chosen != FALSE) {
/*
* We've got one already
*/
return;
}
/*
* Get /chosen node info. prom interface will handle errors.
*/
dnode = prom_chosennode();
/*
* Look for the "iosram" property on the chosen node with a prom
* interface as ddi_find_devinfo() couldn't be used (calls
* ddi_walk_devs() that creates one extra lock on the device tree).
*/
if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
/*
* No I/O Board SBBC set up as console, what to do ?
*/
SBBC_ERR(CE_PANIC, "No SBBC found for Console/TOD \n");
}
if (prom_getprop(dnode, IOSRAM_TOC_PROP,
(caddr_t)&softsp->sram_toc) <= 0) {
/*
* SRAM TOC Offset defaults to 0
*/
SBBC_ERR(CE_WARN, "No SBBC TOC Offset found\n");
softsp->sram_toc = 0;
}
/*
* get the full OBP pathname of this node
*/
if (prom_phandle_to_path((phandle_t)nodeid, master_sbbc,
sizeof (master_sbbc)) < 0) {
SBBC_ERR1(CE_PANIC, "prom_phandle_to_path(%d) failed\n",
nodeid);
}
SGSBBC_DBG_ALL("chosen pathname : %s\n", master_sbbc);
SGSBBC_DBG_ALL("device pathname : %s\n", ddi_pathname(softsp->dip, pn));
if (strcmp(master_sbbc, ddi_pathname(softsp->dip, pn)) == 0) {
/*
* map in the SBBC regs
*/
if (sbbc_map_regs(softsp) != DDI_SUCCESS) {
SBBC_ERR(CE_PANIC, "Can't map the SBBC regs \n");
}
/*
* Only the 'chosen' node is used for iosram_read()/_write()
* Must initialise the tunnel before the console/tod
*
*/
if (iosram_tunnel_init(softsp) == DDI_FAILURE) {
SBBC_ERR(CE_PANIC, "Can't create the SRAM <-> SC "
"comm. tunnel \n");
}
master_chosen = TRUE;
/*
* Verify that an 'interrupts' property
* exists for this device
*/
if (ddi_getproplen(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "interrupts",
&len) != DDI_PROP_SUCCESS) {
SBBC_ERR(CE_PANIC, "No 'interrupts' property for the "
"'chosen' SBBC \n");
}
/*
* add the interrupt handler
* NB
* should this be a high-level interrupt ?
* NB
*/
if (sbbc_add_intr(softsp) == DDI_FAILURE) {
SBBC_ERR(CE_PANIC, "Can't add interrupt handler for "
"'chosen' SBBC \n");
}
sbbc_enable_intr(softsp);
/*
* Create the mailbox
*/
if (sbbc_mbox_create(softsp) != 0) {
cmn_err(CE_WARN, "No IOSRAM MailBox created!\n");
}
}
}
/*
* sbbc_add_instance
* Must be called to hold chosen_lock.
*/
static void
sbbc_add_instance(sbbc_softstate_t *softsp)
{
#ifdef DEBUG
struct sbbc_softstate *sp;
#endif
ASSERT(mutex_owned(&chosen_lock));
#if defined(DEBUG)
/* Verify that this instance is not in the list yet */
for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
ASSERT(sp != softsp);
}
#endif
/*
* Add this instance to the front of the list.
*/
if (sgsbbc_instances != NULL) {
sgsbbc_instances->prev = softsp;
}
softsp->next = sgsbbc_instances;
softsp->prev = NULL;
sgsbbc_instances = softsp;
}
static void
sbbc_remove_instance(sbbc_softstate_t *softsp)
{
struct sbbc_softstate *sp;
for (sp = sgsbbc_instances; sp != NULL; sp = sp->next) {
if (sp == softsp) {
if (sp->next != NULL) {
sp->next->prev = sp->prev;
}
if (sp->prev != NULL) {
sp->prev->next = sp->next;
}
if (sgsbbc_instances == softsp) {
sgsbbc_instances = sp->next;
}
break;
}
}
}
/*
* Generate an SBBC interrupt to the SC
* Called from iosram_send_intr()
*
* send_intr == 0, check if EPLD register clear
* for sync'ing SC/OS
* send_intr == 1, send the interrupt
*/
int
sbbc_send_intr(sbbc_softstate_t *softsp, int send_intr)
{
uchar_t *epld_int;
volatile uchar_t epld_status;
ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
if ((softsp == (sbbc_softstate_t *)NULL) ||
(softsp->epld_regs == (struct sbbc_epld_regs *)NULL))
return (ENXIO);
/*
* Check the L1 EPLD Interrupt register. If the
* interrupt bit is set, theres an interrupt outstanding
* (we assume) so return (EBUSY).
*/
epld_int = &softsp->epld_regs->epld_reg[EPLD_INTERRUPT];
epld_status = ddi_get8(softsp->sbbc_reg_handle2, epld_int);
if (epld_status & INTERRUPT_ON)
return (EBUSY);
if (send_intr == TRUE)
ddi_put8(softsp->sbbc_reg_handle2, epld_int,
(epld_status | INTERRUPT_ON));
return (0);
}
/*
* Map SBBC Internal registers
*
* The call to function should be protected by
* chosen_lock or master_iosram->iosram_lock
* to make sure a tunnel switch will not occur
* in a middle of mapping.
*/
int
sbbc_map_regs(sbbc_softstate_t *softsp)
{
struct ddi_device_acc_attr attr;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
/*
* Map in register set 1, Common Device Regs
* SBCC offset 0x0
*/
if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
(caddr_t *)&softsp->sbbc_regs,
SBBC_REGS_OFFSET, SBBC_REGS_SIZE,
&attr, &softsp->sbbc_reg_handle1) != DDI_SUCCESS) {
cmn_err(CE_WARN, "sbbc%d: unable to map interrupt "
"registers", ddi_get_instance(softsp->dip));
return (DDI_FAILURE);
}
/*
* Map in using register set 1, EPLD
* SBCC offset 0xe000
*/
if (ddi_regs_map_setup(softsp->dip, RNUM_SBBC_REGS,
(caddr_t *)&softsp->epld_regs,
SBBC_EPLD_OFFSET, SBBC_EPLD_SIZE,
&attr, &softsp->sbbc_reg_handle2) != DDI_SUCCESS) {
cmn_err(CE_WARN, "sbbc%d: unable to map EPLD "
"registers", ddi_get_instance(softsp->dip));
return (DDI_FAILURE);
}
/*
* Set up pointers for registers
*/
softsp->port_int_regs = (uint32_t *)((char *)softsp->sbbc_regs +
SBBC_PCI_INT_STATUS);
map_regs_exit:
return (DDI_SUCCESS);
}
/*
* Unmap SBBC Internal registers
*/
static void
sbbc_unmap_regs(sbbc_softstate_t *softsp)
{
if (softsp == NULL)
return;
mutex_enter(&master_iosram->iosram_lock);
if (softsp->sbbc_regs) {
ddi_regs_map_free(&softsp->sbbc_reg_handle1);
softsp->sbbc_regs = NULL;
softsp->port_int_regs = NULL;
}
if (softsp->epld_regs) {
ddi_regs_map_free(&softsp->sbbc_reg_handle2);
softsp->epld_regs = NULL;
}
mutex_exit(&master_iosram->iosram_lock);
return;
}
/*
* This is here to allow the IOSRAM driver get the softstate
* for a chosen node when doing a tunnel switch. Just enables
* us to avoid exporting the sbbcp softstate hook
*/
sbbc_softstate_t *
sbbc_get_soft_state(int instance)
{
return (ddi_get_soft_state(sbbcp, instance));
}
/*
* Add interrupt handlers
*/
int
sbbc_add_intr(sbbc_softstate_t *softsp)
{
int rc = DDI_SUCCESS;
/*
* map in the SBBC interrupts
* Note that the iblock_cookie was initialised
* in the 'attach' routine
*/
if (ddi_add_intr(softsp->dip, 0, &softsp->iblock,
&softsp->idevice, sbbc_intr_handler,
(caddr_t)softsp) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Can't register SBBC "
" interrupt handler\n");
rc = DDI_FAILURE;
}
return (rc);
}
void
sbbc_enable_intr(sbbc_softstate_t *softsp)
{
uint32_t *pci_intr_enable_reg;
/*
* Enable Interrupts now, turn on both INT#A lines
*/
pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +
SBBC_PCI_INT_ENABLE);
ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg,
(uint32_t)SBBC_PCI_ENABLE_INT_A);
}
void
sbbc_disable_intr(sbbc_softstate_t *softsp)
{
uint32_t *pci_intr_enable_reg;
/*
* Disable Interrupts now, turn off both INT#A lines
*/
pci_intr_enable_reg = (uint32_t *)((char *)softsp->sbbc_regs +
SBBC_PCI_INT_ENABLE);
ddi_put32(softsp->sbbc_reg_handle1, pci_intr_enable_reg, 0);
}