1b1c39d80SAlexandre Courbot /*
2b1c39d80SAlexandre Courbot * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3b1c39d80SAlexandre Courbot *
4b1c39d80SAlexandre Courbot * Permission is hereby granted, free of charge, to any person obtaining a
5b1c39d80SAlexandre Courbot * copy of this software and associated documentation files (the "Software"),
6b1c39d80SAlexandre Courbot * to deal in the Software without restriction, including without limitation
7b1c39d80SAlexandre Courbot * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8b1c39d80SAlexandre Courbot * and/or sell copies of the Software, and to permit persons to whom the
9b1c39d80SAlexandre Courbot * Software is furnished to do so, subject to the following conditions:
10b1c39d80SAlexandre Courbot *
11b1c39d80SAlexandre Courbot * The above copyright notice and this permission notice shall be included in
12b1c39d80SAlexandre Courbot * all copies or substantial portions of the Software.
13b1c39d80SAlexandre Courbot *
14b1c39d80SAlexandre Courbot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15b1c39d80SAlexandre Courbot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16b1c39d80SAlexandre Courbot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17b1c39d80SAlexandre Courbot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18b1c39d80SAlexandre Courbot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19b1c39d80SAlexandre Courbot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20b1c39d80SAlexandre Courbot * DEALINGS IN THE SOFTWARE.
21b1c39d80SAlexandre Courbot */
22b1c39d80SAlexandre Courbot #include "priv.h"
2386ce2a71SBen Skeggs
2422dcda45SBen Skeggs #include <core/memory.h>
25989863d7SBen Skeggs #include <subdev/acr.h>
26989863d7SBen Skeggs
2722dcda45SBen Skeggs #include <nvfw/flcn.h>
2886ce2a71SBen Skeggs #include <nvfw/pmu.h>
2986ce2a71SBen Skeggs
3086ce2a71SBen Skeggs static int
gm20b_pmu_acr_bootstrap_falcon_cb(void * priv,struct nvfw_falcon_msg * hdr)31b448a266STimur Tabi gm20b_pmu_acr_bootstrap_falcon_cb(void *priv, struct nvfw_falcon_msg *hdr)
3286ce2a71SBen Skeggs {
3386ce2a71SBen Skeggs struct nv_pmu_acr_bootstrap_falcon_msg *msg =
3486ce2a71SBen Skeggs container_of(hdr, typeof(*msg), msg.hdr);
3586ce2a71SBen Skeggs return msg->falcon_id;
3686ce2a71SBen Skeggs }
3786ce2a71SBen Skeggs
3886ce2a71SBen Skeggs int
gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id)3986ce2a71SBen Skeggs gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *falcon,
4086ce2a71SBen Skeggs enum nvkm_acr_lsf_id id)
4186ce2a71SBen Skeggs {
4286ce2a71SBen Skeggs struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon);
4386ce2a71SBen Skeggs struct nv_pmu_acr_bootstrap_falcon_cmd cmd = {
4486ce2a71SBen Skeggs .cmd.hdr.unit_id = NV_PMU_UNIT_ACR,
4586ce2a71SBen Skeggs .cmd.hdr.size = sizeof(cmd),
4686ce2a71SBen Skeggs .cmd.cmd_type = NV_PMU_ACR_CMD_BOOTSTRAP_FALCON,
4786ce2a71SBen Skeggs .flags = NV_PMU_ACR_BOOTSTRAP_FALCON_FLAGS_RESET_YES,
4886ce2a71SBen Skeggs .falcon_id = id,
4986ce2a71SBen Skeggs };
5086ce2a71SBen Skeggs int ret;
5186ce2a71SBen Skeggs
5286ce2a71SBen Skeggs ret = nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr,
5386ce2a71SBen Skeggs gm20b_pmu_acr_bootstrap_falcon_cb,
5486ce2a71SBen Skeggs &pmu->subdev, msecs_to_jiffies(1000));
5589b34254SThierry Reding if (ret >= 0) {
5689b34254SThierry Reding if (ret != cmd.falcon_id)
5786ce2a71SBen Skeggs ret = -EIO;
5889b34254SThierry Reding else
5989b34254SThierry Reding ret = 0;
6089b34254SThierry Reding }
6189b34254SThierry Reding
6286ce2a71SBen Skeggs return ret;
6386ce2a71SBen Skeggs }
6486ce2a71SBen Skeggs
6522dcda45SBen Skeggs void
gm20b_pmu_acr_bld_patch(struct nvkm_acr * acr,u32 bld,s64 adjust)6622dcda45SBen Skeggs gm20b_pmu_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
6722dcda45SBen Skeggs {
6822dcda45SBen Skeggs struct loader_config hdr;
6922dcda45SBen Skeggs u64 addr;
7022dcda45SBen Skeggs
7122dcda45SBen Skeggs nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
7222dcda45SBen Skeggs addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8);
7322dcda45SBen Skeggs hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8);
7422dcda45SBen Skeggs hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8);
7522dcda45SBen Skeggs addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8);
7622dcda45SBen Skeggs hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8);
7722dcda45SBen Skeggs hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8);
7822dcda45SBen Skeggs addr = ((u64)hdr.overlay_dma_base1 << 40 | hdr.overlay_dma_base << 8);
7922dcda45SBen Skeggs hdr.overlay_dma_base = lower_32_bits((addr + adjust) << 8);
8022dcda45SBen Skeggs hdr.overlay_dma_base1 = upper_32_bits((addr + adjust) << 8);
8122dcda45SBen Skeggs nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
8222dcda45SBen Skeggs
8322dcda45SBen Skeggs loader_config_dump(&acr->subdev, &hdr);
8422dcda45SBen Skeggs }
8522dcda45SBen Skeggs
8622dcda45SBen Skeggs void
gm20b_pmu_acr_bld_write(struct nvkm_acr * acr,u32 bld,struct nvkm_acr_lsfw * lsfw)8722dcda45SBen Skeggs gm20b_pmu_acr_bld_write(struct nvkm_acr *acr, u32 bld,
8822dcda45SBen Skeggs struct nvkm_acr_lsfw *lsfw)
8922dcda45SBen Skeggs {
9022dcda45SBen Skeggs const u64 base = lsfw->offset.img + lsfw->app_start_offset;
9122dcda45SBen Skeggs const u64 code = (base + lsfw->app_resident_code_offset) >> 8;
9222dcda45SBen Skeggs const u64 data = (base + lsfw->app_resident_data_offset) >> 8;
9322dcda45SBen Skeggs const struct loader_config hdr = {
9422dcda45SBen Skeggs .dma_idx = FALCON_DMAIDX_UCODE,
9522dcda45SBen Skeggs .code_dma_base = lower_32_bits(code),
9622dcda45SBen Skeggs .code_size_total = lsfw->app_size,
9722dcda45SBen Skeggs .code_size_to_load = lsfw->app_resident_code_size,
9822dcda45SBen Skeggs .code_entry_point = lsfw->app_imem_entry,
9922dcda45SBen Skeggs .data_dma_base = lower_32_bits(data),
10022dcda45SBen Skeggs .data_size = lsfw->app_resident_data_size,
10122dcda45SBen Skeggs .overlay_dma_base = lower_32_bits(code),
10222dcda45SBen Skeggs .argc = 1,
10322dcda45SBen Skeggs .argv = lsfw->falcon->data.limit - sizeof(struct nv_pmu_args),
10422dcda45SBen Skeggs .code_dma_base1 = upper_32_bits(code),
10522dcda45SBen Skeggs .data_dma_base1 = upper_32_bits(data),
10622dcda45SBen Skeggs .overlay_dma_base1 = upper_32_bits(code),
10722dcda45SBen Skeggs };
10822dcda45SBen Skeggs
10922dcda45SBen Skeggs nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
11022dcda45SBen Skeggs }
11122dcda45SBen Skeggs
112989863d7SBen Skeggs static const struct nvkm_acr_lsf_func
113989863d7SBen Skeggs gm20b_pmu_acr = {
11422dcda45SBen Skeggs .flags = NVKM_ACR_LSF_DMACTL_REQ_CTX,
11522dcda45SBen Skeggs .bld_size = sizeof(struct loader_config),
11622dcda45SBen Skeggs .bld_write = gm20b_pmu_acr_bld_write,
11722dcda45SBen Skeggs .bld_patch = gm20b_pmu_acr_bld_patch,
118de088372SBen Skeggs .bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_PMU) |
119de088372SBen Skeggs BIT_ULL(NVKM_ACR_LSF_FECS) |
120de088372SBen Skeggs BIT_ULL(NVKM_ACR_LSF_GPCCS),
12186ce2a71SBen Skeggs .bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon,
122989863d7SBen Skeggs };
123b1c39d80SAlexandre Courbot
124d114a139SBen Skeggs static int
gm20b_pmu_acr_init_wpr_callback(void * priv,struct nvfw_falcon_msg * hdr)125b448a266STimur Tabi gm20b_pmu_acr_init_wpr_callback(void *priv, struct nvfw_falcon_msg *hdr)
126d114a139SBen Skeggs {
127d114a139SBen Skeggs struct nv_pmu_acr_init_wpr_region_msg *msg =
128d114a139SBen Skeggs container_of(hdr, typeof(*msg), msg.hdr);
129d114a139SBen Skeggs struct nvkm_pmu *pmu = priv;
130d114a139SBen Skeggs struct nvkm_subdev *subdev = &pmu->subdev;
131d114a139SBen Skeggs
132d114a139SBen Skeggs if (msg->error_code) {
133d114a139SBen Skeggs nvkm_error(subdev, "ACR WPR init failure: %d\n",
134d114a139SBen Skeggs msg->error_code);
135d114a139SBen Skeggs return -EINVAL;
136d114a139SBen Skeggs }
137d114a139SBen Skeggs
138d114a139SBen Skeggs nvkm_debug(subdev, "ACR WPR init complete\n");
139d114a139SBen Skeggs complete_all(&pmu->wpr_ready);
140d114a139SBen Skeggs return 0;
141d114a139SBen Skeggs }
142d114a139SBen Skeggs
143d114a139SBen Skeggs static int
gm20b_pmu_acr_init_wpr(struct nvkm_pmu * pmu)144d114a139SBen Skeggs gm20b_pmu_acr_init_wpr(struct nvkm_pmu *pmu)
145d114a139SBen Skeggs {
146d114a139SBen Skeggs struct nv_pmu_acr_init_wpr_region_cmd cmd = {
147d114a139SBen Skeggs .cmd.hdr.unit_id = NV_PMU_UNIT_ACR,
148d114a139SBen Skeggs .cmd.hdr.size = sizeof(cmd),
149d114a139SBen Skeggs .cmd.cmd_type = NV_PMU_ACR_CMD_INIT_WPR_REGION,
150d114a139SBen Skeggs .region_id = 1,
151d114a139SBen Skeggs .wpr_offset = 0,
152d114a139SBen Skeggs };
153d114a139SBen Skeggs
154d114a139SBen Skeggs return nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr,
155d114a139SBen Skeggs gm20b_pmu_acr_init_wpr_callback, pmu, 0);
156d114a139SBen Skeggs }
157d114a139SBen Skeggs
158ccdc0431SBen Skeggs static int
gm20b_pmu_initmsg(struct nvkm_pmu * pmu)159d114a139SBen Skeggs gm20b_pmu_initmsg(struct nvkm_pmu *pmu)
160d114a139SBen Skeggs {
161d114a139SBen Skeggs struct nv_pmu_init_msg msg;
162d114a139SBen Skeggs int ret;
163d114a139SBen Skeggs
164d114a139SBen Skeggs ret = nvkm_falcon_msgq_recv_initmsg(pmu->msgq, &msg, sizeof(msg));
165d114a139SBen Skeggs if (ret)
166d114a139SBen Skeggs return ret;
167d114a139SBen Skeggs
168d114a139SBen Skeggs if (msg.hdr.unit_id != NV_PMU_UNIT_INIT ||
169d114a139SBen Skeggs msg.msg_type != NV_PMU_INIT_MSG_INIT)
170d114a139SBen Skeggs return -EINVAL;
171d114a139SBen Skeggs
172d114a139SBen Skeggs nvkm_falcon_cmdq_init(pmu->hpq, msg.queue_info[0].index,
173d114a139SBen Skeggs msg.queue_info[0].offset,
174d114a139SBen Skeggs msg.queue_info[0].size);
175d114a139SBen Skeggs nvkm_falcon_cmdq_init(pmu->lpq, msg.queue_info[1].index,
176d114a139SBen Skeggs msg.queue_info[1].offset,
177d114a139SBen Skeggs msg.queue_info[1].size);
178d114a139SBen Skeggs nvkm_falcon_msgq_init(pmu->msgq, msg.queue_info[4].index,
179d114a139SBen Skeggs msg.queue_info[4].offset,
180d114a139SBen Skeggs msg.queue_info[4].size);
181d114a139SBen Skeggs return gm20b_pmu_acr_init_wpr(pmu);
182d114a139SBen Skeggs }
183d114a139SBen Skeggs
184ccdc0431SBen Skeggs static void
gm20b_pmu_recv(struct nvkm_pmu * pmu)185937deb06SAlexandre Courbot gm20b_pmu_recv(struct nvkm_pmu *pmu)
186937deb06SAlexandre Courbot {
187d114a139SBen Skeggs if (!pmu->initmsg_received) {
188d114a139SBen Skeggs int ret = pmu->func->initmsg(pmu);
189d114a139SBen Skeggs if (ret) {
190a9d90860SBen Skeggs nvkm_error(&pmu->subdev, "error parsing init message: %d\n", ret);
191d114a139SBen Skeggs return;
192d114a139SBen Skeggs }
193d114a139SBen Skeggs
194d114a139SBen Skeggs pmu->initmsg_received = true;
195d114a139SBen Skeggs }
196d114a139SBen Skeggs
197e1cc5798SBen Skeggs nvkm_falcon_msgq_recv(pmu->msgq);
198937deb06SAlexandre Courbot }
199937deb06SAlexandre Courbot
200a9d90860SBen Skeggs static void
gm20b_pmu_fini(struct nvkm_pmu * pmu)201a9d90860SBen Skeggs gm20b_pmu_fini(struct nvkm_pmu *pmu)
202a9d90860SBen Skeggs {
203a9d90860SBen Skeggs /*TODO: shutdown RTOS. */
204a9d90860SBen Skeggs
205a9d90860SBen Skeggs flush_work(&pmu->recv.work);
206a9d90860SBen Skeggs nvkm_falcon_cmdq_fini(pmu->lpq);
207a9d90860SBen Skeggs nvkm_falcon_cmdq_fini(pmu->hpq);
208a9d90860SBen Skeggs
209a9d90860SBen Skeggs reinit_completion(&pmu->wpr_ready);
210a9d90860SBen Skeggs
211a9d90860SBen Skeggs nvkm_falcon_put(&pmu->falcon, &pmu->subdev);
212a9d90860SBen Skeggs }
213a9d90860SBen Skeggs
214a9d90860SBen Skeggs static int
gm20b_pmu_init(struct nvkm_pmu * pmu)215a9d90860SBen Skeggs gm20b_pmu_init(struct nvkm_pmu *pmu)
216a9d90860SBen Skeggs {
217a9d90860SBen Skeggs struct nvkm_falcon *falcon = &pmu->falcon;
218a9d90860SBen Skeggs struct nv_pmu_args args = { .secure_mode = true };
219a9d90860SBen Skeggs u32 addr_args = falcon->data.limit - sizeof(args);
220a9d90860SBen Skeggs int ret;
221a9d90860SBen Skeggs
222a9d90860SBen Skeggs ret = nvkm_falcon_get(&pmu->falcon, &pmu->subdev);
223a9d90860SBen Skeggs if (ret)
224a9d90860SBen Skeggs return ret;
225a9d90860SBen Skeggs
226a9d90860SBen Skeggs pmu->initmsg_received = false;
227a9d90860SBen Skeggs
228*90741096SBen Skeggs nvkm_falcon_pio_wr(falcon, (u8 *)&args, 0, 0, DMEM, addr_args, sizeof(args), 0, false);
229a9d90860SBen Skeggs nvkm_falcon_start(falcon);
230a9d90860SBen Skeggs return 0;
231a9d90860SBen Skeggs }
232a9d90860SBen Skeggs
233ccdc0431SBen Skeggs const struct nvkm_pmu_func
234b1c39d80SAlexandre Courbot gm20b_pmu = {
2354cdd2450SBen Skeggs .flcn = &gm200_pmu_flcn,
236a9d90860SBen Skeggs .init = gm20b_pmu_init,
237a9d90860SBen Skeggs .fini = gm20b_pmu_fini,
238937deb06SAlexandre Courbot .intr = gt215_pmu_intr,
239937deb06SAlexandre Courbot .recv = gm20b_pmu_recv,
240d114a139SBen Skeggs .initmsg = gm20b_pmu_initmsg,
24138d4e5cfSKarol Herbst .reset = gf100_pmu_reset,
242b1c39d80SAlexandre Courbot };
243b1c39d80SAlexandre Courbot
244989863d7SBen Skeggs #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
245989863d7SBen Skeggs MODULE_FIRMWARE("nvidia/gm20b/pmu/desc.bin");
246989863d7SBen Skeggs MODULE_FIRMWARE("nvidia/gm20b/pmu/image.bin");
247989863d7SBen Skeggs MODULE_FIRMWARE("nvidia/gm20b/pmu/sig.bin");
248989863d7SBen Skeggs #endif
249989863d7SBen Skeggs
250989863d7SBen Skeggs int
gm20b_pmu_load(struct nvkm_pmu * pmu,int ver,const struct nvkm_pmu_fwif * fwif)251989863d7SBen Skeggs gm20b_pmu_load(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif)
252989863d7SBen Skeggs {
2532952a2b4SBen Skeggs return nvkm_acr_lsfw_load_sig_image_desc(&pmu->subdev, &pmu->falcon,
254989863d7SBen Skeggs NVKM_ACR_LSF_PMU, "pmu/",
255989863d7SBen Skeggs ver, fwif->acr);
256989863d7SBen Skeggs }
257989863d7SBen Skeggs
258989863d7SBen Skeggs static const struct nvkm_pmu_fwif
259989863d7SBen Skeggs gm20b_pmu_fwif[] = {
260989863d7SBen Skeggs { 0, gm20b_pmu_load, &gm20b_pmu, &gm20b_pmu_acr },
26138fd546bSBen Skeggs { -1, gm200_pmu_nofw, &gm20b_pmu },
262989863d7SBen Skeggs {}
263989863d7SBen Skeggs };
264989863d7SBen Skeggs
265b1c39d80SAlexandre Courbot int
gm20b_pmu_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_pmu ** ppmu)266e4b15b4cSBen Skeggs gm20b_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
267e4b15b4cSBen Skeggs struct nvkm_pmu **ppmu)
268b1c39d80SAlexandre Courbot {
269e4b15b4cSBen Skeggs return nvkm_pmu_new_(gm20b_pmu_fwif, device, type, inst, ppmu);
270b1c39d80SAlexandre Courbot }
271