/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* IOSRAM leaf driver to SBBC nexus driver. This driver is used
*/
#include <sys/ddi_impldefs.h>
#include <sys/prom_plat.h>
#include <sys/iosramreg.h>
#include <sys/iosramio.h>
#include <sys/iosramvar.h>
#if defined(DEBUG)
int iosram_debug = 0;
static void iosram_dprintf(const char *fmt, ...);
#else /* !DEBUG */
#endif /* !DEBUG */
/*
* IOSRAM module global state
*/
/* more threads waiting on */
/* iosram_tswitch_wait cv */
/* more threads waiting on */
/* iosram_rw_wait cv */
#if defined(DEBUG)
static int iosram_rw_active_max = 0;
#endif
#if IOSRAM_STATS
static void iosram_print_stats(); /* forward declaration */
#endif /* IOSRAM_STATS */
#if IOSRAM_LOG
#endif /* IOSRAM_LOG */
/* driver entry point fn definitions */
/* configuration entry point fn definitions */
/* forward declaractions */
static void iosram_init_hashtab(void);
static int iosram_add_intr(iosramsoft_t *);
static int iosram_remove_intr(iosramsoft_t *);
static void iosram_remove_instance(int instance);
static void iosram_abort_tswitch();
#if defined(DEBUG)
/* forward declaractions for debugging */
static void iosram_print_cback();
static void iosram_print_state(int);
static void iosram_print_flags();
#endif
/*
* cb_ops
*/
iosram_open, /* cb_open */
iosram_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
iosram_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
};
/*
* Declare ops vectors for auto configuration.
*/
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
iosram_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
iosram_attach, /* devo_attach */
iosram_detach, /* devo_detach */
nodev, /* devo_reset */
&iosram_cb_ops, /* devo_cb_ops */
nulldev, /* devo_power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* Loadable module support.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* type of module - driver */
"IOSRAM Leaf driver",
};
};
int
_init(void)
{
int error;
int i;
#if defined(IOSRAM_LOG)
#endif
for (i = 0; i < IOSRAM_HASHSZ; i++) {
iosram_hashtab[i] = NULL;
}
sizeof (struct iosramsoft), 1)) != 0) {
goto failed;
}
goto failed;
}
IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n",
return (error);
#if defined(IOSRAM_LOG)
#endif
IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n",
return (error);
}
int
_fini(void)
{
#ifndef DEBUG
return (EBUSY);
#else /* !DEBUG */
int error;
#if defined(IOSRAM_LOG)
#endif
}
return (error);
#endif /* !DEBUG */
}
int
{
}
static int
{
int instance;
int propval;
int length;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
instance))) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* enable SBBC interrupts if SBBC is mapped in
* restore the value saved during detach
*/
if (softp->sbbc_region) {
}
/*
* Trigger soft interrupt handler to process any pending
* interrupts.
*/
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* If this instance is not tunnel capable, we don't attach it.
*/
if (iosram_tunnel_capable(softp) == 0) {
goto attach_fail;
}
/*
* Need to create an "interrupt-priorities" property to define the PIL
* to be used with the interrupt service routine.
*/
instance));
!= DDI_PROP_SUCCESS) {
"iosram_attach: failed to create property");
goto attach_fail;
}
}
/*
* Get interrupts cookies and initialize per-instance mutexes
*/
!= DDI_SUCCESS) {
goto attach_fail;
}
/*
* Add this instance to the iosram_instances list so that it can be used
* for tunnel in future.
*/
/*
* If this is the chosen IOSRAM and there is no master IOSRAM yet, then
* let's set this instance as the master.
*/
(void) iosram_switch_tunnel(softp);
/*
* XXX Do we need to panic if unable to setup master IOSRAM?
*/
if (iosram_master == NULL) {
"iosram(%d): can't setup master tunnel\n",
instance);
goto attach_fail;
}
}
/*
* Create minor node
*/
DDI_FAILURE) {
/*
* Minor node seems to be needed only for debugging purposes.
* Therefore, there is no need to fail this attach request.
* Simply print a message out.
*/
instance);
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
{
int instance;
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_FAILURE);
}
/*
* Disable SBBC interrupts if SBBC is mapped in
*/
if (softp->sbbc_region) {
/* save current interrupt enable register */
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Indicate that this instance is being detached so that this instance
* does not become a target for tunnel switch in future.
*/
/*
* If this instance is currently the master or the target of the tunnel
* switch, then we need to wait and switch tunnel, if necessary.
*/
(void) iosram_switchfrom(instance);
}
/*
* If the tunnel switch is in progress and we are the master or target
* of tunnel relocation, then we can't detach this instance right now.
*/
return (DDI_FAILURE);
}
/*
* We can't allow master IOSRAM to be detached as we won't be able to
* communicate otherwise.
*/
if (iosram_master == softp) {
return (DDI_FAILURE);
}
/*
* Now remove our instance from the iosram_instances list.
*/
/*
* participating in a tunnel switch. Neither should be the case here.
*/
/*
* Destroy per-instance mutexes
*/
/*
* Finally remove our soft state structure
*/
return (DDI_SUCCESS);
}
/* ARGSUSED0 */
static int
void **result)
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
ret = DDI_FAILURE;
} else {
ret = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
ret = DDI_SUCCESS;
break;
default:
ret = DDI_FAILURE;
break;
}
return (ret);
}
/*ARGSUSED1*/
static int
{
int instance;
return (ENXIO);
}
return (0);
}
/*ARGSUSED1*/
static int
{
int instance;
return (ENXIO);
}
return (0);
}
int
{
int boff;
union {
} word;
int error = 0;
/*
* We try to read from the IOSRAM using double word or word access
* provided both "off" and "buf" are (or can be) double word or word
* aligned. Othewise, we try to align the "off" to a word boundary and
* then try to read data from the IOSRAM using word access, but store it
* into buf buffer using byte access.
*
* aligned, it will always be copied using byte access.
*/
/*
* Acquire lock and look for the requested chunk. If it exists, make
* sure the requested read is within the chunk's bounds and no tunnel
* switch is active.
*/
if (iosram_master == NULL) {
} else if (iosram_tswitch_active) {
}
if (error) {
return (error);
}
/*
* Bump reference count to indicate #thread accessing IOSRAM and release
* the lock.
*/
#if defined(DEBUG)
if (iosram_rw_active > iosram_rw_active_max) {
}
#endif
/* Get starting address and map handle */
/*
* using double word or word access.
*/
}
IOSRAMLOG(2,
"RD: align rep_get8(buf:%p sramp:%p cnt:%x) len:%x\n",
}
/*
* Both source and destination are double word aligned
*/
IOSRAMLOG(2,
"RD: rep_get64(buf:%p sramp:%p cnt:%x) len:%x\n",
/*
* read remaining data using word and byte access
*/
IOSRAMLOG(2,
"RD: get32(buf:%p sramp:%p) len:%x\n",
}
if (len != 0) {
}
/*
* Both source and destination are word aligned
*/
IOSRAMLOG(2,
"RD: rep_get32(buf:%p sramp:%p cnt:%x) len:%x\n",
/*
* copy the remainder using byte access
*/
if (len != 0) {
}
} else if (len != 0) {
/*
* We know that the "off" (i.e. iosramp) is at least word
* aligned. We need to read IOSRAM word at a time and copy it
* byte at a time.
*/
IOSRAMLOG(2,
"RD: unaligned get32(buf:%p sramp:%p) len:%x\n",
}
/*
* copy the remaining data using byte access
*/
if (len != 0) {
}
}
/*
* Reacquire mutex lock, decrement refcnt and if refcnt is 0 and any
* threads are waiting for r/w activity to complete, wake them up.
*/
ASSERT(iosram_rw_active > 0);
if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
iosram_rw_wakeup = 0;
}
return (error);
}
/*
* _iosram_write(key, off, len, dptr, force)
* Internal common routine to write to the IOSRAM.
*/
static int
{
int boff;
union {
} word;
int error = 0;
/*
* We try to write to the IOSRAM using double word or word access
* provided both "off" and "buf" are (or can be) double word or word
* aligned. Othewise, we try to align the "off" to a word boundary and
* then try to write data to the IOSRAM using word access, but read data
* from the buf buffer using byte access.
*
* aligned, it will always be written using byte access.
*/
/*
* Acquire lock and look for the requested chunk. If it exists, make
* sure the requested write is within the chunk's bounds and no tunnel
* switch is active.
*/
if (iosram_master == NULL) {
} else if (iosram_tswitch_active && !force) {
}
if (error) {
return (error);
}
/*
* If this is a forced write and there's a tunnel switch in progress,
* abort the switch.
*/
if (iosram_tswitch_active && force) {
}
/*
* Bump reference count to indicate #thread accessing IOSRAM
* and release the lock.
*/
#if defined(DEBUG)
if (iosram_rw_active > iosram_rw_active_max) {
}
#endif
/* Get starting address and map handle */
/*
* data using double word or word access.
*/
}
IOSRAMLOG(2,
"WR: align rep_put8(buf:%p sramp:%p cnt:%x) len:%x\n",
}
/*
* Both source and destination are double word aligned
*/
IOSRAMLOG(2,
"WR: rep_put64(buf:%p sramp:%p cnt:%x) len:%x\n",
/*
* Copy the remaining data using word & byte access
*/
IOSRAMLOG(2,
}
if (len != 0) {
}
/*
* Both source and destination are word aligned
*/
IOSRAMLOG(2,
"WR: rep_put32(buf:%p sramp:%p cnt:%x) len:%x\n",
/*
* copy the remainder using byte access
*/
if (len != 0) {
}
} else if (len != 0) {
/*
* We know that the "off" is at least word aligned. We
* need to read data from buf buffer byte at a time, and
* write it to the IOSRAM word at a time.
*/
IOSRAMLOG(2,
"WR: unaligned put32(buf:%p sramp:%p) len:%x\n",
}
/*
* copy the remaining data using byte access
*/
if (len != 0) {
}
}
/*
* Reacquire mutex lock, decrement refcnt and if refcnt is 0 and
* any threads are waiting for r/w activity to complete, wake them up.
*/
ASSERT(iosram_rw_active > 0);
if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
iosram_rw_wakeup = 0;
}
return (error);
}
int
{
}
int
{
}
/*
* iosram_register(key, handler, arg)
* Register a handler and an arg for the specified chunk. This handler
* will be invoked when an interrupt is received from the other side and
* the int_pending flag for the corresponding key is marked
* IOSRAM_INT_TO_DOM.
*/
/* ARGSUSED */
int
{
int error = 0;
/*
* Acquire lock and look for the requested chunk. If it exists, and no
* other callback is registered, proceed with the registration.
*/
if (iosram_master == NULL) {
} else {
}
return (error);
}
/*
* iosram_unregister()
* Unregister handler associated with the specified chunk.
*/
int
{
int error = 0;
/*
* Acquire lock and look for the requested chunk. If it exists and has
* a callback registered, unregister it.
*/
if (iosram_master == NULL) {
/*
* If the handler is already busy (being invoked), then we flag
* it so it will be unregistered after the invocation completes.
*/
}
return (error);
}
/*
* iosram_get_flag():
* specified key.
*/
int
{
int error = 0;
/*
* Acquire lock and look for the requested chunk. If it exists, and no
* tunnel switch is in progress, read the chunk's flags.
*/
if (iosram_master == NULL) {
} else if (iosram_tswitch_active) {
} else {
/*
* Read the flags
*/
/*
* Get each flag value that the caller is interested in.
*/
if (data_valid != NULL) {
}
if (int_pending != NULL) {
}
}
return (error);
}
/*
* iosram_set_flag():
* Set data_valid and int_pending flags associated with the specified key.
*/
int
{
int error = 0;
/*
* Acquire lock and look for the requested chunk. If it exists, and no
* tunnel switch is in progress, write the chunk's flags.
*/
if (iosram_master == NULL) {
((data_valid != IOSRAM_DATA_INVALID) &&
(data_valid != IOSRAM_DATA_VALID)) ||
((int_pending != IOSRAM_INT_NONE) &&
(int_pending != IOSRAM_INT_TO_SSC) &&
(int_pending != IOSRAM_INT_TO_DOM))) {
} else if (iosram_tswitch_active) {
} else {
}
return (error);
}
/*
* iosram_ctrl()
* This function provides access to a variety of services not available
* through the basic API.
*/
int
{
int error = 0;
/*
* Acquire lock and do some argument sanity checking.
*/
if (iosram_master == NULL) {
}
if (error != 0) {
return (error);
}
/*
* Arguments seem okay so far, so process the command.
*/
switch (cmd) {
case IOSRAM_CMD_CHUNKLEN:
/*
* Return the length of the chunk indicated by the key.
*/
break;
}
break;
default:
break;
}
return (error);
}
/*
* iosram_hdr_ctrl()
* This function provides an interface for the Mailbox Protocol
* implementation to use when interacting with the IOSRAM header.
*/
int
{
int error = 0;
/*
* Acquire lock and do some argument sanity checking.
*/
if (iosram_master == NULL) {
}
if (error != 0) {
return (error);
}
switch (cmd) {
/*
* Return the value of the sms_mbox_version field.
*/
break;
}
break;
/*
* Set the value of the os_mbox_version field.
*/
(void) iosram_send_intr();
break;
iosram_hdrchange_handler = (void (*)())arg;
break;
default:
break;
}
return (error);
}
/*
* iosram_softintr()
* IOSRAM soft interrupt handler
*/
static uint_t
{
void (*handler)();
int i;
/*
* Do not process interrupt if interrupt handler is already running or
* no interrupts are pending.
*/
}
/*
* It's possible for the SC to send an interrupt on the new master
* before we are able to set our internal state. If so, we'll retrigger
* soft interrupt right after tunnel switch completion.
*/
return (DDI_INTR_CLAIMED);
}
/*
* Do not process interrupt if we are not the master.
*/
return (DDI_INTR_CLAIMED);
}
/*
* If the driver is suspended, then we should not process any
* interrupts. Instead, we trigger a soft interrupt when the driver
* resumes.
*/
return (DDI_INTR_CLAIMED);
}
/*
* Indicate that the IOSRAM interrupt handler is busy. Note that this
* any tunnel switches to start up while we're processing callbacks.
*/
#if defined(DEBUG)
if (iosram_rw_active > iosram_rw_active_max) {
}
#endif
do {
softp->intr_pending = 0;
/*
* Process changes to the IOSRAM header.
*/
if (hdr_changes != 0) {
int error;
0);
if (hdr_changes & IOSRAM_HDRFIELD_TOC_INDEX) {
/*
* XXX is it safe to temporarily release the
* iosram_mutex here?
*/
if (error) {
" TOC invalid; using old TOC.");
}
}
if (iosram_hdrchange_handler != NULL) {
}
}
/*
* Get data_valid/int_pending flags and generate a callback if
* applicable. For now, we read only those flags for which a
* callback has been registered. We can optimize reading of
* flags by reading them all at once and then process them
* later.
*/
chunkp++) {
#if DEBUG
"flag=0x%x handler=%p\n",
#endif
continue;
}
if (flag == IOSRAM_INT_TO_DOM) {
DPRINTF(1,
("IOSRAM(%d): softintr: invoking handler\n",
IOSRAMLOG(1,
"SINTR invoking hdlr:%p arg:%p index:%d\n",
/*
* If iosram_unregister was called while the
* callback was being invoked, complete the
* unregistration here.
*/
"delayed unreg k:0x%08x\n",
}
}
/*
* If there's a tunnel switch waiting to run, give it
* higher priority than these callbacks by bailing out.
* They'll still be invoked on the new master iosram
* when the tunnel switch is done.
*/
if (iosram_tswitch_active) {
break;
}
}
/*
* Indicate IOSRAM interrupt handler is not BUSY any more
*/
ASSERT(iosram_rw_active > 0);
if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
iosram_rw_wakeup = 0;
}
return (DDI_INTR_CLAIMED);
}
/*
* iosram_intr()
* IOSRAM real interrupt handler
*/
static uint_t
{
/*
* The SBBC registers region is not mapped in.
* Set the interrupt pending flag here, and process the
* interrupt after the tunnel switch.
*/
return (DDI_INTR_UNCLAIMED);
}
if (int_status & IOSRAM_SBBC_INT0) {
}
if (int_status & IOSRAM_SBBC_INT1) {
}
if (result == DDI_INTR_CLAIMED) {
int_status));
/*
* Trigger soft interrupt if not executing and
* not suspended.
*/
}
}
return (result);
}
/*
* iosram_send_intr()
* Send an interrupt to the SSP side via AXQ driver
*/
int
{
return (axq_cpu2ssc_intr(0));
}
#if defined(DEBUG)
static void
{
}
#endif /* DEBUG */
/*ARGSUSED1*/
static int
int *rvalp)
{
return (ENXIO);
}
switch (cmd) {
#if defined(DEBUG)
case IOSRAM_GET_FLAG:
{
return (EFAULT);
}
&int_pending);
DPRINTF(1,
("IOSRAM_GET_FLAG: can't copyout req.retval (%x)",
}
return (error);
}
case IOSRAM_SET_FLAG:
{
return (EFAULT);
}
req.int_pending));
}
return (error);
}
case IOSRAM_RD:
{
int len;
return (EFAULT);
}
mode));
}
return (error);
}
case IOSRAM_WR:
{
int len;
return (EFAULT);
}
} else {
bufp);
mode)) {
}
}
return (error);
}
case IOSRAM_TOC:
{
int len;
return (EFAULT);
}
mode)) {
DPRINTF(1,
("IOSRAM_TOC: copyout(%p, %p,%x,%x) failed\n",
mode));
}
return (error);
}
case IOSRAM_SEND_INTR:
{
switch ((int)arg) {
case 0x11:
case 0x22:
case 0x44:
case 0x88:
(int)arg));
break;
case 0xBB:
break;
default:
error = iosram_send_intr();
}
return (error);
}
case IOSRAM_PRINT_CBACK:
break;
case IOSRAM_PRINT_STATE:
iosram_print_state((int)arg);
break;
#if IOSRAM_STATS
case IOSRAM_PRINT_STATS:
break;
#endif
#if IOSRAM_LOG
case IOSRAM_PRINT_LOG:
iosram_print_log((int)arg);
break;
#endif
case IOSRAM_TUNNEL_SWITCH:
break;
case IOSRAM_PRINT_FLAGS:
break;
case IOSRAM_REG_CBACK:
{
return (EFAULT);
}
}
return (error);
}
case IOSRAM_UNREG_CBACK:
{
return (EFAULT);
}
}
return (error);
}
case IOSRAM_SEMA_ACQUIRE:
{
return (error);
}
case IOSRAM_SEMA_RELEASE:
{
error = iosram_sema_release();
return (error);
}
#endif /* DEBUG */
default:
}
return (error);
}
/*
* iosram_switch_tunnel(softp)
* Switch master tunnel to the specified instance
* Must be called while holding iosram_mutex
*/
/*ARGSUSED*/
static int
{
#ifdef DEBUG
#endif
int error = 0;
return (ENXIO);
}
if (iosram_master == softp) {
return (0);
}
/*
* We protect against the softp structure being deallocated by setting
* the IOSRAM_STATE_TSWITCH state flag. The detach routine will check
* for this flag and if set, it will wait for this flag to be reset or
* refuse the detach operation.
*/
if (prev_master) {
}
/*
* Map the target IOSRAM, read the TOC, and register interrupts if not
* already done.
*/
/*
* If there was no previous master, purge the TOC data that
* iosram_read_toc() created.
*/
nchunks = 0;
}
}
/*
* If we are asked to abort tunnel switch, do so now, before invoking
* the OBP callback.
*/
if (iosram_tswitch_aborted) {
/*
* Once the tunnel switch is aborted, this thread should not
* resume. If it does, we simply log a message. We can't unmap
* the new master IOSRAM as it may be accessed in
* iosram_abort_tswitch(). It will be unmapped when it is
* detached.
*/
IOSRAMLOG(1,
"TSWTCH: aborted (pre OBP cback). Thread resumed.\n",
}
if (error) {
IOSRAMLOG(1,
"TSWTCH: map failed instance:%d softp:%p error:%x\n",
goto done;
}
if (prev_master != NULL) {
int result;
/*
* Now invoke the OBP interface to do the tunnel switch.
*/
if (result != 0) {
}
IOSRAMLOG(1,
"TSWTCH: OBP tswitch portid:%x result:%x error:%x\n",
}
if (iosram_tswitch_aborted) {
/*
* Tunnel switch aborted. This thread should not resume.
* For now, we simply log a message, but don't unmap any
* IOSRAM at this stage as it may be accessed within the
* isoram_abort_tswitch(). The IOSRAM will be unmapped
* when that instance is detached.
*/
if (iosram_tswitch_aborted) {
IOSRAMLOG(1,
"TSWTCH: aborted (post OBP cback). Thread"
}
} else if (error) {
/*
* Tunnel switch failed. Continue using previous tunnel.
* However, unmap new (target) IOSRAM.
*/
(void) iosram_remove_intr(softp);
} else {
/*
* Tunnel switch was successful. Set the new master.
* Also unmap old master IOSRAM and remove any interrupts
* associated with that.
*
* Note that a call to iosram_force_write() allows access
* to the IOSRAM while tunnel switch is in progress. That
* means we need to set the new master before unmapping
* the old master.
*/
if (prev_master) {
(void) iosram_remove_intr(prev_master);
}
}
done:
/*
* Clear the tunnel switch flag on the source and destination
* instances.
*/
if (prev_master) {
}
/*
* Since incoming interrupts could get lost during a tunnel switch,
* trigger a soft interrupt just in case. No harm other than a bit
* of wasted effort will be caused if no interrupts were dropped.
*/
(iosram_master->intr_busy == 0)) {
}
return (error);
}
/*
* iosram_abort_tswitch()
* Must be called while holding iosram_mutex.
*/
static void
{
if ((!iosram_tswitch_active) || iosram_tswitch_aborted) {
return;
}
/*
* The first call to iosram_force_write() in the middle of tunnel switch
* will get here. We lookup IOSRAM VALID location and setup appropriate
* master, if one is still valid. We also set iosram_tswitch_aborted to
* prevent reentering this code and to catch if the OBP callback thread
* somehow resumes.
*/
if ((iosram_new_master == NULL) ||
(iosram_new_master = iosram_master)) {
/*
* New master hasn't been selected yet, or OBP callback
* succeeded and we already selected new IOSRAM as master, but
* system crashed in the middle of unmapping previous master or
* cleaning up state. Use the existing master.
*/
return;
}
/*
* System crashed in the middle of tunnel switch and we know that the
* new target has not been marked master yet. That means, the old
* master should still be mapped. We need to abort the tunnel switch
* and setup a valid master, if possible, so that we can write to the
* IOSRAM.
*
* We select a new master based upon the IOSRAM header status fields in
* the previous master IOSRAM and the target IOSRAM as follows:
*
* iosram_master iosram-tswitch
* (Prev Master) (New Target) Decision
* --------------- --------------- -----------
* VALID don't care prev master
* INTRANSIT INVALID prev master
* INTRANSIT INTRANSIT prev master
* INTRANSIT VALID new target
* INVALID INVALID shouldn't ever happen
* INVALID INTRANSIT shouldn't ever happen
* INVALID VALID new target
*/
if (master_valid == IOSRAM_VALID) {
/* EMPTY */
/*
* OBP hasn't been called yet or, if it has, it hasn't started
* copying yet. Use the existing master. Note that the new
* master may not be mapped yet.
*/
} else if (master_valid == IOSRAM_INTRANSIT) {
/*
* The system crashed after OBP started processing the tunnel
* switch but before the iosram driver determined that it was
* complete. Use the new master if it has been marked valid,
* meaning that OBP finished copying data to it, or the old
* master otherwise.
*/
if (new_master_valid == IOSRAM_VALID) {
NULL);
} else {
NULL);
}
} else {
/*
* The system crashed after OBP marked the old master INVALID,
* which means the new master is the way to go.
*/
}
}
/*
* iosram_switchfrom(instance)
* Switch master tunnel away from the specified instance
*/
/*ARGSUSED*/
int
{
int error = 0;
int count;
/*
* Wait if another tunnel switch is in progress
*/
count++) {
}
if (iosram_tswitch_active) {
return (EAGAIN);
}
/*
* Check if the specified instance holds the tunnel. If not,
* then we are done.
*/
return (0);
}
/*
* Before beginning the tunnel switch process, wait for any outstanding
*/
while (iosram_rw_active) {
iosram_rw_wakeup = 1;
}
/*
* If a previous tunnel switch just completed, we have to make sure
* HWAD has enough time to find the new tunnel before we switch
* away from it. Otherwise, OBP's mailbox message to OSD will never
* get through. Just to be paranoid about synchronization of lbolt
* across different CPUs, make sure the current attempt isn't noted
* as starting _before_ the last tunnel switch completed.
*/
if (current_tstamp > iosram_tswitch_tstamp) {
} else {
tstamp_interval = 0;
}
}
/*
* The specified instance holds the tunnel. We need to move it to some
* other IOSRAM. Try out all possible IOSRAMs listed in
* iosram_instances. For now, we always search from the first entry.
* In future, it may be desirable to start where we left off.
*/
if (iosram_tswitch_aborted) {
break;
}
/* we can't switch _to_ the instance we're switching _from_ */
continue;
}
/* skip over instances being detached */
continue;
}
/*
* Try to avoid reverting to the last instance we switched away
* from, as we expect that one to be detached eventually. Keep
* track of it, though, so we can go ahead and try switching to
* it if no other viable candidates are found.
*/
last_master = softp;
continue;
}
/*
* Do the tunnel switch. If successful, record the instance of
* the master we just left behind so we can try to avoid
* reverting to it next time.
*/
if (iosram_switch_tunnel(softp) == 0) {
break;
}
}
/*
* If we failed to switch the tunnel, but we skipped over an instance
* that had previously been switched out of because we expected it to be
* detached, go ahead and try it anyway (unless the tswitch was aborted
* or the instance we skipped is finally being detached).
*/
if (iosram_switch_tunnel(last_master) == 0) {
softp = last_master;
}
}
}
/*
* If there are additional tunnel switches queued up waiting for this
* one to complete, wake them up.
*/
if (iosram_tswitch_wakeup) {
}
return (error);
}
/*
* iosram_tunnel_capable(softp)
* Check if this IOSRAM instance is tunnel-capable by looing at
* "tunnel-capable" property.
*/
static int
{
int proplen;
int tunnel_capable;
/*
* Look up IOSRAM_TUNNELOK_PROP property, if any.
*/
proplen = sizeof (tunnel_capable);
&proplen) != DDI_PROP_SUCCESS) {
tunnel_capable = 0;
}
return (tunnel_capable);
}
static int
{
int rv;
/*
* Map SBBC region in
*/
IOSRAM_SBBC_MAP_OFFSET, sizeof (iosram_sbbc_region_t),
return (rv);
}
/*
* Disable SBBC interrupts. SBBC interrupts are enabled
* once the interrupt handler is registered.
*/
/*
* Clear hardware semaphore value if appropriate.
* When the first SBBC is mapped in by the IOSRAM driver,
* the value of the semaphore should be initialized only
* if it is not held by SMS. For subsequent SBBC's, the
* semaphore will be always initialized.
*/
if (!iosram_master) {
/* the first SBBC is being mapped in */
if (!(IOSRAM_SEMA_IS_HELD(sema_val) &&
/* not held by SMS, we clear the semaphore */
IOSRAM_SEMA_WR(softp, 0);
}
} else {
/* not the first SBBC, we clear the semaphore */
IOSRAM_SEMA_WR(softp, 0);
}
return (0);
}
static int
{
int portid;
int proplen;
/*
* Lookup IOSRAM_REG_PROP property to find out our IOSRAM length
*/
&proplen) != DDI_PROP_SUCCESS) {
instance);
return (DDI_FAILURE);
} else {
}
/*
* To minimize boot time, we map the entire IOSRAM as opposed to
* mapping individual chunk via ddi_regs_map_setup() call.
*/
return (DDI_FAILURE);
}
/*
* Lookup PORTID property on my parent hierarchy
*/
&proplen) != DDI_PROP_SUCCESS) {
instance);
return (DDI_FAILURE);
}
instance);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
}
/*
* Umap SBBC registers region. Shared with handler for SBBC
* interrupts, take intr_mutex.
*/
if (softp->sbbc_region) {
}
}
/*
* iosram_is_chosen(struct iosramsoft *softp)
*
* Looks up "chosen" node property to
* determine if it is the chosen IOSRAM.
*/
static int
{
int nodeid;
int chosen;
/*
* 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).
*/
/*
* Can't find IOSRAM_CHOSEN_PROP property under chosen node
*/
"iosram(%d): can't find chosen iosram property\n",
return (0);
}
/*
* get the full OBP pathname of this node
*/
sizeof (chosen_iosram)) < 0) {
return (0);
}
return (chosen);
}
/*
* iosram_set_master(struct iosramsoft *softp)
*
* Set master tunnel to the specified IOSRAM
* Must be called while holding iosram_mutex.
*/
static void
{
/*
* Clear MASTER flag on any previous IOSRAM master, if any
*/
}
/*
* Setup new IOSRAM master
*/
softp->tswitch_ok++;
}
/*
* iosram_read_toc()
*
* Read the TOC from an IOSRAM instance that has been mapped in.
* If the TOC is flawed or the IOSRAM isn't valid, return an error.
*/
static int
{
int i;
int new_nchunks;
/*
* Never try to read the TOC out of an unmapped IOSRAM.
*/
/*
* Check to make sure this IOSRAM is marked valid. Return
* an error if it isn't.
*/
instance));
return (EINVAL);
}
/*
* Get the location of the TOC.
*/
/*
* Read the index entry from the TOC and make sure it looks correct.
*/
sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
return (EINVAL);
}
/*
* Allocate storage for the new chunks array and initialize it with data
* from the TOC and callback data from the corresponding old chunk, if
* it exists.
*/
sizeof (iosram_chunk_t), KM_SLEEP);
toc_entryp += sizeof (iosram_toc_entry_t);
DPRINTF(1,
("iosram_read_toc(%d): k:%x o:%x l:%x p:%p\n",
} else {
"out of range... off:%x len:%x\n",
sizeof (iosram_chunk_t));
return (EINVAL);
}
/*
* Note the existence of the flags chunk, which is required in
* a correct TOC.
*/
}
/*
* If there was an entry for this chunk in the old list, copy
* the callback data from old to new storage.
*/
if ((nchunks > 0) &&
NULL)) {
sizeof (iosram_cback_t));
}
}
/*
* The TOC is malformed if there is no entry for the flags chunk.
*/
return (EINVAL);
}
/*
* Free any memory that is no longer needed and install the new data
* as current data.
*/
}
chunks = new_chunks;
return (0);
}
/*
* iosram_init_hashtab()
*
* Initialize the hash table and populate it with the IOSRAM
* chunks previously read from the TOC. The caller must hold the
* ioram_mutex lock.
*/
static void
iosram_init_hashtab(void)
{
int i, bucket;
for (i = 0; i < IOSRAM_HASHSZ; i++) {
iosram_hashtab[i] = NULL;
}
if (chunks) {
/*
* Hide the flags chunk by leaving it out of the hash
* table.
*/
continue;
}
/*
* Add the current chunk to the hash table.
*/
}
}
}
/*
* iosram_update_addrs()
*
* Process the chunk list, updating each chunk's basep, which is a pointer
* to the beginning of the chunk's memory in kvaddr space. Record the
* basep value of the flags chunk to speed up flag access. The caller
* must hold the iosram_mutex lock.
*/
static void
{
int i;
/*
* First go through all of the chunks updating their base pointers and
* looking for the flags chunk.
*/
DPRINTF(1,
("iosram_update_addrs flags: o:0x%08x p:%p",
}
}
/*
* Now, go through and update each chunk's flags pointer. This can't be
* done in the first loop because we don't have the address of the flags
* chunk yet.
*/
}
}
/*
* iosram_find_chunk(key)
*
* Return a pointer to iosram_chunk structure corresponding to the
* "key" IOSRAM chunk. The caller must hold the iosram_mutex lock.
*/
static iosram_chunk_t *
{
break;
}
}
return (chunkp);
}
/*
* iosram_add_intr(iosramsoft_t *)
*/
static int
{
"iosram(%d): Can't register softintr.\n",
return (DDI_FAILURE);
}
"iosram(%d): Can't register intr"
return (DDI_FAILURE);
}
/*
* Enable SBBC interrupts
*/
return (DDI_SUCCESS);
}
/*
* iosram_remove_intr(iosramsoft_t *)
*/
static int
{
/*
* Disable SBBC interrupts if SBBC is mapped in
*/
if (softp->sbbc_region) {
}
/*
* Remove SBBC interrupt handler
*/
/*
* Remove soft interrupt handler
*/
}
return (0);
}
/*
* iosram_add_instance(iosramsoft_t *)
* Must be called while holding iosram_mutex
*/
static void
{
#ifdef DEBUG
#endif
#if defined(DEBUG)
/* Verify that this instance is not in the list */
}
#endif
/*
* Add this instance to the list
*/
if (iosram_instances != NULL) {
}
}
/*
* iosram_remove_instance(int instance)
* Must be called while holding iosram_mutex
*/
static void
{
/*
* Remove specified instance from the iosram_instances list so that
* it can't be chosen for tunnel in future.
*/
}
}
if (iosram_instances == softp) {
}
return;
}
}
}
/*
* iosram_sema_acquire: Acquire hardware semaphore.
* Return 0 if the semaphore could be acquired, or one of the following
* possible values:
* EAGAIN: there is a tunnel switch in progress
* EBUSY: the semaphore was already "held"
* ENXIO: an IO error occured (e.g. SBBC not mapped)
* If old_value is not NULL, the location it points to will be updated
* with the semaphore value read when attempting to acquire it.
*/
int
{
int rv;
/*
* Disallow access if there is a tunnel switch in progress.
*/
if (iosram_tswitch_active) {
return (EAGAIN);
}
/*
* Use current master IOSRAM for operation, fail if none is
* currently active.
*/
return (ENXIO);
}
/*
* Fail if SBBC region has not been mapped. This shouldn't
* happen if we have a master IOSRAM, but we double-check.
*/
return (ENXIO);
}
/* read semaphore value */
if (IOSRAM_SEMA_IS_HELD(sema_val)) {
/* semaphore was held by someone else */
} else {
/* semaphore was not held, we just acquired it */
rv = 0;
}
return (rv);
}
/*
* iosram_sema_release: Release hardware semaphore.
* This function will "release" the hardware semaphore, and return 0 on
* success. If an error occured, one of the following values will be
* returned:
* EAGAIN: there is a tunnel switch in progress
* ENXIO: an IO error occured (e.g. SBBC not mapped)
*/
int
iosram_sema_release(void)
{
/*
* Disallow access if there is a tunnel switch in progress.
*/
if (iosram_tswitch_active) {
return (EAGAIN);
}
/*
* Use current master IOSRAM for operation, fail if none is
* currently active.
*/
return (ENXIO);
}
/*
* Fail if SBBC region has not been mapped in. This shouldn't
* happen if we have a master IOSRAM, but we double-check.
*/
return (ENXIO);
}
/* Release semaphore by clearing our semaphore register */
IOSRAM_SEMA_WR(softp, 0);
return (0);
}
#if defined(IOSRAM_LOG)
void
{
seq = iosram_logseq++;
if (iosram_log_print) {
}
} else {
}
}
}
#endif /* IOSRAM_LOG */
#if defined(DEBUG)
/*
* iosram_get_keys(buf, len)
* Return IOSRAM TOC in the specified buffer
*/
static int
{
int error = 0;
int i;
NULL);
/*
* Copy data while holding the lock to prevent any data
* corruption or invalid pointer dereferencing.
*/
if (iosram_master == NULL) {
} else {
i++, chunkp++) {
}
*len = i * sizeof (iosram_toc_entry_t);
}
return (error);
}
/*
* iosram_print_state(instance)
*/
static void
{
if (instance < 0) {
} else {
}
instance);
return;
}
}
/*
* iosram_print_stats()
*/
static void
{
}
static void
{
int i;
/*
* Print callback handlers
*/
}
}
}
static void
{
int i;
if (iosram_master == NULL) {
return;
}
for (i = 0; i < nchunks; i++) {
}
for (i = 0; i < nchunks; i++) {
" %2d: key: 0x%x data_valid:%x int_pending:%x\n",
}
}
/*PRINTFLIKE1*/
static void
{
}
#endif /* DEBUG */
#if IOSRAM_LOG
/*
* iosram_print_log(int cnt)
* Print last few entries of the IOSRAM log in reverse order
*/
static void
{
int i;
if (cnt <= 0) {
cnt = 20;
} else if (cnt > IOSRAM_MAXLOG) {
cnt = IOSRAM_MAXLOG;
}
"\niosram_logseq: 0x%x lbolt: %lx iosram_log_level:%x\n",
(void *)iosram_logbuf, IOSRAM_MAXLOG);
for (i = iosram_logseq; --i >= 0 && --cnt >= 0; ) {
}
} else {
}
}
}
#endif /* IOSRAM_LOG */