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