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