mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 16:54:21 +00:00 
			
		
		
		
	 f4d7b8a6bc
			
		
	
	
		f4d7b8a6bc
		
	
	
	
	
		
			
			Add SET_STATE ioctl to configure device power mode for aie2 device. Three modes are supported initially. POWER_MODE_DEFAULT: Enable clock gating and set DPM (Dynamic Power Management) level to value which has been set by resource solver or maximum DPM level the device supports. POWER_MODE_HIGH: Enable clock gating and set DPM level to maximum DPM level the device supports. POWER_MODE_TURBO: Disable clock gating and set DPM level to maximum DPM level the device supports. Disabling clock gating means all clocks always run on full speed. And the different clock frequency are used based on DPM level been set. Initially, the driver set the power mode to default mode. Co-developed-by: Narendra Gutta <VenkataNarendraKumar.Gutta@amd.com> Signed-off-by: Narendra Gutta <VenkataNarendraKumar.Gutta@amd.com> Co-developed-by: George Yang <George.Yang@amd.com> Signed-off-by: George Yang <George.Yang@amd.com> Reviewed-by: Mario Limonciello <mario.limonciello@amd.com> Signed-off-by: Lizhi Hou <lizhi.hou@amd.com> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://patchwork.freedesktop.org/patch/msgid/20241213232933.1545388-4-lizhi.hou@amd.com
		
			
				
	
	
		
			134 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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"
 | |
| 
 | |
| #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
 | |
| 
 | |
| 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 = 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);
 | |
| 	}
 | |
| 	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);
 | |
| 	}
 | |
| 	ndev->hclk_freq = freq;
 | |
| 	ndev->dpm_level = dpm_level;
 | |
| 
 | |
| 	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
 | |
| 		 ndev->npuclk_freq, ndev->hclk_freq);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
 | |
| {
 | |
| 	int 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);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	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);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 
 | |
| 	XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
 | |
| 		 ndev->npuclk_freq, ndev->hclk_freq);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
 | |
| {
 | |
| 	int 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);
 | |
| }
 |