xref: /linux/drivers/accel/amdxdna/aie_psp.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1dd2f592bSDavid Zhang // SPDX-License-Identifier: GPL-2.0
2dd2f592bSDavid Zhang /*
3dd2f592bSDavid Zhang  * Copyright (C) 2026, Advanced Micro Devices, Inc.
4dd2f592bSDavid Zhang  */
5dd2f592bSDavid Zhang 
6dd2f592bSDavid Zhang #include <drm/drm_device.h>
7dd2f592bSDavid Zhang #include <drm/drm_managed.h>
8dd2f592bSDavid Zhang #include <drm/drm_print.h>
9dd2f592bSDavid Zhang #include <linux/bitfield.h>
10dd2f592bSDavid Zhang #include <linux/iopoll.h>
11dd2f592bSDavid Zhang #include <linux/slab.h>
12dd2f592bSDavid Zhang 
13dd2f592bSDavid Zhang #include "aie.h"
14dd2f592bSDavid Zhang 
15dd2f592bSDavid Zhang #define PSP_STATUS_READY	BIT(31)
16dd2f592bSDavid Zhang 
17dd2f592bSDavid Zhang /* PSP commands */
18dd2f592bSDavid Zhang #define PSP_VALIDATE		1
19dd2f592bSDavid Zhang #define PSP_START		2
20dd2f592bSDavid Zhang #define PSP_RELEASE_TMR		3
21*e69ef214SDavid Zhang #define PSP_VALIDATE_CERT       4
22dd2f592bSDavid Zhang 
23dd2f592bSDavid Zhang /* PSP special arguments */
24dd2f592bSDavid Zhang #define PSP_START_COPY_FW	1
25dd2f592bSDavid Zhang 
26dd2f592bSDavid Zhang /* PSP response error code */
27dd2f592bSDavid Zhang #define PSP_ERROR_CANCEL	0xFFFF0002
28dd2f592bSDavid Zhang #define PSP_ERROR_BAD_STATE	0xFFFF0007
29dd2f592bSDavid Zhang 
30dd2f592bSDavid Zhang #define PSP_FW_ALIGN		0x10000
31*e69ef214SDavid Zhang #define PSP_CFW_ALIGN           0x8000
32dd2f592bSDavid Zhang #define PSP_POLL_INTERVAL	20000	/* us */
33dd2f592bSDavid Zhang #define PSP_POLL_TIMEOUT	1000000	/* us */
34dd2f592bSDavid Zhang 
35*e69ef214SDavid Zhang #define PSP_REG(p, reg) ((p)->conf.psp_regs[reg])
36*e69ef214SDavid Zhang #define PSP_SET_CMD(psp, reg_vals, cmd, arg0, arg1, arg2)		\
37*e69ef214SDavid Zhang ({									\
38*e69ef214SDavid Zhang 	u32 *_regs = reg_vals;						\
39*e69ef214SDavid Zhang 	u32 _cmd = cmd;							\
40*e69ef214SDavid Zhang 	_regs[0] = _cmd;						\
41*e69ef214SDavid Zhang 	_regs[1] = arg0;						\
42*e69ef214SDavid Zhang 	_regs[2] = arg1;						\
43*e69ef214SDavid Zhang 	_regs[3] = ((arg2) | ((_cmd) << 24)) & (psp)->conf.arg2_mask;	\
44*e69ef214SDavid Zhang })
45dd2f592bSDavid Zhang 
46dd2f592bSDavid Zhang struct psp_device {
47dd2f592bSDavid Zhang 	struct drm_device	*ddev;
48dd2f592bSDavid Zhang 	struct psp_config	conf;
49dd2f592bSDavid Zhang 	u32			fw_buf_sz;
50dd2f592bSDavid Zhang 	u64			fw_paddr;
51dd2f592bSDavid Zhang 	void			*fw_buffer;
52*e69ef214SDavid Zhang 	u32                     certfw_buf_sz;
53*e69ef214SDavid Zhang 	u64                     certfw_paddr;
54*e69ef214SDavid Zhang 	void                    *certfw_buffer;
55dd2f592bSDavid Zhang };
56dd2f592bSDavid Zhang 
57dd2f592bSDavid Zhang static int psp_exec(struct psp_device *psp, u32 *reg_vals)
58dd2f592bSDavid Zhang {
59dd2f592bSDavid Zhang 	u32 resp_code;
60dd2f592bSDavid Zhang 	int ret, i;
61dd2f592bSDavid Zhang 	u32 ready;
62dd2f592bSDavid Zhang 
63*e69ef214SDavid Zhang 	/* Check for PSP ready before any write */
64*e69ef214SDavid Zhang 	ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
65*e69ef214SDavid Zhang 				 FIELD_GET(PSP_STATUS_READY, ready),
66*e69ef214SDavid Zhang 				 PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
67*e69ef214SDavid Zhang 	if (ret) {
68*e69ef214SDavid Zhang 		drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
69*e69ef214SDavid Zhang 		return ret;
70*e69ef214SDavid Zhang 	}
71*e69ef214SDavid Zhang 
72dd2f592bSDavid Zhang 	/* Write command and argument registers */
73dd2f592bSDavid Zhang 	for (i = 0; i < PSP_NUM_IN_REGS; i++)
74dd2f592bSDavid Zhang 		writel(reg_vals[i], PSP_REG(psp, i));
75dd2f592bSDavid Zhang 
76dd2f592bSDavid Zhang 	/* clear and set PSP INTR register to kick off */
77dd2f592bSDavid Zhang 	writel(0, PSP_REG(psp, PSP_INTR_REG));
78*e69ef214SDavid Zhang 	writel(psp->conf.notify_val, PSP_REG(psp, PSP_INTR_REG));
79dd2f592bSDavid Zhang 
80dd2f592bSDavid Zhang 	/* PSP should be busy. Wait for ready, so we know task is done. */
81dd2f592bSDavid Zhang 	ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
82dd2f592bSDavid Zhang 				 FIELD_GET(PSP_STATUS_READY, ready),
83dd2f592bSDavid Zhang 				 PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
84dd2f592bSDavid Zhang 	if (ret) {
85dd2f592bSDavid Zhang 		drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
86dd2f592bSDavid Zhang 		return ret;
87dd2f592bSDavid Zhang 	}
88dd2f592bSDavid Zhang 
89dd2f592bSDavid Zhang 	resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
90dd2f592bSDavid Zhang 	if (resp_code) {
91dd2f592bSDavid Zhang 		drm_err(psp->ddev, "fw return error 0x%x", resp_code);
92dd2f592bSDavid Zhang 		return -EIO;
93dd2f592bSDavid Zhang 	}
94dd2f592bSDavid Zhang 
95dd2f592bSDavid Zhang 	return 0;
96dd2f592bSDavid Zhang }
97dd2f592bSDavid Zhang 
98dd2f592bSDavid Zhang int aie_psp_waitmode_poll(struct psp_device *psp)
99dd2f592bSDavid Zhang {
100dd2f592bSDavid Zhang 	struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev);
101dd2f592bSDavid Zhang 	u32 mode_reg;
102dd2f592bSDavid Zhang 	int ret;
103dd2f592bSDavid Zhang 
104dd2f592bSDavid Zhang 	ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg,
105dd2f592bSDavid Zhang 				 (mode_reg & 0x1) == 1,
106dd2f592bSDavid Zhang 				 PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
107dd2f592bSDavid Zhang 	if (ret)
108dd2f592bSDavid Zhang 		XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret);
109dd2f592bSDavid Zhang 
110dd2f592bSDavid Zhang 	return ret;
111dd2f592bSDavid Zhang }
112dd2f592bSDavid Zhang 
113dd2f592bSDavid Zhang void aie_psp_stop(struct psp_device *psp)
114dd2f592bSDavid Zhang {
115*e69ef214SDavid Zhang 	u32 reg_vals[PSP_NUM_IN_REGS];
116dd2f592bSDavid Zhang 	int ret;
117dd2f592bSDavid Zhang 
118*e69ef214SDavid Zhang 	PSP_SET_CMD(psp, reg_vals, PSP_RELEASE_TMR, 0, 0, 0);
119*e69ef214SDavid Zhang 
120dd2f592bSDavid Zhang 	ret = psp_exec(psp, reg_vals);
121dd2f592bSDavid Zhang 	if (ret)
122dd2f592bSDavid Zhang 		drm_err(psp->ddev, "release tmr failed, ret %d", ret);
123dd2f592bSDavid Zhang }
124dd2f592bSDavid Zhang 
125*e69ef214SDavid Zhang static int psp_validate_fw(struct psp_device *psp, u8 cmd, u64 paddr, u32 buf_sz)
126dd2f592bSDavid Zhang {
127dd2f592bSDavid Zhang 	u32 reg_vals[PSP_NUM_IN_REGS];
128dd2f592bSDavid Zhang 	int ret;
129dd2f592bSDavid Zhang 
130*e69ef214SDavid Zhang 	PSP_SET_CMD(psp, reg_vals, cmd, lower_32_bits(paddr),
131*e69ef214SDavid Zhang 		    upper_32_bits(paddr), buf_sz);
132dd2f592bSDavid Zhang 
133dd2f592bSDavid Zhang 	ret = psp_exec(psp, reg_vals);
134*e69ef214SDavid Zhang 	if (ret)
135dd2f592bSDavid Zhang 		drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
136*e69ef214SDavid Zhang 
137dd2f592bSDavid Zhang 	return ret;
138dd2f592bSDavid Zhang }
139dd2f592bSDavid Zhang 
140*e69ef214SDavid Zhang static int psp_start(struct psp_device *psp)
141*e69ef214SDavid Zhang {
142*e69ef214SDavid Zhang 	u32 reg_vals[PSP_NUM_IN_REGS];
143*e69ef214SDavid Zhang 	int ret;
144*e69ef214SDavid Zhang 
145*e69ef214SDavid Zhang 	PSP_SET_CMD(psp, reg_vals, PSP_START, PSP_START_COPY_FW, 0, 0);
146*e69ef214SDavid Zhang 
147dd2f592bSDavid Zhang 	ret = psp_exec(psp, reg_vals);
148*e69ef214SDavid Zhang 	if (ret)
149dd2f592bSDavid Zhang 		drm_err(psp->ddev, "failed to start fw, ret %d", ret);
150*e69ef214SDavid Zhang 
151dd2f592bSDavid Zhang 	return ret;
152dd2f592bSDavid Zhang }
153dd2f592bSDavid Zhang 
154*e69ef214SDavid Zhang int aie_psp_start(struct psp_device *psp)
155*e69ef214SDavid Zhang {
156*e69ef214SDavid Zhang 	int ret;
157*e69ef214SDavid Zhang 
158*e69ef214SDavid Zhang 	ret = psp_validate_fw(psp, PSP_VALIDATE,
159*e69ef214SDavid Zhang 			      psp->fw_paddr, psp->fw_buf_sz);
160*e69ef214SDavid Zhang 	if (ret)
161*e69ef214SDavid Zhang 		return ret;
162*e69ef214SDavid Zhang 
163*e69ef214SDavid Zhang 	if (!psp->certfw_buf_sz)
164*e69ef214SDavid Zhang 		goto psp_start;
165*e69ef214SDavid Zhang 
166*e69ef214SDavid Zhang 	ret = psp_validate_fw(psp, PSP_VALIDATE_CERT,
167*e69ef214SDavid Zhang 			      psp->certfw_paddr, psp->certfw_buf_sz);
168*e69ef214SDavid Zhang 	if (ret)
169*e69ef214SDavid Zhang 		return ret;
170*e69ef214SDavid Zhang psp_start:
171*e69ef214SDavid Zhang 	return psp_start(psp);
172*e69ef214SDavid Zhang }
173*e69ef214SDavid Zhang 
174*e69ef214SDavid Zhang /*
175*e69ef214SDavid Zhang  * PSP requires host physical address to load firmware.
176*e69ef214SDavid Zhang  * Allocate a buffer, obtain its physical address, align, and copy data in.
177*e69ef214SDavid Zhang  */
178*e69ef214SDavid Zhang static void *psp_alloc_fw_buf(struct psp_device *psp, const void *fw_data,
179*e69ef214SDavid Zhang 			      u32 fw_size, u32 align, u32 *buf_sz,
180*e69ef214SDavid Zhang 			      u64 *paddr)
181*e69ef214SDavid Zhang {
182*e69ef214SDavid Zhang 	u32 alloc_sz;
183*e69ef214SDavid Zhang 	void *buffer;
184*e69ef214SDavid Zhang 	u64 offset;
185*e69ef214SDavid Zhang 
186*e69ef214SDavid Zhang 	*buf_sz = ALIGN(fw_size, align);
187*e69ef214SDavid Zhang 	alloc_sz = *buf_sz + align;
188*e69ef214SDavid Zhang 
189*e69ef214SDavid Zhang 	buffer = drmm_kmalloc(psp->ddev, alloc_sz, GFP_KERNEL);
190*e69ef214SDavid Zhang 	if (!buffer)
191*e69ef214SDavid Zhang 		return NULL;
192*e69ef214SDavid Zhang 
193*e69ef214SDavid Zhang 	*paddr = virt_to_phys(buffer);
194*e69ef214SDavid Zhang 	offset = ALIGN(*paddr, align) - *paddr;
195*e69ef214SDavid Zhang 	*paddr += offset;
196*e69ef214SDavid Zhang 	memcpy(buffer + offset, fw_data, fw_size);
197*e69ef214SDavid Zhang 
198*e69ef214SDavid Zhang 	return buffer;
199dd2f592bSDavid Zhang }
200dd2f592bSDavid Zhang 
201dd2f592bSDavid Zhang struct psp_device *aiem_psp_create(struct drm_device *ddev, struct psp_config *conf)
202dd2f592bSDavid Zhang {
203dd2f592bSDavid Zhang 	struct psp_device *psp;
204dd2f592bSDavid Zhang 
205dd2f592bSDavid Zhang 	psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
206dd2f592bSDavid Zhang 	if (!psp)
207dd2f592bSDavid Zhang 		return NULL;
208dd2f592bSDavid Zhang 
209dd2f592bSDavid Zhang 	psp->ddev = ddev;
210*e69ef214SDavid Zhang 	psp->fw_buffer = psp_alloc_fw_buf(psp, conf->fw_buf, conf->fw_size,
211*e69ef214SDavid Zhang 					  PSP_FW_ALIGN, &psp->fw_buf_sz,
212*e69ef214SDavid Zhang 					  &psp->fw_paddr);
213*e69ef214SDavid Zhang 	if (!psp->fw_buffer)
214*e69ef214SDavid Zhang 		return NULL;
215dd2f592bSDavid Zhang 
216*e69ef214SDavid Zhang 	if (!conf->certfw_size) {
217*e69ef214SDavid Zhang 		drm_dbg(ddev, "no cert fw");
218*e69ef214SDavid Zhang 		goto done;
219*e69ef214SDavid Zhang 	}
220*e69ef214SDavid Zhang 
221*e69ef214SDavid Zhang 	/* CERT firmware */
222*e69ef214SDavid Zhang 	psp->certfw_buffer = psp_alloc_fw_buf(psp, conf->certfw_buf,
223*e69ef214SDavid Zhang 					      conf->certfw_size, PSP_CFW_ALIGN,
224*e69ef214SDavid Zhang 					      &psp->certfw_buf_sz,
225*e69ef214SDavid Zhang 					      &psp->certfw_paddr);
226*e69ef214SDavid Zhang 	if (!psp->certfw_buffer) {
227*e69ef214SDavid Zhang 		drm_err(ddev, "no memory for cert fw buffer");
228dd2f592bSDavid Zhang 		return NULL;
229dd2f592bSDavid Zhang 	}
230dd2f592bSDavid Zhang 
231*e69ef214SDavid Zhang done:
232*e69ef214SDavid Zhang 	memcpy(&psp->conf, conf, sizeof(psp->conf));
233dd2f592bSDavid Zhang 
234dd2f592bSDavid Zhang 	return psp;
235dd2f592bSDavid Zhang }
236