1/* $NetBSD: nouveau_nvkm_subdev_pci_base.c,v 1.11 2021/12/19 12:43:45 riastradh Exp $ */ 2 3/* 4 * Copyright 2015 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs <bskeggs@redhat.com> 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_pci_base.c,v 1.11 2021/12/19 12:43:45 riastradh Exp $"); 28 29#include "priv.h" 30#include "agp.h" 31 32#include <core/option.h> 33#include <core/pci.h> 34#include <subdev/mc.h> 35 36u32 37nvkm_pci_rd32(struct nvkm_pci *pci, u16 addr) 38{ 39 return pci->func->rd32(pci, addr); 40} 41 42void 43nvkm_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) 44{ 45 pci->func->wr08(pci, addr, data); 46} 47 48void 49nvkm_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) 50{ 51 pci->func->wr32(pci, addr, data); 52} 53 54u32 55nvkm_pci_mask(struct nvkm_pci *pci, u16 addr, u32 mask, u32 value) 56{ 57 u32 data = pci->func->rd32(pci, addr); 58 pci->func->wr32(pci, addr, (data & ~mask) | value); 59 return data; 60} 61 62void 63nvkm_pci_rom_shadow(struct nvkm_pci *pci, bool shadow) 64{ 65 u32 data = nvkm_pci_rd32(pci, 0x0050); 66 if (shadow) 67 data |= 0x00000001; 68 else 69 data &= ~0x00000001; 70 nvkm_pci_wr32(pci, 0x0050, data); 71} 72 73static irqreturn_t 74nvkm_pci_intr(DRM_IRQ_ARGS) 75{ 76 struct nvkm_pci *pci = arg; 77 struct nvkm_device *device = pci->subdev.device; 78 bool handled = false; 79 80#ifndef __NetBSD__ 81 if (pci->irq < 0) 82 return IRQ_HANDLED; 83#endif 84 85 nvkm_mc_intr_unarm(device); 86 if (pci->msi) 87 pci->func->msi_rearm(pci); 88 nvkm_mc_intr(device, &handled); 89 nvkm_mc_intr_rearm(device); 90 return handled ? IRQ_HANDLED : IRQ_NONE; 91} 92 93static int 94nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend) 95{ 96 struct nvkm_pci *pci = nvkm_pci(subdev); 97 98 if (pci->agp.bridge) 99 nvkm_agp_fini(pci); 100 101 return 0; 102} 103 104static int 105nvkm_pci_preinit(struct nvkm_subdev *subdev) 106{ 107 struct nvkm_pci *pci = nvkm_pci(subdev); 108 if (pci->agp.bridge) 109 nvkm_agp_preinit(pci); 110 return 0; 111} 112 113static int 114nvkm_pci_oneinit(struct nvkm_subdev *subdev) 115{ 116 struct nvkm_pci *pci = nvkm_pci(subdev); 117 struct pci_dev *pdev = pci->pdev; 118 int ret; 119 120 if (pci_is_pcie(pci->pdev)) { 121 ret = nvkm_pcie_oneinit(pci); 122 if (ret) 123 return ret; 124 } 125 126#ifdef __NetBSD__ 127 { 128 const char *const name = device_xname(pci_dev_dev(pdev)); 129 const struct pci_attach_args *pa = &pdev->pd_pa; 130 const char *intrstr; 131 char intrbuf[PCI_INTRSTR_LEN]; 132 133 if (pdev->msi_enabled) { 134 if (pdev->pd_intr_handles == NULL) { 135 if ((ret = pci_msi_alloc_exact(pa, &pci->pci_ihp, 136 1))) { 137 aprint_error_dev(pci_dev_dev(pdev), 138 "couldn't allocate MSI (%s)\n", name); 139 /* XXX errno NetBSD->Linux */ 140 return -ret; 141 } 142 } else { 143 pci->pci_ihp = pdev->pd_intr_handles; 144 pdev->pd_intr_handles = NULL; 145 } 146 } else { 147 if ((ret = pci_intx_alloc(pa, &pci->pci_ihp))) { 148 aprint_error_dev(pci_dev_dev(pdev), 149 "couldn't allocate INTx interrupt (%s)\n", 150 name); 151 152 /* XXX errno NetBSD->Linux */ 153 return -ret; 154 } 155 } 156 157 intrstr = pci_intr_string(pa->pa_pc, pci->pci_ihp[0], 158 intrbuf, sizeof(intrbuf)); 159 pci->pci_intrcookie = pci_intr_establish_xname(pa->pa_pc, 160 pci->pci_ihp[0], IPL_DRM, nvkm_pci_intr, pci, 161 name); 162 if (pci->pci_intrcookie == NULL) { 163 aprint_error_dev(pci_dev_dev(pdev), 164 "couldn't establish interrupt at %s (%s)\n", intrstr, name); 165 pci_intr_release(pa->pa_pc, pci->pci_ihp, 1); 166 pci->pci_ihp = NULL; 167 return -EIO; /* XXX er? */ 168 } 169 170 aprint_normal_dev(pci_dev_dev(pdev), "interrupting at %s (%s)\n", 171 intrstr, name); 172 } 173#else 174 ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci); 175 if (ret) 176 return ret; 177 178 pci->irq = pdev->irq; 179#endif 180 return 0; 181} 182 183static int 184nvkm_pci_init(struct nvkm_subdev *subdev) 185{ 186 struct nvkm_pci *pci = nvkm_pci(subdev); 187 int ret; 188 189 if (pci->agp.bridge) { 190 ret = nvkm_agp_init(pci); 191 if (ret) 192 return ret; 193 } else if (pci_is_pcie(pci->pdev)) { 194 nvkm_pcie_init(pci); 195 } 196 197 if (pci->func->init) 198 pci->func->init(pci); 199 200 /* Ensure MSI interrupts are armed, for the case where there are 201 * already interrupts pending (for whatever reason) at load time. 202 */ 203 if (pci->msi) 204 pci->func->msi_rearm(pci); 205 206 return 0; 207} 208 209static void * 210nvkm_pci_dtor(struct nvkm_subdev *subdev) 211{ 212 struct nvkm_pci *pci = nvkm_pci(subdev); 213 214 nvkm_agp_dtor(pci); 215 216#ifdef __NetBSD__ 217 const struct pci_attach_args *pa = &pci->pdev->pd_pa; 218 if (pci->pci_intrcookie != NULL) { 219 pci_intr_disestablish(pa->pa_pc, pci->pci_intrcookie); 220 pci->pci_intrcookie = NULL; 221 } 222 if (pci->pci_ihp != NULL) { 223 pci_intr_release(pa->pa_pc, pci->pci_ihp, 1); 224 pci->pci_ihp = NULL; 225 } 226#else 227 if (pci->irq >= 0) { 228 /* freq_irq() will call the handler, we use pci->irq == -1 229 * to signal that it's been torn down and should be a noop. 230 */ 231 int irq = pci->irq; 232 pci->irq = -1; 233 free_irq(irq, pci); 234 } 235#endif 236 237 if (pci->msi) 238 pci_disable_msi(pci->pdev); 239 240 return nvkm_pci(subdev); 241} 242 243static const struct nvkm_subdev_func 244nvkm_pci_func = { 245 .dtor = nvkm_pci_dtor, 246 .oneinit = nvkm_pci_oneinit, 247 .preinit = nvkm_pci_preinit, 248 .init = nvkm_pci_init, 249 .fini = nvkm_pci_fini, 250}; 251 252int 253nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device, 254 int index, struct nvkm_pci **ppci) 255{ 256 struct nvkm_pci *pci; 257 258 if (!(pci = *ppci = kzalloc(sizeof(**ppci), GFP_KERNEL))) 259 return -ENOMEM; 260 nvkm_subdev_ctor(&nvkm_pci_func, device, index, &pci->subdev); 261 pci->func = func; 262 pci->pdev = device->func->pci(device)->pdev; 263#ifndef __NetBSD__ 264 pci->irq = -1; 265#endif 266 pci->pcie.speed = -1; 267 pci->pcie.width = -1; 268 269 if (device->type == NVKM_DEVICE_AGP) 270 nvkm_agp_ctor(pci); 271 272 switch (pci->pdev->device & 0x0ff0) { 273 case 0x00f0: 274 case 0x02e0: 275 /* BR02? NFI how these would be handled yet exactly */ 276 break; 277 default: 278 switch (device->chipset) { 279 case 0x84: /* G84, no mode switch with MSI */ 280 case 0xaa: 281 /* reported broken, nv also disable it */ 282 break; 283 default: 284 pci->msi = true; 285 break; 286 } 287 } 288 289#ifdef __BIG_ENDIAN 290 pci->msi = false; 291#endif 292 293 pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi); 294 if (pci->msi && func->msi_rearm) { 295 pci->msi = pci_enable_msi(pci->pdev) == 0; 296 if (pci->msi) 297 nvkm_debug(&pci->subdev, "MSI enabled\n"); 298 } else { 299 pci->msi = false; 300 } 301 302 return 0; 303} 304