xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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