xref: /linux/drivers/staging/media/ipu7/ipu7-boot.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2022 - 2025 Intel Corporation
4  */
5 
6 #include <linux/bug.h>
7 #include <linux/delay.h>
8 #include <linux/device.h>
9 #include <linux/dma-mapping.h>
10 #include <linux/module.h>
11 #include <linux/iopoll.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 
15 #include "abi/ipu7_fw_boot_abi.h"
16 
17 #include "ipu7.h"
18 #include "ipu7-boot.h"
19 #include "ipu7-bus.h"
20 #include "ipu7-buttress-regs.h"
21 #include "ipu7-dma.h"
22 #include "ipu7-platform-regs.h"
23 #include "ipu7-syscom.h"
24 
25 #define IPU_FW_START_STOP_TIMEOUT		2000
26 #define IPU_BOOT_CELL_RESET_TIMEOUT		(2 * USEC_PER_SEC)
27 #define BOOT_STATE_IS_CRITICAL(s)	IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s)
28 #define BOOT_STATE_IS_READY(s)		((s) == IA_GOFO_FW_BOOT_STATE_READY)
29 #define BOOT_STATE_IS_INACTIVE(s)	((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE)
30 
31 struct ipu7_boot_context {
32 	u32 base;
33 	u32 dmem_address;
34 	u32 status_ctrl_reg;
35 	u32 fw_start_address_reg;
36 	u32 fw_code_base_reg;
37 };
38 
39 static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = {
40 	{
41 		/* ISYS */
42 		.dmem_address = IPU_ISYS_DMEM_OFFSET,
43 		.status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS,
44 		.fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR,
45 		.fw_code_base_reg = IS_UC_CTRL_BASE
46 	},
47 	{
48 		/* PSYS */
49 		.dmem_address = IPU_PSYS_DMEM_OFFSET,
50 		.status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS,
51 		.fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR,
52 		.fw_code_base_reg = PS_UC_CTRL_BASE
53 	}
54 };
55 
56 static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev,
57 				enum ia_gofo_buttress_reg_id reg)
58 {
59 	u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX;
60 
61 	return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg);
62 }
63 
64 static void write_fw_boot_param(const struct ipu7_bus_device *adev,
65 				enum ia_gofo_buttress_reg_id reg,
66 				u32 val)
67 {
68 	void __iomem *base = adev->isp->base;
69 
70 	dev_dbg(&adev->auxdev.dev,
71 		"write boot param reg: %d addr: %x val: 0x%x\n",
72 		reg, get_fw_boot_reg_addr(adev, reg), val);
73 	writel(val, base + get_fw_boot_reg_addr(adev, reg));
74 }
75 
76 static u32 read_fw_boot_param(const struct ipu7_bus_device *adev,
77 			      enum ia_gofo_buttress_reg_id reg)
78 {
79 	void __iomem *base = adev->isp->base;
80 
81 	return readl(base + get_fw_boot_reg_addr(adev, reg));
82 }
83 
84 static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev)
85 {
86 	const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
87 	const struct device *dev = &adev->auxdev.dev;
88 	u32 ucx_ctrl_status = ctx->status_ctrl_reg;
89 	u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT;
90 	void __iomem *base = adev->isp->base;
91 	u32 val, val2;
92 	int ret;
93 
94 	dev_dbg(dev, "cell enter reset...\n");
95 	val = readl(base + ucx_ctrl_status);
96 	dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n",
97 		ucx_ctrl_status, val);
98 
99 	dev_dbg(dev, "force cell reset...\n");
100 	val |= UCX_CTL_RESET;
101 	val &= ~UCX_CTL_RUN;
102 
103 	dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
104 		ucx_ctrl_status, val);
105 	writel(val, base + ucx_ctrl_status);
106 
107 	ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
108 				 (val2 & 0x3U) == (val & 0x3U), 100, timeout);
109 	if (ret) {
110 		dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2);
111 		return -ETIMEDOUT;
112 	}
113 
114 	dev_dbg(dev, "cell exit reset...\n");
115 	val = readl(base + ucx_ctrl_status);
116 	WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN),
117 	     "cell status 0x%x", val);
118 
119 	val &= ~(UCX_CTL_RESET | UCX_CTL_RUN);
120 	dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
121 		ucx_ctrl_status, val);
122 	writel(val, base + ucx_ctrl_status);
123 
124 	ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
125 				 (val2 & 0x3U) == (val & 0x3U), 100, timeout);
126 	if (ret) {
127 		dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2);
128 		return -ETIMEDOUT;
129 	}
130 
131 	return 0;
132 }
133 
134 static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev)
135 {
136 	const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
137 	void __iomem *base = adev->isp->base;
138 	const struct device *dev = &adev->auxdev.dev;
139 	u32 val;
140 
141 	dev_dbg(dev, "starting cell...\n");
142 	val = readl(base + ctx->status_ctrl_reg);
143 	WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN));
144 
145 	val &= ~UCX_CTL_RESET;
146 	val |= UCX_CTL_RUN;
147 	dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
148 		ctx->status_ctrl_reg, val);
149 	writel(val, base + ctx->status_ctrl_reg);
150 }
151 
152 static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev)
153 {
154 	const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
155 	void __iomem *base = adev->isp->base;
156 	const struct device *dev = &adev->auxdev.dev;
157 	u32 val;
158 
159 	dev_dbg(dev, "stopping cell...\n");
160 
161 	val = readl(base + ctx->status_ctrl_reg);
162 	val &= ~UCX_CTL_RUN;
163 	dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
164 		ctx->status_ctrl_reg, val);
165 	writel(val, base + ctx->status_ctrl_reg);
166 
167 	/* Wait for uC transactions complete */
168 	usleep_range(10, 20);
169 
170 	val = readl(base + ctx->status_ctrl_reg);
171 	val |= UCX_CTL_RESET;
172 	dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
173 		ctx->status_ctrl_reg, val);
174 	writel(val, base + ctx->status_ctrl_reg);
175 }
176 
177 static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev)
178 {
179 	const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
180 	void __iomem *base = adev->isp->base;
181 
182 	dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n",
183 		ctx->fw_start_address_reg, adev->fw_entry);
184 	writel(adev->fw_entry, base + ctx->fw_start_address_reg);
185 
186 	return ipu7_boot_cell_reset(adev);
187 }
188 
189 static void init_boot_config(struct ia_gofo_boot_config *boot_config,
190 			     u32 length, u8 major)
191 {
192 	/* syscom version, new syscom2 version */
193 	boot_config->length = length;
194 	boot_config->config_version.major = 1U;
195 	boot_config->config_version.minor = 0U;
196 	boot_config->config_version.subminor = 0U;
197 	boot_config->config_version.patch = 0U;
198 
199 	/* msg version for task interface */
200 	boot_config->client_version_support.num_versions = 1U;
201 	boot_config->client_version_support.versions[0].major = major;
202 	boot_config->client_version_support.versions[0].minor = 0U;
203 	boot_config->client_version_support.versions[0].subminor = 0U;
204 	boot_config->client_version_support.versions[0].patch = 0U;
205 }
206 
207 int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev,
208 			       struct syscom_queue_config *qconfigs,
209 			       int num_queues, u32 uc_freq,
210 			       dma_addr_t subsys_config, u8 major)
211 {
212 	u32 total_queue_size_aligned = 0;
213 	struct ipu7_syscom_context *syscom = adev->syscom;
214 	struct ia_gofo_boot_config *boot_config;
215 	struct syscom_queue_params_config *cfgs;
216 	struct device *dev = &adev->auxdev.dev;
217 	struct syscom_config_s *syscfg;
218 	dma_addr_t queue_mem_dma_ptr;
219 	void *queue_mem_ptr;
220 	unsigned int i;
221 
222 	dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n",
223 		num_queues, uc_freq, &subsys_config);
224 	/* Allocate boot config. */
225 	adev->boot_config_size =
226 		sizeof(*cfgs) * num_queues + sizeof(*boot_config);
227 	adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size,
228 					   &adev->boot_config_dma_addr,
229 					   GFP_KERNEL, 0);
230 	if (!adev->boot_config) {
231 		dev_err(dev, "Failed to allocate boot config.\n");
232 		return -ENOMEM;
233 	}
234 
235 	boot_config = adev->boot_config;
236 	memset(boot_config, 0, sizeof(struct ia_gofo_boot_config));
237 	init_boot_config(boot_config, adev->boot_config_size, major);
238 	boot_config->subsys_config = subsys_config;
239 
240 	boot_config->uc_tile_frequency = uc_freq;
241 	boot_config->uc_tile_frequency_units =
242 		IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ;
243 	boot_config->syscom_context_config.max_output_queues =
244 		syscom->num_output_queues;
245 	boot_config->syscom_context_config.max_input_queues =
246 		syscom->num_input_queues;
247 
248 	ipu7_dma_sync_single(adev, adev->boot_config_dma_addr,
249 			     adev->boot_config_size);
250 
251 	for (i = 0; i < num_queues; i++) {
252 		u32 queue_size = qconfigs[i].max_capacity *
253 			qconfigs[i].token_size_in_bytes;
254 
255 		queue_size = ALIGN(queue_size, 64U);
256 		total_queue_size_aligned += queue_size;
257 		qconfigs[i].queue_size = queue_size;
258 	}
259 
260 	/* Allocate queue memory */
261 	syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned,
262 					   &syscom->queue_mem_dma_addr,
263 					   GFP_KERNEL, 0);
264 	if (!syscom->queue_mem) {
265 		dev_err(dev, "Failed to allocate queue memory.\n");
266 		return -ENOMEM;
267 	}
268 	syscom->queue_mem_size = total_queue_size_aligned;
269 
270 	syscfg = &boot_config->syscom_context_config;
271 	cfgs = ipu7_syscom_get_queue_config(syscfg);
272 	queue_mem_ptr = syscom->queue_mem;
273 	queue_mem_dma_ptr = syscom->queue_mem_dma_addr;
274 	for (i = 0; i < num_queues; i++) {
275 		cfgs[i].token_array_mem = queue_mem_dma_ptr;
276 		cfgs[i].max_capacity = qconfigs[i].max_capacity;
277 		cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes;
278 		qconfigs[i].token_array_mem = queue_mem_ptr;
279 		queue_mem_dma_ptr += qconfigs[i].queue_size;
280 		queue_mem_ptr += qconfigs[i].queue_size;
281 	}
282 
283 	ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr,
284 			     total_queue_size_aligned);
285 
286 	return 0;
287 }
288 EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7");
289 
290 void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev)
291 {
292 	struct ipu7_syscom_context *syscom = adev->syscom;
293 
294 	if (syscom->queue_mem) {
295 		ipu7_dma_free(adev, syscom->queue_mem_size,
296 			      syscom->queue_mem,
297 			      syscom->queue_mem_dma_addr, 0);
298 		syscom->queue_mem = NULL;
299 		syscom->queue_mem_dma_addr = 0;
300 	}
301 
302 	if (adev->boot_config) {
303 		ipu7_dma_free(adev, adev->boot_config_size,
304 			      adev->boot_config,
305 			      adev->boot_config_dma_addr, 0);
306 		adev->boot_config = NULL;
307 		adev->boot_config_dma_addr = 0;
308 	}
309 }
310 EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7");
311 
312 int ipu7_boot_start_fw(const struct ipu7_bus_device *adev)
313 {
314 	const struct device *dev = &adev->auxdev.dev;
315 	u32 timeout = IPU_FW_START_STOP_TIMEOUT;
316 	void __iomem *base = adev->isp->base;
317 	u32 boot_state, last_boot_state;
318 	u32 indices_addr, msg_ver, id;
319 	int ret;
320 
321 	ret = ipu7_boot_cell_init(adev);
322 	if (ret)
323 		return ret;
324 
325 	dev_dbg(dev, "start booting fw...\n");
326 	/* store "uninit" state to syscom/boot state reg */
327 	write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
328 			    IA_GOFO_FW_BOOT_STATE_UNINIT);
329 	/*
330 	 * Set registers to zero
331 	 * (not strictly required, but recommended for diagnostics)
332 	 */
333 	write_fw_boot_param(adev,
334 			    IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0);
335 	write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0);
336 	/* store firmware configuration address */
337 	write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID,
338 			    adev->boot_config_dma_addr);
339 
340 	/* Kick uC, then wait for boot complete */
341 	ipu7_boot_cell_start(adev);
342 
343 	last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT;
344 	while (timeout--) {
345 		boot_state = read_fw_boot_param(adev,
346 						IA_GOFO_FW_BOOT_STATE_ID);
347 		if (boot_state != last_boot_state) {
348 			dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n",
349 				last_boot_state, boot_state);
350 			last_boot_state = boot_state;
351 		}
352 		if (BOOT_STATE_IS_CRITICAL(boot_state) ||
353 		    BOOT_STATE_IS_READY(boot_state))
354 			break;
355 		usleep_range(1000, 1200);
356 	}
357 
358 	if (BOOT_STATE_IS_CRITICAL(boot_state)) {
359 		ipu7_dump_fw_error_log(adev);
360 		dev_err(dev, "critical boot state error 0x%x\n", boot_state);
361 		return -EINVAL;
362 	} else if (!BOOT_STATE_IS_READY(boot_state)) {
363 		dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state);
364 		return -ETIMEDOUT;
365 	}
366 	dev_dbg(dev, "fw boot done.\n");
367 
368 	/* Get FW syscom queue indices addr */
369 	id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID;
370 	indices_addr = read_fw_boot_param(adev, id);
371 	adev->syscom->queue_indices = base + indices_addr;
372 	dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr);
373 
374 	/* Get message version. */
375 	msg_ver = read_fw_boot_param(adev,
376 				     IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID);
377 	dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver);
378 
379 	return 0;
380 }
381 EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7");
382 
383 int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev)
384 {
385 	const struct device *dev = &adev->auxdev.dev;
386 	u32 timeout = IPU_FW_START_STOP_TIMEOUT;
387 	u32 boot_state;
388 
389 	boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
390 	if (BOOT_STATE_IS_CRITICAL(boot_state) ||
391 	    !BOOT_STATE_IS_READY(boot_state)) {
392 		dev_err(dev, "fw not ready for shutdown, state 0x%x\n",
393 			boot_state);
394 		return -EBUSY;
395 	}
396 
397 	/* Issue shutdown to start shutdown process */
398 	dev_dbg(dev, "stopping fw...\n");
399 	write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
400 			    IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD);
401 	while (timeout--) {
402 		boot_state = read_fw_boot_param(adev,
403 						IA_GOFO_FW_BOOT_STATE_ID);
404 		if (BOOT_STATE_IS_CRITICAL(boot_state) ||
405 		    BOOT_STATE_IS_INACTIVE(boot_state))
406 			break;
407 		usleep_range(1000, 1200);
408 	}
409 
410 	if (BOOT_STATE_IS_CRITICAL(boot_state)) {
411 		ipu7_dump_fw_error_log(adev);
412 		dev_err(dev, "critical boot state error 0x%x\n", boot_state);
413 		return -EINVAL;
414 	} else if (!BOOT_STATE_IS_INACTIVE(boot_state)) {
415 		dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state);
416 		return -ETIMEDOUT;
417 	}
418 
419 	ipu7_boot_cell_stop(adev);
420 	dev_dbg(dev, "stop fw done.\n");
421 
422 	return 0;
423 }
424 EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7");
425 
426 u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev)
427 {
428 	return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
429 }
430 EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7");
431