1 /* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24 #include "priv.h" 25 26 #include <core/option.h> 27 28 static inline void 29 nvkm_mc_unk260(struct nvkm_mc *mc, u32 data) 30 { 31 const struct nvkm_mc_oclass *impl = (void *)nv_oclass(mc); 32 if (impl->unk260) 33 impl->unk260(mc, data); 34 } 35 36 static inline u32 37 nvkm_mc_intr_mask(struct nvkm_mc *mc) 38 { 39 struct nvkm_device *device = mc->subdev.device; 40 u32 intr = nvkm_rd32(device, 0x000100); 41 if (intr == 0xffffffff) /* likely fallen off the bus */ 42 intr = 0x00000000; 43 return intr; 44 } 45 46 static irqreturn_t 47 nvkm_mc_intr(int irq, void *arg) 48 { 49 struct nvkm_mc *mc = arg; 50 struct nvkm_subdev *subdev = &mc->subdev; 51 struct nvkm_device *device = subdev->device; 52 const struct nvkm_mc_oclass *oclass = (void *)nv_object(mc)->oclass; 53 const struct nvkm_mc_intr *map = oclass->intr; 54 struct nvkm_subdev *unit; 55 u32 intr; 56 57 nvkm_wr32(device, 0x000140, 0x00000000); 58 nvkm_rd32(device, 0x000140); 59 intr = nvkm_mc_intr_mask(mc); 60 if (mc->use_msi) 61 oclass->msi_rearm(mc); 62 63 if (intr) { 64 u32 stat = intr = nvkm_mc_intr_mask(mc); 65 while (map->stat) { 66 if (intr & map->stat) { 67 unit = nvkm_subdev(mc, map->unit); 68 if (unit) 69 nvkm_subdev_intr(unit); 70 stat &= ~map->stat; 71 } 72 map++; 73 } 74 75 if (stat) 76 nvkm_error(subdev, "unknown intr %08x\n", stat); 77 } 78 79 nvkm_wr32(device, 0x000140, 0x00000001); 80 return intr ? IRQ_HANDLED : IRQ_NONE; 81 } 82 83 int 84 _nvkm_mc_fini(struct nvkm_object *object, bool suspend) 85 { 86 struct nvkm_mc *mc = (void *)object; 87 struct nvkm_device *device = mc->subdev.device; 88 nvkm_wr32(device, 0x000140, 0x00000000); 89 return nvkm_subdev_fini_old(&mc->subdev, suspend); 90 } 91 92 int 93 _nvkm_mc_init(struct nvkm_object *object) 94 { 95 struct nvkm_mc *mc = (void *)object; 96 struct nvkm_device *device = mc->subdev.device; 97 int ret = nvkm_subdev_init_old(&mc->subdev); 98 if (ret) 99 return ret; 100 nvkm_wr32(device, 0x000140, 0x00000001); 101 return 0; 102 } 103 104 void 105 _nvkm_mc_dtor(struct nvkm_object *object) 106 { 107 struct nvkm_mc *mc = (void *)object; 108 struct nvkm_device *device = mc->subdev.device; 109 free_irq(mc->irq, mc); 110 if (mc->use_msi) 111 pci_disable_msi(device->pdev); 112 nvkm_subdev_destroy(&mc->subdev); 113 } 114 115 int 116 nvkm_mc_create_(struct nvkm_object *parent, struct nvkm_object *engine, 117 struct nvkm_oclass *bclass, int length, void **pobject) 118 { 119 const struct nvkm_mc_oclass *oclass = (void *)bclass; 120 struct nvkm_device *device = (void *)parent; 121 struct nvkm_mc *mc; 122 int ret; 123 124 ret = nvkm_subdev_create_(parent, engine, bclass, 0, "PMC", 125 "master", length, pobject); 126 mc = *pobject; 127 if (ret) 128 return ret; 129 130 mc->unk260 = nvkm_mc_unk260; 131 132 if (nv_device_is_pci(device)) { 133 switch (device->pdev->device & 0x0ff0) { 134 case 0x00f0: 135 case 0x02e0: 136 /* BR02? NFI how these would be handled yet exactly */ 137 break; 138 default: 139 switch (device->chipset) { 140 case 0xaa: 141 /* reported broken, nv also disable it */ 142 break; 143 default: 144 mc->use_msi = true; 145 break; 146 } 147 } 148 149 mc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI", 150 mc->use_msi); 151 152 if (mc->use_msi && oclass->msi_rearm) { 153 mc->use_msi = pci_enable_msi(device->pdev) == 0; 154 if (mc->use_msi) { 155 nvkm_debug(&mc->subdev, "MSI enabled\n"); 156 oclass->msi_rearm(mc); 157 } 158 } else { 159 mc->use_msi = false; 160 } 161 } 162 163 ret = nv_device_get_irq(device, true); 164 if (ret < 0) 165 return ret; 166 mc->irq = ret; 167 168 ret = request_irq(mc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", mc); 169 if (ret < 0) 170 return ret; 171 172 return 0; 173 } 174