1 /* $NetBSD: pci_kn8ae.c,v 1.34 2021/07/04 22:42:36 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1997 by Matthew Jacob 5 * NASA AMES Research Center. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice immediately at the beginning of the file, without modification, 13 * this list of conditions, and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 24 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 34 35 __KERNEL_RCSID(0, "$NetBSD: pci_kn8ae.c,v 1.34 2021/07/04 22:42:36 thorpej Exp $"); 36 37 #include <sys/types.h> 38 #include <sys/param.h> 39 #include <sys/time.h> 40 #include <sys/systm.h> 41 #include <sys/errno.h> 42 #include <sys/device.h> 43 #include <sys/cpu.h> 44 #include <sys/syslog.h> 45 #include <sys/once.h> 46 47 #include <machine/autoconf.h> 48 #include <machine/rpb.h> 49 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 53 #include <alpha/pci/dwlpxreg.h> 54 #include <alpha/pci/dwlpxvar.h> 55 56 static int dec_kn8ae_intr_map(const struct pci_attach_args *, 57 pci_intr_handle_t *); 58 static const char *dec_kn8ae_intr_string(pci_chipset_tag_t, pci_intr_handle_t, 59 char *, size_t); 60 static const struct evcnt *dec_kn8ae_intr_evcnt(pci_chipset_tag_t, 61 pci_intr_handle_t); 62 static void *dec_kn8ae_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, 63 int, int (*func)(void *), void *); 64 static void dec_kn8ae_intr_disestablish(pci_chipset_tag_t, void *); 65 66 static uint32_t imaskcache[DWLPX_NIONODE][DWLPX_NHOSE][NHPC]; 67 68 static void kn8ae_spurious(void *, u_long); 69 static void kn8ae_enadis_intr(struct dwlpx_config *, pci_intr_handle_t, 70 int); 71 72 struct kn8ae_wrapped_pci_intr { 73 int (*ih_fn)(void *); 74 }; 75 76 static struct kn8ae_wrapped_pci_intr 77 kn8ae_wrapped_pci_intrs[SCB_VECTOIDX(SCB_SIZE - SCB_IOVECBASE)] 78 __read_mostly; 79 80 static void 81 kn8ae_intr_wrapper(void *arg, u_long vec) 82 { 83 const u_long idx = SCB_VECTOIDX(vec - SCB_IOVECBASE); 84 85 KERNEL_LOCK(1, NULL); 86 kn8ae_wrapped_pci_intrs[idx].ih_fn(arg); 87 KERNEL_UNLOCK_ONE(NULL); 88 } 89 90 static ONCE_DECL(pci_kn8ae_once); 91 92 static int 93 pci_kn8ae_init_imaskcache(void) 94 { 95 int io, hose, dev; 96 97 for (io = 0; io < DWLPX_NIONODE; io++) { 98 for (hose = 0; hose < DWLPX_NHOSE; hose++) { 99 for (dev = 0; dev < NHPC; dev++) { 100 imaskcache[io][hose][dev] = DWLPX_IMASK_DFLT; 101 } 102 } 103 } 104 105 return 0; 106 } 107 108 static void 109 pci_kn8ae_pickintr(void *core, bus_space_tag_t iot, bus_space_tag_t memt, 110 pci_chipset_tag_t pc) 111 { 112 113 pc->pc_intr_v = core; 114 pc->pc_intr_map = dec_kn8ae_intr_map; 115 pc->pc_intr_string = dec_kn8ae_intr_string; 116 pc->pc_intr_evcnt = dec_kn8ae_intr_evcnt; 117 pc->pc_intr_establish = dec_kn8ae_intr_establish; 118 pc->pc_intr_disestablish = dec_kn8ae_intr_disestablish; 119 120 /* Not supported on KN8AE. */ 121 pc->pc_pciide_compat_intr_establish = NULL; 122 123 RUN_ONCE(&pci_kn8ae_once, pci_kn8ae_init_imaskcache); 124 } 125 ALPHA_PCI_INTR_INIT(ST_DEC_21000, pci_kn8ae_pickintr) 126 127 #define IH_MAKE(vec, dev, pin) \ 128 ((vec) | ((dev) << 16) | ((pin) << 24)) 129 130 #define IH_VEC(ih) ((ih) & 0xffff) 131 #define IH_DEV(ih) (((ih) >> 16) & 0xff) 132 #define IH_PIN(ih) (((ih) >> 24) & 0xff) 133 134 static int 135 dec_kn8ae_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp) 136 { 137 pcitag_t bustag = pa->pa_intrtag; 138 int buspin = pa->pa_intrpin; 139 pci_chipset_tag_t pc = pa->pa_pc; 140 int device; 141 u_long vec; 142 143 if (buspin == 0) { 144 /* No IRQ used. */ 145 return 1; 146 } 147 if (buspin < 0 || buspin > 4) { 148 printf("dec_kn8ae_intr_map: bad interrupt pin %d\n", buspin); 149 return 1; 150 } 151 pci_decompose_tag(pc, bustag, NULL, &device, NULL); 152 153 mutex_enter(&cpu_lock); 154 vec = scb_alloc(kn8ae_spurious, NULL); 155 mutex_exit(&cpu_lock); 156 if (vec == SCB_ALLOC_FAILED) { 157 printf("dec_kn8ae_intr_map: no vector available for " 158 "device %d pin %d\n", device, buspin); 159 return 1; 160 } 161 162 alpha_pci_intr_handle_init(ihp, IH_MAKE(vec, device, buspin), 0); 163 164 return (0); 165 } 166 167 static const char * 168 dec_kn8ae_intr_string(pci_chipset_tag_t const pc __unused, 169 pci_intr_handle_t const ih, char * const buf, size_t const len) 170 { 171 const u_int ihv = alpha_pci_intr_handle_get_irq(&ih); 172 173 snprintf(buf, len, "vector 0x%x", IH_VEC(ihv)); 174 return buf; 175 } 176 177 static const struct evcnt * 178 dec_kn8ae_intr_evcnt(pci_chipset_tag_t const pc __unused, 179 pci_intr_handle_t const ih __unused) 180 { 181 182 /* XXX for now, no evcnt parent reported */ 183 return (NULL); 184 } 185 186 static void * 187 dec_kn8ae_intr_establish( 188 pci_chipset_tag_t const pc, 189 pci_intr_handle_t const ih, 190 int const level, 191 int (*func)(void *), 192 void *arg) 193 { 194 struct dwlpx_config * const ccp = pc->pc_intr_v; 195 void *cookie; 196 struct scbvec *scb; 197 u_long vec; 198 int pin, device, hpc; 199 void (*scb_func)(void *, u_long); 200 const u_int ihv = alpha_pci_intr_handle_get_irq(&ih); 201 const u_int flags = alpha_pci_intr_handle_get_flags(&ih); 202 203 device = IH_DEV(ihv); 204 pin = IH_PIN(ihv); 205 vec = IH_VEC(ihv); 206 207 mutex_enter(&cpu_lock); 208 209 scb = &scb_iovectab[SCB_VECTOIDX(vec - SCB_IOVECBASE)]; 210 211 if (scb->scb_func != kn8ae_spurious) { 212 mutex_exit(&cpu_lock); 213 printf("dec_kn8ae_intr_establish: vector 0x%lx not mapped\n", 214 vec); 215 return (NULL); 216 } 217 218 /* 219 * NOTE: The PCIA hardware doesn't support interrupt sharing, 220 * so we don't have to worry about it (in theory, at least). 221 */ 222 223 if (flags & ALPHA_INTR_MPSAFE) { 224 scb_func = (void (*)(void *, u_long))func; 225 } else { 226 kn8ae_wrapped_pci_intrs[ 227 SCB_VECTOIDX(vec - SCB_IOVECBASE)].ih_fn = func; 228 scb_func = kn8ae_intr_wrapper; 229 } 230 231 scb_set(vec, scb_func, arg); 232 233 if (device < 4) { 234 hpc = 0; 235 } else if (device < 8) { 236 device -= 4; 237 hpc = 1; 238 } else { 239 device -= 8; 240 hpc = 2; 241 } 242 243 REGVAL(PCIA_DEVVEC(hpc, device, pin) + ccp->cc_sysbase) = vec; 244 kn8ae_enadis_intr(ccp, ih, 1); 245 246 mutex_exit(&cpu_lock); 247 248 cookie = (void *) ih.value; 249 250 return (cookie); 251 } 252 253 static void 254 dec_kn8ae_intr_disestablish(pci_chipset_tag_t const pc, void * const cookie) 255 { 256 struct dwlpx_config * const ccp = pc->pc_intr_v; 257 const u_long ihv = (u_long) cookie; 258 pci_intr_handle_t ih = { .value = ihv }; 259 u_long vec; 260 261 vec = IH_VEC(ihv); 262 263 mutex_enter(&cpu_lock); 264 265 kn8ae_enadis_intr(ccp, ih, 0); 266 267 scb_free(vec); 268 269 mutex_exit(&cpu_lock); 270 } 271 272 static void 273 kn8ae_spurious(void * const arg __unused, u_long const vec) 274 { 275 printf("Spurious interrupt on temporary interrupt vector 0x%lx\n", vec); 276 } 277 278 static void 279 kn8ae_enadis_intr(struct dwlpx_config *ccp, pci_intr_handle_t ih, int onoff) 280 { 281 struct dwlpx_softc *sc = ccp->cc_sc; 282 const u_int ihv = alpha_pci_intr_handle_get_irq(&ih); 283 unsigned long paddr; 284 uint32_t val; 285 int ionode, hose, device, hpc, busp; 286 287 ionode = sc->dwlpx_node - 4; 288 hose = sc->dwlpx_hosenum; 289 290 device = IH_DEV(ihv); 291 busp = (1 << (IH_PIN(ihv) - 1)); 292 293 paddr = (1LL << 39); 294 paddr |= (unsigned long) ionode << 36; 295 paddr |= (unsigned long) hose << 34; 296 297 if (device < 4) { 298 hpc = 0; 299 } else if (device < 8) { 300 hpc = 1; 301 device -= 4; 302 } else { 303 hpc = 2; 304 device -= 8; 305 } 306 busp <<= (device << 2); 307 val = imaskcache[ionode][hose][hpc]; 308 if (onoff) 309 val |= busp; 310 else 311 val &= ~busp; 312 imaskcache[ionode][hose][hpc] = val; 313 #if 0 314 printf("kn8ae_%s_intr: ihv %x imsk 0x%x hpc %d TLSB node %d hose %d\n", 315 onoff? "enable" : "disable", ihv, val, hpc, ionode + 4, hose); 316 #endif 317 const u_long psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH); 318 REGVAL(PCIA_IMASK(hpc) + paddr) = val; 319 alpha_mb(); 320 alpha_pal_swpipl(psl); 321 } 322