1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
4 */
5
6#include <drm/drm_device.h>
7#include <drm/drm_gem_shmem_helper.h>
8#include <drm/drm_print.h>
9#include <drm/gpu_scheduler.h>
10#include <linux/iopoll.h>
11
12#include "aie2_pci.h"
13#include "amdxdna_pci_drv.h"
14#include "amdxdna_pm.h"
15
16#define SMU_RESULT_OK 1
17
18/* SMU commands */
19#define AIE2_SMU_POWER_ON 0x3
20#define AIE2_SMU_POWER_OFF 0x4
21#define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5
22#define AIE2_SMU_SET_HCLK_FREQ 0x6
23#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
24#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
25
26#define NPU4_DPM_TOPS(ndev, dpm_level) \
27({ \
28 typeof(ndev) _ndev = ndev; \
29 (4096 * (_ndev)->total_col * \
30 (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
31})
32
33static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
34 u32 reg_arg, u32 *out)
35{
36 u32 resp;
37 int ret;
38
39 writel(val: 0, SMU_REG(ndev, SMU_RESP_REG));
40 writel(val: reg_arg, SMU_REG(ndev, SMU_ARG_REG));
41 writel(val: reg_cmd, SMU_REG(ndev, SMU_CMD_REG));
42
43 /* Clear and set SMU_INTR_REG to kick off */
44 writel(val: 0, SMU_REG(ndev, SMU_INTR_REG));
45 writel(val: 1, SMU_REG(ndev, SMU_INTR_REG));
46
47 ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
48 resp, AIE2_INTERVAL, AIE2_TIMEOUT);
49 if (ret) {
50 XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
51 return ret;
52 }
53
54 if (out)
55 *out = readl(SMU_REG(ndev, SMU_OUT_REG));
56
57 if (resp != SMU_RESULT_OK) {
58 XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
59 return -EINVAL;
60 }
61
62 return 0;
63}
64
65int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
66{
67 u32 freq;
68 int ret;
69
70 ret = amdxdna_pm_resume_get(xdna: ndev->xdna);
71 if (ret)
72 return ret;
73
74 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
75 reg_arg: ndev->priv->dpm_clk_tbl[dpm_level].npuclk, out: &freq);
76 if (ret) {
77 XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
78 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
79 goto suspend_put;
80 }
81 ndev->npuclk_freq = freq;
82
83 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
84 reg_arg: ndev->priv->dpm_clk_tbl[dpm_level].hclk, out: &freq);
85 if (ret) {
86 XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
87 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
88 goto suspend_put;
89 }
90
91 amdxdna_pm_suspend_put(xdna: ndev->xdna);
92 ndev->hclk_freq = freq;
93 ndev->dpm_level = dpm_level;
94 ndev->max_tops = 2 * ndev->total_col;
95 ndev->curr_tops = ndev->max_tops * freq / 1028;
96
97 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
98 ndev->npuclk_freq, ndev->hclk_freq);
99
100 return 0;
101
102suspend_put:
103 amdxdna_pm_suspend_put(xdna: ndev->xdna);
104 return ret;
105}
106
107int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
108{
109 int ret;
110
111 ret = amdxdna_pm_resume_get(xdna: ndev->xdna);
112 if (ret)
113 return ret;
114
115 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, reg_arg: dpm_level, NULL);
116 if (ret) {
117 XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
118 dpm_level, ret);
119 goto suspend_put;
120 }
121
122 ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, reg_arg: dpm_level, NULL);
123 if (ret) {
124 XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
125 dpm_level, ret);
126 goto suspend_put;
127 }
128
129 amdxdna_pm_suspend_put(xdna: ndev->xdna);
130 ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
131 ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
132 ndev->dpm_level = dpm_level;
133 ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
134 ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
135
136 XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
137 ndev->npuclk_freq, ndev->hclk_freq);
138
139 return 0;
140
141suspend_put:
142 amdxdna_pm_suspend_put(xdna: ndev->xdna);
143 return ret;
144}
145
146int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
147{
148 int ret;
149
150 /*
151 * Failing to set power off indicates an unrecoverable hardware or
152 * firmware error.
153 */
154 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, reg_arg: 0, NULL);
155 if (ret) {
156 XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
157 return ret;
158 }
159
160 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, reg_arg: 0, NULL);
161 if (ret) {
162 XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
163 return ret;
164 }
165
166 return 0;
167}
168
169void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
170{
171 int ret;
172
173 ndev->priv->hw_ops.set_dpm(ndev, 0);
174 ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, reg_arg: 0, NULL);
175 if (ret)
176 XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
177}
178

source code of linux/drivers/accel/amdxdna/aie2_smu.c