1/* $NetBSD: pci_kn8ae.c,v 1.26 2011/04/04 20:37:45 dyoung 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.26 2011/04/04 20:37:45 dyoung 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/malloc.h> 43#include <sys/device.h> 44#include <sys/syslog.h> 45 46#include <machine/autoconf.h> 47 48#include <dev/pci/pcireg.h> 49#include <dev/pci/pcivar.h> 50 51#include <alpha/pci/dwlpxreg.h> 52#include <alpha/pci/dwlpxvar.h> 53#include <alpha/pci/pci_kn8ae.h> 54 55int dec_kn8ae_intr_map(const struct pci_attach_args *, 56 pci_intr_handle_t *); 57const char *dec_kn8ae_intr_string(void *, pci_intr_handle_t); 58const struct evcnt *dec_kn8ae_intr_evcnt(void *, pci_intr_handle_t); 59void *dec_kn8ae_intr_establish(void *, pci_intr_handle_t, 60 int, int (*func)(void *), void *); 61void dec_kn8ae_intr_disestablish(void *, void *); 62 63static uint32_t imaskcache[DWLPX_NIONODE][DWLPX_NHOSE][NHPC]; 64 65void kn8ae_spurious(void *, u_long); 66void kn8ae_enadis_intr(struct dwlpx_config *, pci_intr_handle_t, int); 67 68void 69pci_kn8ae_pickintr(struct dwlpx_config *ccp, int first) 70{ 71 int io, hose, dev; 72 pci_chipset_tag_t pc = &ccp->cc_pc; 73 74 pc->pc_intr_v = ccp; 75 pc->pc_intr_map = dec_kn8ae_intr_map; 76 pc->pc_intr_string = dec_kn8ae_intr_string; 77 pc->pc_intr_evcnt = dec_kn8ae_intr_evcnt; 78 pc->pc_intr_establish = dec_kn8ae_intr_establish; 79 pc->pc_intr_disestablish = dec_kn8ae_intr_disestablish; 80 81 /* Not supported on KN8AE. */ 82 pc->pc_pciide_compat_intr_establish = NULL; 83 84 if (!first) { 85 return; 86 } 87 88 for (io = 0; io < DWLPX_NIONODE; io++) { 89 for (hose = 0; hose < DWLPX_NHOSE; hose++) { 90 for (dev = 0; dev < NHPC; dev++) { 91 imaskcache[io][hose][dev] = DWLPX_IMASK_DFLT; 92 } 93 } 94 } 95} 96 97#define IH_MAKE(vec, dev, pin) \ 98 ((vec) | ((dev) << 16) | ((pin) << 24)) 99 100#define IH_VEC(ih) ((ih) & 0xffff) 101#define IH_DEV(ih) (((ih) >> 16) & 0xff) 102#define IH_PIN(ih) (((ih) >> 24) & 0xff) 103 104int 105dec_kn8ae_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp) 106{ 107 pcitag_t bustag = pa->pa_intrtag; 108 int buspin = pa->pa_intrpin; 109 pci_chipset_tag_t pc = pa->pa_pc; 110 int device; 111 u_long vec; 112 113 if (buspin == 0) { 114 /* No IRQ used. */ 115 return 1; 116 } 117 if (buspin > 4) { 118 printf("dec_kn8ae_intr_map: bad interrupt pin %d\n", buspin); 119 return 1; 120 } 121 pci_decompose_tag(pc, bustag, NULL, &device, NULL); 122 123 vec = scb_alloc(kn8ae_spurious, NULL); 124 if (vec == SCB_ALLOC_FAILED) { 125 printf("dec_kn8ae_intr_map: no vector available for " 126 "device %d pin %d\n", device, buspin); 127 return 1; 128 } 129 130 *ihp = IH_MAKE(vec, device, buspin); 131 132 return (0); 133} 134 135const char * 136dec_kn8ae_intr_string(void *ccv, pci_intr_handle_t ih) 137{ 138 static char irqstr[64]; 139 140 sprintf(irqstr, "vector 0x%lx", IH_VEC(ih)); 141 142 return (irqstr); 143} 144 145const struct evcnt * 146dec_kn8ae_intr_evcnt(void *ccv, pci_intr_handle_t ih) 147{ 148 149 /* XXX for now, no evcnt parent reported */ 150 return (NULL); 151} 152 153void * 154dec_kn8ae_intr_establish( 155 void *ccv, 156 pci_intr_handle_t ih, 157 int level, 158 int (*func)(void *), 159 void *arg) 160{ 161 struct dwlpx_config *ccp = ccv; 162 void *cookie; 163 struct scbvec *scb; 164 u_long vec; 165 int pin, device, hpc; 166 167 device = IH_DEV(ih); 168 pin = IH_PIN(ih); 169 vec = IH_VEC(ih); 170 171 scb = &scb_iovectab[SCB_VECTOIDX(vec - SCB_IOVECBASE)]; 172 173 if (scb->scb_func != kn8ae_spurious) { 174 printf("dec_kn8ae_intr_establish: vector 0x%lx not mapped\n", 175 vec); 176 return (NULL); 177 } 178 179 /* 180 * NOTE: The PCIA hardware doesn't support interrupt sharing, 181 * so we don't have to worry about it (in theory, at least). 182 */ 183 184 scb->scb_arg = arg; 185 alpha_mb(); 186 scb->scb_func = (void (*)(void *, u_long))func; 187 alpha_mb(); 188 189 if (device < 4) { 190 hpc = 0; 191 } else if (device < 8) { 192 device -= 4; 193 hpc = 1; 194 } else { 195 device -= 8; 196 hpc = 2; 197 } 198 REGVAL(PCIA_DEVVEC(hpc, device, pin) + ccp->cc_sysbase) = vec; 199 200 kn8ae_enadis_intr(ccp, ih, 1); 201 202 cookie = (void *) ih; 203 204 return (cookie); 205} 206 207void 208dec_kn8ae_intr_disestablish(void *ccv, void *cookie) 209{ 210 struct dwlpx_config *ccp = ccv; 211 pci_intr_handle_t ih = (u_long) cookie; 212 struct scbvec *scb; 213 u_long vec; 214 215 vec = IH_VEC(ih); 216 217 scb = &scb_iovectab[SCB_VECTOIDX(vec - SCB_IOVECBASE)]; 218 219 kn8ae_enadis_intr(ccp, ih, 0); 220 221 scb_free(vec); 222} 223 224void 225kn8ae_spurious(void *arg, u_long vec) 226{ 227 printf("Spurious interrupt on temporary interrupt vector 0x%lx\n", vec); 228} 229 230void 231kn8ae_enadis_intr(struct dwlpx_config *ccp, pci_intr_handle_t irq, int onoff) 232{ 233 struct dwlpx_softc *sc = ccp->cc_sc; 234 unsigned long paddr; 235 uint32_t val; 236 int ionode, hose, device, hpc, busp, s; 237 238 ionode = sc->dwlpx_node - 4; 239 hose = sc->dwlpx_hosenum; 240 241 device = IH_DEV(irq); 242 busp = (1 << (IH_PIN(irq) - 1)); 243 244 paddr = (1LL << 39); 245 paddr |= (unsigned long) ionode << 36; 246 paddr |= (unsigned long) hose << 34; 247 248 if (device < 4) { 249 hpc = 0; 250 } else if (device < 8) { 251 hpc = 1; 252 device -= 4; 253 } else { 254 hpc = 2; 255 device -= 8; 256 } 257 busp <<= (device << 2); 258 val = imaskcache[ionode][hose][hpc]; 259 if (onoff) 260 val |= busp; 261 else 262 val &= ~busp; 263 imaskcache[ionode][hose][hpc] = val; 264#if 0 265 printf("kn8ae_%s_intr: irq %lx imsk 0x%x hpc %d TLSB node %d hose %d\n", 266 onoff? "enable" : "disable", irq, val, hpc, ionode + 4, hose); 267#endif 268 s = splhigh(); 269 REGVAL(PCIA_IMASK(hpc) + paddr) = val; 270 alpha_mb(); 271 (void) splx(s); 272} 273