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 #include <engine/falcon.h> 23 24 #include <subdev/timer.h> 25 26 void 27 nvkm_falcon_intr(struct nvkm_subdev *subdev) 28 { 29 struct nvkm_falcon *falcon = (void *)subdev; 30 struct nvkm_device *device = falcon->engine.subdev.device; 31 const u32 base = falcon->addr; 32 u32 dispatch = nvkm_rd32(device, base + 0x01c); 33 u32 intr = nvkm_rd32(device, base + 0x008) & dispatch & ~(dispatch >> 16); 34 35 if (intr & 0x00000010) { 36 nvkm_debug(subdev, "ucode halted\n"); 37 nvkm_wr32(device, base + 0x004, 0x00000010); 38 intr &= ~0x00000010; 39 } 40 41 if (intr) { 42 nvkm_error(subdev, "intr %08x\n", intr); 43 nvkm_wr32(device, base + 0x004, intr); 44 } 45 } 46 47 static void * 48 vmemdup(const void *src, size_t len) 49 { 50 void *p = vmalloc(len); 51 52 if (p) 53 memcpy(p, src, len); 54 return p; 55 } 56 57 int 58 _nvkm_falcon_init(struct nvkm_object *object) 59 { 60 struct nvkm_falcon *falcon = (void *)object; 61 struct nvkm_subdev *subdev = &falcon->engine.subdev; 62 struct nvkm_device *device = subdev->device; 63 const struct firmware *fw; 64 char name[32] = "internal"; 65 const u32 base = falcon->addr; 66 int ret, i; 67 u32 caps; 68 69 /* enable engine, and determine its capabilities */ 70 ret = nvkm_engine_init(&falcon->engine); 71 if (ret) 72 return ret; 73 74 if (device->chipset < 0xa3 || 75 device->chipset == 0xaa || device->chipset == 0xac) { 76 falcon->version = 0; 77 falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; 78 } else { 79 caps = nvkm_rd32(device, base + 0x12c); 80 falcon->version = (caps & 0x0000000f); 81 falcon->secret = (caps & 0x00000030) >> 4; 82 } 83 84 caps = nvkm_rd32(device, base + 0x108); 85 falcon->code.limit = (caps & 0x000001ff) << 8; 86 falcon->data.limit = (caps & 0x0003fe00) >> 1; 87 88 nvkm_debug(subdev, "falcon version: %d\n", falcon->version); 89 nvkm_debug(subdev, "secret level: %d\n", falcon->secret); 90 nvkm_debug(subdev, "code limit: %d\n", falcon->code.limit); 91 nvkm_debug(subdev, "data limit: %d\n", falcon->data.limit); 92 93 /* wait for 'uc halted' to be signalled before continuing */ 94 if (falcon->secret && falcon->version < 4) { 95 if (!falcon->version) { 96 nvkm_msec(device, 2000, 97 if (nvkm_rd32(device, base + 0x008) & 0x00000010) 98 break; 99 ); 100 } else { 101 nvkm_msec(device, 2000, 102 if (!(nvkm_rd32(device, base + 0x180) & 0x80000000)) 103 break; 104 ); 105 } 106 nvkm_wr32(device, base + 0x004, 0x00000010); 107 } 108 109 /* disable all interrupts */ 110 nvkm_wr32(device, base + 0x014, 0xffffffff); 111 112 /* no default ucode provided by the engine implementation, try and 113 * locate a "self-bootstrapping" firmware image for the engine 114 */ 115 if (!falcon->code.data) { 116 snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", 117 device->chipset, falcon->addr >> 12); 118 119 ret = request_firmware(&fw, name, nv_device_base(device)); 120 if (ret == 0) { 121 falcon->code.data = vmemdup(fw->data, fw->size); 122 falcon->code.size = fw->size; 123 falcon->data.data = NULL; 124 falcon->data.size = 0; 125 release_firmware(fw); 126 } 127 128 falcon->external = true; 129 } 130 131 /* next step is to try and load "static code/data segment" firmware 132 * images for the engine 133 */ 134 if (!falcon->code.data) { 135 snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", 136 device->chipset, falcon->addr >> 12); 137 138 ret = request_firmware(&fw, name, nv_device_base(device)); 139 if (ret) { 140 nvkm_error(subdev, "unable to load firmware data\n"); 141 return ret; 142 } 143 144 falcon->data.data = vmemdup(fw->data, fw->size); 145 falcon->data.size = fw->size; 146 release_firmware(fw); 147 if (!falcon->data.data) 148 return -ENOMEM; 149 150 snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", 151 device->chipset, falcon->addr >> 12); 152 153 ret = request_firmware(&fw, name, nv_device_base(device)); 154 if (ret) { 155 nvkm_error(subdev, "unable to load firmware code\n"); 156 return ret; 157 } 158 159 falcon->code.data = vmemdup(fw->data, fw->size); 160 falcon->code.size = fw->size; 161 release_firmware(fw); 162 if (!falcon->code.data) 163 return -ENOMEM; 164 } 165 166 nvkm_debug(subdev, "firmware: %s (%s)\n", name, falcon->data.data ? 167 "static code/data segments" : "self-bootstrapping"); 168 169 /* ensure any "self-bootstrapping" firmware image is in vram */ 170 if (!falcon->data.data && !falcon->core) { 171 ret = nvkm_gpuobj_new(object->parent, NULL, falcon->code.size, 172 256, 0, &falcon->core); 173 if (ret) { 174 nvkm_error(subdev, "core allocation failed, %d\n", ret); 175 return ret; 176 } 177 178 nvkm_kmap(falcon->core); 179 for (i = 0; i < falcon->code.size; i += 4) 180 nvkm_wo32(falcon->core, i, falcon->code.data[i / 4]); 181 nvkm_done(falcon->core); 182 } 183 184 /* upload firmware bootloader (or the full code segments) */ 185 if (falcon->core) { 186 if (device->card_type < NV_C0) 187 nvkm_wr32(device, base + 0x618, 0x04000000); 188 else 189 nvkm_wr32(device, base + 0x618, 0x00000114); 190 nvkm_wr32(device, base + 0x11c, 0); 191 nvkm_wr32(device, base + 0x110, falcon->core->addr >> 8); 192 nvkm_wr32(device, base + 0x114, 0); 193 nvkm_wr32(device, base + 0x118, 0x00006610); 194 } else { 195 if (falcon->code.size > falcon->code.limit || 196 falcon->data.size > falcon->data.limit) { 197 nvkm_error(subdev, "ucode exceeds falcon limit(s)\n"); 198 return -EINVAL; 199 } 200 201 if (falcon->version < 3) { 202 nvkm_wr32(device, base + 0xff8, 0x00100000); 203 for (i = 0; i < falcon->code.size / 4; i++) 204 nvkm_wr32(device, base + 0xff4, falcon->code.data[i]); 205 } else { 206 nvkm_wr32(device, base + 0x180, 0x01000000); 207 for (i = 0; i < falcon->code.size / 4; i++) { 208 if ((i & 0x3f) == 0) 209 nvkm_wr32(device, base + 0x188, i >> 6); 210 nvkm_wr32(device, base + 0x184, falcon->code.data[i]); 211 } 212 } 213 } 214 215 /* upload data segment (if necessary), zeroing the remainder */ 216 if (falcon->version < 3) { 217 nvkm_wr32(device, base + 0xff8, 0x00000000); 218 for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) 219 nvkm_wr32(device, base + 0xff4, falcon->data.data[i]); 220 for (; i < falcon->data.limit; i += 4) 221 nvkm_wr32(device, base + 0xff4, 0x00000000); 222 } else { 223 nvkm_wr32(device, base + 0x1c0, 0x01000000); 224 for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) 225 nvkm_wr32(device, base + 0x1c4, falcon->data.data[i]); 226 for (; i < falcon->data.limit / 4; i++) 227 nvkm_wr32(device, base + 0x1c4, 0x00000000); 228 } 229 230 /* start it running */ 231 nvkm_wr32(device, base + 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ 232 nvkm_wr32(device, base + 0x104, 0x00000000); /* ENTRY */ 233 nvkm_wr32(device, base + 0x100, 0x00000002); /* TRIGGER */ 234 nvkm_wr32(device, base + 0x048, 0x00000003); /* FIFO | CHSW */ 235 return 0; 236 } 237 238 int 239 _nvkm_falcon_fini(struct nvkm_object *object, bool suspend) 240 { 241 struct nvkm_falcon *falcon = (void *)object; 242 struct nvkm_device *device = falcon->engine.subdev.device; 243 const u32 base = falcon->addr; 244 245 if (!suspend) { 246 nvkm_gpuobj_ref(NULL, &falcon->core); 247 if (falcon->external) { 248 vfree(falcon->data.data); 249 vfree(falcon->code.data); 250 falcon->code.data = NULL; 251 } 252 } 253 254 nvkm_mask(device, base + 0x048, 0x00000003, 0x00000000); 255 nvkm_wr32(device, base + 0x014, 0xffffffff); 256 257 return nvkm_engine_fini(&falcon->engine, suspend); 258 } 259 260 int 261 nvkm_falcon_create_(struct nvkm_object *parent, struct nvkm_object *engine, 262 struct nvkm_oclass *oclass, u32 addr, bool enable, 263 const char *iname, const char *fname, 264 int length, void **pobject) 265 { 266 struct nvkm_falcon *falcon; 267 int ret; 268 269 ret = nvkm_engine_create_(parent, engine, oclass, enable, iname, 270 fname, length, pobject); 271 falcon = *pobject; 272 if (ret) 273 return ret; 274 275 falcon->addr = addr; 276 return 0; 277 } 278