]>
Commit | Line | Data |
---|---|---|
f2efa4ee WY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2013 - 2020 Intel Corporation | |
3 | ||
4 | #include <linux/clk.h> | |
5 | #include <linux/clkdev.h> | |
6 | #include <linux/clk-provider.h> | |
7 | #include <linux/completion.h> | |
8 | #include <linux/debugfs.h> | |
9 | #include <linux/device.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/elf.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/firmware.h> | |
14 | #include <linux/iopoll.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/pci.h> | |
17 | #include <linux/pm_runtime.h> | |
18 | ||
19 | #include <media/ipu-isys.h> | |
20 | ||
21 | #include "ipu.h" | |
22 | #include "ipu-bus.h" | |
23 | #include "ipu-buttress.h" | |
24 | #include "ipu-platform-buttress-regs.h" | |
25 | #include "ipu-cpd.h" | |
26 | ||
27 | #define BOOTLOADER_STATUS_OFFSET 0x15c | |
28 | ||
29 | #define BOOTLOADER_MAGIC_KEY 0xb00710ad | |
30 | ||
31 | #define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 | |
32 | #define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 | |
33 | #define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE | |
34 | ||
35 | #define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10 | |
36 | ||
37 | #define BUTTRESS_CSE_BOOTLOAD_TIMEOUT 5000000 | |
38 | #define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT 10000000 | |
39 | #define BUTTRESS_CSE_FWRESET_TIMEOUT 100000 | |
40 | ||
41 | #define BUTTRESS_IPC_TX_TIMEOUT 1000 | |
af60d6fc | 42 | #define BUTTRESS_IPC_RESET_TIMEOUT 2000 |
f2efa4ee WY |
43 | #define BUTTRESS_IPC_RX_TIMEOUT 1000 |
44 | #define BUTTRESS_IPC_VALIDITY_TIMEOUT 1000000 | |
45 | #define BUTTRESS_TSC_SYNC_TIMEOUT 5000 | |
46 | ||
47 | #define IPU_BUTTRESS_TSC_LIMIT 500 /* 26 us @ 19.2 MHz */ | |
48 | #define IPU_BUTTRESS_TSC_RETRY 10 | |
49 | ||
50 | #define BUTTRESS_CSE_IPC_RESET_RETRY 4 | |
51 | ||
52 | #define BUTTRESS_IPC_CMD_SEND_RETRY 1 | |
53 | ||
f2efa4ee WY |
54 | static const u32 ipu_adev_irq_mask[] = { |
55 | BUTTRESS_ISR_IS_IRQ, BUTTRESS_ISR_PS_IRQ | |
56 | }; | |
57 | ||
58 | int ipu_buttress_ipc_reset(struct ipu_device *isp, struct ipu_buttress_ipc *ipc) | |
59 | { | |
60 | struct ipu_buttress *b = &isp->buttress; | |
3ebd4441 | 61 | unsigned int retries = BUTTRESS_IPC_RESET_TIMEOUT; |
f2efa4ee WY |
62 | u32 val = 0, csr_in_clr; |
63 | ||
64 | if (!isp->secure_mode) { | |
65 | dev_info(&isp->pdev->dev, "Skip ipc reset for non-secure mode"); | |
66 | return 0; | |
67 | } | |
68 | ||
69 | mutex_lock(&b->ipc_mutex); | |
70 | ||
71 | /* Clear-by-1 CSR (all bits), corresponding internal states. */ | |
72 | val = readl(isp->base + ipc->csr_in); | |
73 | writel(val, isp->base + ipc->csr_in); | |
74 | ||
75 | /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ | |
76 | writel(ENTRY, isp->base + ipc->csr_out); | |
f2efa4ee WY |
77 | /* |
78 | * Clear-by-1 all CSR bits EXCEPT following | |
79 | * bits: | |
80 | * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
81 | * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
82 | * C. Possibly custom bits, depending on | |
83 | * their role. | |
84 | */ | |
85 | csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | | |
86 | BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | | |
87 | BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; | |
88 | ||
3ebd4441 | 89 | while (retries--) { |
af60d6fc | 90 | usleep_range(400, 500); |
f2efa4ee | 91 | val = readl(isp->base + ipc->csr_in); |
af60d6fc WY |
92 | switch (val) { |
93 | case (ENTRY | EXIT): | |
94 | case (ENTRY | EXIT | QUERY): | |
95 | dev_dbg(&isp->pdev->dev, | |
96 | "%s:%s & %s\n", __func__, | |
97 | "IPC_PEER_COMP_ACTIONS_RST_PHASE1", | |
98 | "IPC_PEER_COMP_ACTIONS_RST_PHASE2"); | |
99 | /* | |
100 | * 1) Clear-by-1 CSR bits | |
101 | * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, | |
102 | * IPC_PEER_COMP_ACTIONS_RST_PHASE2). | |
103 | * 2) Set peer CSR bit | |
104 | * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. | |
105 | */ | |
106 | writel(ENTRY | EXIT, isp->base + ipc->csr_in); | |
107 | writel(QUERY, isp->base + ipc->csr_out); | |
108 | break; | |
109 | case ENTRY: | |
3ebd4441 | 110 | case (ENTRY | QUERY): |
af60d6fc WY |
111 | dev_dbg(&isp->pdev->dev, |
112 | "%s:IPC_PEER_COMP_ACTIONS_RST_PHASE1\n", | |
113 | __func__); | |
114 | /* | |
115 | * 1) Clear-by-1 CSR bits | |
116 | * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, | |
117 | * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). | |
118 | * 2) Set peer CSR bit | |
119 | * IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
120 | */ | |
121 | writel(ENTRY | QUERY, isp->base + ipc->csr_in); | |
122 | writel(ENTRY, isp->base + ipc->csr_out); | |
123 | break; | |
124 | case EXIT: | |
3ebd4441 | 125 | case (EXIT | QUERY): |
f2efa4ee WY |
126 | dev_dbg(&isp->pdev->dev, |
127 | "%s: IPC_PEER_COMP_ACTIONS_RST_PHASE2\n", | |
128 | __func__); | |
129 | /* | |
130 | * Clear-by-1 CSR bit | |
131 | * IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
132 | * 1) Clear incoming doorbell. | |
133 | * 2) Clear-by-1 all CSR bits EXCEPT following | |
134 | * bits: | |
135 | * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. | |
136 | * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
137 | * C. Possibly custom bits, depending on | |
138 | * their role. | |
139 | * 3) Set peer CSR bit | |
140 | * IPC_PEER_COMP_ACTIONS_RST_PHASE2. | |
141 | */ | |
142 | writel(EXIT, isp->base + ipc->csr_in); | |
f2efa4ee | 143 | writel(0, isp->base + ipc->db0_in); |
f2efa4ee | 144 | writel(csr_in_clr, isp->base + ipc->csr_in); |
f2efa4ee WY |
145 | writel(EXIT, isp->base + ipc->csr_out); |
146 | ||
147 | /* | |
148 | * Read csr_in again to make sure if RST_PHASE2 is done. | |
149 | * If csr_in is QUERY, it should be handled again. | |
150 | */ | |
af60d6fc | 151 | usleep_range(200, 300); |
f2efa4ee WY |
152 | val = readl(isp->base + ipc->csr_in); |
153 | if (val & QUERY) { | |
154 | dev_dbg(&isp->pdev->dev, | |
155 | "%s: RST_PHASE2 retry csr_in = %x\n", | |
156 | __func__, val); | |
af60d6fc | 157 | break; |
f2efa4ee | 158 | } |
f2efa4ee | 159 | mutex_unlock(&b->ipc_mutex); |
f2efa4ee | 160 | return 0; |
af60d6fc | 161 | case QUERY: |
f2efa4ee WY |
162 | dev_dbg(&isp->pdev->dev, |
163 | "%s: %s\n", __func__, | |
164 | "IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE"); | |
165 | /* | |
166 | * 1) Clear-by-1 CSR bit | |
167 | * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. | |
168 | * 2) Set peer CSR bit | |
169 | * IPC_PEER_COMP_ACTIONS_RST_PHASE1 | |
170 | */ | |
171 | writel(QUERY, isp->base + ipc->csr_in); | |
f2efa4ee | 172 | writel(ENTRY, isp->base + ipc->csr_out); |
af60d6fc WY |
173 | break; |
174 | default: | |
175 | dev_dbg_ratelimited(&isp->pdev->dev, | |
176 | "%s: unexpected CSR 0x%x\n", | |
177 | __func__, val); | |
178 | break; | |
f2efa4ee | 179 | } |
af60d6fc | 180 | } |
f2efa4ee WY |
181 | |
182 | mutex_unlock(&b->ipc_mutex); | |
af60d6fc | 183 | dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n"); |
f2efa4ee WY |
184 | |
185 | return -ETIMEDOUT; | |
186 | } | |
187 | ||
188 | static void | |
189 | ipu_buttress_ipc_validity_close(struct ipu_device *isp, | |
190 | struct ipu_buttress_ipc *ipc) | |
191 | { | |
192 | /* Set bit 5 in CSE CSR */ | |
193 | writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, | |
194 | isp->base + ipc->csr_out); | |
195 | } | |
196 | ||
197 | static int | |
198 | ipu_buttress_ipc_validity_open(struct ipu_device *isp, | |
199 | struct ipu_buttress_ipc *ipc) | |
200 | { | |
201 | unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; | |
202 | unsigned int tout = BUTTRESS_IPC_VALIDITY_TIMEOUT; | |
203 | void __iomem *addr; | |
204 | int ret; | |
205 | u32 val; | |
206 | ||
207 | /* Set bit 3 in CSE CSR */ | |
208 | writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, | |
209 | isp->base + ipc->csr_out); | |
210 | ||
211 | addr = isp->base + ipc->csr_in; | |
212 | ret = readl_poll_timeout(addr, val, val & mask, 200, tout); | |
213 | if (ret) { | |
214 | val = readl(addr); | |
af60d6fc | 215 | dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); |
f2efa4ee WY |
216 | ipu_buttress_ipc_validity_close(isp, ipc); |
217 | } | |
218 | ||
219 | return ret; | |
220 | } | |
221 | ||
222 | static void ipu_buttress_ipc_recv(struct ipu_device *isp, | |
223 | struct ipu_buttress_ipc *ipc, u32 *ipc_msg) | |
224 | { | |
225 | if (ipc_msg) | |
226 | *ipc_msg = readl(isp->base + ipc->data0_in); | |
227 | writel(0, isp->base + ipc->db0_in); | |
228 | } | |
229 | ||
230 | static int ipu_buttress_ipc_send_bulk(struct ipu_device *isp, | |
231 | enum ipu_buttress_ipc_domain ipc_domain, | |
232 | struct ipu_ipc_buttress_bulk_msg *msgs, | |
233 | u32 size) | |
234 | { | |
235 | struct ipu_buttress *b = &isp->buttress; | |
236 | struct ipu_buttress_ipc *ipc; | |
237 | unsigned long tx_timeout_jiffies, rx_timeout_jiffies; | |
238 | u32 val; | |
239 | int ret; | |
240 | int tout; | |
241 | unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY; | |
242 | ||
243 | ipc = ipc_domain == IPU_BUTTRESS_IPC_CSE ? &b->cse : &b->ish; | |
244 | ||
245 | mutex_lock(&b->ipc_mutex); | |
246 | ||
247 | ret = ipu_buttress_ipc_validity_open(isp, ipc); | |
248 | if (ret) { | |
249 | dev_err(&isp->pdev->dev, "IPC validity open failed\n"); | |
250 | goto out; | |
251 | } | |
252 | ||
253 | tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT); | |
254 | rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT); | |
255 | ||
256 | for (i = 0; i < size; i++) { | |
257 | reinit_completion(&ipc->send_complete); | |
258 | if (msgs[i].require_resp) | |
259 | reinit_completion(&ipc->recv_complete); | |
260 | ||
261 | dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n", | |
262 | msgs[i].cmd); | |
263 | writel(msgs[i].cmd, isp->base + ipc->data0_out); | |
264 | ||
265 | val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size; | |
266 | ||
267 | writel(val, isp->base + ipc->db0_out); | |
268 | ||
269 | tout = wait_for_completion_timeout(&ipc->send_complete, | |
270 | tx_timeout_jiffies); | |
271 | if (!tout) { | |
272 | dev_err(&isp->pdev->dev, "send IPC response timeout\n"); | |
273 | if (!retry--) { | |
274 | ret = -ETIMEDOUT; | |
275 | goto out; | |
276 | } | |
277 | ||
278 | /* | |
279 | * WORKAROUND: Sometimes CSE is not | |
280 | * responding on first try, try again. | |
281 | */ | |
282 | writel(0, isp->base + ipc->db0_out); | |
283 | i--; | |
284 | continue; | |
285 | } | |
286 | ||
287 | retry = BUTTRESS_IPC_CMD_SEND_RETRY; | |
288 | ||
289 | if (!msgs[i].require_resp) | |
290 | continue; | |
291 | ||
292 | tout = wait_for_completion_timeout(&ipc->recv_complete, | |
293 | rx_timeout_jiffies); | |
294 | if (!tout) { | |
295 | dev_err(&isp->pdev->dev, "recv IPC response timeout\n"); | |
296 | ret = -ETIMEDOUT; | |
297 | goto out; | |
298 | } | |
299 | ||
300 | if (ipc->nack_mask && | |
301 | (ipc->recv_data & ipc->nack_mask) == ipc->nack) { | |
302 | dev_err(&isp->pdev->dev, | |
303 | "IPC NACK for cmd 0x%x\n", msgs[i].cmd); | |
304 | ret = -ENODEV; | |
305 | goto out; | |
306 | } | |
307 | ||
308 | if (ipc->recv_data != msgs[i].expected_resp) { | |
309 | dev_err(&isp->pdev->dev, | |
310 | "expected resp: 0x%x, IPC response: 0x%x ", | |
311 | msgs[i].expected_resp, ipc->recv_data); | |
312 | ret = -EIO; | |
313 | goto out; | |
314 | } | |
315 | } | |
316 | ||
af60d6fc | 317 | dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n"); |
f2efa4ee WY |
318 | |
319 | out: | |
320 | ipu_buttress_ipc_validity_close(isp, ipc); | |
321 | mutex_unlock(&b->ipc_mutex); | |
322 | return ret; | |
323 | } | |
324 | ||
325 | static int | |
326 | ipu_buttress_ipc_send(struct ipu_device *isp, | |
327 | enum ipu_buttress_ipc_domain ipc_domain, | |
328 | u32 ipc_msg, u32 size, bool require_resp, | |
329 | u32 expected_resp) | |
330 | { | |
331 | struct ipu_ipc_buttress_bulk_msg msg = { | |
332 | .cmd = ipc_msg, | |
333 | .cmd_size = size, | |
334 | .require_resp = require_resp, | |
335 | .expected_resp = expected_resp, | |
336 | }; | |
337 | ||
338 | return ipu_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1); | |
339 | } | |
340 | ||
341 | static irqreturn_t ipu_buttress_call_isr(struct ipu_bus_device *adev) | |
342 | { | |
343 | irqreturn_t ret = IRQ_WAKE_THREAD; | |
344 | ||
345 | if (!adev || !adev->adrv) | |
346 | return IRQ_NONE; | |
347 | ||
348 | if (adev->adrv->isr) | |
349 | ret = adev->adrv->isr(adev); | |
350 | ||
351 | if (ret == IRQ_WAKE_THREAD && !adev->adrv->isr_threaded) | |
352 | ret = IRQ_NONE; | |
353 | ||
354 | adev->adrv->wake_isr_thread = (ret == IRQ_WAKE_THREAD); | |
355 | ||
356 | return ret; | |
357 | } | |
358 | ||
359 | irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) | |
360 | { | |
361 | struct ipu_device *isp = isp_ptr; | |
362 | struct ipu_bus_device *adev[] = { isp->isys, isp->psys }; | |
363 | struct ipu_buttress *b = &isp->buttress; | |
364 | irqreturn_t ret = IRQ_NONE; | |
365 | u32 disable_irqs = 0; | |
366 | u32 irq_status; | |
367 | u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS; | |
368 | unsigned int i; | |
369 | ||
370 | pm_runtime_get(&isp->pdev->dev); | |
371 | ||
372 | if (!pm_runtime_active(&isp->pdev->dev)) { | |
373 | irq_status = readl(isp->base + reg_irq_sts); | |
374 | writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
375 | pm_runtime_put(&isp->pdev->dev); | |
376 | return IRQ_HANDLED; | |
377 | } | |
378 | ||
379 | irq_status = readl(isp->base + reg_irq_sts); | |
380 | if (!irq_status) { | |
381 | pm_runtime_put(&isp->pdev->dev); | |
382 | return IRQ_NONE; | |
383 | } | |
384 | ||
385 | do { | |
386 | writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
387 | ||
388 | for (i = 0; i < ARRAY_SIZE(ipu_adev_irq_mask); i++) { | |
389 | if (irq_status & ipu_adev_irq_mask[i]) { | |
390 | irqreturn_t r = ipu_buttress_call_isr(adev[i]); | |
391 | ||
392 | if (r == IRQ_WAKE_THREAD) { | |
393 | ret = IRQ_WAKE_THREAD; | |
394 | disable_irqs |= ipu_adev_irq_mask[i]; | |
395 | } else if (ret == IRQ_NONE && | |
396 | r == IRQ_HANDLED) { | |
397 | ret = IRQ_HANDLED; | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
402 | if (irq_status & (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | | |
403 | BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | | |
404 | BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | | |
405 | BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH | | |
406 | BUTTRESS_ISR_SAI_VIOLATION) && | |
407 | ret == IRQ_NONE) | |
408 | ret = IRQ_HANDLED; | |
409 | ||
410 | if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) { | |
411 | dev_dbg(&isp->pdev->dev, | |
412 | "BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n"); | |
413 | ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); | |
414 | complete(&b->cse.recv_complete); | |
415 | } | |
416 | ||
417 | if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) { | |
418 | dev_dbg(&isp->pdev->dev, | |
419 | "BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n"); | |
420 | ipu_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data); | |
421 | complete(&b->ish.recv_complete); | |
422 | } | |
423 | ||
424 | if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) { | |
425 | dev_dbg(&isp->pdev->dev, | |
426 | "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); | |
427 | complete(&b->cse.send_complete); | |
428 | } | |
429 | ||
430 | if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) { | |
431 | dev_dbg(&isp->pdev->dev, | |
432 | "BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n"); | |
433 | complete(&b->ish.send_complete); | |
434 | } | |
435 | ||
436 | if (irq_status & BUTTRESS_ISR_SAI_VIOLATION && | |
437 | ipu_buttress_get_secure_mode(isp)) { | |
438 | dev_err(&isp->pdev->dev, | |
439 | "BUTTRESS_ISR_SAI_VIOLATION\n"); | |
440 | WARN_ON(1); | |
441 | } | |
442 | ||
443 | irq_status = readl(isp->base + reg_irq_sts); | |
444 | } while (irq_status && !isp->flr_done); | |
445 | ||
446 | if (disable_irqs) | |
447 | writel(BUTTRESS_IRQS & ~disable_irqs, | |
448 | isp->base + BUTTRESS_REG_ISR_ENABLE); | |
449 | ||
450 | pm_runtime_put(&isp->pdev->dev); | |
451 | ||
452 | return ret; | |
453 | } | |
454 | ||
455 | irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) | |
456 | { | |
457 | struct ipu_device *isp = isp_ptr; | |
458 | struct ipu_bus_device *adev[] = { isp->isys, isp->psys }; | |
459 | irqreturn_t ret = IRQ_NONE; | |
460 | unsigned int i; | |
461 | ||
462 | dev_dbg(&isp->pdev->dev, "isr: Buttress threaded interrupt handler\n"); | |
463 | ||
464 | for (i = 0; i < ARRAY_SIZE(ipu_adev_irq_mask); i++) { | |
465 | if (adev[i] && adev[i]->adrv && | |
466 | adev[i]->adrv->wake_isr_thread && | |
467 | adev[i]->adrv->isr_threaded(adev[i]) == IRQ_HANDLED) | |
468 | ret = IRQ_HANDLED; | |
469 | } | |
470 | ||
471 | writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
472 | ||
473 | return ret; | |
474 | } | |
475 | ||
476 | int ipu_buttress_power(struct device *dev, | |
477 | struct ipu_buttress_ctrl *ctrl, bool on) | |
478 | { | |
479 | struct ipu_device *isp = to_ipu_bus_device(dev)->isp; | |
480 | u32 pwr_sts, val; | |
481 | int ret = 0; | |
482 | ||
483 | if (!ctrl) | |
484 | return 0; | |
485 | ||
486 | /* Until FLR completion nothing is expected to work */ | |
487 | if (isp->flr_done) | |
488 | return 0; | |
489 | ||
490 | mutex_lock(&isp->buttress.power_mutex); | |
491 | ||
492 | if (!on) { | |
493 | val = 0; | |
494 | pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; | |
495 | } else { | |
496 | val = BUTTRESS_FREQ_CTL_START | | |
497 | ctrl->divisor << ctrl->divisor_shift | | |
3ebd4441 WY |
498 | ctrl->qos_floor << BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT | |
499 | BUTTRESS_FREQ_CTL_ICCMAX_LEVEL; | |
f2efa4ee WY |
500 | |
501 | pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; | |
502 | } | |
503 | ||
504 | writel(val, isp->base + ctrl->freq_ctl); | |
505 | ||
506 | ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, | |
507 | val, ((val & ctrl->pwr_sts_mask) == pwr_sts), | |
508 | 100, BUTTRESS_POWER_TIMEOUT); | |
509 | if (ret) | |
510 | dev_err(&isp->pdev->dev, | |
511 | "Change power status timeout with 0x%x\n", val); | |
512 | ||
513 | ctrl->started = !ret && on; | |
514 | ||
515 | mutex_unlock(&isp->buttress.power_mutex); | |
516 | ||
517 | return ret; | |
518 | } | |
519 | ||
520 | static bool secure_mode_enable = 1; | |
521 | module_param(secure_mode_enable, bool, 0660); | |
522 | MODULE_PARM_DESC(secure_mode, "IPU secure mode enable"); | |
523 | ||
524 | void ipu_buttress_set_secure_mode(struct ipu_device *isp) | |
525 | { | |
526 | u8 retry = 100; | |
527 | u32 val, read; | |
528 | ||
529 | /* | |
530 | * HACK to disable possible secure mode. This can be | |
531 | * reverted when CSE is disabling the secure mode | |
532 | */ | |
533 | read = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
534 | ||
535 | if (secure_mode_enable) | |
536 | val = read |= BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; | |
537 | else | |
538 | val = read & ~BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; | |
539 | ||
540 | if (val == read) | |
541 | return; | |
542 | ||
543 | writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); | |
544 | ||
545 | /* In B0, for some registers in buttress, because of a hw bug, write | |
546 | * might not succeed at first attempt. Write twice until the | |
547 | * write is successful | |
548 | */ | |
549 | writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); | |
550 | ||
551 | while (retry--) { | |
552 | read = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
553 | if (read == val) | |
554 | break; | |
555 | ||
556 | writel(val, isp->base + BUTTRESS_REG_SECURITY_CTL); | |
557 | ||
558 | if (retry == 0) | |
559 | dev_err(&isp->pdev->dev, | |
560 | "update security control register failed\n"); | |
561 | } | |
562 | } | |
563 | ||
564 | bool ipu_buttress_get_secure_mode(struct ipu_device *isp) | |
565 | { | |
566 | u32 val; | |
567 | ||
568 | val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
569 | ||
570 | return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; | |
571 | } | |
572 | ||
573 | bool ipu_buttress_auth_done(struct ipu_device *isp) | |
574 | { | |
575 | u32 val; | |
576 | ||
577 | if (!isp->secure_mode) | |
578 | return 1; | |
579 | ||
580 | val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); | |
581 | ||
582 | return (val & BUTTRESS_SECURITY_CTL_FW_SETUP_MASK) == | |
583 | BUTTRESS_SECURITY_CTL_AUTH_DONE; | |
584 | } | |
585 | EXPORT_SYMBOL(ipu_buttress_auth_done); | |
586 | ||
587 | static void ipu_buttress_set_psys_ratio(struct ipu_device *isp, | |
588 | unsigned int psys_divisor, | |
589 | unsigned int psys_qos_floor) | |
590 | { | |
591 | struct ipu_buttress_ctrl *ctrl = isp->psys->ctrl; | |
592 | ||
593 | mutex_lock(&isp->buttress.power_mutex); | |
594 | ||
595 | if (ctrl->divisor == psys_divisor && ctrl->qos_floor == psys_qos_floor) | |
596 | goto out_mutex_unlock; | |
597 | ||
598 | ctrl->divisor = psys_divisor; | |
599 | ctrl->qos_floor = psys_qos_floor; | |
600 | ||
601 | if (ctrl->started) { | |
602 | /* | |
603 | * According to documentation driver initiates DVFS | |
604 | * transition by writing wanted ratio, floor ratio and start | |
605 | * bit. No need to stop PS first | |
606 | */ | |
607 | writel(BUTTRESS_FREQ_CTL_START | | |
608 | ctrl->qos_floor << BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT | | |
609 | psys_divisor, isp->base + BUTTRESS_REG_PS_FREQ_CTL); | |
610 | } | |
611 | ||
612 | out_mutex_unlock: | |
613 | mutex_unlock(&isp->buttress.power_mutex); | |
614 | } | |
615 | ||
3ebd4441 WY |
616 | static void ipu_buttress_set_isys_ratio(struct ipu_device *isp, |
617 | unsigned int isys_divisor) | |
618 | { | |
619 | struct ipu_buttress_ctrl *ctrl = isp->isys->ctrl; | |
620 | ||
621 | mutex_lock(&isp->buttress.power_mutex); | |
622 | ||
623 | if (ctrl->divisor == isys_divisor) | |
624 | goto out_mutex_unlock; | |
625 | ||
626 | ctrl->divisor = isys_divisor; | |
627 | ||
628 | if (ctrl->started) { | |
629 | writel(BUTTRESS_FREQ_CTL_START | | |
630 | ctrl->qos_floor << BUTTRESS_FREQ_CTL_QOS_FLOOR_SHIFT | | |
631 | isys_divisor, isp->base + BUTTRESS_REG_IS_FREQ_CTL); | |
632 | } | |
633 | ||
634 | out_mutex_unlock: | |
635 | mutex_unlock(&isp->buttress.power_mutex); | |
636 | } | |
637 | ||
f2efa4ee WY |
638 | static void ipu_buttress_set_psys_freq(struct ipu_device *isp, |
639 | unsigned int freq) | |
640 | { | |
641 | unsigned int psys_ratio = freq / BUTTRESS_PS_FREQ_STEP; | |
642 | ||
643 | if (isp->buttress.psys_force_ratio) | |
644 | return; | |
645 | ||
646 | ipu_buttress_set_psys_ratio(isp, psys_ratio, psys_ratio); | |
647 | } | |
648 | ||
649 | void | |
650 | ipu_buttress_add_psys_constraint(struct ipu_device *isp, | |
651 | struct ipu_buttress_constraint *constraint) | |
652 | { | |
653 | struct ipu_buttress *b = &isp->buttress; | |
654 | ||
655 | mutex_lock(&b->cons_mutex); | |
656 | list_add(&constraint->list, &b->constraints); | |
657 | ||
658 | if (constraint->min_freq > b->psys_min_freq) { | |
659 | isp->buttress.psys_min_freq = min(constraint->min_freq, | |
660 | b->psys_fused_freqs.max_freq); | |
661 | ipu_buttress_set_psys_freq(isp, b->psys_min_freq); | |
662 | } | |
663 | mutex_unlock(&b->cons_mutex); | |
664 | } | |
665 | EXPORT_SYMBOL_GPL(ipu_buttress_add_psys_constraint); | |
666 | ||
667 | void | |
668 | ipu_buttress_remove_psys_constraint(struct ipu_device *isp, | |
669 | struct ipu_buttress_constraint *constraint) | |
670 | { | |
671 | struct ipu_buttress *b = &isp->buttress; | |
672 | struct ipu_buttress_constraint *c; | |
673 | unsigned int min_freq = 0; | |
674 | ||
675 | mutex_lock(&b->cons_mutex); | |
676 | list_del(&constraint->list); | |
677 | ||
678 | if (constraint->min_freq >= b->psys_min_freq) { | |
679 | list_for_each_entry(c, &b->constraints, list) | |
680 | if (c->min_freq > min_freq) | |
681 | min_freq = c->min_freq; | |
682 | ||
683 | b->psys_min_freq = clamp(min_freq, | |
684 | b->psys_fused_freqs.efficient_freq, | |
685 | b->psys_fused_freqs.max_freq); | |
686 | ipu_buttress_set_psys_freq(isp, b->psys_min_freq); | |
687 | } | |
688 | mutex_unlock(&b->cons_mutex); | |
689 | } | |
690 | EXPORT_SYMBOL_GPL(ipu_buttress_remove_psys_constraint); | |
691 | ||
692 | int ipu_buttress_reset_authentication(struct ipu_device *isp) | |
693 | { | |
694 | int ret; | |
695 | u32 val; | |
696 | ||
697 | if (!isp->secure_mode) { | |
698 | dev_dbg(&isp->pdev->dev, | |
699 | "Non-secure mode -> skip authentication\n"); | |
700 | return 0; | |
701 | } | |
702 | ||
703 | writel(BUTTRESS_FW_RESET_CTL_START, isp->base + | |
704 | BUTTRESS_REG_FW_RESET_CTL); | |
705 | ||
706 | ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, | |
707 | val & BUTTRESS_FW_RESET_CTL_DONE, 500, | |
708 | BUTTRESS_CSE_FWRESET_TIMEOUT); | |
709 | if (ret) { | |
710 | dev_err(&isp->pdev->dev, | |
af60d6fc | 711 | "Time out while resetting authentication state\n"); |
f2efa4ee WY |
712 | } else { |
713 | dev_info(&isp->pdev->dev, | |
af60d6fc | 714 | "FW reset for authentication done\n"); |
f2efa4ee WY |
715 | writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); |
716 | /* leave some time for HW restore */ | |
717 | usleep_range(800, 1000); | |
718 | } | |
719 | ||
720 | return ret; | |
721 | } | |
722 | ||
723 | int ipu_buttress_map_fw_image(struct ipu_bus_device *sys, | |
724 | const struct firmware *fw, struct sg_table *sgt) | |
725 | { | |
726 | struct page **pages; | |
727 | const void *addr; | |
728 | unsigned long n_pages, i; | |
729 | int rval; | |
730 | ||
731 | n_pages = PAGE_ALIGN(fw->size) >> PAGE_SHIFT; | |
732 | ||
733 | pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); | |
734 | if (!pages) | |
735 | return -ENOMEM; | |
736 | ||
737 | addr = fw->data; | |
738 | for (i = 0; i < n_pages; i++) { | |
739 | struct page *p = vmalloc_to_page(addr); | |
740 | ||
741 | if (!p) { | |
742 | rval = -ENODEV; | |
743 | goto out; | |
744 | } | |
745 | pages[i] = p; | |
746 | addr += PAGE_SIZE; | |
747 | } | |
748 | ||
749 | rval = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size, | |
750 | GFP_KERNEL); | |
751 | if (rval) { | |
752 | rval = -ENOMEM; | |
753 | goto out; | |
754 | } | |
755 | ||
756 | n_pages = dma_map_sg(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); | |
757 | if (n_pages != sgt->nents) { | |
758 | rval = -ENOMEM; | |
759 | sg_free_table(sgt); | |
760 | goto out; | |
761 | } | |
762 | ||
763 | dma_sync_sg_for_device(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); | |
764 | ||
765 | out: | |
766 | kfree(pages); | |
767 | ||
768 | return rval; | |
769 | } | |
770 | EXPORT_SYMBOL_GPL(ipu_buttress_map_fw_image); | |
771 | ||
772 | int ipu_buttress_unmap_fw_image(struct ipu_bus_device *sys, | |
773 | struct sg_table *sgt) | |
774 | { | |
775 | dma_unmap_sg(&sys->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); | |
776 | sg_free_table(sgt); | |
777 | ||
778 | return 0; | |
779 | } | |
780 | EXPORT_SYMBOL_GPL(ipu_buttress_unmap_fw_image); | |
781 | ||
782 | int ipu_buttress_authenticate(struct ipu_device *isp) | |
783 | { | |
784 | struct ipu_psys_pdata *psys_pdata; | |
785 | struct ipu_buttress *b = &isp->buttress; | |
786 | u32 data, mask, done, fail; | |
787 | int rval; | |
788 | ||
789 | if (!isp->secure_mode) { | |
790 | dev_dbg(&isp->pdev->dev, | |
791 | "Non-secure mode -> skip authentication\n"); | |
792 | return 0; | |
793 | } | |
794 | ||
795 | psys_pdata = isp->psys->pdata; | |
796 | ||
797 | mutex_lock(&b->auth_mutex); | |
798 | ||
799 | if (ipu_buttress_auth_done(isp)) { | |
800 | rval = 0; | |
801 | goto iunit_power_off; | |
802 | } | |
803 | ||
804 | /* | |
805 | * Write address of FIT table to FW_SOURCE register | |
806 | * Let's use fw address. I.e. not using FIT table yet | |
807 | */ | |
808 | data = lower_32_bits(isp->pkg_dir_dma_addr); | |
809 | writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO); | |
810 | ||
811 | data = upper_32_bits(isp->pkg_dir_dma_addr); | |
812 | writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI); | |
813 | ||
814 | /* | |
815 | * Write boot_load into IU2CSEDATA0 | |
816 | * Write sizeof(boot_load) | 0x2 << CLIENT_ID to | |
817 | * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as | |
818 | */ | |
819 | dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n"); | |
820 | rval = ipu_buttress_ipc_send(isp, IPU_BUTTRESS_IPC_CSE, | |
821 | BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, | |
822 | 1, 1, | |
823 | BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); | |
824 | if (rval) { | |
825 | dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); | |
826 | goto iunit_power_off; | |
827 | } | |
828 | ||
829 | mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; | |
830 | done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; | |
831 | fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; | |
832 | rval = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, | |
833 | ((data & mask) == done || | |
834 | (data & mask) == fail), 500, | |
835 | BUTTRESS_CSE_BOOTLOAD_TIMEOUT); | |
836 | if (rval) { | |
af60d6fc | 837 | dev_err(&isp->pdev->dev, "CSE boot_load timeout\n"); |
f2efa4ee WY |
838 | goto iunit_power_off; |
839 | } | |
840 | ||
841 | data = readl(isp->base + BUTTRESS_REG_SECURITY_CTL) & mask; | |
842 | if (data == fail) { | |
843 | dev_err(&isp->pdev->dev, "CSE auth failed\n"); | |
844 | rval = -EINVAL; | |
845 | goto iunit_power_off; | |
846 | } | |
847 | ||
848 | rval = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET, | |
849 | data, data == BOOTLOADER_MAGIC_KEY, 500, | |
850 | BUTTRESS_CSE_BOOTLOAD_TIMEOUT); | |
851 | if (rval) { | |
af60d6fc | 852 | dev_err(&isp->pdev->dev, "Expect magic number timeout 0x%x\n", |
f2efa4ee WY |
853 | data); |
854 | goto iunit_power_off; | |
855 | } | |
856 | ||
857 | /* | |
858 | * Write authenticate_run into IU2CSEDATA0 | |
859 | * Write sizeof(boot_load) | 0x2 << CLIENT_ID to | |
860 | * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as | |
861 | */ | |
862 | dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n"); | |
863 | rval = ipu_buttress_ipc_send(isp, IPU_BUTTRESS_IPC_CSE, | |
864 | BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, | |
865 | 1, 1, | |
866 | BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); | |
867 | if (rval) { | |
868 | dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n"); | |
869 | goto iunit_power_off; | |
870 | } | |
871 | ||
872 | done = BUTTRESS_SECURITY_CTL_AUTH_DONE; | |
873 | rval = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, | |
874 | ((data & mask) == done || | |
875 | (data & mask) == fail), 500, | |
876 | BUTTRESS_CSE_AUTHENTICATE_TIMEOUT); | |
877 | if (rval) { | |
878 | dev_err(&isp->pdev->dev, "CSE authenticate timeout\n"); | |
879 | goto iunit_power_off; | |
880 | } | |
881 | ||
882 | data = readl(isp->base + BUTTRESS_REG_SECURITY_CTL) & mask; | |
883 | if (data == fail) { | |
884 | dev_err(&isp->pdev->dev, "CSE boot_load failed\n"); | |
885 | rval = -EINVAL; | |
886 | goto iunit_power_off; | |
887 | } | |
888 | ||
889 | dev_info(&isp->pdev->dev, "CSE authenticate_run done\n"); | |
890 | ||
891 | iunit_power_off: | |
892 | mutex_unlock(&b->auth_mutex); | |
893 | ||
894 | return rval; | |
895 | } | |
f2efa4ee WY |
896 | |
897 | static int ipu_buttress_send_tsc_request(struct ipu_device *isp) | |
898 | { | |
899 | u32 val, mask, shift, done; | |
900 | int ret; | |
901 | ||
902 | mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK; | |
903 | shift = BUTTRESS_PWR_STATE_HH_STATUS_SHIFT; | |
904 | ||
905 | writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC, | |
906 | isp->base + BUTTRESS_REG_FABRIC_CMD); | |
907 | ||
908 | val = readl(isp->base + BUTTRESS_REG_PWR_STATE); | |
909 | val = (val & mask) >> shift; | |
910 | if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) { | |
af60d6fc | 911 | dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); |
f2efa4ee WY |
912 | return -EINVAL; |
913 | } | |
914 | ||
915 | done = BUTTRESS_PWR_STATE_HH_STATE_DONE; | |
916 | ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val, | |
917 | ((val & mask) >> shift == done), 500, | |
918 | BUTTRESS_TSC_SYNC_TIMEOUT); | |
919 | if (ret) | |
af60d6fc | 920 | dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); |
f2efa4ee WY |
921 | |
922 | return ret; | |
923 | } | |
924 | ||
925 | int ipu_buttress_start_tsc_sync(struct ipu_device *isp) | |
926 | { | |
927 | unsigned int i; | |
928 | ||
929 | for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { | |
930 | int ret; | |
931 | ||
932 | ret = ipu_buttress_send_tsc_request(isp); | |
933 | if (ret == -ETIMEDOUT) { | |
934 | u32 val; | |
935 | /* set tsw soft reset */ | |
936 | val = readl(isp->base + BUTTRESS_REG_TSW_CTL); | |
937 | val = val | BUTTRESS_TSW_CTL_SOFT_RESET; | |
938 | writel(val, isp->base + BUTTRESS_REG_TSW_CTL); | |
939 | /* clear tsw soft reset */ | |
940 | val = val & (~BUTTRESS_TSW_CTL_SOFT_RESET); | |
941 | writel(val, isp->base + BUTTRESS_REG_TSW_CTL); | |
942 | ||
943 | continue; | |
944 | } | |
945 | return ret; | |
946 | } | |
947 | ||
af60d6fc | 948 | dev_err(&isp->pdev->dev, "TSC sync failed(timeout)\n"); |
f2efa4ee WY |
949 | |
950 | return -ETIMEDOUT; | |
951 | } | |
952 | EXPORT_SYMBOL(ipu_buttress_start_tsc_sync); | |
953 | ||
954 | struct clk_ipu_sensor { | |
955 | struct ipu_device *isp; | |
956 | struct clk_hw hw; | |
957 | unsigned int id; | |
958 | unsigned long rate; | |
959 | }; | |
960 | ||
961 | #define to_clk_ipu_sensor(_hw) container_of(_hw, struct clk_ipu_sensor, hw) | |
962 | ||
f2efa4ee WY |
963 | int ipu_buttress_tsc_read(struct ipu_device *isp, u64 *val) |
964 | { | |
5a771b35 | 965 | u32 tsc_hi_1, tsc_hi_2, tsc_lo; |
f2efa4ee | 966 | unsigned long flags; |
f2efa4ee | 967 | |
5a771b35 HY |
968 | local_irq_save(flags); |
969 | tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI); | |
970 | tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); | |
971 | tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI); | |
972 | if (tsc_hi_1 == tsc_hi_2) { | |
973 | *val = (u64)tsc_hi_1 << 32 | tsc_lo; | |
974 | } else { | |
975 | /* Check if TSC has rolled over */ | |
976 | if (tsc_lo & BIT(31)) | |
977 | *val = (u64)tsc_hi_1 << 32 | tsc_lo; | |
978 | else | |
979 | *val = (u64)tsc_hi_2 << 32 | tsc_lo; | |
980 | } | |
981 | local_irq_restore(flags); | |
f2efa4ee | 982 | |
5a771b35 | 983 | return 0; |
f2efa4ee WY |
984 | } |
985 | EXPORT_SYMBOL_GPL(ipu_buttress_tsc_read); | |
986 | ||
987 | #ifdef CONFIG_DEBUG_FS | |
988 | ||
989 | static int ipu_buttress_reg_open(struct inode *inode, struct file *file) | |
990 | { | |
991 | if (!inode->i_private) | |
992 | return -EACCES; | |
993 | ||
994 | file->private_data = inode->i_private; | |
995 | return 0; | |
996 | } | |
997 | ||
998 | static ssize_t ipu_buttress_reg_read(struct file *file, char __user *buf, | |
999 | size_t count, loff_t *ppos) | |
1000 | { | |
1001 | struct debugfs_reg32 *reg = file->private_data; | |
1002 | u8 tmp[11]; | |
1003 | u32 val = readl((void __iomem *)reg->offset); | |
1004 | int len = scnprintf(tmp, sizeof(tmp), "0x%08x", val); | |
1005 | ||
1006 | return simple_read_from_buffer(buf, len, ppos, &tmp, len); | |
1007 | } | |
1008 | ||
1009 | static ssize_t ipu_buttress_reg_write(struct file *file, | |
1010 | const char __user *buf, | |
1011 | size_t count, loff_t *ppos) | |
1012 | { | |
1013 | struct debugfs_reg32 *reg = file->private_data; | |
1014 | u32 val; | |
1015 | int rval; | |
1016 | ||
1017 | rval = kstrtou32_from_user(buf, count, 0, &val); | |
1018 | if (rval) | |
1019 | return rval; | |
1020 | ||
1021 | writel(val, (void __iomem *)reg->offset); | |
1022 | ||
1023 | return count; | |
1024 | } | |
1025 | ||
1026 | static struct debugfs_reg32 buttress_regs[] = { | |
1027 | {"IU2CSEDB0", BUTTRESS_REG_IU2CSEDB0}, | |
1028 | {"IU2CSEDATA0", BUTTRESS_REG_IU2CSEDATA0}, | |
1029 | {"CSE2IUDB0", BUTTRESS_REG_CSE2IUDB0}, | |
1030 | {"CSE2IUDATA0", BUTTRESS_REG_CSE2IUDATA0}, | |
1031 | {"CSE2IUCSR", BUTTRESS_REG_CSE2IUCSR}, | |
1032 | {"IU2CSECSR", BUTTRESS_REG_IU2CSECSR}, | |
1033 | }; | |
1034 | ||
1035 | static const struct file_operations ipu_buttress_reg_fops = { | |
1036 | .owner = THIS_MODULE, | |
1037 | .open = ipu_buttress_reg_open, | |
1038 | .read = ipu_buttress_reg_read, | |
1039 | .write = ipu_buttress_reg_write, | |
1040 | }; | |
1041 | ||
1042 | static int ipu_buttress_start_tsc_sync_set(void *data, u64 val) | |
1043 | { | |
1044 | struct ipu_device *isp = data; | |
1045 | ||
1046 | return ipu_buttress_start_tsc_sync(isp); | |
1047 | } | |
1048 | ||
1049 | DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_start_tsc_sync_fops, NULL, | |
1050 | ipu_buttress_start_tsc_sync_set, "%llu\n"); | |
1051 | ||
1052 | static int ipu_buttress_tsc_get(void *data, u64 *val) | |
1053 | { | |
1054 | return ipu_buttress_tsc_read(data, val); | |
1055 | } | |
1056 | DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_tsc_fops, ipu_buttress_tsc_get, | |
1057 | NULL, "%llu\n"); | |
1058 | ||
1059 | static int ipu_buttress_psys_force_freq_get(void *data, u64 *val) | |
1060 | { | |
1061 | struct ipu_device *isp = data; | |
1062 | ||
1063 | *val = isp->buttress.psys_force_ratio * BUTTRESS_PS_FREQ_STEP; | |
1064 | ||
1065 | return 0; | |
1066 | } | |
1067 | ||
1068 | static int ipu_buttress_psys_force_freq_set(void *data, u64 val) | |
1069 | { | |
1070 | struct ipu_device *isp = data; | |
1071 | ||
1072 | if (val && (val < BUTTRESS_MIN_FORCE_PS_FREQ || | |
1073 | val > BUTTRESS_MAX_FORCE_PS_FREQ)) | |
1074 | return -EINVAL; | |
1075 | ||
1076 | do_div(val, BUTTRESS_PS_FREQ_STEP); | |
1077 | isp->buttress.psys_force_ratio = val; | |
1078 | ||
1079 | if (isp->buttress.psys_force_ratio) | |
1080 | ipu_buttress_set_psys_ratio(isp, | |
1081 | isp->buttress.psys_force_ratio, | |
1082 | isp->buttress.psys_force_ratio); | |
1083 | else | |
1084 | ipu_buttress_set_psys_freq(isp, isp->buttress.psys_min_freq); | |
1085 | ||
1086 | return 0; | |
1087 | } | |
1088 | ||
fad9119b | 1089 | static int ipu_buttress_isys_freq_get(void *data, u64 *val) |
3ebd4441 WY |
1090 | { |
1091 | struct ipu_device *isp = data; | |
1092 | u32 reg_val; | |
1093 | int rval; | |
1094 | ||
1095 | rval = pm_runtime_get_sync(&isp->isys->dev); | |
1096 | if (rval < 0) { | |
1097 | pm_runtime_put(&isp->isys->dev); | |
1098 | dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); | |
1099 | return rval; | |
1100 | } | |
1101 | ||
1102 | reg_val = readl(isp->base + BUTTRESS_REG_IS_FREQ_CTL); | |
1103 | ||
1104 | pm_runtime_put(&isp->isys->dev); | |
1105 | ||
1106 | *val = IPU_IS_FREQ_RATIO_BASE * | |
1107 | (reg_val & IPU_BUTTRESS_IS_FREQ_CTL_DIVISOR_MASK); | |
1108 | ||
1109 | return 0; | |
1110 | } | |
1111 | ||
fad9119b | 1112 | static int ipu_buttress_isys_freq_set(void *data, u64 val) |
3ebd4441 WY |
1113 | { |
1114 | struct ipu_device *isp = data; | |
1115 | int rval; | |
1116 | ||
1117 | if (val < BUTTRESS_MIN_FORCE_IS_FREQ || | |
1118 | val > BUTTRESS_MAX_FORCE_IS_FREQ) | |
1119 | return -EINVAL; | |
1120 | ||
1121 | rval = pm_runtime_get_sync(&isp->isys->dev); | |
1122 | if (rval < 0) { | |
1123 | pm_runtime_put(&isp->isys->dev); | |
1124 | dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); | |
1125 | return rval; | |
1126 | } | |
1127 | ||
1128 | do_div(val, BUTTRESS_IS_FREQ_STEP); | |
1129 | if (val) | |
1130 | ipu_buttress_set_isys_ratio(isp, val); | |
1131 | ||
1132 | pm_runtime_put(&isp->isys->dev); | |
1133 | ||
1134 | return 0; | |
1135 | } | |
1136 | ||
f2efa4ee WY |
1137 | DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_force_freq_fops, |
1138 | ipu_buttress_psys_force_freq_get, | |
1139 | ipu_buttress_psys_force_freq_set, "%llu\n"); | |
1140 | ||
1141 | DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_freq_fops, | |
1142 | ipu_buttress_psys_freq_get, NULL, "%llu\n"); | |
1143 | ||
1144 | DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_isys_freq_fops, | |
3ebd4441 WY |
1145 | ipu_buttress_isys_freq_get, |
1146 | ipu_buttress_isys_freq_set, "%llu\n"); | |
f2efa4ee WY |
1147 | |
1148 | int ipu_buttress_debugfs_init(struct ipu_device *isp) | |
1149 | { | |
1150 | struct debugfs_reg32 *reg = | |
1151 | devm_kcalloc(&isp->pdev->dev, ARRAY_SIZE(buttress_regs), | |
1152 | sizeof(*reg), GFP_KERNEL); | |
1153 | struct dentry *dir, *file; | |
1154 | int i; | |
1155 | ||
1156 | if (!reg) | |
1157 | return -ENOMEM; | |
1158 | ||
1159 | dir = debugfs_create_dir("buttress", isp->ipu_dir); | |
1160 | if (!dir) | |
1161 | return -ENOMEM; | |
1162 | ||
1163 | for (i = 0; i < ARRAY_SIZE(buttress_regs); i++, reg++) { | |
1164 | reg->offset = (unsigned long)isp->base + | |
1165 | buttress_regs[i].offset; | |
1166 | reg->name = buttress_regs[i].name; | |
1167 | file = debugfs_create_file(reg->name, 0700, | |
1168 | dir, reg, &ipu_buttress_reg_fops); | |
1169 | if (!file) | |
1170 | goto err; | |
1171 | } | |
1172 | ||
1173 | file = debugfs_create_file("start_tsc_sync", 0200, dir, isp, | |
1174 | &ipu_buttress_start_tsc_sync_fops); | |
1175 | if (!file) | |
1176 | goto err; | |
1177 | file = debugfs_create_file("tsc", 0400, dir, isp, | |
1178 | &ipu_buttress_tsc_fops); | |
1179 | if (!file) | |
1180 | goto err; | |
1181 | file = debugfs_create_file("psys_force_freq", 0700, dir, isp, | |
1182 | &ipu_buttress_psys_force_freq_fops); | |
1183 | if (!file) | |
1184 | goto err; | |
1185 | ||
1186 | file = debugfs_create_file("psys_freq", 0400, dir, isp, | |
1187 | &ipu_buttress_psys_freq_fops); | |
1188 | if (!file) | |
1189 | goto err; | |
1190 | ||
3ebd4441 | 1191 | file = debugfs_create_file("isys_freq", 0700, dir, isp, |
f2efa4ee WY |
1192 | &ipu_buttress_isys_freq_fops); |
1193 | if (!file) | |
1194 | goto err; | |
1195 | ||
1196 | return 0; | |
1197 | err: | |
1198 | debugfs_remove_recursive(dir); | |
1199 | return -ENOMEM; | |
1200 | } | |
1201 | ||
1202 | #endif /* CONFIG_DEBUG_FS */ | |
1203 | ||
1204 | u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks) | |
1205 | { | |
1206 | u64 ns = ticks * 10000; | |
1207 | /* | |
1208 | * TSC clock frequency is 19.2MHz, | |
1209 | * converting TSC tick count to ns is calculated by: | |
1210 | * ns = ticks * 1000 000 000 / 19.2Mhz | |
1211 | * = ticks * 1000 000 000 / 19200000Hz | |
1212 | * = ticks * 10000 / 192 ns | |
1213 | */ | |
1214 | do_div(ns, 192); | |
1215 | ||
1216 | return ns; | |
1217 | } | |
1218 | EXPORT_SYMBOL_GPL(ipu_buttress_tsc_ticks_to_ns); | |
1219 | ||
1220 | static ssize_t psys_fused_min_freq_show(struct device *dev, | |
1221 | struct device_attribute *attr, | |
1222 | char *buf) | |
1223 | { | |
1224 | struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); | |
1225 | ||
1226 | return snprintf(buf, PAGE_SIZE, "%u\n", | |
1227 | isp->buttress.psys_fused_freqs.min_freq); | |
1228 | } | |
1229 | ||
1230 | static DEVICE_ATTR_RO(psys_fused_min_freq); | |
1231 | ||
1232 | static ssize_t psys_fused_max_freq_show(struct device *dev, | |
1233 | struct device_attribute *attr, | |
1234 | char *buf) | |
1235 | { | |
1236 | struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); | |
1237 | ||
1238 | return snprintf(buf, PAGE_SIZE, "%u\n", | |
1239 | isp->buttress.psys_fused_freqs.max_freq); | |
1240 | } | |
1241 | ||
1242 | static DEVICE_ATTR_RO(psys_fused_max_freq); | |
1243 | ||
1244 | static ssize_t psys_fused_efficient_freq_show(struct device *dev, | |
1245 | struct device_attribute *attr, | |
1246 | char *buf) | |
1247 | { | |
1248 | struct ipu_device *isp = pci_get_drvdata(to_pci_dev(dev)); | |
1249 | ||
1250 | return snprintf(buf, PAGE_SIZE, "%u\n", | |
1251 | isp->buttress.psys_fused_freqs.efficient_freq); | |
1252 | } | |
1253 | ||
1254 | static DEVICE_ATTR_RO(psys_fused_efficient_freq); | |
1255 | ||
1256 | int ipu_buttress_restore(struct ipu_device *isp) | |
1257 | { | |
1258 | struct ipu_buttress *b = &isp->buttress; | |
1259 | ||
1260 | writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
1261 | writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
1262 | writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT); | |
1263 | ||
1264 | return 0; | |
1265 | } | |
1266 | ||
1267 | int ipu_buttress_init(struct ipu_device *isp) | |
1268 | { | |
1269 | struct ipu_buttress *b = &isp->buttress; | |
1270 | int rval, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; | |
1271 | ||
1272 | mutex_init(&b->power_mutex); | |
1273 | mutex_init(&b->auth_mutex); | |
1274 | mutex_init(&b->cons_mutex); | |
1275 | mutex_init(&b->ipc_mutex); | |
f2efa4ee WY |
1276 | init_completion(&b->ish.send_complete); |
1277 | init_completion(&b->cse.send_complete); | |
1278 | init_completion(&b->ish.recv_complete); | |
1279 | init_completion(&b->cse.recv_complete); | |
1280 | ||
1281 | b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; | |
1282 | b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; | |
1283 | b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; | |
1284 | b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; | |
1285 | b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; | |
1286 | b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; | |
1287 | b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; | |
1288 | b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; | |
1289 | ||
1290 | /* no ISH on IPU6 */ | |
1291 | memset(&b->ish, 0, sizeof(b->ish)); | |
1292 | INIT_LIST_HEAD(&b->constraints); | |
1293 | ||
1294 | ipu_buttress_set_secure_mode(isp); | |
1295 | isp->secure_mode = ipu_buttress_get_secure_mode(isp); | |
1296 | if (isp->secure_mode != secure_mode_enable) | |
af60d6fc | 1297 | dev_warn(&isp->pdev->dev, "Unable to set secure mode\n"); |
f2efa4ee WY |
1298 | |
1299 | dev_info(&isp->pdev->dev, "IPU in %s mode\n", | |
1300 | isp->secure_mode ? "secure" : "non-secure"); | |
1301 | ||
1302 | b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT); | |
1303 | writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR); | |
1304 | writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
1305 | ||
1306 | rval = device_create_file(&isp->pdev->dev, | |
1307 | &dev_attr_psys_fused_min_freq); | |
1308 | if (rval) { | |
1309 | dev_err(&isp->pdev->dev, "Create min freq file failed\n"); | |
1310 | goto err_mutex_destroy; | |
1311 | } | |
1312 | ||
1313 | rval = device_create_file(&isp->pdev->dev, | |
1314 | &dev_attr_psys_fused_max_freq); | |
1315 | if (rval) { | |
1316 | dev_err(&isp->pdev->dev, "Create max freq file failed\n"); | |
1317 | goto err_remove_min_freq_file; | |
1318 | } | |
1319 | ||
1320 | rval = device_create_file(&isp->pdev->dev, | |
1321 | &dev_attr_psys_fused_efficient_freq); | |
1322 | if (rval) { | |
1323 | dev_err(&isp->pdev->dev, "Create efficient freq file failed\n"); | |
1324 | goto err_remove_max_freq_file; | |
1325 | } | |
1326 | ||
1327 | /* | |
1328 | * We want to retry couple of time in case CSE initialization | |
1329 | * is delayed for reason or another. | |
1330 | */ | |
1331 | do { | |
1332 | rval = ipu_buttress_ipc_reset(isp, &b->cse); | |
1333 | if (rval) { | |
af60d6fc WY |
1334 | dev_warn(&isp->pdev->dev, |
1335 | "IPC reset protocol failed, retrying\n"); | |
f2efa4ee | 1336 | } else { |
af60d6fc | 1337 | dev_info(&isp->pdev->dev, "IPC reset done\n"); |
f2efa4ee WY |
1338 | return 0; |
1339 | } | |
1340 | } while (ipc_reset_retry--); | |
1341 | ||
1342 | dev_err(&isp->pdev->dev, "IPC reset protocol failed\n"); | |
1343 | ||
1344 | err_remove_max_freq_file: | |
1345 | device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_max_freq); | |
1346 | err_remove_min_freq_file: | |
1347 | device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_min_freq); | |
1348 | err_mutex_destroy: | |
1349 | mutex_destroy(&b->power_mutex); | |
1350 | mutex_destroy(&b->auth_mutex); | |
1351 | mutex_destroy(&b->cons_mutex); | |
1352 | mutex_destroy(&b->ipc_mutex); | |
1353 | ||
1354 | return rval; | |
1355 | } | |
1356 | ||
1357 | void ipu_buttress_exit(struct ipu_device *isp) | |
1358 | { | |
1359 | struct ipu_buttress *b = &isp->buttress; | |
1360 | ||
1361 | writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE); | |
1362 | ||
1363 | device_remove_file(&isp->pdev->dev, | |
1364 | &dev_attr_psys_fused_efficient_freq); | |
1365 | device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_max_freq); | |
1366 | device_remove_file(&isp->pdev->dev, &dev_attr_psys_fused_min_freq); | |
1367 | ||
1368 | mutex_destroy(&b->power_mutex); | |
1369 | mutex_destroy(&b->auth_mutex); | |
1370 | mutex_destroy(&b->cons_mutex); | |
1371 | mutex_destroy(&b->ipc_mutex); | |
1372 | } |