/*
* 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
*/
/*
*/
/*
* Schizo specifics implementation:
* interrupt mapping register
* PBM configuration
* ECC and PBM error handling
* Iommu mapping handling
* Streaming Cache flushing
*/
#include <sys/sysmacros.h>
#include <sys/iommutsb.h>
#include <sys/ddi_impldefs.h>
/*LINTLIBRARY*/
static void iommu_ctx_free(iommu_t *);
static int iommu_tlb_scrub(iommu_t *, int);
/* called by pci_attach() DDI_ATTACH to initialize pci objects */
int
{
int ret;
/* Perform allocations first to avoid delicate unwinding. */
return (DDI_FAILURE);
return (DDI_FAILURE);
}
}
/* second side attach */
}
if (cmn_p->pci_common_refcnt == 0)
/*
* The initialization of cb internal interrupts depends on ib
*/
if (cmn_p->pci_common_refcnt == 0) {
} else
if (cmn_p->pci_common_refcnt == 0) {
} else
goto done;
done:
if (ret != DDI_SUCCESS)
return (ret);
}
/* called by pci_detach() DDI_DETACH to destroy pci objects */
void
{
/* schizo non-shared objects */
if (cmn_p->pci_common_refcnt != 0) {
return;
}
/* schizo shared objects - uses cmn_p, must be destroyed before cmn */
}
/* called by pci_attach() DDI_RESUME to (re)initialize pci objects */
void
{
if (cmn_p->pci_common_attachcnt == 0)
if (cmn_p->pci_common_attachcnt == 0)
}
/* called by pci_detach() DDI_SUSPEND to suspend pci objects */
void
{
}
/*
* add an additional 0x35 or 0x36 ino interrupt on platforms don't have them
* This routine has multiple places that assumes interrupt takes one cell
* each and cell size is same as integer size.
*/
static int
{
if (intr_cnt == CBNINTR_CDMA)
intr_cnt++;
(int *)new_intr_buf, intr_cnt))
goto teardown;
goto teardown;
}
goto teardown;
}
return (DDI_SUCCESS);
return (ret);
}
{
int instance;
return (XMITS_SC_MAX_PRF);
else
return (0);
}
static void
{
#ifdef PBM_CDMA_DEBUG
#endif /* PBM_CDMA_DEBUG */
wait:
start_time = gethrtime();
continue;
if (--fail_cnt > 0)
goto wait;
break;
}
#ifdef PBM_CDMA_DEBUG
pbm_p->pbm_cdma_to_cnt++;
else {
}
#endif /* PBM_CDMA_DEBUG */
}
#if !defined(lint)
#endif
/*
*
* Called from interrupt wrapper: the associated ino is used to index
* the distinctive register bit.
* Called from pci_dma_sync(): the bit belongs to PBM is shared
* for all calls from pci_dma_sync(). Xmits requires serialization
* while Tomatillo does not.
*/
void
{
int i;
if (chip_type == PCI_CHIP_SCHIZO) {
return;
}
locked = 0;
locked = 1;
}
for (i = 0; i < 5; i++) {
goto done;
}
start_time = gethrtime();
break;
}
done:
/* optional: stdphysio(sync_reg_pa - 8, ino_mask); */
if (locked)
if (tomatillo_store_store_wrka) {
#if !defined(lint)
#endif
#if !defined(lint)
#endif
}
}
/*ARGSUSED*/
void
{
}
/*
* map_pci_registers
*
* This function is called from the attach routine to map the registers
* accessed by this driver.
*
* used by: pci_attach()
*
* return value: DDI_FAILURE on failure
*/
int
{
int len;
/*
* Register set 0 is PCI CSR Base
*/
len = 0;
goto fail;
}
/*
* Register set 1 is Schizo CSR Base
*/
len = 1;
goto fail;
}
/*
* The third register set contains the bridge's configuration
* header. This header is at the very beginning of the bridge's
* configuration space. This space has litte-endian byte order.
*/
len = 2;
goto fail;
}
goto done;
/*
* The optional fourth register bank points to the
* interrupt concentrator registers.
*/
len = 3;
goto fail;
}
done:
return (DDI_SUCCESS);
fail:
;
return (DDI_FAILURE);
}
/*
* unmap_pci_registers:
*
* This routine unmap the registers mapped by map_pci_registers.
*
* used by: pci_detach()
*
* return value: none
*/
void
{
int i;
for (i = 0; i < 4; i++) {
}
}
{
/* ensure that cpu_id is only 10 bits. */
}
{
return (((reg & COMMON_INTR_MAP_REG_TID) >>
((reg & SCHIZO_INTR_MAP_REG_NID) >>
}
uint64_t *
{
/*
* Schizo maps all interrupts in one contiguous area.
* (PCI_CSRBase + 0x00.1000 + INO * 8).
*/
}
uint64_t *
{
/*
* Schizo maps clear intr. registers in contiguous area.
* (PCI_CSRBase + 0x00.1400 + INO * 8).
*/
}
/*
* schizo does not have mapping register per slot, so no sharing
* is done.
*/
/*ARGSUSED*/
void
{
}
/*
* return true if there are interrupts using this mapping register
*/
/*ARGSUSED*/
int
{
return (ino_p->ino_ipil_size);
}
void
{
}
{
}
/*
* Return the cpuid to to be used for an ino. We have no special cpu
* assignment constraints for this nexus, so just call intr_dist_cpuid().
*/
/* ARGSUSED */
{
return (intr_dist_cpuid());
}
void
{
if (!pci_buserr_interrupt)
return;
}
int
{
if (!pci_buserr_interrupt)
return (DDI_SUCCESS);
}
void
{
if (pci_buserr_interrupt)
}
{
}
{
}
/*
* Useful on psycho only.
*/
int
{
return (DDI_FAILURE);
}
void
{
uint64_t l;
ushort_t s = 0;
/*
* See if any SERR# signals are asserted. We'll clear them later.
*/
if (l & COMMON_PCI_CTRL_SERR)
/*
* Determine if PCI bus is running at 33 or 66 mhz.
*/
if (l & COMMON_PCI_CTRL_SPEED)
else
if (pci_set_dto_value & mask) {
l &= ~(3ull << SCHIZO_PCI_CTRL_PTO_SHIFT);
l |= pci_dto_value << SCHIZO_PCI_CTRL_PTO_SHIFT;
l |= (3ull << SCHIZO_PCI_CTRL_PTO_SHIFT);
}
/*
* Enable error interrupts.
*/
if (pci_error_intr_enable & mask)
else
l &= ~SCHIZO_PCI_CTRL_ERR_INT_EN;
/*
* Enable pci streaming byte errors and error interrupts.
*/
if (pci_sbh_error_intr_enable & mask)
else
l &= ~SCHIZO_PCI_CTRL_SBH_INT_EN;
/*
* Enable pci discard timeout error interrupt.
*/
if (pci_mmu_error_intr_enable & mask)
else
l &= ~SCHIZO_PCI_CTRL_MMU_INT_EN;
/*
* Enable PCI-X error interrupts.
*/
if (xmits_error_intr_enable & mask)
else
l &= ~XMITS_PCI_CTRL_X_ERRINT_EN;
/*
* Panic if older XMITS hardware is found.
*/
"unsupported on XMITS version %d\n",
if (xmits_perr_recov_int_enable) {
/*
* Enable interrupt on PERR
*/
}
}
/*
* Enable parity error detection on internal memories
*/
}
/*
*/
if ((pci_bus_parking_enable & mask) &&
"no-bus-parking"))
l |= SCHIZO_PCI_CTRL_ARB_PARK;
else
l &= ~SCHIZO_PCI_CTRL_ARB_PARK;
/*
* Enable arbitration.
*/
/*
* Make sure SERR is clear
*/
l |= COMMON_PCI_CTRL_SERR;
/*
* Enable DTO interrupt, if desired.
*/
mask))
l |= (TOMATILLO_PCI_CTRL_DTO_INT_EN);
else
l &= ~(TOMATILLO_PCI_CTRL_DTO_INT_EN);
/*
* Now finally write the control register with the appropriate value.
*/
*pbm_p->pbm_ctrl_reg = l;
/*
* Enable IO Prefetch on Tomatillo
*/
SCHIZO_PCI_CTRL_REG_OFFSET) >> 3);
(1 << TOMATILLO_POFFSET_SHIFT) |
}
/*
* Allow DMA write parity errors to generate an interrupt.
* This is implemented on Schizo 2.5 and greater and XMITS 3.0
* and greater. Setting this on earlier versions of XMITS 3.0
* has no affect.
*/
SCHIZO_PCI_CTRL_REG_OFFSET) >> 3);
}
/*
* Clear any PBM errors.
*/
l = (SCHIZO_PCI_AFSR_E_MASK << SCHIZO_PCI_AFSR_PE_SHIFT) |
*pbm_p->pbm_async_flt_status_reg = l;
/*
* Allow the diag register to be set based upon variable that
*/
l = *pbm_p->pbm_diag_reg;
/*
*/
if (pci_retry_disable & mask)
else
l &= ~COMMON_PCI_DIAG_DIS_RETRY;
/*
*/
if (pci_intsync_disable & mask)
else
l &= ~COMMON_PCI_DIAG_DIS_INTSYNC;
/*
*/
if (pci_enable_retry_arb & mask)
l &= ~SCHIZO_PCI_DIAG_DIS_RTRY_ARB;
else
*pbm_p->pbm_diag_reg = l;
/*
* Enable SERR# and parity reporting via command register.
*/
/*
* Clear error bits in configuration status register.
*/
s = PCI_STAT_PERROR | PCI_STAT_S_PERROR |
/*
* The current versions of the obp are suppose to set the latency
* timer register but do not. Bug 1234181 is open against this
* problem. Until this bug is fixed we check to see if the obp
* has attempted to set the latency timer register by checking
* for the existence of a "latency-timer" property.
*/
"pbm_configure: set schizo latency timer to %x\n",
}
/*
*
* NOTE: current implementation resets UPPR_RTRY counter for
* _all_ XMITS' PBMs and does not support tuning per PBM.
*/
if (xurc) {
" uppr_rtry counter = 0x%lx\n",
}
}
}
{
/*
* Disable error and streaming byte hole interrupts via the
* PBM control register.
*/
*pbm_p->pbm_ctrl_reg &=
/*
* Disable error interrupts via the interrupt mapping register.
*/
return (BF_NONE);
}
/*
* Layout of the dvma context bucket bitmap entry:
*
* 63 - 56 55 - 0
* 8-bit lock 56-bit, each represent one context
* DCB_LOCK_BITS DCB_BMAP_BITS
*/
{
return (0);
}
/* clear lock bits */
;
if (i < DCB_BMAP_BITS)
"get_dvma_context: ctx_mask=0x%x.%x ctx=0x%x\n",
return (ctx);
}
void
{
"free_dvma_context: ctx=0x%x\n", ctx);
;
/* clear lock bits */
}
int
{
if (!*reg_addr) {
return (DDI_SUCCESS);
}
if (!matchreg)
return (DDI_SUCCESS);
do {
if (matchreg & 1)
matchreg >>= 1;
} while (matchreg);
return (DDI_SUCCESS);
if (pci_ctx_flush_warn)
return (DDI_FAILURE);
}
void
{
if ((!tm_mtlb_gc_manual) &&
tm_mtlb_gc = 1;
/* Workaround for the Tomatillo ASIC Erratum #72 */
pci_spurintr_msgs = 0;
}
}
if (chip_id < SCHIZO_VER_23)
/*
* schizo control status reg bank is on the 2nd "reg" property entry
*
* ALL internal interrupts except pbm interrupts are shared by both
* sides, 1st-side-attached is used as *the* owner.
*/
<< MMU_PAGESHIFT;
}
void
{
}
{
}
/*
* overwrite dvma end address (only on virtual-dma systems)
* initialize tsb size
* reset context bits
* return: IOMMU CSR bank base address (VA)
*/
{
int dvma_prop_len;
uintptr_t a;
/*
* Initializations for Tomatillo's micro TLB bug. errata #82
*/
if (tm_mtlb_gc) {
iommu_p->iommu_mtlb_nreq = 0;
iommu_p->iommu_mtlb_npgs = 0;
kmem_zalloc(sizeof (dvma_unbind_req_t) *
}
goto tsb_done;
if (dvma_prop_len != sizeof (pci_dvma_range_prop_t)) {
goto tsb_end;
}
/*
* Determine the virtual address of the register block
* containing the iommu control registers and determine
* the virtual address of schizo specific iommu registers.
*/
(uint64_t *)(a + SCHIZO_IOMMU_FLUSH_CTX_REG_OFFSET);
(uint64_t *)(a + TOMATILLO_IOMMU_ERR_TFAR_OFFSET);
return (a); /* PCICSRBase */
}
void
{
if (pci_use_contexts)
if (iommu_p->iommu_mtlb_req_p) {
iommu_p->iommu_mtlb_nreq = 0;
}
}
{
return ((uintptr_t)
}
/* ARGSUSED */
static boolean_t
{
if (pbm_p->pbm_quiesce_count > 0) {
}
return (B_TRUE);
}
static boolean_t
{
if (pbm_p->pbm_quiesce_count > 0) {
if (code == 0) {
} else {
*ctrl_reg_p = ctrl_reg;
}
}
return (B_TRUE);
}
void
{
extern int segkmem_reloc;
(uint64_t *)(a + SCHIZO_PCI_ASYNC_FLT_ADDR_REG_OFFSET);
/*
* Create a property to indicate that this node supports DVMA
* page relocation.
*/
}
/*
* Register a panic callback so we can unquiesce this bus
* if it has been placed in the quiesced state.
*/
goto non_schizo;
/*
* This is a software workaround to fix schizo hardware bug.
* Create a boolean property and its existence means consistent
* dma sync should not be done while in prom. The usb polled
* code (OHCI,EHCI) will check for this property and will not
* do dma sync if this property exist.
*/
}
return;
}
(uint64_t *)(a + XMITS_UPPER_RETRY_COUNTER_REG_OFFSET);
}
void
{
}
{
/*
* Determine virtual addresses of bridge specific registers,
*/
return (a);
}
void
{
uintptr_t a;
/*
* Determine the virtual addresses of the stream cache
*/
(uint64_t *)(a + SCHIZO_SC_CTX_MATCH_REG_OFFSET);
/*
* Determine the virtual addresses of the streaming cache
* diagnostic access registers.
*/
}
/*ARGSUSED*/
int
{
/*
* Schizo does not support interrupt proxies.
*/
return (0);
}
/*
* pcisch error handling 101:
*
* The various functions below are responsible for error handling. Given
* a particular error, they must gather the appropriate state, report all
* errors with correct payload, and attempt recovery where ever possible.
*
* Recovery in the context of this driver is being able notify a leaf device
* of the failed transaction. This leaf device may either be the master or
* target for this transaction and may have already received an error
* notification via a PCI interrupt. Notification is done via DMA and access
* handles. If we capture an address for the transaction then we can map it
* to a handle(if the leaf device is fma-compliant) and fault the handle as
* well as call the device driver registered callback.
*
* The hardware can either interrupt or trap upon detection of an error, in
* some rare cases it also causes a fatal reset.
*
* cb_buserr_intr() is responsible for handling control block
* errors(errors which stem from the host bus side of the bridge). Since
* we support multiple chips and host bus standards, cb_buserr_intr will
* call a bus specific error handler to report and handle the detected
* error. Since this error can either affect or orginate from either of the
* two PCI busses which are connected to the bridge, we need to call
* pci_pbm_err_handler() for each bus as well to report their errors. We
* also need to gather possible errors which have been detected by their
* compliant children(via ndi_fm_handler_dispatch()).
*
* pbm_error_intr() and ecc_intr() are responsible for PCI Block Module
* errors(generic PCI + bridge specific) and ECC errors, respectively. They
* are common between pcisch and pcipsy and therefore exist in pci_pbm.c and
* pci_ecc.c. To support error handling certain chip specific handlers
* must exist and they are defined below.
*
* cpu_deferred_error() and cpu_async_error(), handle the traps that may
* have originated from IO space. They call into the registered IO callbacks
* to report and handle errors that may have caused the trap.
*
* pci_pbm_err_handler() is called by pbm_error_intr() or pci_err_callback()
* related errors which are detected by the chip.
*
* pci_pbm_err_handler() calls a generic interface pbm_afsr_report()(pci_pbm.c)
* to report the pbm specific errors and attempt to map the failed address
* (if captured) to a device instance. pbm_afsr_report() calls a chip specific
* pci_pbm_err_handler() also calls iommu_err_handler() to handle IOMMU related
* errors.
*
* iommu_err_handler() can recover from most errors, as long as the requesting
* device is notified and the iommu can be flushed. If an IOMMU error occurs
* due to a UE then it will be passed on to the ecc_err_handler() for
* subsequent handling.
*
* ecc_err_handler()(pci_ecc.c) also calls a chip specific interface to
* interpret the afsr, pci_ecc_classify(). ecc_err_handler() also calls
* pci_pbm_err_handler() to report any pbm errors detected.
*
* To make sure that the trap code and the interrupt code are not going
* to step on each others toes we have a per chip pci_fm_mutex. This also
* makes it necessary for us to be caution while we are at a high PIL, so
* that we do not cause a subsequent trap that causes us to hang.
*
* The attempt to commonize code was meant to keep in line with the current
* pci driver implementation and it was not meant to confuse. If you are
* confused then don't worry, I was too.
*
*/
static void
{
int i;
}
/* Gather PBM state information for both sides of this chip */
for (i = 0; i < 2; i++) {
continue;
}
}
static void
{
}
};
/*
* Function used to handle and log Safari bus errors.
*/
static int
{
int i;
int fatal = 0;
}
}
if (fatal)
return (DDI_FM_FATAL);
return (DDI_FM_OK);
}
/*
* Check pbm va log register for captured errant address, and fail handle
* if in per device cache.
* Called from jbus_err_handler.
*/
static int
{
int i;
/*
* Check VA log register for address associated with error,
* if no address is registered then return failure
*/
for (i = 0; i < 2; i++) {
continue;
/*
* Look up and fault handle associated with
* logged DMA address
*/
(void *)addr);
if (ret == DDI_FM_NONFATAL)
break;
}
}
return (ret);
}
};
/*
* Function used to handle and log Jbus errors.
*/
static int
{
int fatal = 0;
int nonfatal = 0;
int i;
continue;
if (jbus_err_tbl[i].cb_fatal) {
continue;
}
!= DDI_FM_NONFATAL) {
fatal++;
}
}
DDI_FM_OK));
}
/*
* Control Block error interrupt handler.
*/
{
int i;
/*
* Check for related errors in PBM and IOMMU. The IOMMU could cause
* a timeout on the jbus due to an IOMMU miss, so we need to check and
* log the IOMMU error registers.
*/
for (i = 0; i < 2; i++) {
continue;
ret = DDI_FM_FATAL;
}
/* Cleanup and reset error bits */
if (ret == DDI_FM_FATAL) {
fm_panic("Fatal System Bus Error has occurred\n");
}
return (DDI_INTR_CLAIMED);
}
};
/*
* pci_ecc_classify, called by ecc_handler to classify ecc errors
* and determine if we should panic or not.
*/
void
{
int i, j, ret = 0;
ecc_err_tbl[i].ecc_type) ||
continue;
if (!ecc_err_tbl[i].ecc_pri ||
1 : 0;
break;
}
if (flag == ACC_HANDLE &&
}
/*
* Lookup and fault errant handle
*/
for (j = 0; j < 2; ++j) {
continue;
(void *)&ecc_err_p->ecc_err_addr);
if (ret == DDI_FM_NONFATAL) {
fatal = 0;
break;
} else
fatal++;
}
break;
}
if (fatal)
else if (flag != ACC_HANDLE)
}
/*
* Tables to define PCI-X Split Completion errors
*/
};
sizeof (pcix_completer_errs)/sizeof (pcix_err_msg_rec_t),
};
/*
* Tables for the PCI-X error status messages
*/
};
sizeof (pcix_stat_errs)/sizeof (pcix_err_msg_rec_t),
/*
* walk thru a table of error messages, printing as appropriate
*
* t - the table of messages to parse
* err - the error to match against
*/
static int
{
int nerr = 0;
int j;
for (j = 0; j < t.err_rec_num; j++) {
!= msg_key)
continue;
t.err_msg_tbl[j].msg_class,
t.err_msg_tbl[j].msg_str);
nerr++;
}
}
/*
* Decodes primary(bit 27-24) or secondary(bit 15-12) PCI-X split
* completion error message class and index in PBM AFSR.
*/
static void
{
sizeof (struct pcix_err_tbl);
int i;
for (i = 0; i < num_classes; i++) {
pcix_split_errs_tbl[i], pbm_err_p);
break;
}
}
}
/*
* Report PBM PCI-X Error Status Register if in PCI-X mode
*
* Once a PCI-X fault tree is constructed, the code below may need to
* change.
*/
static int
{
int fatal = 0;
int nonfatal = 0;
uint32_t e;
return (DDI_FM_OK);
nonfatal++;
}
nonfatal++;
}
if (e) {
fatal++;
else
nonfatal++;
}
if (e) {
fatal++;
else
nonfatal++;
}
return (DDI_FM_OK);
else if (fatal)
return (DDI_FM_FATAL);
return (DDI_FM_NONFATAL);
}
};
/*
* pci_pbm_classify, called by pbm_afsr_report to classify piow afsr.
*/
int
{
int nerr = 0;
int i;
else
nerr++;
break;
}
}
return (nerr);
}
/*
* Function used to handle and log IOMMU errors. Called by pci_pbm_err_handler,
* with pci_fm_mutex held.
*/
static int
{
int err = 0;
int fatal = 0;
int nonfatal = 0;
int ret;
return (DDI_FM_NONFATAL);
}
/*
* Need to make sure a Target Abort was signalled to the device if
* we have any hope of recovering. Tomatillo does not send a TA for
* DMA Writes that result in a Translation Error, thus fooling the
* device into believing everything is as it expects. Ignorance
* is bliss, but knowledge is power.
*/
if (stat & TOMATILLO_IOMMU_ERR_ILLTSBTBW) {
err = 1;
if (!ta_signalled)
fatal++;
else
nonfatal++;
}
if (stat & TOMATILLO_IOMMU_ERR_BAD_VA) {
err = 1;
if (!ta_signalled)
fatal++;
else
nonfatal++;
}
if (!err) {
switch (stat) {
fatal++;
break;
/*
* Fault the address in iommu_tfar
* register to inform target driver of error
*/
if (ret != DDI_FM_NONFATAL)
if (ta_signalled)
nonfatal++;
else
fatal++;
else
nonfatal++;
break;
fatal++;
break;
case TOMATILLO_IOMMU_ECC_ERR:
break;
}
}
if (fatal)
return (DDI_FM_FATAL);
else if (nonfatal)
return (DDI_FM_NONFATAL);
return (DDI_FM_OK);
}
int
{
PCI_STAT_S_SYSERR | PCI_STAT_PERROR)) ||
COMMON_PCI_CTRL_SERR)) ||
return (1);
pbm_pcix_stat_reg = (uint64_t *)(a +
return (1);
return (1);
}
return (0);
}
};
/*
* It is called by the pbm_error_intr as well as the pci_err_callback(trap
* callback). To protect access we hold the pci_fm_mutex when calling
* this function.
*/
int
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int i, ret = 0;
if (caller == PCI_TRAP_CALL) {
/*
* For ddi_caut_get treat all events as nonfatal.
* The trampoline will set err_ena = 0, err_status =
* NONFATAL. We only really call this function so that
* pci_clear_error() and ndi_fm_handler_dispatch() will
* get called.
*/
nonfatal++;
goto done;
} else {
/*
* For ddi_caut_put treat all events as nonfatal. Here
* we have the handle and can call ndi_fm_acc_err_set().
*/
nonfatal++;
goto done;
}
/*
* For ddi_peek treat all events as nonfatal. We only
* really call this function so that pci_clear_error()
* and ndi_fm_handler_dispatch() will get called.
*/
nonfatal++;
goto done;
/*
* For ddi_poke we can treat as nonfatal if the
* following conditions are met :
* 2. Make sure no secondary error bits set
* since it gets much more complicated when a
* PCI-to-PCI bridge is present.
*/
nonfatal++;
goto done;
/*
* MAs behind a PCI-X bridge get sent back to
* the host as a Split Completion Error Message.
* We handle this the same as the above check.
*/
nonfatal++;
goto done;
}
nonfatal++;
goto done;
}
}
if (ret == DDI_FM_FATAL)
fatal++;
else
nonfatal++;
}
== DDI_FM_FATAL)
fatal++;
else if (ret == DDI_FM_NONFATAL)
nonfatal++;
== DDI_FM_FATAL)
fatal++;
else if (ret == DDI_FM_NONFATAL)
nonfatal++;
!prierr) {
if (pci_pbm_err_tbl[i].pbm_flag)
fatal++;
else
nonfatal++;
if (caller == PCI_TRAP_CALL &&
}
}
fatal++;
else
nonfatal++;
}
/*
* PBM Received System Error - During any transaction, or
* at any point on the bus, some device may detect a critical
* error and signal a system error to the system.
*/
/*
* may be expected (master abort from pci-pci bridge during
* poke will generate SERR)
*/
"ereport_post: %s", buf);
}
unknown++;
}
/*
* PCI Retry Timeout - Device fails to retry deferred
* transaction within timeout. Only Tomatillo
*/
if (pci_dto_fault_warn == CE_PANIC)
fatal++;
else
nonfatal++;
}
/*
* PBM Detected Data Parity Error - DPE detected during a DMA Write
* or PIO Read. Later case is taken care of by cpu_deferred_error
* and sent here to be logged.
*/
/*
* If we have an address then fault
* it, if not probe for errant device
*/
ret = DDI_FM_FATAL;
if (caller != PCI_TRAP_CALL) {
if (pbm_err.pbm_va_log) {
}
if (ret == DDI_FM_NONFATAL)
nonfatal++;
else
fatal++;
} else
nonfatal++;
}
/* PBM Detected IOMMU Error */
== DDI_FM_FATAL)
fatal++;
else
nonfatal++;
}
done:
if (ret == DDI_FM_FATAL) {
fatal++;
} else if (ret == DDI_FM_NONFATAL) {
nonfatal++;
} else if (ret == DDI_FM_UNKNOWN) {
unknown++;
}
/*
* RSERR not claimed as nonfatal by a child is considered fatal
*/
fatal++;
/* Cleanup and reset error bits */
}
/*
* Function returns TRUE if a Primary error is Split Completion Error
* that indicates a Master Abort occured behind a PCI-X bridge.
* This function should only be called for busses running in PCI-X mode.
*/
static int
{
return (0);
if (msg & PCIX_CLASS_BRIDGE)
if (msg & PCIX_BRIDGE_MASTER_ABORT) {
return (1);
}
}
return (0);
}
/*
* pci_pbm_err_handler and the cb_buserr_intr. This function must be
* called while pci_fm_mutex is held.
*/
static void
{
/*
* Capture all pbm error state for later logging
*/
/*
* Record errant slot for Xmits and Schizo
* Not stored in Tomatillo
*/
/*
* The bit 51 on XMITS rev1.0 is same as
* SCHIZO_PCI_CTRL_ERR_SLOT_LOCK on schizo2.3. But
* this bit needs to be cleared to be able to latch
* the slot info on next fault.
* But in XMITS Rev2.0, this bit indicates a DMA Write
* Parity error.
*/
/*
* top 32 bits are W1C and we just want to
* clear SLOT_LOCK. Leave bottom 32 bits
* unchanged
*/
*pbm_p->pbm_ctrl_reg =
0xffffffff);
}
}
}
/*
* Tomatillo specific registers
*/
(void *)(uintptr_t)*(a + TOMATILLO_TGT_ERR_VALOG_OFFSET));
}
/*
* Xmits PCI-X register
*/
pbm_pcix_stat_reg = (uint64_t *)(a +
}
}
/*
* is complete. Only clearing error bits which have been logged. Called by
* pci_pbm_err_handler and pci_bus_exit.
*/
static void
{
}
}
void
{
/*
* for poke() support - called from POKE_FLUSH. Spin waiting
* for MA, TA or SERR to be cleared by a pbm_error_intr().
* We have to wait for SERR too in case the device is beyond
* a pci-pci bridge.
*/
while (((pbm_afsr >> SCHIZO_PCI_AFSR_PE_SHIFT) &
(pbm_ctl_stat & COMMON_PCI_CTRL_SERR)) {
}
}
/*
* Function used to convert the 32 bit captured PCI error address
* to the full Safari or Jbus address. This is so we can look this address
* up in our handle caches.
*/
void
{
if (afsr & SCHIZO_PCI_AFSR_CONF_SPACE) {
} else if (afsr & SCHIZO_PCI_AFSR_IO_SPACE) {
} else if (afsr & SCHIZO_PCI_AFSR_MEM_SPACE) {
}
}
};
/*
* Function used to convert the 32 bit PIO address captured for a
*/
static void
{
int i, pci_side = 0;
int swap = 0;
return;
/*
* Using the csr_base address to determine which side
* we are on.
*/
if (pci_csr_base & PCI_SIDE_ADDR_MASK)
pci_side = 1;
else
pci_side = 0;
swap = 1;
if (region == SCH_REG_SAFARI_REGS)
*afar |= schizo_base;
break;
}
}
if (swap) {
return;
}
}
/*
* Function used to post control block specific ereports.
*/
static void
{
/*
* We do not use ddi_fm_ereport_post because we need to set a
* special detector here. Since we do not have a device path for
* the bridge chip we use what we think it should be to aid in
* diagnosis.
*/
return;
if (ptr)
*ptr = '\0';
NULL);
NULL);
}
}
/*
* Function used to post IOMMU specific ereports.
*/
static void
{
NULL);
}
/*
* Function used to post PCI-X generic ereports.
* This function needs to be fixed once the Fault Boundary Analysis
* for PCI-X is conducted. The payload should be made more generic.
*/
static void
{
NULL);
}
static void
{
}
/*
* iommu_tlb_scrub():
* Exam TLB entries through TLB diagnostic registers and look for errors.
* scrub = 1 : cleanup all error bits in tlb, called in FAULT_RESET case
* scrub = 0 : log all error conditions to console, FAULT_LOG case
* In both cases, it returns number of errors found in tlb entries.
*/
static int
{
int i, nerr = 0;
for (i = 0; i < IOMMU_TLB_ENTRIES; i++) {
if (!(tag & TLBTAG_ERR_BIT))
continue;
if (errstat == TLBTAG_ERRSTAT_INVALID) {
if (scrub)
} else
nerr++;
if (scrub)
continue;
"\tContext=%lx %sWritable %sStreamable\n"
"\tPCI Page Size=%sk Address in page %lx\n",
}
return (nerr);
}
/*
* pci_iommu_disp: calculates the displacement needed in tomatillo's
* iommu control register and modifies the control value template
* from caller. It also clears any error status bit that are new
* in tomatillo.
* return value: an 8-bit mask to enable corresponding 512 MB segments
* suitable for tomatillo's target address register.
* 0x00: no programming is needed, use existing value from prom
* 0x60: use segment 5 and 6 to form a 1GB dvma range
*/
static uint64_t
{
return (0);
/* iommu ctrl reg error bits are W1C */
if (ctl_old >> TOMATIILO_IOMMU_ERR_REG_SHIFT) {
}
return (0);
/* Tomatillo 2.0 and later, and 1GB DVMA range */
}
void
{
"\npci_iommu_config: pbm_csr_p=%llx pbm_ctl=%llx",
"\n\tiommu_ctl_p=%llx iommu_ctl=%llx",
"\n\tcfgpa=%llx tgt_space_p=%llx mask=%x tsb=%llx\n",
if (!cfgpa)
goto reprog;
/* disable PBM arbiters - turn off bits 0-7 */
/*
* For non-XMITS, flush any previous writes. This is only
* necessary for host bridges that may have a USB keywboard
* attached. XMITS does not.
*/
if (mask)
*tgt_space_p = mask;
*tsb_bar_p = tsb_bar_val;
*iommu_ctl_p = iommu_ctl;
}
int
{
"portid", -1));
}
/*
* Schizo Safari Performance Events.
*/
schizo_saf_events[] = {
{"saf_bus_cycles", 0x1}, {"saf_pause_asserted_cycles", 0x2},
{"saf_frn_coherent_cmds", 0x3}, {"saf_frn_coherent_hits", 0x4},
{"saf_my_coherent_cmds", 0x5}, {"saf_my_coherent_hits", 0x6},
{"saf_frn_io_cmds", 0x7}, {"saf_frn_io_hits", 0x8},
{"merge_buffer", 0x9}, {"interrupts", 0xa},
{"csr_pios", 0xc}, {"upa_pios", 0xd},
{"pcia_pios", 0xe}, {"pcib_pios", 0xf},
{"saf_pause_seen_cycles", 0x11}, {"dvma_reads", 0x12},
{"dvma_writes", 0x13}, {"saf_orq_full_cycles", 0x14},
{"saf_data_in_cycles", 0x15}, {"saf_data_out_cycles", 0x16},
{"clear_pic", 0x1f}
};
/*
* Schizo PCI Performance Events.
*/
schizo_pci_events[] = {
{"dvma_stream_rd", 0x0}, {"dvma_stream_wr", 0x1},
{"dvma_const_rd", 0x2}, {"dvma_const_wr", 0x3},
{"dvma_stream_buf_mis", 0x4}, {"dvma_cycles", 0x5},
{"dvma_wd_xfr", 0x6}, {"pio_cycles", 0x7},
{"dvma_tlb_misses", 0x10}, {"interrupts", 0x11},
{"saf_inter_nack", 0x12}, {"pio_reads", 0x13},
{"pio_writes", 0x14}, {"dvma_rd_buf_timeout", 0x15},
{"dvma_rd_rtry_stc", 0x16}, {"dvma_wr_rtry_stc", 0x17},
{"dvma_rd_rtry_nonstc", 0x18}, {"dvma_wr_rtry_nonstc", 0x19},
{"E*_slow_transitions", 0x1a}, {"E*_slow_cycles_per_64", 0x1b},
{"clear_pic", 0x1f}
};
/*
* Create the picN kstats for the pci
* and safari events.
*/
void
{
if (pci_name_kstat == NULL) {
} else {
sizeof (schizo_pci_events) / sizeof (pci_kev_mask_t);
pci_create_name_kstat("pcis",
}
if (saf_name_kstat == NULL) {
} else {
sizeof (schizo_saf_events) / sizeof (pci_kev_mask_t);
}
}
void
{
if (pci_name_kstat != NULL) {
}
if (saf_name_kstat != NULL) {
}
}
/*
* Create 'counters' kstat for pci events.
*/
void
{
}
}
void
{
}
void
{
}
/*
* Extract the drivers binding name to identify which chip
* we're binding to. Whenever a new bus bridge is created, the driver alias
* entry should be added here to identify the device if needed. If a device
* isn't added, the identity defaults to PCI_CHIP_UNIDENTIFIED.
*/
static uint32_t
{
"version#", 0);
DDI_PROP_DONTPASS, "module-revision#", 0);
}
return (PCI_CHIP_UNIDENTIFIED);
}
/*
* Setup a physical pointer to one leaf config space area. This
* is used in several places in order to do a dummy read which
* guarantees the nexus (and not a bus master) has gained control
* of the bus.
*/
static void
{
int reg_len;
continue;
break;
}
}
void
{
/*
* hardware problems between the request and grant signals which
* causes a bus hang. One workaround, which is applied here,
* is to disable bus parking if the child contains the property
* pci-req-removal. Note that if the bus is quiesced we must mask
* off the parking bit in the saved control registers, since the
* quiesce operation temporarily turns off PCI bus parking.
*/
"pci-req-removal") == 1) {
if (pbm_p->pbm_quiesce_count > 0) {
} else {
}
}
int value;
/*
* Due to a XMITS bug, we need to set the outstanding
* split transactions to 1 for all PCI-X functions
* behind the leaf.
*/
(xmits_max_read_bytes << 2);
"Workaround: value = %x\n", value);
(void) ndi_prop_update_int(DDI_DEV_T_NONE,
}
"pcix diag bugcntl=0x%lx, tunable=0x%lx, mode=%s\n",
"PCI-X":"PCI"));
"pcix diag reg=0x%lx (CUR)\n",
/*
* Due to a XMITS 3.x hw bug, we need to
* read PBM's xmits pci ctrl status register to
* determine mode (PCI or PCI-X) and then update
* PBM's pcix diag register with new BUG_FIX_CNTL
* bits (47:32) _if_ different from tunable's mode
* based value. This update is performed only once
* during the PBM's first child init.
*
* Per instructions from xmits hw engineering,
* non-BUG_FIX_CNTL bits should not be preserved
* when updating the pcix diag register. Such bits
* should be written as 0s.
*/
*pbm_pcix_diag_reg = tunable <<
" pcix diag reg=0x%lx (NEW)\n",
}
}
}
}
void
{
}
static int
int flag)
{
switch (flag) {
case PCI_OBJ_INTR_ADD:
break;
case PCI_OBJ_INTR_REMOVE:
break;
default:
ret = DDI_FAILURE;
break;
}
return (ret);
}
int
{
int r;
DDI_SUCCESS));
}
void
{
}
static uint_t
{
#ifdef PBM_CDMA_DEBUG
#endif /* PBM_CDMA_DEBUG */
return (DDI_INTR_CLAIMED);
}
int
{
return (DDI_SUCCESS);
}
void
{
}
void
{
/* Save CDMA interrupt state */
}
void
{
/* Restore CDMA interrupt state */
}
/*
* pci_bus_quiesce
*
* This function is called as the corresponding control ops routine
* to a DDI_CTLOPS_QUIESCE command. Its mission is to halt all DMA
* activity on the bus by disabling arbitration/parking.
*/
int
{
if (pbm_p->pbm_quiesce_count++ == 0) {
ctrl_reg = *ctrl_reg_p;
*ctrl_reg_p = ctrl_reg;
#ifdef DEBUG
ctrl_reg = *ctrl_reg_p;
if ((ctrl_reg & (SCHIZO_PCI_CTRL_ARB_EN_MASK |
SCHIZO_PCI_CTRL_ARB_PARK)) != 0)
#endif
if (pbm_p->pbm_anychild_cfgpa)
}
return (DDI_SUCCESS);
}
/*
* pci_bus_unquiesce
*
* This function is called as the corresponding control ops routine
* to a DDI_CTLOPS_UNQUIESCE command. Its mission is to resume paused
* DMA activity on the bus by re-enabling arbitration (and maybe parking).
*/
int
{
#ifdef DEBUG
#endif
if (--pbm_p->pbm_quiesce_count == 0) {
#ifdef DEBUG
ctrl_reg = *ctrl_reg_p;
if ((ctrl_reg & (SCHIZO_PCI_CTRL_ARB_EN_MASK |
SCHIZO_PCI_CTRL_ARB_PARK)) == 0)
#endif
}
return (DDI_SUCCESS);
}
int
pci_reloc_getkey(void)
{
return (0x200);
}
static void
int npages)
{
int i, preserv_count = 0;
goto done;
/* read TLB */
for (i = 0; i < IOMMU_TLB_ENTRIES; i++)
/* for each request search the TLB for a matching address */
for (i = 0; i < IOMMU_TLB_ENTRIES; i++) {
break;
}
if (i >= IOMMU_TLB_ENTRIES) {
continue;
}
/* if an empty slot exists */
}
done:
}
void
{
if (tm_mtlb_gc)
else
}
/*
* pci_iommu_bypass_end_configure
*
* Support for 42-bit bus width to SAFARI and JBUS in DVMA and
* iommu bypass transfers:
*/
{
return ((dma_bypass_addr_t)SAFARI_JBUS_IOMMU_BYPASS_END);
}