125cf1a30Sjl /*
225cf1a30Sjl * CDDL HEADER START
325cf1a30Sjl *
425cf1a30Sjl * The contents of this file are subject to the terms of the
525cf1a30Sjl * Common Development and Distribution License (the "License").
625cf1a30Sjl * You may not use this file except in compliance with the License.
725cf1a30Sjl *
825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl * See the License for the specific language governing permissions
1125cf1a30Sjl * and limitations under the License.
1225cf1a30Sjl *
1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl *
1925cf1a30Sjl * CDDL HEADER END
2025cf1a30Sjl */
2125cf1a30Sjl /*
2225cf1a30Sjl * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2325cf1a30Sjl * Use is subject to license terms.
2425cf1a30Sjl */
2525cf1a30Sjl
2625cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI"
2725cf1a30Sjl
2825cf1a30Sjl /*
2925cf1a30Sjl * CMU-CH PBM implementation:
3025cf1a30Sjl * initialization
3125cf1a30Sjl * Bus error interrupt handler
3225cf1a30Sjl */
3325cf1a30Sjl
3425cf1a30Sjl #include <sys/types.h>
3525cf1a30Sjl #include <sys/kmem.h>
3625cf1a30Sjl #include <sys/spl.h>
3725cf1a30Sjl #include <sys/sysmacros.h>
3825cf1a30Sjl #include <sys/sunddi.h>
3925cf1a30Sjl #include <sys/fm/protocol.h>
4025cf1a30Sjl #include <sys/fm/util.h>
4125cf1a30Sjl #include <sys/machsystm.h>
4225cf1a30Sjl #include <sys/async.h>
4325cf1a30Sjl #include <sys/ddi_impldefs.h>
4425cf1a30Sjl #include <sys/ontrap.h>
4525cf1a30Sjl #include <sys/pcicmu/pcicmu.h>
4625cf1a30Sjl #include <sys/membar.h>
4725cf1a30Sjl #include <sys/ivintr.h>
4825cf1a30Sjl
4925cf1a30Sjl static uint_t pcmu_pbm_error_intr(caddr_t a);
5025cf1a30Sjl
5125cf1a30Sjl /* The nexus interrupt priority values */
5225cf1a30Sjl int pcmu_pil[] = {14, 14, 14, 14, 14, 14};
5325cf1a30Sjl
5425cf1a30Sjl void
pcmu_pbm_create(pcmu_t * pcmu_p)5525cf1a30Sjl pcmu_pbm_create(pcmu_t *pcmu_p)
5625cf1a30Sjl {
5725cf1a30Sjl pcmu_pbm_t *pcbm_p;
5825cf1a30Sjl int len;
5925cf1a30Sjl dev_info_t *dip = pcmu_p->pcmu_dip;
6025cf1a30Sjl
6125cf1a30Sjl /*
6225cf1a30Sjl * Allocate a state structure for the PBM and cross-link it
6325cf1a30Sjl * to its per pci node state structure.
6425cf1a30Sjl */
6525cf1a30Sjl pcbm_p = (pcmu_pbm_t *)kmem_zalloc(sizeof (pcmu_pbm_t), KM_SLEEP);
6625cf1a30Sjl pcmu_p->pcmu_pcbm_p = pcbm_p;
6725cf1a30Sjl pcbm_p->pcbm_pcmu_p = pcmu_p;
6825cf1a30Sjl
6925cf1a30Sjl len = snprintf(pcbm_p->pcbm_nameinst_str,
7025cf1a30Sjl sizeof (pcbm_p->pcbm_nameinst_str), "%s%d", NAMEINST(dip));
7125cf1a30Sjl pcbm_p->pcbm_nameaddr_str = pcbm_p->pcbm_nameinst_str + ++len;
7225cf1a30Sjl (void) snprintf(pcbm_p->pcbm_nameaddr_str,
7325cf1a30Sjl sizeof (pcbm_p->pcbm_nameinst_str) - len, "%s@%s", NAMEADDR(dip));
7425cf1a30Sjl
7525cf1a30Sjl pcmu_pbm_setup(pcbm_p);
7625cf1a30Sjl
7725cf1a30Sjl PCMU_DBG4(PCMU_DBG_ATTACH, dip,
7825cf1a30Sjl "pcmu_pbm_create: ctrl=%x, afsr=%x, afar=%x, diag=%x\n",
7925cf1a30Sjl pcbm_p->pcbm_ctrl_reg, pcbm_p->pcbm_async_flt_status_reg,
8025cf1a30Sjl pcbm_p->pcbm_async_flt_addr_reg, pcbm_p->pcbm_diag_reg);
8125cf1a30Sjl PCMU_DBG1(PCMU_DBG_ATTACH, dip, "pcmu_pbm_create: conf=%x\n",
8225cf1a30Sjl pcbm_p->pcbm_config_header);
8325cf1a30Sjl
8425cf1a30Sjl /*
8525cf1a30Sjl * Register a function to disable pbm error interrupts during a panic.
8625cf1a30Sjl */
8725cf1a30Sjl bus_func_register(BF_TYPE_ERRDIS,
8825cf1a30Sjl (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
8925cf1a30Sjl
9025cf1a30Sjl /*
9125cf1a30Sjl * create the interrupt-priorities property if it doesn't
9225cf1a30Sjl * already exist to provide a hint as to the PIL level for
9325cf1a30Sjl * our interrupt.
9425cf1a30Sjl */
9525cf1a30Sjl if (ddi_getproplen(DDI_DEV_T_ANY, dip,
9625cf1a30Sjl DDI_PROP_DONTPASS, "interrupt-priorities",
9725cf1a30Sjl &len) != DDI_PROP_SUCCESS) {
9825cf1a30Sjl /* Create the interrupt-priorities property. */
9925cf1a30Sjl (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
10025cf1a30Sjl DDI_PROP_CANSLEEP, "interrupt-priorities",
10125cf1a30Sjl (caddr_t)pcmu_pil, sizeof (pcmu_pil));
10225cf1a30Sjl }
10325cf1a30Sjl pcmu_pbm_configure(pcbm_p);
10425cf1a30Sjl }
10525cf1a30Sjl
10625cf1a30Sjl int
pcmu_pbm_register_intr(pcmu_pbm_t * pcbm_p)10725cf1a30Sjl pcmu_pbm_register_intr(pcmu_pbm_t *pcbm_p)
10825cf1a30Sjl {
10925cf1a30Sjl pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
11025cf1a30Sjl uint32_t mondo;
11125cf1a30Sjl int r = DDI_SUCCESS;
11225cf1a30Sjl
11325cf1a30Sjl pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
11425cf1a30Sjl
11525cf1a30Sjl /*
11625cf1a30Sjl * Install the PCI error interrupt handler.
11725cf1a30Sjl */
11825cf1a30Sjl mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
11925cf1a30Sjl pcmu_p->pcmu_inos[CBNINTR_PBM]);
12025cf1a30Sjl
121*b0fc0e77Sgovinda VERIFY(add_ivintr(mondo, pcmu_pil[CBNINTR_PBM],
122*b0fc0e77Sgovinda (intrfunc)pcmu_pbm_error_intr, (caddr_t)pcmu_p, NULL, NULL) == 0);
12325cf1a30Sjl
12425cf1a30Sjl pcbm_p->pcbm_iblock_cookie = (void *)(uintptr_t)pcmu_pil[CBNINTR_PBM];
12525cf1a30Sjl
12625cf1a30Sjl /*
12725cf1a30Sjl * Create the pokefault mutex at the PIL below the error interrupt.
12825cf1a30Sjl */
12925cf1a30Sjl
13025cf1a30Sjl mutex_init(&pcbm_p->pcbm_pokeflt_mutex, NULL, MUTEX_DRIVER,
13125cf1a30Sjl (void *)(uintptr_t)ipltospl(spltoipl(
13225cf1a30Sjl (int)(uintptr_t)pcbm_p->pcbm_iblock_cookie) - 1));
13325cf1a30Sjl
13425cf1a30Sjl return (PCMU_ATTACH_RETCODE(PCMU_PBM_OBJ, PCMU_OBJ_INTR_ADD, r));
13525cf1a30Sjl }
13625cf1a30Sjl
13725cf1a30Sjl void
pcmu_pbm_destroy(pcmu_t * pcmu_p)13825cf1a30Sjl pcmu_pbm_destroy(pcmu_t *pcmu_p)
13925cf1a30Sjl {
14025cf1a30Sjl pcmu_pbm_t *pcbm_p = pcmu_p->pcmu_pcbm_p;
14125cf1a30Sjl pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
14225cf1a30Sjl uint32_t mondo;
14325cf1a30Sjl
14425cf1a30Sjl PCMU_DBG0(PCMU_DBG_DETACH, pcmu_p->pcmu_dip, "pcmu_pbm_destroy:\n");
14525cf1a30Sjl
14625cf1a30Sjl mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
14725cf1a30Sjl pcmu_p->pcmu_inos[CBNINTR_PBM]);
14825cf1a30Sjl
14925cf1a30Sjl /*
15025cf1a30Sjl * Free the pokefault mutex.
15125cf1a30Sjl */
15225cf1a30Sjl mutex_destroy(&pcbm_p->pcbm_pokeflt_mutex);
15325cf1a30Sjl
15425cf1a30Sjl /*
15525cf1a30Sjl * Remove the error interrupt.
15625cf1a30Sjl */
15725cf1a30Sjl intr_dist_rem(pcmu_pbm_intr_dist, pcbm_p);
15825cf1a30Sjl pcmu_ib_intr_disable(pib_p,
15925cf1a30Sjl pcmu_p->pcmu_inos[CBNINTR_PBM], PCMU_IB_INTR_WAIT);
160*b0fc0e77Sgovinda
161*b0fc0e77Sgovinda VERIFY(rem_ivintr(mondo, pcmu_pil[CBNINTR_PBM]) == 0);
16225cf1a30Sjl
16325cf1a30Sjl /*
16425cf1a30Sjl * Remove the error disable function.
16525cf1a30Sjl */
16625cf1a30Sjl bus_func_unregister(BF_TYPE_ERRDIS,
16725cf1a30Sjl (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
16825cf1a30Sjl
16925cf1a30Sjl pcmu_pbm_teardown(pcbm_p);
17025cf1a30Sjl
17125cf1a30Sjl /*
17225cf1a30Sjl * Free the pbm state structure.
17325cf1a30Sjl */
17425cf1a30Sjl kmem_free(pcbm_p, sizeof (pcmu_pbm_t));
17525cf1a30Sjl pcmu_p->pcmu_pcbm_p = NULL;
17625cf1a30Sjl }
17725cf1a30Sjl
17825cf1a30Sjl static uint_t
pcmu_pbm_error_intr(caddr_t a)17925cf1a30Sjl pcmu_pbm_error_intr(caddr_t a)
18025cf1a30Sjl {
18125cf1a30Sjl pcmu_t *pcmu_p = (pcmu_t *)a;
18225cf1a30Sjl pcmu_pbm_t *pcbm_p = pcmu_p->pcmu_pcbm_p;
18325cf1a30Sjl ddi_fm_error_t derr;
18425cf1a30Sjl int err = DDI_FM_OK;
18525cf1a30Sjl on_trap_data_t *otp = pcbm_p->pcbm_ontrap_data;
18625cf1a30Sjl
18725cf1a30Sjl bzero(&derr, sizeof (ddi_fm_error_t));
18825cf1a30Sjl derr.fme_version = DDI_FME_VERSION;
18925cf1a30Sjl mutex_enter(&pcmu_p->pcmu_err_mutex);
19025cf1a30Sjl if ((otp != NULL) && (otp->ot_prot & OT_DATA_ACCESS)) {
19125cf1a30Sjl /*
19225cf1a30Sjl * ddi_poke protection, check nexus and children for
19325cf1a30Sjl * expected errors.
19425cf1a30Sjl */
19525cf1a30Sjl otp->ot_trap |= OT_DATA_ACCESS;
19625cf1a30Sjl membar_sync();
19725cf1a30Sjl derr.fme_flag = DDI_FM_ERR_POKE;
19825cf1a30Sjl err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
19925cf1a30Sjl (void *)pcmu_p, PCI_INTR_CALL);
20025cf1a30Sjl } else if (pcmu_check_error(pcmu_p) != 0) {
20125cf1a30Sjl /*
20225cf1a30Sjl * unprotected error, check for all errors.
20325cf1a30Sjl */
20425cf1a30Sjl if (pcmu_errtrig_pa) {
20525cf1a30Sjl (void) ldphysio(pcmu_errtrig_pa);
20625cf1a30Sjl }
20725cf1a30Sjl derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
20825cf1a30Sjl err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
20925cf1a30Sjl (void *)pcmu_p, PCI_INTR_CALL);
21025cf1a30Sjl }
21125cf1a30Sjl
21225cf1a30Sjl if (err == DDI_FM_FATAL) {
21325cf1a30Sjl if (pcmu_panic_on_fatal_errors) {
21425cf1a30Sjl mutex_exit(&pcmu_p->pcmu_err_mutex);
21525cf1a30Sjl cmn_err(CE_PANIC, "%s-%d: Fatal PCI bus error(s)\n",
21625cf1a30Sjl ddi_driver_name(pcmu_p->pcmu_dip),
21725cf1a30Sjl ddi_get_instance(pcmu_p->pcmu_dip));
21825cf1a30Sjl }
21925cf1a30Sjl }
22025cf1a30Sjl
22125cf1a30Sjl mutex_exit(&pcmu_p->pcmu_err_mutex);
22225cf1a30Sjl pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
22325cf1a30Sjl return (DDI_INTR_CLAIMED);
22425cf1a30Sjl }
22525cf1a30Sjl
22625cf1a30Sjl void
pcmu_pbm_suspend(pcmu_pbm_t * pcbm_p)22725cf1a30Sjl pcmu_pbm_suspend(pcmu_pbm_t *pcbm_p)
22825cf1a30Sjl {
22925cf1a30Sjl pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
23025cf1a30Sjl pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
23125cf1a30Sjl pcbm_p->pcbm_imr_save = *ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino);
23225cf1a30Sjl }
23325cf1a30Sjl
23425cf1a30Sjl void
pcmu_pbm_resume(pcmu_pbm_t * pcbm_p)23525cf1a30Sjl pcmu_pbm_resume(pcmu_pbm_t *pcbm_p)
23625cf1a30Sjl {
23725cf1a30Sjl pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
23825cf1a30Sjl pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
23925cf1a30Sjl
24025cf1a30Sjl pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, ino);
24125cf1a30Sjl *ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino) = pcbm_p->pcbm_imr_save;
24225cf1a30Sjl }
24325cf1a30Sjl
24425cf1a30Sjl void
pcmu_pbm_intr_dist(void * arg)24525cf1a30Sjl pcmu_pbm_intr_dist(void *arg)
24625cf1a30Sjl {
24725cf1a30Sjl pcmu_pbm_t *pcbm_p = (pcmu_pbm_t *)arg;
24825cf1a30Sjl pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
24925cf1a30Sjl pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
25025cf1a30Sjl pcmu_ib_ino_t ino =
25125cf1a30Sjl PCMU_IB_MONDO_TO_INO(pcmu_p->pcmu_inos[CBNINTR_PBM]);
25225cf1a30Sjl mutex_enter(&pib_p->pib_intr_lock);
25325cf1a30Sjl pcmu_ib_intr_dist_nintr(pib_p, ino, ib_intr_map_reg_addr(pib_p, ino));
25425cf1a30Sjl mutex_exit(&pib_p->pib_intr_lock);
25525cf1a30Sjl }
25625cf1a30Sjl
25725cf1a30Sjl /*
25825cf1a30Sjl * Function used to log PBM AFSR register bits and to lookup and fault
25925cf1a30Sjl * handle associated with PBM AFAR register. Called by
26025cf1a30Sjl * pcmu_pbm_err_handler with pcmu_err_mutex held.
26125cf1a30Sjl */
26225cf1a30Sjl int
pcmu_pbm_afsr_report(dev_info_t * dip,uint64_t fme_ena,pcmu_pbm_errstate_t * pbm_err_p)26325cf1a30Sjl pcmu_pbm_afsr_report(dev_info_t *dip, uint64_t fme_ena,
26425cf1a30Sjl pcmu_pbm_errstate_t *pbm_err_p)
26525cf1a30Sjl {
26625cf1a30Sjl int fatal = 0;
26725cf1a30Sjl /* LINTED variable */
26825cf1a30Sjl pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip));
26925cf1a30Sjl
27025cf1a30Sjl ASSERT(MUTEX_HELD(&pcmu_p->pcmu_err_mutex));
27125cf1a30Sjl
27225cf1a30Sjl pbm_err_p->pcbm_pri = PBM_PRIMARY;
27325cf1a30Sjl (void) pcmu_pbm_classify(pbm_err_p);
27425cf1a30Sjl
27525cf1a30Sjl /*
27625cf1a30Sjl * We are currently not dealing with the multiple error
27725cf1a30Sjl * case, for any secondary errors we will panic.
27825cf1a30Sjl */
27925cf1a30Sjl pbm_err_p->pcbm_pri = PBM_SECONDARY;
28025cf1a30Sjl if (pcmu_pbm_classify(pbm_err_p)) {
28125cf1a30Sjl fatal++;
28225cf1a30Sjl pcmu_pbm_ereport_post(dip, fme_ena, pbm_err_p);
28325cf1a30Sjl }
28425cf1a30Sjl
28525cf1a30Sjl if (fatal) {
28625cf1a30Sjl return (DDI_FM_FATAL);
28725cf1a30Sjl }
28825cf1a30Sjl return (DDI_FM_NONFATAL);
28925cf1a30Sjl }
290