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