1 // SPDX-License-Identifier: GPL-2.0-only
3 * AMD Secure Processor Dynamic Boost Control interface
5 * Copyright (C) 2023 Advanced Micro Devices, Inc.
7 * Author: Mario Limonciello <mario.limonciello@amd.com>
12 #define DBC_DEFAULT_TIMEOUT (10 * MSEC_PER_SEC)
18 #define DBC_ERROR_ACCESS_DENIED 0x0001
19 #define DBC_ERROR_EXCESS_DATA 0x0004
20 #define DBC_ERROR_BAD_PARAMETERS 0x0006
21 #define DBC_ERROR_BAD_STATE 0x0007
22 #define DBC_ERROR_NOT_IMPLEMENTED 0x0009
23 #define DBC_ERROR_BUSY 0x000D
24 #define DBC_ERROR_MESSAGE_FAILURE 0x0307
25 #define DBC_ERROR_OVERFLOW 0x300F
26 #define DBC_ERROR_SIGNATURE_INVALID 0x3072
28 static struct error_map error_codes
[] = {
29 {DBC_ERROR_ACCESS_DENIED
, -EACCES
},
30 {DBC_ERROR_EXCESS_DATA
, -E2BIG
},
31 {DBC_ERROR_BAD_PARAMETERS
, -EINVAL
},
32 {DBC_ERROR_BAD_STATE
, -EAGAIN
},
33 {DBC_ERROR_MESSAGE_FAILURE
, -ENOENT
},
34 {DBC_ERROR_NOT_IMPLEMENTED
, -ENOENT
},
35 {DBC_ERROR_BUSY
, -EBUSY
},
36 {DBC_ERROR_OVERFLOW
, -ENFILE
},
37 {DBC_ERROR_SIGNATURE_INVALID
, -EPERM
},
41 static inline int send_dbc_cmd_thru_ext(struct psp_dbc_device
*dbc_dev
, int msg
)
43 dbc_dev
->mbox
->ext_req
.header
.sub_cmd_id
= msg
;
45 return psp_extended_mailbox_cmd(dbc_dev
->psp
,
47 (struct psp_ext_request
*)dbc_dev
->mbox
);
50 static inline int send_dbc_cmd_thru_pa(struct psp_dbc_device
*dbc_dev
, int msg
)
52 return psp_send_platform_access_msg(msg
,
53 (struct psp_request
*)dbc_dev
->mbox
);
56 static int send_dbc_cmd(struct psp_dbc_device
*dbc_dev
, int msg
)
61 ret
= dbc_dev
->use_ext
? send_dbc_cmd_thru_ext(dbc_dev
, msg
) :
62 send_dbc_cmd_thru_pa(dbc_dev
, msg
);
67 "msg 0x%x failed with PSP error: 0x%x\n",
68 msg
, *dbc_dev
->result
);
70 for (i
= 0; error_codes
[i
].psp
; i
++) {
71 if (*dbc_dev
->result
== error_codes
[i
].psp
)
72 return error_codes
[i
].ret
;
79 static int send_dbc_nonce(struct psp_dbc_device
*dbc_dev
)
83 *dbc_dev
->payload_size
= dbc_dev
->header_size
+ sizeof(struct dbc_user_nonce
);
84 ret
= send_dbc_cmd(dbc_dev
, PSP_DYNAMIC_BOOST_GET_NONCE
);
86 dev_dbg(dbc_dev
->dev
, "retrying get nonce\n");
87 ret
= send_dbc_cmd(dbc_dev
, PSP_DYNAMIC_BOOST_GET_NONCE
);
93 static int send_dbc_parameter(struct psp_dbc_device
*dbc_dev
)
95 struct dbc_user_param
*user_param
= (struct dbc_user_param
*)dbc_dev
->payload
;
97 switch (user_param
->msg_index
) {
98 case PARAM_SET_FMAX_CAP
:
99 case PARAM_SET_PWR_CAP
:
100 case PARAM_SET_GFX_MODE
:
101 return send_dbc_cmd(dbc_dev
, PSP_DYNAMIC_BOOST_SET_PARAMETER
);
102 case PARAM_GET_FMAX_CAP
:
103 case PARAM_GET_PWR_CAP
:
104 case PARAM_GET_CURR_TEMP
:
105 case PARAM_GET_FMAX_MAX
:
106 case PARAM_GET_FMAX_MIN
:
107 case PARAM_GET_SOC_PWR_MAX
:
108 case PARAM_GET_SOC_PWR_MIN
:
109 case PARAM_GET_SOC_PWR_CUR
:
110 case PARAM_GET_GFX_MODE
:
111 return send_dbc_cmd(dbc_dev
, PSP_DYNAMIC_BOOST_GET_PARAMETER
);
117 void dbc_dev_destroy(struct psp_device
*psp
)
119 struct psp_dbc_device
*dbc_dev
= psp
->dbc_data
;
124 misc_deregister(&dbc_dev
->char_dev
);
125 mutex_destroy(&dbc_dev
->ioctl_mutex
);
126 psp
->dbc_data
= NULL
;
129 static long dbc_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
)
131 struct psp_device
*psp_master
= psp_get_master_device();
132 void __user
*argp
= (void __user
*)arg
;
133 struct psp_dbc_device
*dbc_dev
;
136 if (!psp_master
|| !psp_master
->dbc_data
)
138 dbc_dev
= psp_master
->dbc_data
;
140 mutex_lock(&dbc_dev
->ioctl_mutex
);
144 if (copy_from_user(dbc_dev
->payload
, argp
, sizeof(struct dbc_user_nonce
))) {
149 ret
= send_dbc_nonce(dbc_dev
);
153 if (copy_to_user(argp
, dbc_dev
->payload
, sizeof(struct dbc_user_nonce
))) {
159 if (copy_from_user(dbc_dev
->payload
, argp
, sizeof(struct dbc_user_setuid
))) {
164 *dbc_dev
->payload_size
= dbc_dev
->header_size
+ sizeof(struct dbc_user_setuid
);
165 ret
= send_dbc_cmd(dbc_dev
, PSP_DYNAMIC_BOOST_SET_UID
);
169 if (copy_to_user(argp
, dbc_dev
->payload
, sizeof(struct dbc_user_setuid
))) {
175 if (copy_from_user(dbc_dev
->payload
, argp
, sizeof(struct dbc_user_param
))) {
180 *dbc_dev
->payload_size
= dbc_dev
->header_size
+ sizeof(struct dbc_user_param
);
181 ret
= send_dbc_parameter(dbc_dev
);
185 if (copy_to_user(argp
, dbc_dev
->payload
, sizeof(struct dbc_user_param
))) {
195 mutex_unlock(&dbc_dev
->ioctl_mutex
);
200 static const struct file_operations dbc_fops
= {
201 .owner
= THIS_MODULE
,
202 .unlocked_ioctl
= dbc_ioctl
,
205 int dbc_dev_init(struct psp_device
*psp
)
207 struct device
*dev
= psp
->dev
;
208 struct psp_dbc_device
*dbc_dev
;
211 dbc_dev
= devm_kzalloc(dev
, sizeof(*dbc_dev
), GFP_KERNEL
);
215 BUILD_BUG_ON(sizeof(union dbc_buffer
) > PAGE_SIZE
);
216 dbc_dev
->mbox
= (void *)devm_get_free_pages(dev
, GFP_KERNEL
| __GFP_ZERO
, 0);
217 if (!dbc_dev
->mbox
) {
222 psp
->dbc_data
= dbc_dev
;
226 if (psp
->capability
.dbc_thru_ext
) {
227 dbc_dev
->use_ext
= true;
228 dbc_dev
->payload_size
= &dbc_dev
->mbox
->ext_req
.header
.payload_size
;
229 dbc_dev
->result
= &dbc_dev
->mbox
->ext_req
.header
.status
;
230 dbc_dev
->payload
= &dbc_dev
->mbox
->ext_req
.buf
;
231 dbc_dev
->header_size
= sizeof(struct psp_ext_req_buffer_hdr
);
233 dbc_dev
->payload_size
= &dbc_dev
->mbox
->pa_req
.header
.payload_size
;
234 dbc_dev
->result
= &dbc_dev
->mbox
->pa_req
.header
.status
;
235 dbc_dev
->payload
= &dbc_dev
->mbox
->pa_req
.buf
;
236 dbc_dev
->header_size
= sizeof(struct psp_req_buffer_hdr
);
239 ret
= send_dbc_nonce(dbc_dev
);
240 if (ret
== -EACCES
) {
241 dev_dbg(dbc_dev
->dev
,
242 "dynamic boost control was previously authenticated\n");
245 dev_dbg(dbc_dev
->dev
, "dynamic boost control is %savailable\n",
252 dbc_dev
->char_dev
.minor
= MISC_DYNAMIC_MINOR
;
253 dbc_dev
->char_dev
.name
= "dbc";
254 dbc_dev
->char_dev
.fops
= &dbc_fops
;
255 dbc_dev
->char_dev
.mode
= 0600;
256 ret
= misc_register(&dbc_dev
->char_dev
);
260 mutex_init(&dbc_dev
->ioctl_mutex
);
265 devm_free_pages(dev
, (unsigned long)dbc_dev
->mbox
);
268 psp
->dbc_data
= NULL
;
269 devm_kfree(dev
, dbc_dev
);