18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2012 Red Hat Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#include <engine/falcon.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <core/gpuobj.h>
258c2ecf20Sopenharmony_ci#include <subdev/mc.h>
268c2ecf20Sopenharmony_ci#include <subdev/timer.h>
278c2ecf20Sopenharmony_ci#include <engine/fifo.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int
308c2ecf20Sopenharmony_cinvkm_falcon_oclass_get(struct nvkm_oclass *oclass, int index)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon = nvkm_falcon(oclass->engine);
338c2ecf20Sopenharmony_ci	int c = 0;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	while (falcon->func->sclass[c].oclass) {
368c2ecf20Sopenharmony_ci		if (c++ == index) {
378c2ecf20Sopenharmony_ci			oclass->base = falcon->func->sclass[index];
388c2ecf20Sopenharmony_ci			return index;
398c2ecf20Sopenharmony_ci		}
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	return c;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int
468c2ecf20Sopenharmony_cinvkm_falcon_cclass_bind(struct nvkm_object *object, struct nvkm_gpuobj *parent,
478c2ecf20Sopenharmony_ci			int align, struct nvkm_gpuobj **pgpuobj)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	return nvkm_gpuobj_new(object->engine->subdev.device, 256,
508c2ecf20Sopenharmony_ci			       align, true, parent, pgpuobj);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const struct nvkm_object_func
548c2ecf20Sopenharmony_cinvkm_falcon_cclass = {
558c2ecf20Sopenharmony_ci	.bind = nvkm_falcon_cclass_bind,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void
598c2ecf20Sopenharmony_cinvkm_falcon_intr(struct nvkm_engine *engine)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon = nvkm_falcon(engine);
628c2ecf20Sopenharmony_ci	struct nvkm_subdev *subdev = &falcon->engine.subdev;
638c2ecf20Sopenharmony_ci	struct nvkm_device *device = subdev->device;
648c2ecf20Sopenharmony_ci	const u32 base = falcon->addr;
658c2ecf20Sopenharmony_ci	u32 dest = nvkm_rd32(device, base + 0x01c);
668c2ecf20Sopenharmony_ci	u32 intr = nvkm_rd32(device, base + 0x008) & dest & ~(dest >> 16);
678c2ecf20Sopenharmony_ci	u32 inst = nvkm_rd32(device, base + 0x050) & 0x3fffffff;
688c2ecf20Sopenharmony_ci	struct nvkm_fifo_chan *chan;
698c2ecf20Sopenharmony_ci	unsigned long flags;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	chan = nvkm_fifo_chan_inst(device->fifo, (u64)inst << 12, &flags);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (intr & 0x00000040) {
748c2ecf20Sopenharmony_ci		if (falcon->func->intr) {
758c2ecf20Sopenharmony_ci			falcon->func->intr(falcon, chan);
768c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x004, 0x00000040);
778c2ecf20Sopenharmony_ci			intr &= ~0x00000040;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (intr & 0x00000010) {
828c2ecf20Sopenharmony_ci		nvkm_debug(subdev, "ucode halted\n");
838c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x004, 0x00000010);
848c2ecf20Sopenharmony_ci		intr &= ~0x00000010;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (intr)  {
888c2ecf20Sopenharmony_ci		nvkm_error(subdev, "intr %08x\n", intr);
898c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x004, intr);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	nvkm_fifo_chan_put(device->fifo, flags, &chan);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int
968c2ecf20Sopenharmony_cinvkm_falcon_fini(struct nvkm_engine *engine, bool suspend)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon = nvkm_falcon(engine);
998c2ecf20Sopenharmony_ci	struct nvkm_device *device = falcon->engine.subdev.device;
1008c2ecf20Sopenharmony_ci	const u32 base = falcon->addr;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (!suspend) {
1038c2ecf20Sopenharmony_ci		nvkm_memory_unref(&falcon->core);
1048c2ecf20Sopenharmony_ci		if (falcon->external) {
1058c2ecf20Sopenharmony_ci			vfree(falcon->data.data);
1068c2ecf20Sopenharmony_ci			vfree(falcon->code.data);
1078c2ecf20Sopenharmony_ci			falcon->code.data = NULL;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (nvkm_mc_enabled(device, engine->subdev.index)) {
1128c2ecf20Sopenharmony_ci		nvkm_mask(device, base + 0x048, 0x00000003, 0x00000000);
1138c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x014, 0xffffffff);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void *
1198c2ecf20Sopenharmony_civmemdup(const void *src, size_t len)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	void *p = vmalloc(len);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (p)
1248c2ecf20Sopenharmony_ci		memcpy(p, src, len);
1258c2ecf20Sopenharmony_ci	return p;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int
1298c2ecf20Sopenharmony_cinvkm_falcon_oneinit(struct nvkm_engine *engine)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon = nvkm_falcon(engine);
1328c2ecf20Sopenharmony_ci	struct nvkm_subdev *subdev = &falcon->engine.subdev;
1338c2ecf20Sopenharmony_ci	struct nvkm_device *device = subdev->device;
1348c2ecf20Sopenharmony_ci	const u32 base = falcon->addr;
1358c2ecf20Sopenharmony_ci	u32 caps;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* determine falcon capabilities */
1388c2ecf20Sopenharmony_ci	if (device->chipset <  0xa3 ||
1398c2ecf20Sopenharmony_ci	    device->chipset == 0xaa || device->chipset == 0xac) {
1408c2ecf20Sopenharmony_ci		falcon->version = 0;
1418c2ecf20Sopenharmony_ci		falcon->secret  = (falcon->addr == 0x087000) ? 1 : 0;
1428c2ecf20Sopenharmony_ci	} else {
1438c2ecf20Sopenharmony_ci		caps = nvkm_rd32(device, base + 0x12c);
1448c2ecf20Sopenharmony_ci		falcon->version = (caps & 0x0000000f);
1458c2ecf20Sopenharmony_ci		falcon->secret  = (caps & 0x00000030) >> 4;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	caps = nvkm_rd32(device, base + 0x108);
1498c2ecf20Sopenharmony_ci	falcon->code.limit = (caps & 0x000001ff) << 8;
1508c2ecf20Sopenharmony_ci	falcon->data.limit = (caps & 0x0003fe00) >> 1;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	nvkm_debug(subdev, "falcon version: %d\n", falcon->version);
1538c2ecf20Sopenharmony_ci	nvkm_debug(subdev, "secret level: %d\n", falcon->secret);
1548c2ecf20Sopenharmony_ci	nvkm_debug(subdev, "code limit: %d\n", falcon->code.limit);
1558c2ecf20Sopenharmony_ci	nvkm_debug(subdev, "data limit: %d\n", falcon->data.limit);
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int
1608c2ecf20Sopenharmony_cinvkm_falcon_init(struct nvkm_engine *engine)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon = nvkm_falcon(engine);
1638c2ecf20Sopenharmony_ci	struct nvkm_subdev *subdev = &falcon->engine.subdev;
1648c2ecf20Sopenharmony_ci	struct nvkm_device *device = subdev->device;
1658c2ecf20Sopenharmony_ci	const struct firmware *fw;
1668c2ecf20Sopenharmony_ci	char name[32] = "internal";
1678c2ecf20Sopenharmony_ci	const u32 base = falcon->addr;
1688c2ecf20Sopenharmony_ci	int ret, i;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* wait for 'uc halted' to be signalled before continuing */
1718c2ecf20Sopenharmony_ci	if (falcon->secret && falcon->version < 4) {
1728c2ecf20Sopenharmony_ci		if (!falcon->version) {
1738c2ecf20Sopenharmony_ci			nvkm_msec(device, 2000,
1748c2ecf20Sopenharmony_ci				if (nvkm_rd32(device, base + 0x008) & 0x00000010)
1758c2ecf20Sopenharmony_ci					break;
1768c2ecf20Sopenharmony_ci			);
1778c2ecf20Sopenharmony_ci		} else {
1788c2ecf20Sopenharmony_ci			nvkm_msec(device, 2000,
1798c2ecf20Sopenharmony_ci				if (!(nvkm_rd32(device, base + 0x180) & 0x80000000))
1808c2ecf20Sopenharmony_ci					break;
1818c2ecf20Sopenharmony_ci			);
1828c2ecf20Sopenharmony_ci		}
1838c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x004, 0x00000010);
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* disable all interrupts */
1878c2ecf20Sopenharmony_ci	nvkm_wr32(device, base + 0x014, 0xffffffff);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* no default ucode provided by the engine implementation, try and
1908c2ecf20Sopenharmony_ci	 * locate a "self-bootstrapping" firmware image for the engine
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (!falcon->code.data) {
1938c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x",
1948c2ecf20Sopenharmony_ci			 device->chipset, falcon->addr >> 12);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		ret = request_firmware(&fw, name, device->dev);
1978c2ecf20Sopenharmony_ci		if (ret == 0) {
1988c2ecf20Sopenharmony_ci			falcon->code.data = vmemdup(fw->data, fw->size);
1998c2ecf20Sopenharmony_ci			falcon->code.size = fw->size;
2008c2ecf20Sopenharmony_ci			falcon->data.data = NULL;
2018c2ecf20Sopenharmony_ci			falcon->data.size = 0;
2028c2ecf20Sopenharmony_ci			release_firmware(fw);
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		falcon->external = true;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* next step is to try and load "static code/data segment" firmware
2098c2ecf20Sopenharmony_ci	 * images for the engine
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	if (!falcon->code.data) {
2128c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd",
2138c2ecf20Sopenharmony_ci			 device->chipset, falcon->addr >> 12);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		ret = request_firmware(&fw, name, device->dev);
2168c2ecf20Sopenharmony_ci		if (ret) {
2178c2ecf20Sopenharmony_ci			nvkm_error(subdev, "unable to load firmware data\n");
2188c2ecf20Sopenharmony_ci			return -ENODEV;
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		falcon->data.data = vmemdup(fw->data, fw->size);
2228c2ecf20Sopenharmony_ci		falcon->data.size = fw->size;
2238c2ecf20Sopenharmony_ci		release_firmware(fw);
2248c2ecf20Sopenharmony_ci		if (!falcon->data.data)
2258c2ecf20Sopenharmony_ci			return -ENOMEM;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc",
2288c2ecf20Sopenharmony_ci			 device->chipset, falcon->addr >> 12);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		ret = request_firmware(&fw, name, device->dev);
2318c2ecf20Sopenharmony_ci		if (ret) {
2328c2ecf20Sopenharmony_ci			nvkm_error(subdev, "unable to load firmware code\n");
2338c2ecf20Sopenharmony_ci			return -ENODEV;
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		falcon->code.data = vmemdup(fw->data, fw->size);
2378c2ecf20Sopenharmony_ci		falcon->code.size = fw->size;
2388c2ecf20Sopenharmony_ci		release_firmware(fw);
2398c2ecf20Sopenharmony_ci		if (!falcon->code.data)
2408c2ecf20Sopenharmony_ci			return -ENOMEM;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	nvkm_debug(subdev, "firmware: %s (%s)\n", name, falcon->data.data ?
2448c2ecf20Sopenharmony_ci		   "static code/data segments" : "self-bootstrapping");
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* ensure any "self-bootstrapping" firmware image is in vram */
2478c2ecf20Sopenharmony_ci	if (!falcon->data.data && !falcon->core) {
2488c2ecf20Sopenharmony_ci		ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST,
2498c2ecf20Sopenharmony_ci				      falcon->code.size, 256, false,
2508c2ecf20Sopenharmony_ci				      &falcon->core);
2518c2ecf20Sopenharmony_ci		if (ret) {
2528c2ecf20Sopenharmony_ci			nvkm_error(subdev, "core allocation failed, %d\n", ret);
2538c2ecf20Sopenharmony_ci			return ret;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		nvkm_kmap(falcon->core);
2578c2ecf20Sopenharmony_ci		for (i = 0; i < falcon->code.size; i += 4)
2588c2ecf20Sopenharmony_ci			nvkm_wo32(falcon->core, i, falcon->code.data[i / 4]);
2598c2ecf20Sopenharmony_ci		nvkm_done(falcon->core);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* upload firmware bootloader (or the full code segments) */
2638c2ecf20Sopenharmony_ci	if (falcon->core) {
2648c2ecf20Sopenharmony_ci		u64 addr = nvkm_memory_addr(falcon->core);
2658c2ecf20Sopenharmony_ci		if (device->card_type < NV_C0)
2668c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x618, 0x04000000);
2678c2ecf20Sopenharmony_ci		else
2688c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x618, 0x00000114);
2698c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x11c, 0);
2708c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x110, addr >> 8);
2718c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x114, 0);
2728c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x118, 0x00006610);
2738c2ecf20Sopenharmony_ci	} else {
2748c2ecf20Sopenharmony_ci		if (falcon->code.size > falcon->code.limit ||
2758c2ecf20Sopenharmony_ci		    falcon->data.size > falcon->data.limit) {
2768c2ecf20Sopenharmony_ci			nvkm_error(subdev, "ucode exceeds falcon limit(s)\n");
2778c2ecf20Sopenharmony_ci			return -EINVAL;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (falcon->version < 3) {
2818c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0xff8, 0x00100000);
2828c2ecf20Sopenharmony_ci			for (i = 0; i < falcon->code.size / 4; i++)
2838c2ecf20Sopenharmony_ci				nvkm_wr32(device, base + 0xff4, falcon->code.data[i]);
2848c2ecf20Sopenharmony_ci		} else {
2858c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x180, 0x01000000);
2868c2ecf20Sopenharmony_ci			for (i = 0; i < falcon->code.size / 4; i++) {
2878c2ecf20Sopenharmony_ci				if ((i & 0x3f) == 0)
2888c2ecf20Sopenharmony_ci					nvkm_wr32(device, base + 0x188, i >> 6);
2898c2ecf20Sopenharmony_ci				nvkm_wr32(device, base + 0x184, falcon->code.data[i]);
2908c2ecf20Sopenharmony_ci			}
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* upload data segment (if necessary), zeroing the remainder */
2958c2ecf20Sopenharmony_ci	if (falcon->version < 3) {
2968c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0xff8, 0x00000000);
2978c2ecf20Sopenharmony_ci		for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
2988c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0xff4, falcon->data.data[i]);
2998c2ecf20Sopenharmony_ci		for (; i < falcon->data.limit; i += 4)
3008c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0xff4, 0x00000000);
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		nvkm_wr32(device, base + 0x1c0, 0x01000000);
3038c2ecf20Sopenharmony_ci		for (i = 0; !falcon->core && i < falcon->data.size / 4; i++)
3048c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x1c4, falcon->data.data[i]);
3058c2ecf20Sopenharmony_ci		for (; i < falcon->data.limit / 4; i++)
3068c2ecf20Sopenharmony_ci			nvkm_wr32(device, base + 0x1c4, 0x00000000);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* start it running */
3108c2ecf20Sopenharmony_ci	nvkm_wr32(device, base + 0x10c, 0x00000001); /* BLOCK_ON_FIFO */
3118c2ecf20Sopenharmony_ci	nvkm_wr32(device, base + 0x104, 0x00000000); /* ENTRY */
3128c2ecf20Sopenharmony_ci	nvkm_wr32(device, base + 0x100, 0x00000002); /* TRIGGER */
3138c2ecf20Sopenharmony_ci	nvkm_wr32(device, base + 0x048, 0x00000003); /* FIFO | CHSW */
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (falcon->func->init)
3168c2ecf20Sopenharmony_ci		falcon->func->init(falcon);
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic void *
3218c2ecf20Sopenharmony_cinvkm_falcon_dtor(struct nvkm_engine *engine)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	return nvkm_falcon(engine);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic const struct nvkm_engine_func
3278c2ecf20Sopenharmony_cinvkm_falcon = {
3288c2ecf20Sopenharmony_ci	.dtor = nvkm_falcon_dtor,
3298c2ecf20Sopenharmony_ci	.oneinit = nvkm_falcon_oneinit,
3308c2ecf20Sopenharmony_ci	.init = nvkm_falcon_init,
3318c2ecf20Sopenharmony_ci	.fini = nvkm_falcon_fini,
3328c2ecf20Sopenharmony_ci	.intr = nvkm_falcon_intr,
3338c2ecf20Sopenharmony_ci	.fifo.sclass = nvkm_falcon_oclass_get,
3348c2ecf20Sopenharmony_ci	.cclass = &nvkm_falcon_cclass,
3358c2ecf20Sopenharmony_ci};
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ciint
3388c2ecf20Sopenharmony_cinvkm_falcon_new_(const struct nvkm_falcon_func *func,
3398c2ecf20Sopenharmony_ci		 struct nvkm_device *device, int index, bool enable,
3408c2ecf20Sopenharmony_ci		 u32 addr, struct nvkm_engine **pengine)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct nvkm_falcon *falcon;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!(falcon = kzalloc(sizeof(*falcon), GFP_KERNEL)))
3458c2ecf20Sopenharmony_ci		return -ENOMEM;
3468c2ecf20Sopenharmony_ci	falcon->func = func;
3478c2ecf20Sopenharmony_ci	falcon->addr = addr;
3488c2ecf20Sopenharmony_ci	falcon->code.data = func->code.data;
3498c2ecf20Sopenharmony_ci	falcon->code.size = func->code.size;
3508c2ecf20Sopenharmony_ci	falcon->data.data = func->data.data;
3518c2ecf20Sopenharmony_ci	falcon->data.size = func->data.size;
3528c2ecf20Sopenharmony_ci	*pengine = &falcon->engine;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return nvkm_engine_ctor(&nvkm_falcon, device, index,
3558c2ecf20Sopenharmony_ci				enable, &falcon->engine);
3568c2ecf20Sopenharmony_ci}
357