1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * CMU-CH Control Block object 30 */ 31 #include <sys/types.h> 32 #include <sys/kmem.h> 33 #include <sys/systm.h> 34 #include <sys/async.h> 35 #include <sys/sunddi.h> 36 #include <sys/ddi_impldefs.h> 37 #include <sys/pcicmu/pcicmu.h> 38 #include <sys/machsystm.h> 39 40 /*LINTLIBRARY*/ 41 42 extern uint64_t xc_tick_jump_limit; 43 44 void 45 pcmu_cb_create(pcmu_t *pcmu_p) 46 { 47 pcmu_cb_t *pcb_p = (pcmu_cb_t *) 48 kmem_zalloc(sizeof (pcmu_cb_t), KM_SLEEP); 49 mutex_init(&pcb_p->pcb_intr_lock, NULL, MUTEX_DRIVER, NULL); 50 pcmu_p->pcmu_cb_p = pcb_p; 51 pcb_p->pcb_pcmu_p = pcmu_p; 52 pcmu_cb_setup(pcmu_p); 53 } 54 55 void 56 pcmu_cb_destroy(pcmu_t *pcmu_p) 57 { 58 pcmu_cb_t *pcb_p = pcmu_p->pcmu_cb_p; 59 60 intr_dist_rem(pcmu_cb_intr_dist, pcb_p); 61 pcmu_cb_teardown(pcmu_p); 62 pcmu_p->pcmu_cb_p = NULL; 63 mutex_destroy(&pcb_p->pcb_intr_lock); 64 kmem_free(pcb_p, sizeof (pcmu_cb_t)); 65 } 66 67 uint64_t 68 pcmu_cb_ino_to_map_pa(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino) 69 { 70 return (pcb_p->pcb_map_pa + ((ino & 0x1f) << 3)); 71 } 72 73 uint64_t 74 pcmu_cb_ino_to_clr_pa(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino) 75 { 76 return (pcb_p->pcb_clr_pa + ((ino & 0x1f) << 3)); 77 } 78 79 static void 80 pcmu_cb_set_nintr_reg(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino, uint64_t value) 81 { 82 uint64_t pa = pcmu_cb_ino_to_clr_pa(pcb_p, ino); 83 84 PCMU_DBG3(PCMU_DBG_CB|PCMU_DBG_CONT, NULL, 85 "pci-%x pcmu_cb_set_nintr_reg: ino=%x PA=%016llx\n", 86 pcb_p->pcb_pcmu_p->pcmu_id, ino, pa); 87 88 stdphysio(pa, value); 89 (void) lddphysio(pa); /* flush the previous write */ 90 } 91 92 /* 93 * enable an internal interrupt source: 94 * if an interrupt is shared by both sides, record it in pcb_inos[] and 95 * cb will own its distribution. 96 */ 97 void 98 pcmu_cb_enable_nintr(pcmu_t *pcmu_p, pcmu_cb_nintr_index_t idx) 99 { 100 pcmu_cb_t *pcb_p = pcmu_p->pcmu_cb_p; 101 pcmu_ib_ino_t ino = PCMU_IB_MONDO_TO_INO(pcmu_p->pcmu_inos[idx]); 102 pcmu_ib_mondo_t mondo = PCMU_CB_INO_TO_MONDO(pcb_p, ino); 103 uint32_t cpu_id; 104 uint64_t reg, pa; 105 pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p; 106 volatile uint64_t *imr_p = ib_intr_map_reg_addr(pib_p, ino); 107 108 ASSERT(idx < CBNINTR_MAX); 109 pa = pcmu_cb_ino_to_map_pa(pcb_p, ino); 110 111 mutex_enter(&pcb_p->pcb_intr_lock); 112 cpu_id = intr_dist_cpuid(); 113 114 cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p, cpu_id, imr_p); 115 116 reg = ib_get_map_reg(mondo, cpu_id); 117 stdphysio(pa, reg); 118 119 ASSERT(pcb_p->pcb_inos[idx] == 0); 120 pcb_p->pcb_inos[idx] = ino; 121 122 pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE); 123 mutex_exit(&pcb_p->pcb_intr_lock); 124 125 PCMU_DBG3(PCMU_DBG_CB|PCMU_DBG_CONT, NULL, 126 "pci-%x pcmu_cb_enable_nintr: ino=%x cpu_id=%x\n", 127 pcmu_p->pcmu_id, ino, cpu_id); 128 PCMU_DBG2(PCMU_DBG_CB|PCMU_DBG_CONT, NULL, 129 "\tPA=%016llx data=%016llx\n", pa, reg); 130 } 131 132 static void 133 pcmu_cb_disable_nintr_reg(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino, int wait) 134 { 135 uint64_t tmp, map_reg_pa = pcmu_cb_ino_to_map_pa(pcb_p, ino); 136 ASSERT(MUTEX_HELD(&pcb_p->pcb_intr_lock)); 137 138 /* mark interrupt invalid in mapping register */ 139 tmp = lddphysio(map_reg_pa) & ~PCMU_INTR_MAP_REG_VALID; 140 stdphysio(map_reg_pa, tmp); 141 (void) lddphysio(map_reg_pa); /* flush previous write */ 142 143 if (wait) { 144 hrtime_t start_time; 145 hrtime_t prev, curr, interval, jump; 146 hrtime_t intr_timeout; 147 uint64_t state_reg_pa = pcb_p->pcb_obsta_pa; 148 uint_t shift = (ino & 0x1f) << 1; 149 150 /* busy wait if there is interrupt being processed */ 151 /* unless panic or timeout for interrupt pending is reached */ 152 153 intr_timeout = pcmu_intrpend_timeout; 154 jump = TICK_TO_NSEC(xc_tick_jump_limit); 155 start_time = curr = gethrtime(); 156 while ((((lddphysio(state_reg_pa) >> shift) & 157 PCMU_CLEAR_INTR_REG_MASK) == 158 PCMU_CLEAR_INTR_REG_PENDING) && !panicstr) { 159 /* 160 * If we have a really large jump in hrtime, it is most 161 * probably because we entered the debugger (or OBP, 162 * in general). So, we adjust the timeout accordingly 163 * to prevent declaring an interrupt timeout. The 164 * master-interrupt mechanism in OBP should deliver 165 * the interrupts properly. 166 */ 167 prev = curr; 168 curr = gethrtime(); 169 interval = curr - prev; 170 if (interval > jump) 171 intr_timeout += interval; 172 if (curr - start_time > intr_timeout) { 173 cmn_err(CE_WARN, "pcmu@%x " 174 "pcmu_cb_disable_nintr_reg(%lx,%x) timeout", 175 pcb_p->pcb_pcmu_p->pcmu_id, map_reg_pa, 176 PCMU_CB_INO_TO_MONDO(pcb_p, ino)); 177 break; 178 } 179 } 180 } 181 } 182 183 void 184 pcmu_cb_disable_nintr(pcmu_cb_t *pcb_p, pcmu_cb_nintr_index_t idx, int wait) 185 { 186 pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p; 187 volatile uint64_t *imr_p; 188 pcmu_ib_ino_t ino = pcb_p->pcb_inos[idx]; 189 ASSERT(idx < CBNINTR_MAX); 190 ASSERT(ino); 191 192 imr_p = ib_intr_map_reg_addr(pib_p, ino); 193 mutex_enter(&pcb_p->pcb_intr_lock); 194 pcmu_cb_disable_nintr_reg(pcb_p, ino, wait); 195 pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_PENDING); 196 pcb_p->pcb_inos[idx] = 0; 197 mutex_exit(&pcb_p->pcb_intr_lock); 198 u2u_ittrans_cleanup((u2u_ittrans_data_t *)(pcb_p->pcb_ittrans_cookie), 199 imr_p); 200 } 201 202 void 203 pcmu_cb_clear_nintr(pcmu_cb_t *pcb_p, pcmu_cb_nintr_index_t idx) 204 { 205 pcmu_ib_ino_t ino = pcb_p->pcb_inos[idx]; 206 ASSERT(idx < CBNINTR_MAX); 207 ASSERT(ino); 208 pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE); 209 } 210 211 void 212 pcmu_cb_intr_dist(void *arg) 213 { 214 int i; 215 pcmu_cb_t *pcb_p = (pcmu_cb_t *)arg; 216 217 mutex_enter(&pcb_p->pcb_intr_lock); 218 for (i = 0; i < pcb_p->pcb_no_of_inos; i++) { 219 uint64_t mr_pa; 220 volatile uint64_t imr; 221 pcmu_ib_mondo_t mondo; 222 uint32_t cpu_id; 223 pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p; 224 volatile uint64_t *imr_p; 225 226 pcmu_ib_ino_t ino = pcb_p->pcb_inos[i]; 227 if (!ino) /* skip non-shared interrupts */ 228 continue; 229 230 mr_pa = pcmu_cb_ino_to_map_pa(pcb_p, ino); 231 imr = lddphysio(mr_pa); 232 if (!PCMU_IB_INO_INTR_ISON(imr)) 233 continue; 234 235 mondo = PCMU_CB_INO_TO_MONDO(pcb_p, ino); 236 cpu_id = intr_dist_cpuid(); 237 imr_p = ib_intr_map_reg_addr(pib_p, ino); 238 239 cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p, cpu_id, imr_p); 240 241 pcmu_cb_disable_nintr_reg(pcb_p, ino, PCMU_IB_INTR_WAIT); 242 stdphysio(mr_pa, ib_get_map_reg(mondo, cpu_id)); 243 (void) lddphysio(mr_pa); /* flush previous write */ 244 } 245 mutex_exit(&pcb_p->pcb_intr_lock); 246 } 247 248 void 249 pcmu_cb_suspend(pcmu_cb_t *pcb_p) 250 { 251 int i, inos = pcb_p->pcb_no_of_inos; 252 ASSERT(!pcb_p->pcb_imr_save); 253 pcb_p->pcb_imr_save = kmem_alloc(inos * sizeof (uint64_t), KM_SLEEP); 254 255 /* 256 * save the internal interrupts' mapping registers content 257 * 258 * The PBM IMR really doesn't need to be saved, as it is 259 * different per side and is handled by pcmu_pbm_suspend/resume. 260 * But it complicates the logic. 261 */ 262 for (i = 0; i < inos; i++) { 263 uint64_t pa; 264 pcmu_ib_ino_t ino = pcb_p->pcb_inos[i]; 265 if (!ino) 266 continue; 267 pa = pcmu_cb_ino_to_map_pa(pcb_p, ino); 268 pcb_p->pcb_imr_save[i] = lddphysio(pa); 269 } 270 } 271 272 void 273 pcmu_cb_resume(pcmu_cb_t *pcb_p) 274 { 275 int i; 276 for (i = 0; i < pcb_p->pcb_no_of_inos; i++) { 277 uint64_t pa; 278 pcmu_ib_ino_t ino = pcb_p->pcb_inos[i]; 279 if (!ino) 280 continue; 281 pa = pcmu_cb_ino_to_map_pa(pcb_p, ino); 282 pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE); 283 stdphysio(pa, pcb_p->pcb_imr_save[i]); /* restore IMR */ 284 } 285 kmem_free(pcb_p->pcb_imr_save, 286 pcb_p->pcb_no_of_inos * sizeof (uint64_t)); 287 pcb_p->pcb_imr_save = NULL; 288 } 289