blob: e9cde40ccb227d161f585a0561c49eacfef4ec7b [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 * Author: Pierre Lee <pierre.lee@mediatek.com>
5 */
6
7#include "clk-fhctl.h"
8#include <mtk_tinysys_ipi.h>
9#include "../../misc/mediatek/mcupm/mt6880/mcupm_ipi_id.h"
10#include "clk-fhctl-debug.h"
11
12#define FHCTL_D_LEN 9
13#define MAX_SSC_RATE 8
14
15unsigned int ack_data = 0;
16static DEFINE_SPINLOCK(fhctl_lock);
17
18/* mcupm IPI CMD. Should sync with mt_freqhopping.h in tinysys driver. */
19enum FH_DEVCTL_CMD_ID {
20 FH_DCTL_CMD_SSC_ENABLE = 0x1004,
21 FH_DCTL_CMD_SSC_DISABLE = 0x1005,
22 FH_DCTL_CMD_GENERAL_DFS = 0x1006,
23 FH_DCTL_CMD_ARM_DFS = 0x1007,
24 FH_DCTL_CMD_SSC_TBL_CONFIG = 0x100A,
25 FH_DCTL_CMD_PLL_PAUSE = 0x100E,
26 FH_DCTL_CMD_MAX
27};
28
29struct freqhopping_ioctl {
30 unsigned int pll_id;
31 struct freqhopping_ssc {
32 unsigned int idx_pattern; /* idx_pattern: Deprecated Field */
33 unsigned int dt;
34 unsigned int df;
35 unsigned int upbnd;
36 unsigned int lowbnd;
37 unsigned int dds; /* dds: Deprecated Field */
38 } ssc_setting; /* used only when user-define */
39 int result;
40};
41
42struct fhctl_ipi_data {
43 unsigned int cmd;
44 union {
45 struct freqhopping_ioctl fh_ctl;
46 unsigned int args[8];
47 } u;
48};
49
50static int mt_fh_ipi_init(void)
51{
52 int ret = 0;
53 ack_data = 0;
54 ret = mtk_ipi_register(&mcupm_ipidev, CH_S_FHCTL, NULL,
55 NULL, (void *)&ack_data);
56 if (ret) {
57 pr_err("[MCUPM] ipi_register fail, ret %d\n", ret);
58 return -1;
59 }
60 return 0;
61}
62
63static int fhctl_to_mcupm_command(unsigned int cmd,
64 struct fhctl_ipi_data *ipi_data)
65{
66 int ret = 0;
67 pr_debug("send ipi command %x", cmd);
68
69 switch (cmd) {
70 case FH_DCTL_CMD_SSC_ENABLE:
71 case FH_DCTL_CMD_SSC_DISABLE:
72 case FH_DCTL_CMD_GENERAL_DFS:
73 case FH_DCTL_CMD_ARM_DFS:
74 case FH_DCTL_CMD_SSC_TBL_CONFIG:
75 case FH_DCTL_CMD_PLL_PAUSE:
76 ipi_data->cmd = cmd;
77
78 ret = mtk_ipi_send_compl(&mcupm_ipidev, CH_S_FHCTL,
79 IPI_SEND_POLLING, ipi_data, FHCTL_D_LEN, 10);
80 if (ret != 0)
81 pr_info("mcupm_ipi_send_sync error(%d) ret:%d - %d",
82 cmd, ret, ack_data);
83 else if (ack_data < 0)
84 pr_info("cmd(%d) return error(%d)", cmd, ack_data);
85 break;
86 default:
87 pr_info("[Error]Undefined IPI command");
88 break;
89 } /* switch */
90
91 pr_debug("send ipi command %x, response: ack_data: %d",
92 cmd, ack_data);
93
94 return ret;
95}
96
97static int clk_mt_fh_mcupm_pll_init(struct clk_mt_fhctl *fh)
98{
99 struct fhctl_ipi_data ipi_data;
100 int pll_id;
101
102 pll_id = fh->pll_data->pll_id;
103
104 /* Check default enable SSC */
105 if (fh->pll_data->pll_default_ssc_rate > 0) {
106 /* Init mcupm g_pll_ssc_setting_tbl table */
107 ipi_data.u.fh_ctl.pll_id = pll_id;
108 ipi_data.u.fh_ctl.ssc_setting.dt = 0;
109 ipi_data.u.fh_ctl.ssc_setting.df = 0;
110 ipi_data.u.fh_ctl.ssc_setting.upbnd = 0;
111 ipi_data.u.fh_ctl.ssc_setting.lowbnd =
112 fh->pll_data->pll_default_ssc_rate;
113 fhctl_to_mcupm_command(FH_DCTL_CMD_SSC_TBL_CONFIG, &ipi_data);
114
115 pr_debug("Default Enable SSC PLL_ID:%d SSC_RATE:0~-%d",
116 pll_id, fh->pll_data->pll_default_ssc_rate);
117
118 /* Default Enable SSC to 0~-N%; */
119 fh->hal_ops->pll_ssc_enable(fh,
120 fh->pll_data->pll_default_ssc_rate);
121 }
122
123 return 0;
124}
125
126static int __clk_mt_fh_mcupm_pll_pause(struct clk_mt_fhctl *fh, bool pause)
127{
128 struct fhctl_ipi_data ipi_data;
129 int pll_id;
130
131 pll_id = fh->pll_data->pll_id;
132
133 /* Only for support pause in CPU PLL. */
134 if (fh->pll_data->pll_type != FH_PLL_TYPE_CPU)
135 return -EPERM;
136
137 ipi_data.u.args[0] = pll_id;
138 ipi_data.u.args[1] = (pause) ? 1 : 0;
139 fhctl_to_mcupm_command(FH_DCTL_CMD_PLL_PAUSE, &ipi_data);
140
141 return 0;
142}
143
144static int clk_mt_fh_mcupm_pll_unpause(struct clk_mt_fhctl *fh)
145{
146 return __clk_mt_fh_mcupm_pll_pause(fh, false);
147}
148
149static int clk_mt_fh_mcupm_pll_pause(struct clk_mt_fhctl *fh)
150{
151
152 return __clk_mt_fh_mcupm_pll_pause(fh, true);
153}
154
155static int clk_mt_fh_mcupm_pll_ssc_disable(struct clk_mt_fhctl *fh)
156{
157 struct freqhopping_ioctl fh_ctl;
158 struct fhctl_ipi_data ipi_data;
159 int pll_id;
160
161 pll_id = fh->pll_data->pll_id;
162
163 fh_ctl.pll_id = pll_id;
164 fh_ctl.result = 0;
165
166 memset(&ipi_data, 0, sizeof(struct fhctl_ipi_data));
167 memcpy(&ipi_data.u.fh_ctl, &fh_ctl,
168 sizeof(struct freqhopping_ioctl));
169
170 fhctl_to_mcupm_command(FH_DCTL_CMD_SSC_DISABLE, &ipi_data);
171
172 return 0;
173}
174
175
176static int clk_mt_fh_mcupm_pll_ssc_enable(struct clk_mt_fhctl *fh, int ssc_rate)
177{
178 struct freqhopping_ioctl fh_ctl;
179 struct fhctl_ipi_data ipi_data;
180 int pll_id;
181
182 pll_id = fh->pll_data->pll_id;
183 fh_ctl.pll_id = pll_id;
184 fh_ctl.result = 0;
185
186 if (fh->pll_data->pll_type == FH_PLL_TYPE_NOT_SUPPORT) {
187 pr_info("%s not support SSC.", fh->pll_data->pll_name);
188 return -EPERM;
189 }
190
191 if (ssc_rate > MAX_SSC_RATE) {
192 pr_info("[Error] ssc_rate:%d over spec!!!", ssc_rate);
193 return -EINVAL;
194 }
195
196 fh_ctl.ssc_setting.dt = 0; /* default setting */
197 fh_ctl.ssc_setting.df = 9; /* default setting */
198 fh_ctl.ssc_setting.upbnd = 0; /* default setting */
199 fh_ctl.ssc_setting.lowbnd = ssc_rate;
200
201 memset(&ipi_data, 0, sizeof(struct fhctl_ipi_data));
202 memcpy(&ipi_data.u.fh_ctl, &fh_ctl,
203 sizeof(struct freqhopping_ioctl));
204
205 fhctl_to_mcupm_command(FH_DCTL_CMD_SSC_ENABLE, &ipi_data);
206
207 pr_info("PLL:%d ssc rate change [O]:%d => [N]:%d ",
208 pll_id, fh->pll_data->pll_default_ssc_rate, ssc_rate);
209
210 /* Update clock ssc rate variable. */
211
212 fh->pll_data->pll_default_ssc_rate = ssc_rate;
213
214 return 0;
215}
216
217static void ipi_get_data(unsigned int cmd)
218{
219 struct fhctl_ipi_data ipi_data;
220 int ret;
221 pr_info("[Hopping] debug cmd<%x>\n", cmd);
222 memset(&ipi_data, 0, sizeof(struct fhctl_ipi_data));
223 ipi_data.cmd = cmd;
224 /* 3 sec for debug */
225 ret = mtk_ipi_send_compl(&mcupm_ipidev, CH_S_FHCTL,
226 IPI_SEND_POLLING, &ipi_data,
227 FHCTL_D_LEN, 3000);
228 pr_info("ret<%d>, ack_data<%x>\n",
229 ret, ack_data);
230}
231
232static int clk_mt_fh_mcupm_pll_hopping(struct clk_mt_fhctl *fh,
233 unsigned int new_dds,
234 int postdiv)
235{
236 struct fhctl_ipi_data ipi_data;
237 struct pll_status fh_log;
238 static unsigned int fh_txid;
239 int ret_from_ipi;
240 int pll_id, cmd_id;
241 unsigned long flags;
242 u64 time_ns;
243
244 pll_id = fh->pll_data->pll_id;
245
246 /* CPU is forbidden hopping in AP side. (clk driver owner reqest) */
247 if ((fh->pll_data->pll_type == FH_PLL_TYPE_NOT_SUPPORT) ||
248 (fh->pll_data->pll_type == FH_PLL_TYPE_CPU)) {
249 pr_info("%s not support hopping in AP side.",
250 fh->pll_data->pll_name);
251 return 0;
252 }
253
254 spin_lock_irqsave(&fhctl_lock, flags);
255
256 cmd_id = FH_DCTL_CMD_GENERAL_DFS;
257
258 memset(&ipi_data, 0, sizeof(struct fhctl_ipi_data));
259
260 fh_txid = (fh_txid + 1) % FH_LOG_MAX_IPI_IDX;
261
262 ipi_data.u.args[0] = pll_id;
263 ipi_data.u.args[1] = new_dds;
264 ipi_data.u.args[2] = postdiv;
265 ipi_data.u.args[7] = fh_txid;
266
267 mt_fhctl_log_b4_hopping(fh, new_dds, fh_txid, &fh_log); //get history log by kernel api dump
268 time_ns = ktime_to_ns(ktime_get());
269 ret_from_ipi = fhctl_to_mcupm_command(cmd_id, &ipi_data);
270
271 mt_fhctl_log_af_hopping(fh, ret_from_ipi, ack_data, &fh_log, ipi_get_data,time_ns);//get moment+get history log by kernel api dump
272
273 spin_unlock_irqrestore(&fhctl_lock, flags);
274
275 return 0;
276}
277
278const struct fhctl_ipi_ops ipi_ops = {
279 .ipi_init = mt_fh_ipi_init,
280};
281
282const struct clk_mt_fhctl_hal_ops mt_fhctl_hal_ops = {
283 .pll_init = clk_mt_fh_mcupm_pll_init,
284 .pll_unpause = clk_mt_fh_mcupm_pll_unpause,
285 .pll_pause = clk_mt_fh_mcupm_pll_pause,
286 .pll_ssc_disable = clk_mt_fh_mcupm_pll_ssc_disable,
287 .pll_ssc_enable = clk_mt_fh_mcupm_pll_ssc_enable,
288 .pll_hopping = clk_mt_fh_mcupm_pll_hopping,
289};
290
291