xref: /linux/drivers/media/pci/intel/ipu6/ipu6-buttress.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1ab29a247SBingbu Cao // SPDX-License-Identifier: GPL-2.0-only
2ab29a247SBingbu Cao /*
3ab29a247SBingbu Cao  * Copyright (C) 2013--2024 Intel Corporation
4ab29a247SBingbu Cao  */
5ab29a247SBingbu Cao 
6ab29a247SBingbu Cao #include <linux/bitfield.h>
7ab29a247SBingbu Cao #include <linux/bits.h>
8ab29a247SBingbu Cao #include <linux/completion.h>
9ab29a247SBingbu Cao #include <linux/delay.h>
10ab29a247SBingbu Cao #include <linux/device.h>
11ab29a247SBingbu Cao #include <linux/dma-mapping.h>
12ab29a247SBingbu Cao #include <linux/firmware.h>
13ab29a247SBingbu Cao #include <linux/interrupt.h>
14ab29a247SBingbu Cao #include <linux/iopoll.h>
15ab29a247SBingbu Cao #include <linux/math64.h>
16ab29a247SBingbu Cao #include <linux/mm.h>
17ab29a247SBingbu Cao #include <linux/mutex.h>
18ab29a247SBingbu Cao #include <linux/pci.h>
19ab29a247SBingbu Cao #include <linux/pfn.h>
20ab29a247SBingbu Cao #include <linux/pm_runtime.h>
21ab29a247SBingbu Cao #include <linux/scatterlist.h>
22ab29a247SBingbu Cao #include <linux/slab.h>
23ab29a247SBingbu Cao #include <linux/time64.h>
24ab29a247SBingbu Cao 
25ab29a247SBingbu Cao #include "ipu6.h"
26ab29a247SBingbu Cao #include "ipu6-bus.h"
27ab29a247SBingbu Cao #include "ipu6-buttress.h"
28ab29a247SBingbu Cao #include "ipu6-platform-buttress-regs.h"
29ab29a247SBingbu Cao 
30ab29a247SBingbu Cao #define BOOTLOADER_STATUS_OFFSET       0x15c
31ab29a247SBingbu Cao 
32ab29a247SBingbu Cao #define BOOTLOADER_MAGIC_KEY		0xb00710ad
33ab29a247SBingbu Cao 
34ab29a247SBingbu Cao #define ENTRY	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
35ab29a247SBingbu Cao #define EXIT	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
36ab29a247SBingbu Cao #define QUERY	BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
37ab29a247SBingbu Cao 
38ab29a247SBingbu Cao #define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX	10
39ab29a247SBingbu Cao 
40ab29a247SBingbu Cao #define BUTTRESS_POWER_TIMEOUT_US		(200 * USEC_PER_MSEC)
41ab29a247SBingbu Cao 
42ab29a247SBingbu Cao #define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US	(5 * USEC_PER_SEC)
43ab29a247SBingbu Cao #define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US	(10 * USEC_PER_SEC)
44ab29a247SBingbu Cao #define BUTTRESS_CSE_FWRESET_TIMEOUT_US		(100 * USEC_PER_MSEC)
45ab29a247SBingbu Cao 
46ab29a247SBingbu Cao #define BUTTRESS_IPC_TX_TIMEOUT_MS		MSEC_PER_SEC
47ab29a247SBingbu Cao #define BUTTRESS_IPC_RX_TIMEOUT_MS		MSEC_PER_SEC
48ab29a247SBingbu Cao #define BUTTRESS_IPC_VALIDITY_TIMEOUT_US	(1 * USEC_PER_SEC)
49ab29a247SBingbu Cao #define BUTTRESS_TSC_SYNC_TIMEOUT_US		(5 * USEC_PER_MSEC)
50ab29a247SBingbu Cao 
51ab29a247SBingbu Cao #define BUTTRESS_IPC_RESET_RETRY		2000
52ab29a247SBingbu Cao #define BUTTRESS_CSE_IPC_RESET_RETRY	4
53ab29a247SBingbu Cao #define BUTTRESS_IPC_CMD_SEND_RETRY	1
54ab29a247SBingbu Cao 
55ab29a247SBingbu Cao #define BUTTRESS_MAX_CONSECUTIVE_IRQS	100
56ab29a247SBingbu Cao 
57ab29a247SBingbu Cao static const u32 ipu6_adev_irq_mask[2] = {
58ab29a247SBingbu Cao 	BUTTRESS_ISR_IS_IRQ,
59ab29a247SBingbu Cao 	BUTTRESS_ISR_PS_IRQ
60ab29a247SBingbu Cao };
61ab29a247SBingbu Cao 
ipu6_buttress_ipc_reset(struct ipu6_device * isp,struct ipu6_buttress_ipc * ipc)62ab29a247SBingbu Cao int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
63ab29a247SBingbu Cao 			    struct ipu6_buttress_ipc *ipc)
64ab29a247SBingbu Cao {
65ab29a247SBingbu Cao 	unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
66ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
67ab29a247SBingbu Cao 	u32 val = 0, csr_in_clr;
68ab29a247SBingbu Cao 
69ab29a247SBingbu Cao 	if (!isp->secure_mode) {
70ab29a247SBingbu Cao 		dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
71ab29a247SBingbu Cao 		return 0;
72ab29a247SBingbu Cao 	}
73ab29a247SBingbu Cao 
74ab29a247SBingbu Cao 	mutex_lock(&b->ipc_mutex);
75ab29a247SBingbu Cao 
76ab29a247SBingbu Cao 	/* Clear-by-1 CSR (all bits), corresponding internal states. */
77ab29a247SBingbu Cao 	val = readl(isp->base + ipc->csr_in);
78ab29a247SBingbu Cao 	writel(val, isp->base + ipc->csr_in);
79ab29a247SBingbu Cao 
80ab29a247SBingbu Cao 	/* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
81ab29a247SBingbu Cao 	writel(ENTRY, isp->base + ipc->csr_out);
82ab29a247SBingbu Cao 	/*
83ab29a247SBingbu Cao 	 * Clear-by-1 all CSR bits EXCEPT following
84ab29a247SBingbu Cao 	 * bits:
85ab29a247SBingbu Cao 	 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
86ab29a247SBingbu Cao 	 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
87ab29a247SBingbu Cao 	 * C. Possibly custom bits, depending on
88ab29a247SBingbu Cao 	 * their role.
89ab29a247SBingbu Cao 	 */
90ab29a247SBingbu Cao 	csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
91ab29a247SBingbu Cao 		BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
92ab29a247SBingbu Cao 		BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
93ab29a247SBingbu Cao 
94ab29a247SBingbu Cao 	do {
95ab29a247SBingbu Cao 		usleep_range(400, 500);
96ab29a247SBingbu Cao 		val = readl(isp->base + ipc->csr_in);
97ab29a247SBingbu Cao 		switch (val) {
98ab29a247SBingbu Cao 		case ENTRY | EXIT:
99ab29a247SBingbu Cao 		case ENTRY | EXIT | QUERY:
100ab29a247SBingbu Cao 			/*
101ab29a247SBingbu Cao 			 * 1) Clear-by-1 CSR bits
102ab29a247SBingbu Cao 			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
103ab29a247SBingbu Cao 			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
104ab29a247SBingbu Cao 			 * 2) Set peer CSR bit
105ab29a247SBingbu Cao 			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
106ab29a247SBingbu Cao 			 */
107ab29a247SBingbu Cao 			writel(ENTRY | EXIT, isp->base + ipc->csr_in);
108ab29a247SBingbu Cao 			writel(QUERY, isp->base + ipc->csr_out);
109ab29a247SBingbu Cao 			break;
110ab29a247SBingbu Cao 		case ENTRY:
111ab29a247SBingbu Cao 		case ENTRY | QUERY:
112ab29a247SBingbu Cao 			/*
113ab29a247SBingbu Cao 			 * 1) Clear-by-1 CSR bits
114ab29a247SBingbu Cao 			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
115ab29a247SBingbu Cao 			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
116ab29a247SBingbu Cao 			 * 2) Set peer CSR bit
117ab29a247SBingbu Cao 			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
118ab29a247SBingbu Cao 			 */
119ab29a247SBingbu Cao 			writel(ENTRY | QUERY, isp->base + ipc->csr_in);
120ab29a247SBingbu Cao 			writel(ENTRY, isp->base + ipc->csr_out);
121ab29a247SBingbu Cao 			break;
122ab29a247SBingbu Cao 		case EXIT:
123ab29a247SBingbu Cao 		case EXIT | QUERY:
124ab29a247SBingbu Cao 			/*
125ab29a247SBingbu Cao 			 * Clear-by-1 CSR bit
126ab29a247SBingbu Cao 			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
127ab29a247SBingbu Cao 			 * 1) Clear incoming doorbell.
128ab29a247SBingbu Cao 			 * 2) Clear-by-1 all CSR bits EXCEPT following
129ab29a247SBingbu Cao 			 * bits:
130ab29a247SBingbu Cao 			 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
131ab29a247SBingbu Cao 			 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
132ab29a247SBingbu Cao 			 * C. Possibly custom bits, depending on
133ab29a247SBingbu Cao 			 * their role.
134ab29a247SBingbu Cao 			 * 3) Set peer CSR bit
135ab29a247SBingbu Cao 			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
136ab29a247SBingbu Cao 			 */
137ab29a247SBingbu Cao 			writel(EXIT, isp->base + ipc->csr_in);
138ab29a247SBingbu Cao 			writel(0, isp->base + ipc->db0_in);
139ab29a247SBingbu Cao 			writel(csr_in_clr, isp->base + ipc->csr_in);
140ab29a247SBingbu Cao 			writel(EXIT, isp->base + ipc->csr_out);
141ab29a247SBingbu Cao 
142ab29a247SBingbu Cao 			/*
143ab29a247SBingbu Cao 			 * Read csr_in again to make sure if RST_PHASE2 is done.
144ab29a247SBingbu Cao 			 * If csr_in is QUERY, it should be handled again.
145ab29a247SBingbu Cao 			 */
146ab29a247SBingbu Cao 			usleep_range(200, 300);
147ab29a247SBingbu Cao 			val = readl(isp->base + ipc->csr_in);
148ab29a247SBingbu Cao 			if (val & QUERY) {
149ab29a247SBingbu Cao 				dev_dbg(&isp->pdev->dev,
150ab29a247SBingbu Cao 					"RST_PHASE2 retry csr_in = %x\n", val);
151ab29a247SBingbu Cao 				break;
152ab29a247SBingbu Cao 			}
153ab29a247SBingbu Cao 			mutex_unlock(&b->ipc_mutex);
154ab29a247SBingbu Cao 			return 0;
155ab29a247SBingbu Cao 		case QUERY:
156ab29a247SBingbu Cao 			/*
157ab29a247SBingbu Cao 			 * 1) Clear-by-1 CSR bit
158ab29a247SBingbu Cao 			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
159ab29a247SBingbu Cao 			 * 2) Set peer CSR bit
160ab29a247SBingbu Cao 			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1
161ab29a247SBingbu Cao 			 */
162ab29a247SBingbu Cao 			writel(QUERY, isp->base + ipc->csr_in);
163ab29a247SBingbu Cao 			writel(ENTRY, isp->base + ipc->csr_out);
164ab29a247SBingbu Cao 			break;
165ab29a247SBingbu Cao 		default:
166*da1d582aSSakari Ailus 			dev_dbg_ratelimited(&isp->pdev->dev,
167ab29a247SBingbu Cao 					    "Unexpected CSR 0x%x\n", val);
168ab29a247SBingbu Cao 			break;
169ab29a247SBingbu Cao 		}
170ab29a247SBingbu Cao 	} while (retries--);
171ab29a247SBingbu Cao 
172ab29a247SBingbu Cao 	mutex_unlock(&b->ipc_mutex);
173ab29a247SBingbu Cao 	dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
174ab29a247SBingbu Cao 
175ab29a247SBingbu Cao 	return -ETIMEDOUT;
176ab29a247SBingbu Cao }
177ab29a247SBingbu Cao 
ipu6_buttress_ipc_validity_close(struct ipu6_device * isp,struct ipu6_buttress_ipc * ipc)178ab29a247SBingbu Cao static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
179ab29a247SBingbu Cao 					     struct ipu6_buttress_ipc *ipc)
180ab29a247SBingbu Cao {
181ab29a247SBingbu Cao 	writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
182ab29a247SBingbu Cao 	       isp->base + ipc->csr_out);
183ab29a247SBingbu Cao }
184ab29a247SBingbu Cao 
185ab29a247SBingbu Cao static int
ipu6_buttress_ipc_validity_open(struct ipu6_device * isp,struct ipu6_buttress_ipc * ipc)186ab29a247SBingbu Cao ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
187ab29a247SBingbu Cao 				struct ipu6_buttress_ipc *ipc)
188ab29a247SBingbu Cao {
189ab29a247SBingbu Cao 	unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
190ab29a247SBingbu Cao 	void __iomem *addr;
191ab29a247SBingbu Cao 	int ret;
192ab29a247SBingbu Cao 	u32 val;
193ab29a247SBingbu Cao 
194ab29a247SBingbu Cao 	writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
195ab29a247SBingbu Cao 	       isp->base + ipc->csr_out);
196ab29a247SBingbu Cao 
197ab29a247SBingbu Cao 	addr = isp->base + ipc->csr_in;
198ab29a247SBingbu Cao 	ret = readl_poll_timeout(addr, val, val & mask, 200,
199ab29a247SBingbu Cao 				 BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
200ab29a247SBingbu Cao 	if (ret) {
201ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
202ab29a247SBingbu Cao 		ipu6_buttress_ipc_validity_close(isp, ipc);
203ab29a247SBingbu Cao 	}
204ab29a247SBingbu Cao 
205ab29a247SBingbu Cao 	return ret;
206ab29a247SBingbu Cao }
207ab29a247SBingbu Cao 
ipu6_buttress_ipc_recv(struct ipu6_device * isp,struct ipu6_buttress_ipc * ipc,u32 * ipc_msg)208ab29a247SBingbu Cao static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
209ab29a247SBingbu Cao 				   struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
210ab29a247SBingbu Cao {
211ab29a247SBingbu Cao 	if (ipc_msg)
212ab29a247SBingbu Cao 		*ipc_msg = readl(isp->base + ipc->data0_in);
213ab29a247SBingbu Cao 	writel(0, isp->base + ipc->db0_in);
214ab29a247SBingbu Cao }
215ab29a247SBingbu Cao 
ipu6_buttress_ipc_send_bulk(struct ipu6_device * isp,enum ipu6_buttress_ipc_domain ipc_domain,struct ipu6_ipc_buttress_bulk_msg * msgs,u32 size)216ab29a247SBingbu Cao static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
217ab29a247SBingbu Cao 				       enum ipu6_buttress_ipc_domain ipc_domain,
218ab29a247SBingbu Cao 				       struct ipu6_ipc_buttress_bulk_msg *msgs,
219ab29a247SBingbu Cao 				       u32 size)
220ab29a247SBingbu Cao {
221ab29a247SBingbu Cao 	unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
222ab29a247SBingbu Cao 	unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
223ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
224ab29a247SBingbu Cao 	struct ipu6_buttress_ipc *ipc;
225ab29a247SBingbu Cao 	u32 val;
226ab29a247SBingbu Cao 	int ret;
227ab29a247SBingbu Cao 	int tout;
228ab29a247SBingbu Cao 
229ab29a247SBingbu Cao 	ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
230ab29a247SBingbu Cao 
231ab29a247SBingbu Cao 	mutex_lock(&b->ipc_mutex);
232ab29a247SBingbu Cao 
233ab29a247SBingbu Cao 	ret = ipu6_buttress_ipc_validity_open(isp, ipc);
234ab29a247SBingbu Cao 	if (ret) {
235ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "IPC validity open failed\n");
236ab29a247SBingbu Cao 		goto out;
237ab29a247SBingbu Cao 	}
238ab29a247SBingbu Cao 
239ab29a247SBingbu Cao 	tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
240ab29a247SBingbu Cao 	rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
241ab29a247SBingbu Cao 
242ab29a247SBingbu Cao 	for (i = 0; i < size; i++) {
243ab29a247SBingbu Cao 		reinit_completion(&ipc->send_complete);
244ab29a247SBingbu Cao 		if (msgs[i].require_resp)
245ab29a247SBingbu Cao 			reinit_completion(&ipc->recv_complete);
246ab29a247SBingbu Cao 
247ab29a247SBingbu Cao 		dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
248ab29a247SBingbu Cao 			msgs[i].cmd);
249ab29a247SBingbu Cao 		writel(msgs[i].cmd, isp->base + ipc->data0_out);
250ab29a247SBingbu Cao 		val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
251ab29a247SBingbu Cao 		writel(val, isp->base + ipc->db0_out);
252ab29a247SBingbu Cao 
253ab29a247SBingbu Cao 		tout = wait_for_completion_timeout(&ipc->send_complete,
254ab29a247SBingbu Cao 						   tx_timeout_jiffies);
255ab29a247SBingbu Cao 		if (!tout) {
256ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev, "send IPC response timeout\n");
257ab29a247SBingbu Cao 			if (!retry--) {
258ab29a247SBingbu Cao 				ret = -ETIMEDOUT;
259ab29a247SBingbu Cao 				goto out;
260ab29a247SBingbu Cao 			}
261ab29a247SBingbu Cao 
262ab29a247SBingbu Cao 			/* Try again if CSE is not responding on first try */
263ab29a247SBingbu Cao 			writel(0, isp->base + ipc->db0_out);
264ab29a247SBingbu Cao 			i--;
265ab29a247SBingbu Cao 			continue;
266ab29a247SBingbu Cao 		}
267ab29a247SBingbu Cao 
268ab29a247SBingbu Cao 		retry = BUTTRESS_IPC_CMD_SEND_RETRY;
269ab29a247SBingbu Cao 
270ab29a247SBingbu Cao 		if (!msgs[i].require_resp)
271ab29a247SBingbu Cao 			continue;
272ab29a247SBingbu Cao 
273ab29a247SBingbu Cao 		tout = wait_for_completion_timeout(&ipc->recv_complete,
274ab29a247SBingbu Cao 						   rx_timeout_jiffies);
275ab29a247SBingbu Cao 		if (!tout) {
276ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
277ab29a247SBingbu Cao 			ret = -ETIMEDOUT;
278ab29a247SBingbu Cao 			goto out;
279ab29a247SBingbu Cao 		}
280ab29a247SBingbu Cao 
281ab29a247SBingbu Cao 		if (ipc->nack_mask &&
282ab29a247SBingbu Cao 		    (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
283ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev,
284ab29a247SBingbu Cao 				"IPC NACK for cmd 0x%x\n", msgs[i].cmd);
285ab29a247SBingbu Cao 			ret = -EIO;
286ab29a247SBingbu Cao 			goto out;
287ab29a247SBingbu Cao 		}
288ab29a247SBingbu Cao 
289ab29a247SBingbu Cao 		if (ipc->recv_data != msgs[i].expected_resp) {
290ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev,
291ab29a247SBingbu Cao 				"expected resp: 0x%x, IPC response: 0x%x ",
292ab29a247SBingbu Cao 				msgs[i].expected_resp, ipc->recv_data);
293ab29a247SBingbu Cao 			ret = -EIO;
294ab29a247SBingbu Cao 			goto out;
295ab29a247SBingbu Cao 		}
296ab29a247SBingbu Cao 	}
297ab29a247SBingbu Cao 
298ab29a247SBingbu Cao 	dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
299ab29a247SBingbu Cao 
300ab29a247SBingbu Cao out:
301ab29a247SBingbu Cao 	ipu6_buttress_ipc_validity_close(isp, ipc);
302ab29a247SBingbu Cao 	mutex_unlock(&b->ipc_mutex);
303ab29a247SBingbu Cao 	return ret;
304ab29a247SBingbu Cao }
305ab29a247SBingbu Cao 
306ab29a247SBingbu Cao static int
ipu6_buttress_ipc_send(struct ipu6_device * isp,enum ipu6_buttress_ipc_domain ipc_domain,u32 ipc_msg,u32 size,bool require_resp,u32 expected_resp)307ab29a247SBingbu Cao ipu6_buttress_ipc_send(struct ipu6_device *isp,
308ab29a247SBingbu Cao 		       enum ipu6_buttress_ipc_domain ipc_domain,
309ab29a247SBingbu Cao 		       u32 ipc_msg, u32 size, bool require_resp,
310ab29a247SBingbu Cao 		       u32 expected_resp)
311ab29a247SBingbu Cao {
312ab29a247SBingbu Cao 	struct ipu6_ipc_buttress_bulk_msg msg = {
313ab29a247SBingbu Cao 		.cmd = ipc_msg,
314ab29a247SBingbu Cao 		.cmd_size = size,
315ab29a247SBingbu Cao 		.require_resp = require_resp,
316ab29a247SBingbu Cao 		.expected_resp = expected_resp,
317ab29a247SBingbu Cao 	};
318ab29a247SBingbu Cao 
319ab29a247SBingbu Cao 	return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
320ab29a247SBingbu Cao }
321ab29a247SBingbu Cao 
ipu6_buttress_call_isr(struct ipu6_bus_device * adev)322ab29a247SBingbu Cao static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
323ab29a247SBingbu Cao {
324ab29a247SBingbu Cao 	irqreturn_t ret = IRQ_WAKE_THREAD;
325ab29a247SBingbu Cao 
326ab29a247SBingbu Cao 	if (!adev || !adev->auxdrv || !adev->auxdrv_data)
327ab29a247SBingbu Cao 		return IRQ_NONE;
328ab29a247SBingbu Cao 
329ab29a247SBingbu Cao 	if (adev->auxdrv_data->isr)
330ab29a247SBingbu Cao 		ret = adev->auxdrv_data->isr(adev);
331ab29a247SBingbu Cao 
332ab29a247SBingbu Cao 	if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
333ab29a247SBingbu Cao 		ret = IRQ_NONE;
334ab29a247SBingbu Cao 
335ab29a247SBingbu Cao 	return ret;
336ab29a247SBingbu Cao }
337ab29a247SBingbu Cao 
ipu6_buttress_isr(int irq,void * isp_ptr)338ab29a247SBingbu Cao irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
339ab29a247SBingbu Cao {
340ab29a247SBingbu Cao 	struct ipu6_device *isp = isp_ptr;
341ab29a247SBingbu Cao 	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
342ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
343ab29a247SBingbu Cao 	u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
344ab29a247SBingbu Cao 	irqreturn_t ret = IRQ_NONE;
345ab29a247SBingbu Cao 	u32 disable_irqs = 0;
346ab29a247SBingbu Cao 	u32 irq_status;
347ab29a247SBingbu Cao 	u32 i, count = 0;
348ab29a247SBingbu Cao 
349ab29a247SBingbu Cao 	pm_runtime_get_noresume(&isp->pdev->dev);
350ab29a247SBingbu Cao 
351ab29a247SBingbu Cao 	irq_status = readl(isp->base + reg_irq_sts);
352ab29a247SBingbu Cao 	if (!irq_status) {
353ab29a247SBingbu Cao 		pm_runtime_put_noidle(&isp->pdev->dev);
354ab29a247SBingbu Cao 		return IRQ_NONE;
355ab29a247SBingbu Cao 	}
356ab29a247SBingbu Cao 
357ab29a247SBingbu Cao 	do {
358ab29a247SBingbu Cao 		writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
359ab29a247SBingbu Cao 
360ab29a247SBingbu Cao 		for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
361ab29a247SBingbu Cao 			irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
362ab29a247SBingbu Cao 
363ab29a247SBingbu Cao 			if (!(irq_status & ipu6_adev_irq_mask[i]))
364ab29a247SBingbu Cao 				continue;
365ab29a247SBingbu Cao 
366ab29a247SBingbu Cao 			if (r == IRQ_WAKE_THREAD) {
367ab29a247SBingbu Cao 				ret = IRQ_WAKE_THREAD;
368ab29a247SBingbu Cao 				disable_irqs |= ipu6_adev_irq_mask[i];
369ab29a247SBingbu Cao 			} else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
370ab29a247SBingbu Cao 				ret = IRQ_HANDLED;
371ab29a247SBingbu Cao 			}
372ab29a247SBingbu Cao 		}
373ab29a247SBingbu Cao 
374ab29a247SBingbu Cao 		if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
375ab29a247SBingbu Cao 			ret = IRQ_HANDLED;
376ab29a247SBingbu Cao 
377ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
378ab29a247SBingbu Cao 			dev_dbg(&isp->pdev->dev,
379ab29a247SBingbu Cao 				"BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
380ab29a247SBingbu Cao 			ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
381ab29a247SBingbu Cao 			complete(&b->cse.recv_complete);
382ab29a247SBingbu Cao 		}
383ab29a247SBingbu Cao 
384ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
385ab29a247SBingbu Cao 			dev_dbg(&isp->pdev->dev,
386ab29a247SBingbu Cao 				"BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
387ab29a247SBingbu Cao 			ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
388ab29a247SBingbu Cao 			complete(&b->ish.recv_complete);
389ab29a247SBingbu Cao 		}
390ab29a247SBingbu Cao 
391ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
392ab29a247SBingbu Cao 			dev_dbg(&isp->pdev->dev,
393ab29a247SBingbu Cao 				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
394ab29a247SBingbu Cao 			complete(&b->cse.send_complete);
395ab29a247SBingbu Cao 		}
396ab29a247SBingbu Cao 
397ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
398ab29a247SBingbu Cao 			dev_dbg(&isp->pdev->dev,
399ab29a247SBingbu Cao 				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
400ab29a247SBingbu Cao 			complete(&b->ish.send_complete);
401ab29a247SBingbu Cao 		}
402ab29a247SBingbu Cao 
403ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
404ab29a247SBingbu Cao 		    ipu6_buttress_get_secure_mode(isp))
405ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev,
406ab29a247SBingbu Cao 				"BUTTRESS_ISR_SAI_VIOLATION\n");
407ab29a247SBingbu Cao 
408ab29a247SBingbu Cao 		if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
409ab29a247SBingbu Cao 				  BUTTRESS_ISR_PS_FATAL_MEM_ERR))
410ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev,
411ab29a247SBingbu Cao 				"BUTTRESS_ISR_FATAL_MEM_ERR\n");
412ab29a247SBingbu Cao 
413ab29a247SBingbu Cao 		if (irq_status & BUTTRESS_ISR_UFI_ERROR)
414ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
415ab29a247SBingbu Cao 
416ab29a247SBingbu Cao 		if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
417ab29a247SBingbu Cao 			dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
418ab29a247SBingbu Cao 			ret = IRQ_NONE;
419ab29a247SBingbu Cao 			break;
420ab29a247SBingbu Cao 		}
421ab29a247SBingbu Cao 
422ab29a247SBingbu Cao 		irq_status = readl(isp->base + reg_irq_sts);
423ab29a247SBingbu Cao 	} while (irq_status);
424ab29a247SBingbu Cao 
425ab29a247SBingbu Cao 	if (disable_irqs)
426ab29a247SBingbu Cao 		writel(BUTTRESS_IRQS & ~disable_irqs,
427ab29a247SBingbu Cao 		       isp->base + BUTTRESS_REG_ISR_ENABLE);
428ab29a247SBingbu Cao 
429ab29a247SBingbu Cao 	pm_runtime_put(&isp->pdev->dev);
430ab29a247SBingbu Cao 
431ab29a247SBingbu Cao 	return ret;
432ab29a247SBingbu Cao }
433ab29a247SBingbu Cao 
ipu6_buttress_isr_threaded(int irq,void * isp_ptr)434ab29a247SBingbu Cao irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
435ab29a247SBingbu Cao {
436ab29a247SBingbu Cao 	struct ipu6_device *isp = isp_ptr;
437ab29a247SBingbu Cao 	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
438ab29a247SBingbu Cao 	const struct ipu6_auxdrv_data *drv_data = NULL;
439ab29a247SBingbu Cao 	irqreturn_t ret = IRQ_NONE;
440ab29a247SBingbu Cao 	unsigned int i;
441ab29a247SBingbu Cao 
442ab29a247SBingbu Cao 	for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
443ab29a247SBingbu Cao 		drv_data = adev[i]->auxdrv_data;
444ab29a247SBingbu Cao 		if (!drv_data)
445ab29a247SBingbu Cao 			continue;
446ab29a247SBingbu Cao 
447ab29a247SBingbu Cao 		if (drv_data->wake_isr_thread &&
448ab29a247SBingbu Cao 		    drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
449ab29a247SBingbu Cao 			ret = IRQ_HANDLED;
450ab29a247SBingbu Cao 	}
451ab29a247SBingbu Cao 
452ab29a247SBingbu Cao 	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
453ab29a247SBingbu Cao 
454ab29a247SBingbu Cao 	return ret;
455ab29a247SBingbu Cao }
456ab29a247SBingbu Cao 
ipu6_buttress_power(struct device * dev,struct ipu6_buttress_ctrl * ctrl,bool on)457ab29a247SBingbu Cao int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
458ab29a247SBingbu Cao 			bool on)
459ab29a247SBingbu Cao {
460ab29a247SBingbu Cao 	struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
461ab29a247SBingbu Cao 	u32 pwr_sts, val;
462ab29a247SBingbu Cao 	int ret;
463ab29a247SBingbu Cao 
464ab29a247SBingbu Cao 	if (!ctrl)
465ab29a247SBingbu Cao 		return 0;
466ab29a247SBingbu Cao 
467ab29a247SBingbu Cao 	mutex_lock(&isp->buttress.power_mutex);
468ab29a247SBingbu Cao 
469ab29a247SBingbu Cao 	if (!on) {
470ab29a247SBingbu Cao 		val = 0;
471ab29a247SBingbu Cao 		pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
472ab29a247SBingbu Cao 	} else {
473ab29a247SBingbu Cao 		val = BUTTRESS_FREQ_CTL_START |
474ab29a247SBingbu Cao 			FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
475ab29a247SBingbu Cao 				   ctrl->ratio) |
476ab29a247SBingbu Cao 			FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
477ab29a247SBingbu Cao 				   ctrl->qos_floor) |
478ab29a247SBingbu Cao 			BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
479ab29a247SBingbu Cao 
480ab29a247SBingbu Cao 		pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
481ab29a247SBingbu Cao 	}
482ab29a247SBingbu Cao 
483ab29a247SBingbu Cao 	writel(val, isp->base + ctrl->freq_ctl);
484ab29a247SBingbu Cao 
485ab29a247SBingbu Cao 	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
486ab29a247SBingbu Cao 				 val, (val & ctrl->pwr_sts_mask) == pwr_sts,
487ab29a247SBingbu Cao 				 100, BUTTRESS_POWER_TIMEOUT_US);
488ab29a247SBingbu Cao 	if (ret)
489ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev,
490ab29a247SBingbu Cao 			"Change power status timeout with 0x%x\n", val);
491ab29a247SBingbu Cao 
492ab29a247SBingbu Cao 	ctrl->started = !ret && on;
493ab29a247SBingbu Cao 
494ab29a247SBingbu Cao 	mutex_unlock(&isp->buttress.power_mutex);
495ab29a247SBingbu Cao 
496ab29a247SBingbu Cao 	return ret;
497ab29a247SBingbu Cao }
498ab29a247SBingbu Cao 
ipu6_buttress_get_secure_mode(struct ipu6_device * isp)499ab29a247SBingbu Cao bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
500ab29a247SBingbu Cao {
501ab29a247SBingbu Cao 	u32 val;
502ab29a247SBingbu Cao 
503ab29a247SBingbu Cao 	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
504ab29a247SBingbu Cao 
505ab29a247SBingbu Cao 	return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
506ab29a247SBingbu Cao }
507ab29a247SBingbu Cao 
ipu6_buttress_auth_done(struct ipu6_device * isp)508ab29a247SBingbu Cao bool ipu6_buttress_auth_done(struct ipu6_device *isp)
509ab29a247SBingbu Cao {
510ab29a247SBingbu Cao 	u32 val;
511ab29a247SBingbu Cao 
512ab29a247SBingbu Cao 	if (!isp->secure_mode)
513ab29a247SBingbu Cao 		return true;
514ab29a247SBingbu Cao 
515ab29a247SBingbu Cao 	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
516ab29a247SBingbu Cao 	val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
517ab29a247SBingbu Cao 
518ab29a247SBingbu Cao 	return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
519ab29a247SBingbu Cao }
520ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
521ab29a247SBingbu Cao 
ipu6_buttress_reset_authentication(struct ipu6_device * isp)522ab29a247SBingbu Cao int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
523ab29a247SBingbu Cao {
524ab29a247SBingbu Cao 	int ret;
525ab29a247SBingbu Cao 	u32 val;
526ab29a247SBingbu Cao 
527ab29a247SBingbu Cao 	if (!isp->secure_mode) {
528ab29a247SBingbu Cao 		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
529ab29a247SBingbu Cao 		return 0;
530ab29a247SBingbu Cao 	}
531ab29a247SBingbu Cao 
532ab29a247SBingbu Cao 	writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
533ab29a247SBingbu Cao 	       BUTTRESS_REG_FW_RESET_CTL);
534ab29a247SBingbu Cao 
535ab29a247SBingbu Cao 	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
536ab29a247SBingbu Cao 				 val & BUTTRESS_FW_RESET_CTL_DONE, 500,
537ab29a247SBingbu Cao 				 BUTTRESS_CSE_FWRESET_TIMEOUT_US);
538ab29a247SBingbu Cao 	if (ret) {
539ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev,
540ab29a247SBingbu Cao 			"Time out while resetting authentication state\n");
541ab29a247SBingbu Cao 		return ret;
542ab29a247SBingbu Cao 	}
543ab29a247SBingbu Cao 
544ab29a247SBingbu Cao 	dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
545ab29a247SBingbu Cao 	writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
546ab29a247SBingbu Cao 	/* leave some time for HW restore */
547ab29a247SBingbu Cao 	usleep_range(800, 1000);
548ab29a247SBingbu Cao 
549ab29a247SBingbu Cao 	return 0;
550ab29a247SBingbu Cao }
551ab29a247SBingbu Cao 
ipu6_buttress_map_fw_image(struct ipu6_bus_device * sys,const struct firmware * fw,struct sg_table * sgt)552ab29a247SBingbu Cao int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
553ab29a247SBingbu Cao 			       const struct firmware *fw, struct sg_table *sgt)
554ab29a247SBingbu Cao {
5558a09bb1bSSakari Ailus 	bool is_vmalloc = is_vmalloc_addr(fw->data);
556ab29a247SBingbu Cao 	struct page **pages;
557ab29a247SBingbu Cao 	const void *addr;
558ab29a247SBingbu Cao 	unsigned long n_pages;
559ab29a247SBingbu Cao 	unsigned int i;
560ab29a247SBingbu Cao 	int ret;
561ab29a247SBingbu Cao 
5628a09bb1bSSakari Ailus 	if (!is_vmalloc && !virt_addr_valid(fw->data))
5638a09bb1bSSakari Ailus 		return -EDOM;
5648a09bb1bSSakari Ailus 
565ab29a247SBingbu Cao 	n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
566ab29a247SBingbu Cao 
567ab29a247SBingbu Cao 	pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
568ab29a247SBingbu Cao 	if (!pages)
569ab29a247SBingbu Cao 		return -ENOMEM;
570ab29a247SBingbu Cao 
571ab29a247SBingbu Cao 	addr = fw->data;
572ab29a247SBingbu Cao 	for (i = 0; i < n_pages; i++) {
5738a09bb1bSSakari Ailus 		struct page *p = is_vmalloc ?
5748a09bb1bSSakari Ailus 			vmalloc_to_page(addr) : virt_to_page(addr);
575ab29a247SBingbu Cao 
576ab29a247SBingbu Cao 		if (!p) {
577ab29a247SBingbu Cao 			ret = -ENOMEM;
578ab29a247SBingbu Cao 			goto out;
579ab29a247SBingbu Cao 		}
580ab29a247SBingbu Cao 		pages[i] = p;
581ab29a247SBingbu Cao 		addr += PAGE_SIZE;
582ab29a247SBingbu Cao 	}
583ab29a247SBingbu Cao 
584ab29a247SBingbu Cao 	ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
585ab29a247SBingbu Cao 					GFP_KERNEL);
586ab29a247SBingbu Cao 	if (ret) {
587ab29a247SBingbu Cao 		ret = -ENOMEM;
588ab29a247SBingbu Cao 		goto out;
589ab29a247SBingbu Cao 	}
590ab29a247SBingbu Cao 
591ab29a247SBingbu Cao 	ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
592ab29a247SBingbu Cao 	if (ret < 0) {
593ab29a247SBingbu Cao 		ret = -ENOMEM;
594ab29a247SBingbu Cao 		sg_free_table(sgt);
595ab29a247SBingbu Cao 		goto out;
596ab29a247SBingbu Cao 	}
597ab29a247SBingbu Cao 
598ab29a247SBingbu Cao 	dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
599ab29a247SBingbu Cao 
600ab29a247SBingbu Cao out:
601ab29a247SBingbu Cao 	kfree(pages);
602ab29a247SBingbu Cao 
603ab29a247SBingbu Cao 	return ret;
604ab29a247SBingbu Cao }
605ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
606ab29a247SBingbu Cao 
ipu6_buttress_unmap_fw_image(struct ipu6_bus_device * sys,struct sg_table * sgt)607ab29a247SBingbu Cao void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
608ab29a247SBingbu Cao 				  struct sg_table *sgt)
609ab29a247SBingbu Cao {
610ab29a247SBingbu Cao 	dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
611ab29a247SBingbu Cao 	sg_free_table(sgt);
612ab29a247SBingbu Cao }
613ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
614ab29a247SBingbu Cao 
ipu6_buttress_authenticate(struct ipu6_device * isp)615ab29a247SBingbu Cao int ipu6_buttress_authenticate(struct ipu6_device *isp)
616ab29a247SBingbu Cao {
617ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
618ab29a247SBingbu Cao 	struct ipu6_psys_pdata *psys_pdata;
619ab29a247SBingbu Cao 	u32 data, mask, done, fail;
620ab29a247SBingbu Cao 	int ret;
621ab29a247SBingbu Cao 
622ab29a247SBingbu Cao 	if (!isp->secure_mode) {
623ab29a247SBingbu Cao 		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
624ab29a247SBingbu Cao 		return 0;
625ab29a247SBingbu Cao 	}
626ab29a247SBingbu Cao 
627ab29a247SBingbu Cao 	psys_pdata = isp->psys->pdata;
628ab29a247SBingbu Cao 
629ab29a247SBingbu Cao 	mutex_lock(&b->auth_mutex);
630ab29a247SBingbu Cao 
631ab29a247SBingbu Cao 	if (ipu6_buttress_auth_done(isp)) {
632ab29a247SBingbu Cao 		ret = 0;
633ab29a247SBingbu Cao 		goto out_unlock;
634ab29a247SBingbu Cao 	}
635ab29a247SBingbu Cao 
636ab29a247SBingbu Cao 	/*
637ab29a247SBingbu Cao 	 * Write address of FIT table to FW_SOURCE register
638ab29a247SBingbu Cao 	 * Let's use fw address. I.e. not using FIT table yet
639ab29a247SBingbu Cao 	 */
640ab29a247SBingbu Cao 	data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
641ab29a247SBingbu Cao 	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
642ab29a247SBingbu Cao 
643ab29a247SBingbu Cao 	data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
644ab29a247SBingbu Cao 	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
645ab29a247SBingbu Cao 
646ab29a247SBingbu Cao 	/*
647ab29a247SBingbu Cao 	 * Write boot_load into IU2CSEDATA0
648ab29a247SBingbu Cao 	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
649ab29a247SBingbu Cao 	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
650ab29a247SBingbu Cao 	 */
651ab29a247SBingbu Cao 	dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
652ab29a247SBingbu Cao 
653ab29a247SBingbu Cao 	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
654ab29a247SBingbu Cao 				     BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
655ab29a247SBingbu Cao 				     1, true,
656ab29a247SBingbu Cao 				     BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
657ab29a247SBingbu Cao 	if (ret) {
658ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
659ab29a247SBingbu Cao 		goto out_unlock;
660ab29a247SBingbu Cao 	}
661ab29a247SBingbu Cao 
662ab29a247SBingbu Cao 	mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
663ab29a247SBingbu Cao 	done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
664ab29a247SBingbu Cao 	fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
665ab29a247SBingbu Cao 	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
666ab29a247SBingbu Cao 				 ((data & mask) == done ||
667ab29a247SBingbu Cao 				  (data & mask) == fail), 500,
668ab29a247SBingbu Cao 				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
669ab29a247SBingbu Cao 	if (ret) {
670ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
671ab29a247SBingbu Cao 		goto out_unlock;
672ab29a247SBingbu Cao 	}
673ab29a247SBingbu Cao 
674ab29a247SBingbu Cao 	if ((data & mask) == fail) {
675ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE auth failed\n");
676ab29a247SBingbu Cao 		ret = -EINVAL;
677ab29a247SBingbu Cao 		goto out_unlock;
678ab29a247SBingbu Cao 	}
679ab29a247SBingbu Cao 
680ab29a247SBingbu Cao 	ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
681ab29a247SBingbu Cao 				 data, data == BOOTLOADER_MAGIC_KEY, 500,
682ab29a247SBingbu Cao 				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
683ab29a247SBingbu Cao 	if (ret) {
684ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
685ab29a247SBingbu Cao 			data);
686ab29a247SBingbu Cao 		goto out_unlock;
687ab29a247SBingbu Cao 	}
688ab29a247SBingbu Cao 
689ab29a247SBingbu Cao 	/*
690ab29a247SBingbu Cao 	 * Write authenticate_run into IU2CSEDATA0
691ab29a247SBingbu Cao 	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
692ab29a247SBingbu Cao 	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
693ab29a247SBingbu Cao 	 */
694ab29a247SBingbu Cao 	dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
695ab29a247SBingbu Cao 	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
696ab29a247SBingbu Cao 				     BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
697ab29a247SBingbu Cao 				     1, true,
698ab29a247SBingbu Cao 				     BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
699ab29a247SBingbu Cao 	if (ret) {
700ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
701ab29a247SBingbu Cao 		goto out_unlock;
702ab29a247SBingbu Cao 	}
703ab29a247SBingbu Cao 
704ab29a247SBingbu Cao 	done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
705ab29a247SBingbu Cao 	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
706ab29a247SBingbu Cao 				 ((data & mask) == done ||
707ab29a247SBingbu Cao 				  (data & mask) == fail), 500,
708ab29a247SBingbu Cao 				 BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
709ab29a247SBingbu Cao 	if (ret) {
710ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
711ab29a247SBingbu Cao 		goto out_unlock;
712ab29a247SBingbu Cao 	}
713ab29a247SBingbu Cao 
714ab29a247SBingbu Cao 	if ((data & mask) == fail) {
715ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
716ab29a247SBingbu Cao 		ret = -EINVAL;
717ab29a247SBingbu Cao 		goto out_unlock;
718ab29a247SBingbu Cao 	}
719ab29a247SBingbu Cao 
720ab29a247SBingbu Cao 	dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
721ab29a247SBingbu Cao 
722ab29a247SBingbu Cao out_unlock:
723ab29a247SBingbu Cao 	mutex_unlock(&b->auth_mutex);
724ab29a247SBingbu Cao 
725ab29a247SBingbu Cao 	return ret;
726ab29a247SBingbu Cao }
727ab29a247SBingbu Cao 
ipu6_buttress_send_tsc_request(struct ipu6_device * isp)728ab29a247SBingbu Cao static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
729ab29a247SBingbu Cao {
730ab29a247SBingbu Cao 	u32 val, mask, done;
731ab29a247SBingbu Cao 	int ret;
732ab29a247SBingbu Cao 
733ab29a247SBingbu Cao 	mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
734ab29a247SBingbu Cao 
735ab29a247SBingbu Cao 	writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
736ab29a247SBingbu Cao 	       isp->base + BUTTRESS_REG_FABRIC_CMD);
737ab29a247SBingbu Cao 
738ab29a247SBingbu Cao 	val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
739ab29a247SBingbu Cao 	val = FIELD_GET(mask, val);
740ab29a247SBingbu Cao 	if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
741ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
742ab29a247SBingbu Cao 		return -EINVAL;
743ab29a247SBingbu Cao 	}
744ab29a247SBingbu Cao 
745ab29a247SBingbu Cao 	done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
746ab29a247SBingbu Cao 	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
747ab29a247SBingbu Cao 				 FIELD_GET(mask, val) == done, 500,
748ab29a247SBingbu Cao 				 BUTTRESS_TSC_SYNC_TIMEOUT_US);
749ab29a247SBingbu Cao 	if (ret)
750ab29a247SBingbu Cao 		dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
751ab29a247SBingbu Cao 
752ab29a247SBingbu Cao 	return ret;
753ab29a247SBingbu Cao }
754ab29a247SBingbu Cao 
ipu6_buttress_start_tsc_sync(struct ipu6_device * isp)755ab29a247SBingbu Cao int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
756ab29a247SBingbu Cao {
757ab29a247SBingbu Cao 	unsigned int i;
758ab29a247SBingbu Cao 
759ab29a247SBingbu Cao 	for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
760ab29a247SBingbu Cao 		u32 val;
761ab29a247SBingbu Cao 		int ret;
762ab29a247SBingbu Cao 
763ab29a247SBingbu Cao 		ret = ipu6_buttress_send_tsc_request(isp);
764ab29a247SBingbu Cao 		if (ret != -ETIMEDOUT)
765ab29a247SBingbu Cao 			return ret;
766ab29a247SBingbu Cao 
767ab29a247SBingbu Cao 		val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
768ab29a247SBingbu Cao 		val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
769ab29a247SBingbu Cao 		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
770ab29a247SBingbu Cao 		val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
771ab29a247SBingbu Cao 		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
772ab29a247SBingbu Cao 	}
773ab29a247SBingbu Cao 
774ab29a247SBingbu Cao 	dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
775ab29a247SBingbu Cao 
776ab29a247SBingbu Cao 	return -ETIMEDOUT;
777ab29a247SBingbu Cao }
778ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
779ab29a247SBingbu Cao 
ipu6_buttress_tsc_read(struct ipu6_device * isp,u64 * val)780ab29a247SBingbu Cao void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
781ab29a247SBingbu Cao {
782ab29a247SBingbu Cao 	u32 tsc_hi_1, tsc_hi_2, tsc_lo;
783ab29a247SBingbu Cao 	unsigned long flags;
784ab29a247SBingbu Cao 
785ab29a247SBingbu Cao 	local_irq_save(flags);
786ab29a247SBingbu Cao 	tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
787ab29a247SBingbu Cao 	tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
788ab29a247SBingbu Cao 	tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
789ab29a247SBingbu Cao 	if (tsc_hi_1 == tsc_hi_2) {
790ab29a247SBingbu Cao 		*val = (u64)tsc_hi_1 << 32 | tsc_lo;
791ab29a247SBingbu Cao 	} else {
792ab29a247SBingbu Cao 		/* Check if TSC has rolled over */
793ab29a247SBingbu Cao 		if (tsc_lo & BIT(31))
794ab29a247SBingbu Cao 			*val = (u64)tsc_hi_1 << 32 | tsc_lo;
795ab29a247SBingbu Cao 		else
796ab29a247SBingbu Cao 			*val = (u64)tsc_hi_2 << 32 | tsc_lo;
797ab29a247SBingbu Cao 	}
798ab29a247SBingbu Cao 	local_irq_restore(flags);
799ab29a247SBingbu Cao }
800ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
801ab29a247SBingbu Cao 
ipu6_buttress_tsc_ticks_to_ns(u64 ticks,const struct ipu6_device * isp)802ab29a247SBingbu Cao u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
803ab29a247SBingbu Cao {
804ab29a247SBingbu Cao 	u64 ns = ticks * 10000;
805ab29a247SBingbu Cao 
806ab29a247SBingbu Cao 	/*
807ab29a247SBingbu Cao 	 * converting TSC tick count to ns is calculated by:
808ab29a247SBingbu Cao 	 * Example (TSC clock frequency is 19.2MHz):
809ab29a247SBingbu Cao 	 * ns = ticks * 1000 000 000 / 19.2Mhz
810ab29a247SBingbu Cao 	 *    = ticks * 1000 000 000 / 19200000Hz
811ab29a247SBingbu Cao 	 *    = ticks * 10000 / 192 ns
812ab29a247SBingbu Cao 	 */
813ab29a247SBingbu Cao 	return div_u64(ns, isp->buttress.ref_clk);
814ab29a247SBingbu Cao }
815ab29a247SBingbu Cao EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
816ab29a247SBingbu Cao 
ipu6_buttress_restore(struct ipu6_device * isp)817ab29a247SBingbu Cao void ipu6_buttress_restore(struct ipu6_device *isp)
818ab29a247SBingbu Cao {
819ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
820ab29a247SBingbu Cao 
821ab29a247SBingbu Cao 	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
822ab29a247SBingbu Cao 	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
823ab29a247SBingbu Cao 	writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
824ab29a247SBingbu Cao }
825ab29a247SBingbu Cao 
ipu6_buttress_init(struct ipu6_device * isp)826ab29a247SBingbu Cao int ipu6_buttress_init(struct ipu6_device *isp)
827ab29a247SBingbu Cao {
828ab29a247SBingbu Cao 	int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
829ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
830ab29a247SBingbu Cao 	u32 val;
831ab29a247SBingbu Cao 
832ab29a247SBingbu Cao 	mutex_init(&b->power_mutex);
833ab29a247SBingbu Cao 	mutex_init(&b->auth_mutex);
834ab29a247SBingbu Cao 	mutex_init(&b->cons_mutex);
835ab29a247SBingbu Cao 	mutex_init(&b->ipc_mutex);
836ab29a247SBingbu Cao 	init_completion(&b->ish.send_complete);
837ab29a247SBingbu Cao 	init_completion(&b->cse.send_complete);
838ab29a247SBingbu Cao 	init_completion(&b->ish.recv_complete);
839ab29a247SBingbu Cao 	init_completion(&b->cse.recv_complete);
840ab29a247SBingbu Cao 
841ab29a247SBingbu Cao 	b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
842ab29a247SBingbu Cao 	b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
843ab29a247SBingbu Cao 	b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
844ab29a247SBingbu Cao 	b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
845ab29a247SBingbu Cao 	b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
846ab29a247SBingbu Cao 	b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
847ab29a247SBingbu Cao 	b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
848ab29a247SBingbu Cao 	b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
849ab29a247SBingbu Cao 
850ab29a247SBingbu Cao 	/* no ISH on IPU6 */
851ab29a247SBingbu Cao 	memset(&b->ish, 0, sizeof(b->ish));
852ab29a247SBingbu Cao 	INIT_LIST_HEAD(&b->constraints);
853ab29a247SBingbu Cao 
854ab29a247SBingbu Cao 	isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
855ab29a247SBingbu Cao 	dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
856ab29a247SBingbu Cao 		 isp->secure_mode ? "secure" : "non-secure",
857ab29a247SBingbu Cao 		 readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
858ab29a247SBingbu Cao 		 readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
859ab29a247SBingbu Cao 
860ab29a247SBingbu Cao 	b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
861ab29a247SBingbu Cao 	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
862ab29a247SBingbu Cao 	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
863ab29a247SBingbu Cao 
864ab29a247SBingbu Cao 	/* get ref_clk frequency by reading the indication in btrs control */
865ab29a247SBingbu Cao 	val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
866ab29a247SBingbu Cao 	val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
867ab29a247SBingbu Cao 
868ab29a247SBingbu Cao 	switch (val) {
869ab29a247SBingbu Cao 	case 0x0:
870ab29a247SBingbu Cao 		b->ref_clk = 240;
871ab29a247SBingbu Cao 		break;
872ab29a247SBingbu Cao 	case 0x1:
873ab29a247SBingbu Cao 		b->ref_clk = 192;
874ab29a247SBingbu Cao 		break;
875ab29a247SBingbu Cao 	case 0x2:
876ab29a247SBingbu Cao 		b->ref_clk = 384;
877ab29a247SBingbu Cao 		break;
878ab29a247SBingbu Cao 	default:
879ab29a247SBingbu Cao 		dev_warn(&isp->pdev->dev,
880ab29a247SBingbu Cao 			 "Unsupported ref clock, use 19.2Mhz by default.\n");
881ab29a247SBingbu Cao 		b->ref_clk = 192;
882ab29a247SBingbu Cao 		break;
883ab29a247SBingbu Cao 	}
884ab29a247SBingbu Cao 
885ab29a247SBingbu Cao 	/* Retry couple of times in case of CSE initialization is delayed */
886ab29a247SBingbu Cao 	do {
887ab29a247SBingbu Cao 		ret = ipu6_buttress_ipc_reset(isp, &b->cse);
888ab29a247SBingbu Cao 		if (ret) {
889ab29a247SBingbu Cao 			dev_warn(&isp->pdev->dev,
890ab29a247SBingbu Cao 				 "IPC reset protocol failed, retrying\n");
891ab29a247SBingbu Cao 		} else {
892ab29a247SBingbu Cao 			dev_dbg(&isp->pdev->dev, "IPC reset done\n");
893ab29a247SBingbu Cao 			return 0;
894ab29a247SBingbu Cao 		}
895ab29a247SBingbu Cao 	} while (ipc_reset_retry--);
896ab29a247SBingbu Cao 
897ab29a247SBingbu Cao 	dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
898ab29a247SBingbu Cao 
899ab29a247SBingbu Cao 	mutex_destroy(&b->power_mutex);
900ab29a247SBingbu Cao 	mutex_destroy(&b->auth_mutex);
901ab29a247SBingbu Cao 	mutex_destroy(&b->cons_mutex);
902ab29a247SBingbu Cao 	mutex_destroy(&b->ipc_mutex);
903ab29a247SBingbu Cao 
904ab29a247SBingbu Cao 	return ret;
905ab29a247SBingbu Cao }
906ab29a247SBingbu Cao 
ipu6_buttress_exit(struct ipu6_device * isp)907ab29a247SBingbu Cao void ipu6_buttress_exit(struct ipu6_device *isp)
908ab29a247SBingbu Cao {
909ab29a247SBingbu Cao 	struct ipu6_buttress *b = &isp->buttress;
910ab29a247SBingbu Cao 
911ab29a247SBingbu Cao 	writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
912ab29a247SBingbu Cao 
913ab29a247SBingbu Cao 	mutex_destroy(&b->power_mutex);
914ab29a247SBingbu Cao 	mutex_destroy(&b->auth_mutex);
915ab29a247SBingbu Cao 	mutex_destroy(&b->cons_mutex);
916ab29a247SBingbu Cao 	mutex_destroy(&b->ipc_mutex);
917ab29a247SBingbu Cao }
918