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