1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2020 Samsung Electronics Co., Ltd. 4 * Copyright 2020 Google LLC. 5 * Copyright 2025 Linaro Ltd. 6 */ 7 8 #include <linux/array_size.h> 9 #include <linux/bitfield.h> 10 #include <linux/firmware/samsung/exynos-acpm-protocol.h> 11 #include <linux/ktime.h> 12 #include <linux/types.h> 13 #include <linux/units.h> 14 15 #include "exynos-acpm.h" 16 #include "exynos-acpm-dvfs.h" 17 18 #define ACPM_DVFS_ID GENMASK(11, 0) 19 #define ACPM_DVFS_REQ_TYPE GENMASK(15, 0) 20 21 #define ACPM_DVFS_FREQ_REQ 0 22 #define ACPM_DVFS_FREQ_GET 1 23 24 static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, 25 unsigned int acpm_chan_id, bool response) 26 { 27 xfer->acpm_chan_id = acpm_chan_id; 28 xfer->txcnt = cmdlen; 29 xfer->txd = cmd; 30 31 if (response) { 32 xfer->rxcnt = cmdlen; 33 xfer->rxd = cmd; 34 } 35 } 36 37 static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id, 38 unsigned long rate) 39 { 40 cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); 41 cmd[1] = rate / HZ_PER_KHZ; 42 cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_REQ); 43 cmd[3] = ktime_to_ms(ktime_get()); 44 } 45 46 int acpm_dvfs_set_rate(struct acpm_handle *handle, 47 unsigned int acpm_chan_id, unsigned int clk_id, 48 unsigned long rate) 49 { 50 struct acpm_xfer xfer = {0}; 51 u32 cmd[4]; 52 53 acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); 54 acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, false); 55 56 return acpm_do_xfer(handle, &xfer); 57 } 58 59 static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) 60 { 61 cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); 62 cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_GET); 63 cmd[3] = ktime_to_ms(ktime_get()); 64 } 65 66 unsigned long acpm_dvfs_get_rate(struct acpm_handle *handle, 67 unsigned int acpm_chan_id, unsigned int clk_id) 68 { 69 struct acpm_xfer xfer; 70 unsigned int cmd[4] = {0}; 71 int ret; 72 73 acpm_dvfs_init_get_rate_cmd(cmd, clk_id); 74 acpm_dvfs_set_xfer(&xfer, cmd, ARRAY_SIZE(cmd), acpm_chan_id, true); 75 76 ret = acpm_do_xfer(handle, &xfer); 77 if (ret) 78 return 0; 79 80 return xfer.rxd[1] * HZ_PER_KHZ; 81 } 82