summaryrefslogtreecommitdiff
path: root/drivers/accel/amdxdna/aie2_smu.c
blob: bd94ee96c2bc0654efe4a9f451554294bf115108 (plain)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
 */

#include <drm/drm_device.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/iopoll.h>

#include "aie2_pci.h"
#include "amdxdna_pci_drv.h"
#include "amdxdna_pm.h"

#define SMU_RESULT_OK		1

/* SMU commands */
#define AIE2_SMU_POWER_ON		0x3
#define AIE2_SMU_POWER_OFF		0x4
#define AIE2_SMU_SET_MPNPUCLK_FREQ	0x5
#define AIE2_SMU_SET_HCLK_FREQ		0x6
#define AIE2_SMU_SET_SOFT_DPMLEVEL	0x7
#define AIE2_SMU_SET_HARD_DPMLEVEL	0x8

#define NPU4_DPM_TOPS(ndev, dpm_level) \
({ \
	typeof(ndev) _ndev = ndev; \
	(4096 * (_ndev)->total_col * \
	 (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
})

static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
			 u32 reg_arg, u32 *out)
{
	u32 resp;
	int ret;

	writel(0, SMU_REG(ndev, SMU_RESP_REG));
	writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG));
	writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG));

	/* Clear and set SMU_INTR_REG to kick off */
	writel(0, SMU_REG(ndev, SMU_INTR_REG));
	writel(1, SMU_REG(ndev, SMU_INTR_REG));

	ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
				 resp, AIE2_INTERVAL, AIE2_TIMEOUT);
	if (ret) {
		XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
		return ret;
	}

	if (out)
		*out = readl(SMU_REG(ndev, SMU_OUT_REG));

	if (resp != SMU_RESULT_OK) {
		XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
		return -EINVAL;
	}

	return 0;
}

int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
	u32 freq;
	int ret;

	ret = amdxdna_pm_resume_get(ndev->xdna);
	if (ret)
		return ret;

	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
			    ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
			 ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
		goto suspend_put;
	}
	ndev->npuclk_freq = freq;

	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
			    ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
			 ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
		goto suspend_put;
	}

	amdxdna_pm_suspend_put(ndev->xdna);
	ndev->hclk_freq = freq;
	ndev->dpm_level = dpm_level;
	ndev->max_tops = 2 * ndev->total_col;
	ndev->curr_tops = ndev->max_tops * freq / 1028;

	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
		 ndev->npuclk_freq, ndev->hclk_freq);

	return 0;

suspend_put:
	amdxdna_pm_suspend_put(ndev->xdna);
	return ret;
}

int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
	int ret;

	ret = amdxdna_pm_resume_get(ndev->xdna);
	if (ret)
		return ret;

	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
			 dpm_level, ret);
		goto suspend_put;
	}

	ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
			 dpm_level, ret);
		goto suspend_put;
	}

	amdxdna_pm_suspend_put(ndev->xdna);
	ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
	ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
	ndev->dpm_level = dpm_level;
	ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
	ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);

	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
		 ndev->npuclk_freq, ndev->hclk_freq);

	return 0;

suspend_put:
	amdxdna_pm_suspend_put(ndev->xdna);
	return ret;
}

int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
{
	int ret;

	/*
	 * Failing to set power off indicates an unrecoverable hardware or
	 * firmware error.
	 */
	ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
		return ret;
	}

	ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
	if (ret) {
		XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
		return ret;
	}

	return 0;
}

void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
{
	int ret;

	ndev->priv->hw_ops.set_dpm(ndev, 0);
	ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
	if (ret)
		XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
}