diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/gpu/drm/nouveau/nvkm/subdev/acr | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/acr')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c | 440 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c | 486 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c | 135 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c | 284 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c | 112 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c | 58 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c | 177 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c | 253 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h | 154 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c | 230 |
11 files changed, 2339 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild new file mode 100644 index 000000000..5b9f64a89 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/Kbuild @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/acr/base.o +nvkm-y += nvkm/subdev/acr/hsfw.o +nvkm-y += nvkm/subdev/acr/lsfw.o +nvkm-y += nvkm/subdev/acr/gm200.o +nvkm-y += nvkm/subdev/acr/gm20b.o +nvkm-y += nvkm/subdev/acr/gp102.o +nvkm-y += nvkm/subdev/acr/gp108.o +nvkm-y += nvkm/subdev/acr/gp10b.o +nvkm-y += nvkm/subdev/acr/tu102.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c new file mode 100644 index 000000000..c962df991 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c @@ -0,0 +1,440 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/firmware.h> +#include <core/memory.h> +#include <subdev/mmu.h> + +static struct nvkm_acr_hsf * +nvkm_acr_hsf_find(struct nvkm_acr *acr, const char *name) +{ + struct nvkm_acr_hsf *hsf; + list_for_each_entry(hsf, &acr->hsf, head) { + if (!strcmp(hsf->name, name)) + return hsf; + } + return NULL; +} + +int +nvkm_acr_hsf_boot(struct nvkm_acr *acr, const char *name) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_hsf *hsf; + int ret; + + hsf = nvkm_acr_hsf_find(acr, name); + if (!hsf) + return -EINVAL; + + nvkm_debug(subdev, "executing %s binary\n", hsf->name); + ret = nvkm_falcon_get(hsf->falcon, subdev); + if (ret) + return ret; + + ret = hsf->func->boot(acr, hsf); + nvkm_falcon_put(hsf->falcon, subdev); + if (ret) { + nvkm_error(subdev, "%s binary failed\n", hsf->name); + return ret; + } + + nvkm_debug(subdev, "%s binary completed successfully\n", hsf->name); + return 0; +} + +static void +nvkm_acr_unload(struct nvkm_acr *acr) +{ + if (acr->done) { + nvkm_acr_hsf_boot(acr, "unload"); + acr->done = false; + } +} + +static int +nvkm_acr_load(struct nvkm_acr *acr) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_lsf *lsf; + u64 start, limit; + int ret; + + if (list_empty(&acr->lsf)) { + nvkm_debug(subdev, "No LSF(s) present.\n"); + return 0; + } + + ret = acr->func->init(acr); + if (ret) + return ret; + + acr->func->wpr_check(acr, &start, &limit); + + if (start != acr->wpr_start || limit != acr->wpr_end) { + nvkm_error(subdev, "WPR not configured as expected: " + "%016llx-%016llx vs %016llx-%016llx\n", + acr->wpr_start, acr->wpr_end, start, limit); + return -EIO; + } + + acr->done = true; + + list_for_each_entry(lsf, &acr->lsf, head) { + if (lsf->func->boot) { + ret = lsf->func->boot(lsf->falcon); + if (ret) + break; + } + } + + return ret; +} + +static int +nvkm_acr_reload(struct nvkm_acr *acr) +{ + nvkm_acr_unload(acr); + return nvkm_acr_load(acr); +} + +static struct nvkm_acr_lsf * +nvkm_acr_falcon(struct nvkm_device *device) +{ + struct nvkm_acr *acr = device->acr; + struct nvkm_acr_lsf *lsf; + + if (acr) { + list_for_each_entry(lsf, &acr->lsf, head) { + if (lsf->func->bootstrap_falcon) + return lsf; + } + } + + return NULL; +} + +int +nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask) +{ + struct nvkm_acr_lsf *acrflcn = nvkm_acr_falcon(device); + struct nvkm_acr *acr = device->acr; + unsigned long id; + + /* If there's no LS FW managing bootstrapping of other LS falcons, + * we depend on the HS firmware being able to do it instead. + */ + if (!acrflcn) { + /* Which isn't possible everywhere... */ + if ((mask & acr->func->bootstrap_falcons) == mask) { + int ret = nvkm_acr_reload(acr); + if (ret) + return ret; + + return acr->done ? 0 : -EINVAL; + } + return -ENOSYS; + } + + if ((mask & acrflcn->func->bootstrap_falcons) != mask) + return -ENOSYS; + + if (acrflcn->func->bootstrap_multiple_falcons) { + return acrflcn->func-> + bootstrap_multiple_falcons(acrflcn->falcon, mask); + } + + for_each_set_bit(id, &mask, NVKM_ACR_LSF_NUM) { + int ret = acrflcn->func->bootstrap_falcon(acrflcn->falcon, id); + if (ret) + return ret; + } + + return 0; +} + +bool +nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr *acr = device->acr; + + if (acr) { + if (acr->managed_falcons & BIT_ULL(id)) + return true; + } + + return false; +} + +static int +nvkm_acr_fini(struct nvkm_subdev *subdev, bool suspend) +{ + nvkm_acr_unload(nvkm_acr(subdev)); + return 0; +} + +static int +nvkm_acr_init(struct nvkm_subdev *subdev) +{ + if (!nvkm_acr_falcon(subdev->device)) + return 0; + + return nvkm_acr_load(nvkm_acr(subdev)); +} + +static void +nvkm_acr_cleanup(struct nvkm_acr *acr) +{ + nvkm_acr_lsfw_del_all(acr); + nvkm_acr_hsfw_del_all(acr); + nvkm_firmware_put(acr->wpr_fw); + acr->wpr_fw = NULL; +} + +static int +nvkm_acr_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_device *device = subdev->device; + struct nvkm_acr *acr = nvkm_acr(subdev); + struct nvkm_acr_hsfw *hsfw; + struct nvkm_acr_lsfw *lsfw, *lsft; + struct nvkm_acr_lsf *lsf; + u32 wpr_size = 0; + u64 falcons; + int ret, i; + + if (list_empty(&acr->hsfw)) { + nvkm_debug(subdev, "No HSFW(s)\n"); + nvkm_acr_cleanup(acr); + return 0; + } + + /* Determine layout/size of WPR image up-front, as we need to know + * it to allocate memory before we begin constructing it. + */ + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + /* Cull unknown falcons that are present in WPR image. */ + if (acr->wpr_fw) { + if (!lsfw->func) { + nvkm_acr_lsfw_del(lsfw); + continue; + } + + wpr_size = acr->wpr_fw->size; + } + + /* Ensure we've fetched falcon configuration. */ + ret = nvkm_falcon_get(lsfw->falcon, subdev); + if (ret) + return ret; + + nvkm_falcon_put(lsfw->falcon, subdev); + + if (!(lsf = kmalloc(sizeof(*lsf), GFP_KERNEL))) + return -ENOMEM; + lsf->func = lsfw->func; + lsf->falcon = lsfw->falcon; + lsf->id = lsfw->id; + list_add_tail(&lsf->head, &acr->lsf); + acr->managed_falcons |= BIT_ULL(lsf->id); + } + + /* Ensure the falcon that'll provide ACR functions is booted first. */ + lsf = nvkm_acr_falcon(device); + if (lsf) { + falcons = lsf->func->bootstrap_falcons; + list_move(&lsf->head, &acr->lsf); + } else { + falcons = acr->func->bootstrap_falcons; + } + + /* Cull falcons that can't be bootstrapped, or the HSFW can fail to + * boot and leave the GPU in a weird state. + */ + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + if (!(falcons & BIT_ULL(lsfw->id))) { + nvkm_warn(subdev, "%s falcon cannot be bootstrapped\n", + nvkm_acr_lsf_id(lsfw->id)); + nvkm_acr_lsfw_del(lsfw); + } + } + + if (!acr->wpr_fw || acr->wpr_comp) + wpr_size = acr->func->wpr_layout(acr); + + /* Allocate/Locate WPR + fill ucode blob pointer. + * + * dGPU: allocate WPR + shadow blob + * Tegra: locate WPR with regs, ensure size is sufficient, + * allocate ucode blob. + */ + ret = acr->func->wpr_alloc(acr, wpr_size); + if (ret) + return ret; + + nvkm_debug(subdev, "WPR region is from 0x%llx-0x%llx (shadow 0x%llx)\n", + acr->wpr_start, acr->wpr_end, acr->shadow_start); + + /* Write WPR to ucode blob. */ + nvkm_kmap(acr->wpr); + if (acr->wpr_fw && !acr->wpr_comp) + nvkm_wobj(acr->wpr, 0, acr->wpr_fw->data, acr->wpr_fw->size); + + if (!acr->wpr_fw || acr->wpr_comp) + acr->func->wpr_build(acr, nvkm_acr_falcon(device)); + acr->func->wpr_patch(acr, (s64)acr->wpr_start - acr->wpr_prev); + + if (acr->wpr_fw && acr->wpr_comp) { + nvkm_kmap(acr->wpr); + for (i = 0; i < acr->wpr_fw->size; i += 4) { + u32 us = nvkm_ro32(acr->wpr, i); + u32 fw = ((u32 *)acr->wpr_fw->data)[i/4]; + if (fw != us) { + nvkm_warn(subdev, "%08x: %08x %08x\n", + i, us, fw); + } + } + return -EINVAL; + } + nvkm_done(acr->wpr); + + /* Allocate instance block for ACR-related stuff. */ + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, true, + &acr->inst); + if (ret) + return ret; + + ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "acr", &acr->vmm); + if (ret) + return ret; + + acr->vmm->debug = acr->subdev.debug; + + ret = nvkm_vmm_join(acr->vmm, acr->inst); + if (ret) + return ret; + + /* Load HS firmware blobs into ACR VMM. */ + list_for_each_entry(hsfw, &acr->hsfw, head) { + nvkm_debug(subdev, "loading %s fw\n", hsfw->name); + ret = hsfw->func->load(acr, hsfw); + if (ret) + return ret; + } + + /* Kill temporary data. */ + nvkm_acr_cleanup(acr); + return 0; +} + +static void * +nvkm_acr_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_acr *acr = nvkm_acr(subdev); + struct nvkm_acr_hsf *hsf, *hst; + struct nvkm_acr_lsf *lsf, *lst; + + list_for_each_entry_safe(hsf, hst, &acr->hsf, head) { + nvkm_vmm_put(acr->vmm, &hsf->vma); + nvkm_memory_unref(&hsf->ucode); + kfree(hsf->imem); + list_del(&hsf->head); + kfree(hsf); + } + + nvkm_vmm_part(acr->vmm, acr->inst); + nvkm_vmm_unref(&acr->vmm); + nvkm_memory_unref(&acr->inst); + + nvkm_memory_unref(&acr->wpr); + + list_for_each_entry_safe(lsf, lst, &acr->lsf, head) { + list_del(&lsf->head); + kfree(lsf); + } + + nvkm_acr_cleanup(acr); + return acr; +} + +static const struct nvkm_subdev_func +nvkm_acr = { + .dtor = nvkm_acr_dtor, + .oneinit = nvkm_acr_oneinit, + .init = nvkm_acr_init, + .fini = nvkm_acr_fini, +}; + +static int +nvkm_acr_ctor_wpr(struct nvkm_acr *acr, int ver) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_device *device = subdev->device; + int ret; + + ret = nvkm_firmware_get(subdev, "acr/wpr", ver, &acr->wpr_fw); + if (ret < 0) + return ret; + + /* Pre-add LSFs in the order they appear in the FW WPR image so that + * we're able to do a binary comparison with our own generator. + */ + ret = acr->func->wpr_parse(acr); + if (ret) + return ret; + + acr->wpr_comp = nvkm_boolopt(device->cfgopt, "NvAcrWprCompare", false); + acr->wpr_prev = nvkm_longopt(device->cfgopt, "NvAcrWprPrevAddr", 0); + return 0; +} + +int +nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device, + int index, struct nvkm_acr **pacr) +{ + struct nvkm_acr *acr; + long wprfw; + + if (!(acr = *pacr = kzalloc(sizeof(*acr), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_acr, device, index, &acr->subdev); + INIT_LIST_HEAD(&acr->hsfw); + INIT_LIST_HEAD(&acr->lsfw); + INIT_LIST_HEAD(&acr->hsf); + INIT_LIST_HEAD(&acr->lsf); + + fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr); + if (IS_ERR(fwif)) + return PTR_ERR(fwif); + + acr->func = fwif->func; + + wprfw = nvkm_longopt(device->cfgopt, "NvAcrWpr", -1); + if (wprfw >= 0) { + int ret = nvkm_acr_ctor_wpr(acr, wprfw); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c new file mode 100644 index 000000000..18502fd6e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm200.c @@ -0,0 +1,486 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/falcon.h> +#include <core/firmware.h> +#include <core/memory.h> +#include <subdev/mc.h> +#include <subdev/mmu.h> +#include <subdev/pmu.h> +#include <subdev/timer.h> + +#include <nvfw/acr.h> +#include <nvfw/flcn.h> + +const struct nvkm_acr_func +gm200_acr = { +}; + +int +gm200_acr_nofw(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + nvkm_warn(&acr->subdev, "firmware unavailable\n"); + return 0; +} + +int +gm200_acr_init(struct nvkm_acr *acr) +{ + return nvkm_acr_hsf_boot(acr, "load"); +} + +void +gm200_acr_wpr_check(struct nvkm_acr *acr, u64 *start, u64 *limit) +{ + struct nvkm_device *device = acr->subdev.device; + + nvkm_wr32(device, 0x100cd4, 2); + *start = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8; + nvkm_wr32(device, 0x100cd4, 3); + *limit = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8; + *limit = *limit + 0x20000; +} + +void +gm200_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct wpr_header hdr; + struct lsb_header lsb; + struct nvkm_acr_lsf *lsfw; + u32 offset = 0; + + do { + nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr)); + wpr_header_dump(subdev, &hdr); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id != hdr.falcon_id) + continue; + + nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb)); + lsb_header_dump(subdev, &lsb); + + lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust); + break; + } + offset += sizeof(hdr); + } while (hdr.falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID); +} + +void +gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *lsfw, + struct lsb_header_tail *hdr) +{ + hdr->ucode_off = lsfw->offset.img; + hdr->ucode_size = lsfw->ucode_size; + hdr->data_size = lsfw->data_size; + hdr->bl_code_size = lsfw->bootloader_size; + hdr->bl_imem_off = lsfw->bootloader_imem_offset; + hdr->bl_data_off = lsfw->offset.bld; + hdr->bl_data_size = lsfw->bl_data_size; + hdr->app_code_off = lsfw->app_start_offset + + lsfw->app_resident_code_offset; + hdr->app_code_size = lsfw->app_resident_code_size; + hdr->app_data_off = lsfw->app_start_offset + + lsfw->app_resident_data_offset; + hdr->app_data_size = lsfw->app_resident_data_size; + hdr->flags = lsfw->func->flags; +} + +static int +gm200_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw) +{ + struct lsb_header hdr; + + if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature))) + return -EINVAL; + + memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size); + gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail); + + nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr)); + return 0; +} + +int +gm200_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct wpr_header hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_PMU, + .lazy_bootstrap = rtos && lsfw->id != rtos->id, + .status = WPR_HEADER_V0_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gm200_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V0_FALCON_ID_INVALID); + return 0; +} + +static int +gm200_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST, + ALIGN(wpr_size, 0x40000), 0x40000, true, + &acr->wpr); + if (ret) + return ret; + + acr->wpr_start = nvkm_memory_addr(acr->wpr); + acr->wpr_end = acr->wpr_start + nvkm_memory_size(acr->wpr); + return 0; +} + +u32 +gm200_acr_wpr_layout(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw; + u32 wpr = 0; + + wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + wpr = ALIGN(wpr, 256); + lsfw->offset.lsb = wpr; + wpr += sizeof(struct lsb_header); + + wpr = ALIGN(wpr, 4096); + lsfw->offset.img = wpr; + wpr += lsfw->img.size; + + wpr = ALIGN(wpr, 256); + lsfw->offset.bld = wpr; + lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256); + wpr += lsfw->bl_data_size; + } + + return wpr; +} + +int +gm200_acr_wpr_parse(struct nvkm_acr *acr) +{ + const struct wpr_header *hdr = (void *)acr->wpr_fw->data; + struct nvkm_acr_lsfw *lsfw; + + while (hdr->falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID) { + wpr_header_dump(&acr->subdev, hdr); + lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + } + + return 0; +} + +void +gm200_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc_v1 hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = hsf->vma->addr + hsf->data_addr, + .data_size = hsf->data_size, + }; + + flcn_bl_dmem_desc_v1_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +int +gm200_acr_hsfw_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf, + u32 intr_clear, u32 mbox0_ok) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_device *device = subdev->device; + struct nvkm_falcon *falcon = hsf->falcon; + u32 mbox0, mbox1; + int ret; + + /* Reset falcon. */ + nvkm_falcon_reset(falcon); + nvkm_falcon_bind_context(falcon, acr->inst); + + /* Load bootloader into IMEM. */ + nvkm_falcon_load_imem(falcon, hsf->imem, + falcon->code.limit - hsf->imem_size, + hsf->imem_size, + hsf->imem_tag, + 0, false); + + /* Load bootloader data into DMEM. */ + hsf->func->bld(acr, hsf); + + /* Boot the falcon. */ + nvkm_mc_intr_mask(device, falcon->owner->index, false); + + nvkm_falcon_wr32(falcon, 0x040, 0xdeada5a5); + nvkm_falcon_set_start_addr(falcon, hsf->imem_tag << 8); + nvkm_falcon_start(falcon); + ret = nvkm_falcon_wait_for_halt(falcon, 100); + if (ret) + return ret; + + /* Check for successful completion. */ + mbox0 = nvkm_falcon_rd32(falcon, 0x040); + mbox1 = nvkm_falcon_rd32(falcon, 0x044); + nvkm_debug(subdev, "mailbox %08x %08x\n", mbox0, mbox1); + if (mbox0 && mbox0 != mbox0_ok) + return -EIO; + + nvkm_falcon_clear_interrupt(falcon, intr_clear); + nvkm_mc_intr_mask(device, falcon->owner->index, true); + return ret; +} + +int +gm200_acr_hsfw_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw, + struct nvkm_falcon *falcon) +{ + struct nvkm_subdev *subdev = &acr->subdev; + struct nvkm_acr_hsf *hsf; + int ret; + + /* Patch the appropriate signature (production/debug) into the FW + * image, as determined by the mode the falcon is in. + */ + ret = nvkm_falcon_get(falcon, subdev); + if (ret) + return ret; + + if (hsfw->sig.patch_loc) { + if (!falcon->debug) { + nvkm_debug(subdev, "patching production signature\n"); + memcpy(hsfw->image + hsfw->sig.patch_loc, + hsfw->sig.prod.data, + hsfw->sig.prod.size); + } else { + nvkm_debug(subdev, "patching debug signature\n"); + memcpy(hsfw->image + hsfw->sig.patch_loc, + hsfw->sig.dbg.data, + hsfw->sig.dbg.size); + } + } + + nvkm_falcon_put(falcon, subdev); + + if (!(hsf = kzalloc(sizeof(*hsf), GFP_KERNEL))) + return -ENOMEM; + hsf->func = hsfw->func; + hsf->name = hsfw->name; + list_add_tail(&hsf->head, &acr->hsf); + + hsf->imem_size = hsfw->imem_size; + hsf->imem_tag = hsfw->imem_tag; + hsf->imem = kmemdup(hsfw->imem, hsfw->imem_size, GFP_KERNEL); + if (!hsf->imem) + return -ENOMEM; + + hsf->non_sec_addr = hsfw->non_sec_addr; + hsf->non_sec_size = hsfw->non_sec_size; + hsf->sec_addr = hsfw->sec_addr; + hsf->sec_size = hsfw->sec_size; + hsf->data_addr = hsfw->data_addr; + hsf->data_size = hsfw->data_size; + + /* Make the FW image accessible to the HS bootloader. */ + ret = nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST, + hsfw->image_size, 0x1000, false, &hsf->ucode); + if (ret) + return ret; + + nvkm_kmap(hsf->ucode); + nvkm_wobj(hsf->ucode, 0, hsfw->image, hsfw->image_size); + nvkm_done(hsf->ucode); + + ret = nvkm_vmm_get(acr->vmm, 12, nvkm_memory_size(hsf->ucode), + &hsf->vma); + if (ret) + return ret; + + ret = nvkm_memory_map(hsf->ucode, 0, acr->vmm, hsf->vma, NULL, 0); + if (ret) + return ret; + + hsf->falcon = falcon; + return 0; +} + +int +gm200_acr_unload_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0, 0x1d); +} + +int +gm200_acr_unload_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +const struct nvkm_acr_hsf_func +gm200_acr_unload_0 = { + .load = gm200_acr_unload_load, + .boot = gm200_acr_unload_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gm200/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gm204/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gm206/acr/ucode_unload.bin"); +MODULE_FIRMWARE("nvidia/gp100/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gm200_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_unload_0 }, + {} +}; + +int +gm200_acr_load_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0x10, 0); +} + +static int +gm200_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->wpr_region_id = 1; + desc->regions.no_regions = 2; + desc->regions.region_props[0].start_addr = acr->wpr_start >> 8; + desc->regions.region_props[0].end_addr = acr->wpr_end >> 8; + desc->regions.region_props[0].region_id = 1; + desc->regions.region_props[0].read_mask = 0xf; + desc->regions.region_props[0].write_mask = 0xc; + desc->regions.region_props[0].client_mask = 0x2; + flcn_acr_desc_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +static const struct nvkm_acr_hsf_func +gm200_acr_load_0 = { + .load = gm200_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gm200/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm200/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gm204/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm204/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gm206/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm206/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp100/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp100/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gm200_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gm200_acr_0 = { + .load = gm200_acr_load_fwif, + .unload = gm200_acr_unload_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm200_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, + .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_FECS) | + BIT_ULL(NVKM_ACR_LSF_GPCCS), +}; + +static int +gm200_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gm200_acr_fwif[] = { + { 0, gm200_acr_load, &gm200_acr_0 }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gm200_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gm200_acr_fwif, device, index, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c new file mode 100644 index 000000000..b1ecc5815 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gm20b.c @@ -0,0 +1,135 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/firmware.h> +#include <core/memory.h> +#include <subdev/mmu.h> +#include <subdev/pmu.h> + +#include <nvfw/acr.h> +#include <nvfw/flcn.h> + +int +gm20b_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + struct nvkm_subdev *subdev = &acr->subdev; + + acr->func->wpr_check(acr, &acr->wpr_start, &acr->wpr_end); + + if ((acr->wpr_end - acr->wpr_start) < wpr_size) { + nvkm_error(subdev, "WPR image too big for WPR!\n"); + return -ENOSPC; + } + + return nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST, + wpr_size, 0, true, &acr->wpr); +} + +static void +gm20b_acr_load_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr >> 8, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = (hsf->vma->addr + hsf->data_addr) >> 8, + .data_size = hsf->data_size, + }; + + flcn_bl_dmem_desc_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +static int +gm20b_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->ucode_blob_base = nvkm_memory_addr(acr->wpr); + desc->ucode_blob_size = nvkm_memory_size(acr->wpr); + flcn_acr_desc_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon); +} + +const struct nvkm_acr_hsf_func +gm20b_acr_load_0 = { + .load = gm20b_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm20b_acr_load_bld, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE("nvidia/gm20b/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gm20b/acr/ucode_load.bin"); +#endif + +static const struct nvkm_acr_hsf_fwif +gm20b_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm20b_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gm20b_acr = { + .load = gm20b_acr_load_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm20b_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +int +gm20b_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gm20b_acr_fwif[] = { + { 0, gm20b_acr_load, &gm20b_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gm20b_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gm20b_acr_fwif, device, index, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c new file mode 100644 index 000000000..e5c8303a5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp102.c @@ -0,0 +1,284 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/firmware.h> +#include <core/memory.h> +#include <subdev/mmu.h> +#include <engine/sec2.h> + +#include <nvfw/acr.h> +#include <nvfw/flcn.h> + +void +gp102_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust) +{ + struct wpr_header_v1 hdr; + struct lsb_header_v1 lsb; + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + + do { + nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr)); + wpr_header_v1_dump(&acr->subdev, &hdr); + + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id != hdr.falcon_id) + continue; + + nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb)); + lsb_header_v1_dump(&acr->subdev, &lsb); + + lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust); + break; + } + + offset += sizeof(hdr); + } while (hdr.falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID); +} + +int +gp102_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw) +{ + struct lsb_header_v1 hdr; + + if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature))) + return -EINVAL; + + memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size); + gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail); + + nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr)); + return 0; +} + +int +gp102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct lsf_signature_v1 *sig = (void *)lsfw->sig->data; + struct wpr_header_v1 hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_SEC2, + .lazy_bootstrap = rtos && lsfw->id != rtos->id, + .bin_version = sig->version, + .status = WPR_HEADER_V1_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gp102_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID); + return 0; +} + +int +gp102_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size) +{ + int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST, + ALIGN(wpr_size, 0x40000) << 1, 0x40000, true, + &acr->wpr); + if (ret) + return ret; + + acr->shadow_start = nvkm_memory_addr(acr->wpr); + acr->wpr_start = acr->shadow_start + (nvkm_memory_size(acr->wpr) >> 1); + acr->wpr_end = acr->wpr_start + (nvkm_memory_size(acr->wpr) >> 1); + return 0; +} + +u32 +gp102_acr_wpr_layout(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw; + u32 wpr = 0; + + wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header_v1); + wpr = ALIGN(wpr, 256); + + wpr += 0x100; /* Shared sub-WPR headers. */ + + list_for_each_entry(lsfw, &acr->lsfw, head) { + wpr = ALIGN(wpr, 256); + lsfw->offset.lsb = wpr; + wpr += sizeof(struct lsb_header_v1); + + wpr = ALIGN(wpr, 4096); + lsfw->offset.img = wpr; + wpr += lsfw->img.size; + + wpr = ALIGN(wpr, 256); + lsfw->offset.bld = wpr; + lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256); + wpr += lsfw->bl_data_size; + } + + return wpr; +} + +int +gp102_acr_wpr_parse(struct nvkm_acr *acr) +{ + const struct wpr_header_v1 *hdr = (void *)acr->wpr_fw->data; + struct nvkm_acr_lsfw *lsfw; + + while (hdr->falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID) { + wpr_header_v1_dump(&acr->subdev, hdr); + lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + } + + return 0; +} + +MODULE_FIRMWARE("nvidia/gp102/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp104/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp106/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gp107/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gp102_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm200_acr_unload_0 }, + {} +}; + +int +gp102_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + struct flcn_acr_desc_v1 *desc = (void *)&hsfw->image[hsfw->data_addr]; + + desc->wpr_region_id = 1; + desc->regions.no_regions = 2; + desc->regions.region_props[0].start_addr = acr->wpr_start >> 8; + desc->regions.region_props[0].end_addr = acr->wpr_end >> 8; + desc->regions.region_props[0].region_id = 1; + desc->regions.region_props[0].read_mask = 0xf; + desc->regions.region_props[0].write_mask = 0xc; + desc->regions.region_props[0].client_mask = 0x2; + desc->regions.region_props[0].shadow_mem_start_addr = + acr->shadow_start >> 8; + flcn_acr_desc_v1_dump(&acr->subdev, desc); + + return gm200_acr_hsfw_load(acr, hsfw, + &acr->subdev.device->sec2->falcon); +} + +static const struct nvkm_acr_hsf_func +gp102_acr_load_0 = { + .load = gp102_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gm200_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp102/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp102/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp104/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp104/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp106/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp106/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gp107/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp107/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gp102_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp102_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp102_acr = { + .load = gp102_acr_load_fwif, + .unload = gp102_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_build = gp102_acr_wpr_build, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +int +gp102_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad", + acr, "acr/bl", "acr/ucode_load", "load"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/unload_bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +gp102_acr_fwif[] = { + { 0, gp102_acr_load, &gp102_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp102_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp102_acr_fwif, device, index, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c new file mode 100644 index 000000000..67a7c1410 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp108.c @@ -0,0 +1,112 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <subdev/mmu.h> + +#include <nvfw/flcn.h> + +void +gp108_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + struct flcn_bl_dmem_desc_v2 hsdesc = { + .ctx_dma = FALCON_DMAIDX_VIRT, + .code_dma_base = hsf->vma->addr, + .non_sec_code_off = hsf->non_sec_addr, + .non_sec_code_size = hsf->non_sec_size, + .sec_code_off = hsf->sec_addr, + .sec_code_size = hsf->sec_size, + .code_entry_point = 0, + .data_dma_base = hsf->vma->addr + hsf->data_addr, + .data_size = hsf->data_size, + .argc = 0, + .argv = 0, + }; + + flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hsdesc); + + nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0); +} + +const struct nvkm_acr_hsf_func +gp108_acr_unload_0 = { + .load = gm200_acr_unload_load, + .boot = gm200_acr_unload_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp108/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/gv100/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +gp108_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_unload_0 }, + {} +}; + +static const struct nvkm_acr_hsf_func +gp108_acr_load_0 = { + .load = gp102_acr_load_load, + .boot = gm200_acr_load_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/gp108/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp108/acr/ucode_load.bin"); + +MODULE_FIRMWARE("nvidia/gv100/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gv100/acr/ucode_load.bin"); + +static const struct nvkm_acr_hsf_fwif +gp108_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp108_acr = { + .load = gp108_acr_load_fwif, + .unload = gp108_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_build = gp102_acr_wpr_build, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +static const struct nvkm_acr_fwif +gp108_acr_fwif[] = { + { 0, gp102_acr_load, &gp108_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp108_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp108_acr_fwif, device, index, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c new file mode 100644 index 000000000..8249f0d2d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/gp10b.c @@ -0,0 +1,58 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) +MODULE_FIRMWARE("nvidia/gp10b/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/gp10b/acr/ucode_load.bin"); +#endif + +static const struct nvkm_acr_hsf_fwif +gp10b_acr_load_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gm20b_acr_load_0 }, + {} +}; + +static const struct nvkm_acr_func +gp10b_acr = { + .load = gp10b_acr_load_fwif, + .wpr_parse = gm200_acr_wpr_parse, + .wpr_layout = gm200_acr_wpr_layout, + .wpr_alloc = gm20b_acr_wpr_alloc, + .wpr_build = gm200_acr_wpr_build, + .wpr_patch = gm200_acr_wpr_patch, + .wpr_check = gm200_acr_wpr_check, + .init = gm200_acr_init, +}; + +static const struct nvkm_acr_fwif +gp10b_acr_fwif[] = { + { 0, gm20b_acr_load, &gp10b_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +gp10b_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(gp10b_acr_fwif, device, index, pacr); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c new file mode 100644 index 000000000..a6ea89a5d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/hsfw.c @@ -0,0 +1,177 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/firmware.h> + +#include <nvfw/fw.h> +#include <nvfw/hs.h> + +static void +nvkm_acr_hsfw_del(struct nvkm_acr_hsfw *hsfw) +{ + list_del(&hsfw->head); + kfree(hsfw->imem); + kfree(hsfw->image); + kfree(hsfw->sig.prod.data); + kfree(hsfw->sig.dbg.data); + kfree(hsfw); +} + +void +nvkm_acr_hsfw_del_all(struct nvkm_acr *acr) +{ + struct nvkm_acr_hsfw *hsfw, *hsft; + list_for_each_entry_safe(hsfw, hsft, &acr->hsfw, head) { + nvkm_acr_hsfw_del(hsfw); + } +} + +static int +nvkm_acr_hsfw_load_image(struct nvkm_acr *acr, const char *name, int ver, + struct nvkm_acr_hsfw *hsfw) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct firmware *fw; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_hs_header *fwhdr; + const struct nvfw_hs_load_header *lhdr; + u32 loc, sig; + int ret; + + ret = nvkm_firmware_get(subdev, name, ver, &fw); + if (ret < 0) + return ret; + + hdr = nvfw_bin_hdr(subdev, fw->data); + fwhdr = nvfw_hs_header(subdev, fw->data + hdr->header_offset); + + /* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's + * standard format, and don't have the indirection seen in the 0x10de + * case. + */ + switch (hdr->bin_magic) { + case 0x000010de: + loc = *(u32 *)(fw->data + fwhdr->patch_loc); + sig = *(u32 *)(fw->data + fwhdr->patch_sig); + break; + case 0x3b1d14f0: + loc = fwhdr->patch_loc; + sig = fwhdr->patch_sig; + break; + default: + ret = -EINVAL; + goto done; + } + + lhdr = nvfw_hs_load_header(subdev, fw->data + fwhdr->hdr_offset); + + if (!(hsfw->image = kmalloc(hdr->data_size, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memcpy(hsfw->image, fw->data + hdr->data_offset, hdr->data_size); + hsfw->image_size = hdr->data_size; + hsfw->non_sec_addr = lhdr->non_sec_code_off; + hsfw->non_sec_size = lhdr->non_sec_code_size; + hsfw->sec_addr = lhdr->apps[0]; + hsfw->sec_size = lhdr->apps[lhdr->num_apps]; + hsfw->data_addr = lhdr->data_dma_base; + hsfw->data_size = lhdr->data_size; + + hsfw->sig.prod.size = fwhdr->sig_prod_size; + hsfw->sig.prod.data = kmemdup(fw->data + fwhdr->sig_prod_offset + sig, + hsfw->sig.prod.size, GFP_KERNEL); + if (!hsfw->sig.prod.data) { + ret = -ENOMEM; + goto done; + } + + hsfw->sig.dbg.size = fwhdr->sig_dbg_size; + hsfw->sig.dbg.data = kmemdup(fw->data + fwhdr->sig_dbg_offset + sig, + hsfw->sig.dbg.size, GFP_KERNEL); + if (!hsfw->sig.dbg.data) { + ret = -ENOMEM; + goto done; + } + + hsfw->sig.patch_loc = loc; +done: + nvkm_firmware_put(fw); + return ret; +} + +static int +nvkm_acr_hsfw_load_bl(struct nvkm_acr *acr, const char *name, int ver, + struct nvkm_acr_hsfw *hsfw) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_bl_desc *desc; + const struct firmware *fw; + u8 *data; + int ret; + + ret = nvkm_firmware_get(subdev, name, ver, &fw); + if (ret) + return ret; + + hdr = nvfw_bin_hdr(subdev, fw->data); + desc = nvfw_bl_desc(subdev, fw->data + hdr->header_offset); + data = (void *)fw->data + hdr->data_offset; + + hsfw->imem_size = desc->code_size; + hsfw->imem_tag = desc->start_tag; + hsfw->imem = kmemdup(data + desc->code_off, desc->code_size, GFP_KERNEL); + nvkm_firmware_put(fw); + if (!hsfw->imem) + return -ENOMEM; + else + return 0; +} + +int +nvkm_acr_hsfw_load(struct nvkm_acr *acr, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *fwif) +{ + struct nvkm_acr_hsfw *hsfw; + int ret; + + if (!(hsfw = kzalloc(sizeof(*hsfw), GFP_KERNEL))) + return -ENOMEM; + + hsfw->func = fwif->func; + hsfw->name = name; + list_add_tail(&hsfw->head, &acr->hsfw); + + ret = nvkm_acr_hsfw_load_bl(acr, bl, version, hsfw); + if (ret) + goto done; + + ret = nvkm_acr_hsfw_load_image(acr, fw, version, hsfw); +done: + if (ret) + nvkm_acr_hsfw_del(hsfw); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c new file mode 100644 index 000000000..9b1cf6711 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c @@ -0,0 +1,253 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" +#include <core/falcon.h> +#include <core/firmware.h> +#include <nvfw/fw.h> +#include <nvfw/ls.h> + +void +nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *lsfw) +{ + nvkm_blob_dtor(&lsfw->img); + nvkm_firmware_put(lsfw->sig); + list_del(&lsfw->head); + kfree(lsfw); +} + +void +nvkm_acr_lsfw_del_all(struct nvkm_acr *acr) +{ + struct nvkm_acr_lsfw *lsfw, *lsft; + list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { + nvkm_acr_lsfw_del(lsfw); + } +} + +static struct nvkm_acr_lsfw * +nvkm_acr_lsfw_get(struct nvkm_acr *acr, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr_lsfw *lsfw; + list_for_each_entry(lsfw, &acr->lsfw, head) { + if (lsfw->id == id) + return lsfw; + } + return NULL; +} + +struct nvkm_acr_lsfw * +nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr, + struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id) +{ + struct nvkm_acr_lsfw *lsfw; + + if (!acr || list_empty(&acr->hsfw)) + return ERR_PTR(-ENOSYS); + + lsfw = nvkm_acr_lsfw_get(acr, id); + if (lsfw && lsfw->func) { + nvkm_error(&acr->subdev, "LSFW %d redefined\n", id); + return ERR_PTR(-EEXIST); + } + + if (!lsfw) { + if (!(lsfw = kzalloc(sizeof(*lsfw), GFP_KERNEL))) + return ERR_PTR(-ENOMEM); + + lsfw->id = id; + list_add_tail(&lsfw->head, &acr->lsfw); + } + + lsfw->func = func; + lsfw->falcon = falcon; + return lsfw; +} + +static struct nvkm_acr_lsfw * +nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func, + const struct firmware **pdesc) +{ + struct nvkm_acr *acr = subdev->device->acr; + struct nvkm_acr_lsfw *lsfw; + int ret; + + if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) + return lsfw; + + ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); + if (ret) + goto done; + + ret = nvkm_firmware_load_blob(subdev, path, "image", ver, &lsfw->img); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "desc", ver, pdesc); +done: + if (ret) { + nvkm_acr_lsfw_del(lsfw); + return ERR_PTR(ret); + } + + return lsfw; +} + +static void +nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head *desc, + struct nvkm_acr_lsfw *lsfw) +{ + lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256); + lsfw->bootloader_imem_offset = desc->bootloader_imem_offset; + + lsfw->app_size = ALIGN(desc->app_size, 256); + lsfw->app_start_offset = desc->app_start_offset; + lsfw->app_imem_entry = desc->app_imem_entry; + lsfw->app_resident_code_offset = desc->app_resident_code_offset; + lsfw->app_resident_code_size = desc->app_resident_code_size; + lsfw->app_resident_data_offset = desc->app_resident_data_offset; + lsfw->app_resident_data_size = desc->app_resident_data_size; + + lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + + lsfw->bootloader_size; + lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - + lsfw->ucode_size; +} + +int +nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + const struct firmware *fw; + struct nvkm_acr_lsfw *lsfw; + + lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, + func, &fw); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + + nvkm_acr_lsfw_from_desc(&nvfw_ls_desc(subdev, fw->data)->head, lsfw); + nvkm_firmware_put(fw); + return 0; +} + +int +nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + const struct firmware *fw; + struct nvkm_acr_lsfw *lsfw; + + lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, + func, &fw); + if (IS_ERR(lsfw)) + return PTR_ERR(lsfw); + + nvkm_acr_lsfw_from_desc(&nvfw_ls_desc_v1(subdev, fw->data)->head, lsfw); + nvkm_firmware_put(fw); + return 0; +} + +int +nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev *subdev, + struct nvkm_falcon *falcon, + enum nvkm_acr_lsf_id id, + const char *path, int ver, + const struct nvkm_acr_lsf_func *func) +{ + struct nvkm_acr *acr = subdev->device->acr; + struct nvkm_acr_lsfw *lsfw; + const struct firmware *bl = NULL, *inst = NULL, *data = NULL; + const struct nvfw_bin_hdr *hdr; + const struct nvfw_bl_desc *desc; + u32 *bldata; + int ret; + + if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) + return PTR_ERR(lsfw); + + ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl); + if (ret) + goto done; + + hdr = nvfw_bin_hdr(subdev, bl->data); + desc = nvfw_bl_desc(subdev, bl->data + hdr->header_offset); + bldata = (void *)(bl->data + hdr->data_offset); + + ret = nvkm_firmware_load_name(subdev, path, "inst", ver, &inst); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "data", ver, &data); + if (ret) + goto done; + + ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); + if (ret) + goto done; + + lsfw->bootloader_size = ALIGN(desc->code_size, 256); + lsfw->bootloader_imem_offset = desc->start_tag << 8; + + lsfw->app_start_offset = lsfw->bootloader_size; + lsfw->app_imem_entry = 0; + lsfw->app_resident_code_offset = 0; + lsfw->app_resident_code_size = ALIGN(inst->size, 256); + lsfw->app_resident_data_offset = lsfw->app_resident_code_size; + lsfw->app_resident_data_size = ALIGN(data->size, 256); + lsfw->app_size = lsfw->app_resident_code_size + + lsfw->app_resident_data_size; + + lsfw->img.size = lsfw->bootloader_size + lsfw->app_size; + if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) { + ret = -ENOMEM; + goto done; + } + + memcpy(lsfw->img.data, bldata, lsfw->bootloader_size); + memcpy(lsfw->img.data + lsfw->app_start_offset + + lsfw->app_resident_code_offset, inst->data, inst->size); + memcpy(lsfw->img.data + lsfw->app_start_offset + + lsfw->app_resident_data_offset, data->data, data->size); + + lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + + lsfw->bootloader_size; + lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - + lsfw->ucode_size; + +done: + if (ret) + nvkm_acr_lsfw_del(lsfw); + nvkm_firmware_put(data); + nvkm_firmware_put(inst); + nvkm_firmware_put(bl); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h new file mode 100644 index 000000000..d71af17a1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/priv.h @@ -0,0 +1,154 @@ +#ifndef __NVKM_ACR_PRIV_H__ +#define __NVKM_ACR_PRIV_H__ +#include <subdev/acr.h> +struct lsb_header_tail; + +struct nvkm_acr_fwif { + int version; + int (*load)(struct nvkm_acr *, int version, + const struct nvkm_acr_fwif *); + const struct nvkm_acr_func *func; +}; + +int gm200_acr_nofw(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); +int gm20b_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); +int gp102_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *); + +struct nvkm_acr_lsf; +struct nvkm_acr_func { + const struct nvkm_acr_hsf_fwif *load; + const struct nvkm_acr_hsf_fwif *ahesasc; + const struct nvkm_acr_hsf_fwif *asb; + const struct nvkm_acr_hsf_fwif *unload; + int (*wpr_parse)(struct nvkm_acr *); + u32 (*wpr_layout)(struct nvkm_acr *); + int (*wpr_alloc)(struct nvkm_acr *, u32 wpr_size); + int (*wpr_build)(struct nvkm_acr *, struct nvkm_acr_lsf *rtos); + void (*wpr_patch)(struct nvkm_acr *, s64 adjust); + void (*wpr_check)(struct nvkm_acr *, u64 *start, u64 *limit); + int (*init)(struct nvkm_acr *); + void (*fini)(struct nvkm_acr *); + u64 bootstrap_falcons; +}; + +extern const struct nvkm_acr_func gm200_acr; +int gm200_acr_wpr_parse(struct nvkm_acr *); +u32 gm200_acr_wpr_layout(struct nvkm_acr *); +int gm200_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *); +void gm200_acr_wpr_patch(struct nvkm_acr *, s64); +void gm200_acr_wpr_check(struct nvkm_acr *, u64 *, u64 *); +void gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *, + struct lsb_header_tail *); +int gm200_acr_init(struct nvkm_acr *); + +int gm20b_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size); + +int gp102_acr_wpr_parse(struct nvkm_acr *); +u32 gp102_acr_wpr_layout(struct nvkm_acr *); +int gp102_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size); +int gp102_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *); +int gp102_acr_wpr_build_lsb(struct nvkm_acr *, struct nvkm_acr_lsfw *); +void gp102_acr_wpr_patch(struct nvkm_acr *, s64); + +struct nvkm_acr_hsfw { + const struct nvkm_acr_hsf_func *func; + const char *name; + struct list_head head; + + u32 imem_size; + u32 imem_tag; + u32 *imem; + + u8 *image; + u32 image_size; + u32 non_sec_addr; + u32 non_sec_size; + u32 sec_addr; + u32 sec_size; + u32 data_addr; + u32 data_size; + + struct { + struct { + void *data; + u32 size; + } prod, dbg; + u32 patch_loc; + } sig; +}; + +struct nvkm_acr_hsf_fwif { + int version; + int (*load)(struct nvkm_acr *, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *); + const struct nvkm_acr_hsf_func *func; +}; + +int nvkm_acr_hsfw_load(struct nvkm_acr *, const char *, const char *, + const char *, int, const struct nvkm_acr_hsf_fwif *); +void nvkm_acr_hsfw_del_all(struct nvkm_acr *); + +struct nvkm_acr_hsf { + const struct nvkm_acr_hsf_func *func; + const char *name; + struct list_head head; + + u32 imem_size; + u32 imem_tag; + u32 *imem; + + u32 non_sec_addr; + u32 non_sec_size; + u32 sec_addr; + u32 sec_size; + u32 data_addr; + u32 data_size; + + struct nvkm_memory *ucode; + struct nvkm_vma *vma; + struct nvkm_falcon *falcon; +}; + +struct nvkm_acr_hsf_func { + int (*load)(struct nvkm_acr *, struct nvkm_acr_hsfw *); + int (*boot)(struct nvkm_acr *, struct nvkm_acr_hsf *); + void (*bld)(struct nvkm_acr *, struct nvkm_acr_hsf *); +}; + +int gm200_acr_hsfw_load(struct nvkm_acr *, struct nvkm_acr_hsfw *, + struct nvkm_falcon *); +int gm200_acr_hsfw_boot(struct nvkm_acr *, struct nvkm_acr_hsf *, + u32 clear_intr, u32 mbox0_ok); + +int gm200_acr_load_boot(struct nvkm_acr *, struct nvkm_acr_hsf *); + +extern const struct nvkm_acr_hsf_func gm200_acr_unload_0; +int gm200_acr_unload_load(struct nvkm_acr *, struct nvkm_acr_hsfw *); +int gm200_acr_unload_boot(struct nvkm_acr *, struct nvkm_acr_hsf *); +void gm200_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *); + +extern const struct nvkm_acr_hsf_func gm20b_acr_load_0; + +int gp102_acr_load_load(struct nvkm_acr *, struct nvkm_acr_hsfw *); + +extern const struct nvkm_acr_hsf_func gp108_acr_unload_0; +void gp108_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *); + +int nvkm_acr_new_(const struct nvkm_acr_fwif *, struct nvkm_device *, int, + struct nvkm_acr **); +int nvkm_acr_hsf_boot(struct nvkm_acr *, const char *name); + +struct nvkm_acr_lsf { + const struct nvkm_acr_lsf_func *func; + struct nvkm_falcon *falcon; + enum nvkm_acr_lsf_id id; + struct list_head head; +}; + +struct nvkm_acr_lsfw *nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *, + struct nvkm_acr *, struct nvkm_falcon *, + enum nvkm_acr_lsf_id); +void nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *); +void nvkm_acr_lsfw_del_all(struct nvkm_acr *); +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c new file mode 100644 index 000000000..c4981bce9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/tu102.c @@ -0,0 +1,230 @@ +/* + * Copyright 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "priv.h" + +#include <core/firmware.h> +#include <core/memory.h> +#include <subdev/gsp.h> +#include <subdev/pmu.h> +#include <engine/sec2.h> + +#include <nvfw/acr.h> + +static int +tu102_acr_init(struct nvkm_acr *acr) +{ + int ret = nvkm_acr_hsf_boot(acr, "AHESASC"); + if (ret) + return ret; + + return nvkm_acr_hsf_boot(acr, "ASB"); +} + +static int +tu102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos) +{ + struct nvkm_acr_lsfw *lsfw; + u32 offset = 0; + int ret; + + /*XXX: shared sub-WPR headers, fill terminator for now. */ + nvkm_wo32(acr->wpr, 0x200, 0xffffffff); + + /* Fill per-LSF structures. */ + list_for_each_entry(lsfw, &acr->lsfw, head) { + struct lsf_signature_v1 *sig = (void *)lsfw->sig->data; + struct wpr_header_v1 hdr = { + .falcon_id = lsfw->id, + .lsb_offset = lsfw->offset.lsb, + .bootstrap_owner = NVKM_ACR_LSF_GSPLITE, + .lazy_bootstrap = 1, + .bin_version = sig->version, + .status = WPR_HEADER_V1_STATUS_COPY, + }; + + /* Write WPR header. */ + nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr)); + offset += sizeof(hdr); + + /* Write LSB header. */ + ret = gp102_acr_wpr_build_lsb(acr, lsfw); + if (ret) + return ret; + + /* Write ucode image. */ + nvkm_wobj(acr->wpr, lsfw->offset.img, + lsfw->img.data, + lsfw->img.size); + + /* Write bootloader data. */ + lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw); + } + + /* Finalise WPR. */ + nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID); + return 0; +} + +static int +tu102_acr_hsfw_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf) +{ + return gm200_acr_hsfw_boot(acr, hsf, 0, 0); +} + +static int +tu102_acr_hsfw_nofw(struct nvkm_acr *acr, const char *bl, const char *fw, + const char *name, int version, + const struct nvkm_acr_hsf_fwif *fwif) +{ + return 0; +} + +MODULE_FIRMWARE("nvidia/tu102/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu104/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu106/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu116/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_unload.bin"); + +MODULE_FIRMWARE("nvidia/tu117/acr/unload_bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_unload.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_unload_fwif[] = { + { 0, nvkm_acr_hsfw_load, &gp108_acr_unload_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static int +tu102_acr_asb_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw) +{ + return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->gsp->falcon); +} + +static const struct nvkm_acr_hsf_func +tu102_acr_asb_0 = { + .load = tu102_acr_asb_load, + .boot = tu102_acr_hsfw_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_asb.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_asb.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_asb_fwif[] = { + { 0, nvkm_acr_hsfw_load, &tu102_acr_asb_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static const struct nvkm_acr_hsf_func +tu102_acr_ahesasc_0 = { + .load = gp102_acr_load_load, + .boot = tu102_acr_hsfw_boot, + .bld = gp108_acr_hsfw_bld, +}; + +MODULE_FIRMWARE("nvidia/tu102/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu102/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu104/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu104/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu106/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu106/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu116/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu116/acr/ucode_ahesasc.bin"); + +MODULE_FIRMWARE("nvidia/tu117/acr/bl.bin"); +MODULE_FIRMWARE("nvidia/tu117/acr/ucode_ahesasc.bin"); + +static const struct nvkm_acr_hsf_fwif +tu102_acr_ahesasc_fwif[] = { + { 0, nvkm_acr_hsfw_load, &tu102_acr_ahesasc_0 }, + { -1, tu102_acr_hsfw_nofw }, + {} +}; + +static const struct nvkm_acr_func +tu102_acr = { + .ahesasc = tu102_acr_ahesasc_fwif, + .asb = tu102_acr_asb_fwif, + .unload = tu102_acr_unload_fwif, + .wpr_parse = gp102_acr_wpr_parse, + .wpr_layout = gp102_acr_wpr_layout, + .wpr_alloc = gp102_acr_wpr_alloc, + .wpr_patch = gp102_acr_wpr_patch, + .wpr_build = tu102_acr_wpr_build, + .wpr_check = gm200_acr_wpr_check, + .init = tu102_acr_init, +}; + +static int +tu102_acr_load(struct nvkm_acr *acr, int version, + const struct nvkm_acr_fwif *fwif) +{ + struct nvkm_subdev *subdev = &acr->subdev; + const struct nvkm_acr_hsf_fwif *hsfwif; + + hsfwif = nvkm_firmware_load(subdev, fwif->func->ahesasc, "AcrAHESASC", + acr, "acr/bl", "acr/ucode_ahesasc", + "AHESASC"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->asb, "AcrASB", + acr, "acr/bl", "acr/ucode_asb", "ASB"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload", + acr, "acr/unload_bl", "acr/ucode_unload", + "unload"); + if (IS_ERR(hsfwif)) + return PTR_ERR(hsfwif); + + return 0; +} + +static const struct nvkm_acr_fwif +tu102_acr_fwif[] = { + { 0, tu102_acr_load, &tu102_acr }, + { -1, gm200_acr_nofw, &gm200_acr }, + {} +}; + +int +tu102_acr_new(struct nvkm_device *device, int index, struct nvkm_acr **pacr) +{ + return nvkm_acr_new_(tu102_acr_fwif, device, index, pacr); +} |