clk-hi6220-stub.c (6455B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Hi6220 stub clock driver 4 * 5 * Copyright (c) 2015 Hisilicon Limited. 6 * Copyright (c) 2015 Linaro Limited. 7 * 8 * Author: Leo Yan <leo.yan@linaro.org> 9 */ 10 11#include <linux/clk-provider.h> 12#include <linux/err.h> 13#include <linux/kernel.h> 14#include <linux/mfd/syscon.h> 15#include <linux/mailbox_client.h> 16#include <linux/of.h> 17#include <linux/of_device.h> 18#include <linux/regmap.h> 19 20/* Stub clocks id */ 21#define HI6220_STUB_ACPU0 0 22#define HI6220_STUB_ACPU1 1 23#define HI6220_STUB_GPU 2 24#define HI6220_STUB_DDR 5 25 26/* Mailbox message */ 27#define HI6220_MBOX_MSG_LEN 8 28 29#define HI6220_MBOX_FREQ 0xA 30#define HI6220_MBOX_CMD_SET 0x3 31#define HI6220_MBOX_OBJ_AP 0x0 32 33/* CPU dynamic frequency scaling */ 34#define ACPU_DFS_FREQ_MAX 0x1724 35#define ACPU_DFS_CUR_FREQ 0x17CC 36#define ACPU_DFS_FLAG 0x1B30 37#define ACPU_DFS_FREQ_REQ 0x1B34 38#define ACPU_DFS_FREQ_LMT 0x1B38 39#define ACPU_DFS_LOCK_FLAG 0xAEAEAEAE 40 41#define to_stub_clk(hw) container_of(hw, struct hi6220_stub_clk, hw) 42 43struct hi6220_stub_clk { 44 u32 id; 45 46 struct device *dev; 47 struct clk_hw hw; 48 49 struct regmap *dfs_map; 50 struct mbox_client cl; 51 struct mbox_chan *mbox; 52}; 53 54struct hi6220_mbox_msg { 55 unsigned char type; 56 unsigned char cmd; 57 unsigned char obj; 58 unsigned char src; 59 unsigned char para[4]; 60}; 61 62union hi6220_mbox_data { 63 unsigned int data[HI6220_MBOX_MSG_LEN]; 64 struct hi6220_mbox_msg msg; 65}; 66 67static unsigned int hi6220_acpu_get_freq(struct hi6220_stub_clk *stub_clk) 68{ 69 unsigned int freq; 70 71 regmap_read(stub_clk->dfs_map, ACPU_DFS_CUR_FREQ, &freq); 72 return freq; 73} 74 75static int hi6220_acpu_set_freq(struct hi6220_stub_clk *stub_clk, 76 unsigned int freq) 77{ 78 union hi6220_mbox_data data; 79 80 /* set the frequency in sram */ 81 regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, freq); 82 83 /* compound mailbox message */ 84 data.msg.type = HI6220_MBOX_FREQ; 85 data.msg.cmd = HI6220_MBOX_CMD_SET; 86 data.msg.obj = HI6220_MBOX_OBJ_AP; 87 data.msg.src = HI6220_MBOX_OBJ_AP; 88 89 mbox_send_message(stub_clk->mbox, &data); 90 return 0; 91} 92 93static int hi6220_acpu_round_freq(struct hi6220_stub_clk *stub_clk, 94 unsigned int freq) 95{ 96 unsigned int limit_flag, limit_freq = UINT_MAX; 97 unsigned int max_freq; 98 99 /* check the constrained frequency */ 100 regmap_read(stub_clk->dfs_map, ACPU_DFS_FLAG, &limit_flag); 101 if (limit_flag == ACPU_DFS_LOCK_FLAG) 102 regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, &limit_freq); 103 104 /* check the supported maximum frequency */ 105 regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_MAX, &max_freq); 106 107 /* calculate the real maximum frequency */ 108 max_freq = min(max_freq, limit_freq); 109 110 if (WARN_ON(freq > max_freq)) 111 freq = max_freq; 112 113 return freq; 114} 115 116static unsigned long hi6220_stub_clk_recalc_rate(struct clk_hw *hw, 117 unsigned long parent_rate) 118{ 119 u32 rate = 0; 120 struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 121 122 switch (stub_clk->id) { 123 case HI6220_STUB_ACPU0: 124 rate = hi6220_acpu_get_freq(stub_clk); 125 126 /* convert from kHz to Hz */ 127 rate *= 1000; 128 break; 129 130 default: 131 dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 132 __func__, stub_clk->id); 133 break; 134 } 135 136 return rate; 137} 138 139static int hi6220_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, 140 unsigned long parent_rate) 141{ 142 struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 143 unsigned long new_rate = rate / 1000; /* kHz */ 144 int ret = 0; 145 146 switch (stub_clk->id) { 147 case HI6220_STUB_ACPU0: 148 ret = hi6220_acpu_set_freq(stub_clk, new_rate); 149 if (ret < 0) 150 return ret; 151 152 break; 153 154 default: 155 dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 156 __func__, stub_clk->id); 157 break; 158 } 159 160 pr_debug("%s: set rate=%ldkHz\n", __func__, new_rate); 161 return ret; 162} 163 164static long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, 165 unsigned long *parent_rate) 166{ 167 struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 168 unsigned long new_rate = rate / 1000; /* kHz */ 169 170 switch (stub_clk->id) { 171 case HI6220_STUB_ACPU0: 172 new_rate = hi6220_acpu_round_freq(stub_clk, new_rate); 173 174 /* convert from kHz to Hz */ 175 new_rate *= 1000; 176 break; 177 178 default: 179 dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 180 __func__, stub_clk->id); 181 break; 182 } 183 184 return new_rate; 185} 186 187static const struct clk_ops hi6220_stub_clk_ops = { 188 .recalc_rate = hi6220_stub_clk_recalc_rate, 189 .round_rate = hi6220_stub_clk_round_rate, 190 .set_rate = hi6220_stub_clk_set_rate, 191}; 192 193static int hi6220_stub_clk_probe(struct platform_device *pdev) 194{ 195 struct device *dev = &pdev->dev; 196 struct clk_init_data init; 197 struct hi6220_stub_clk *stub_clk; 198 struct clk *clk; 199 struct device_node *np = pdev->dev.of_node; 200 int ret; 201 202 stub_clk = devm_kzalloc(dev, sizeof(*stub_clk), GFP_KERNEL); 203 if (!stub_clk) 204 return -ENOMEM; 205 206 stub_clk->dfs_map = syscon_regmap_lookup_by_phandle(np, 207 "hisilicon,hi6220-clk-sram"); 208 if (IS_ERR(stub_clk->dfs_map)) { 209 dev_err(dev, "failed to get sram regmap\n"); 210 return PTR_ERR(stub_clk->dfs_map); 211 } 212 213 stub_clk->hw.init = &init; 214 stub_clk->dev = dev; 215 stub_clk->id = HI6220_STUB_ACPU0; 216 217 /* Use mailbox client with blocking mode */ 218 stub_clk->cl.dev = dev; 219 stub_clk->cl.tx_done = NULL; 220 stub_clk->cl.tx_block = true; 221 stub_clk->cl.tx_tout = 500; 222 stub_clk->cl.knows_txdone = false; 223 224 /* Allocate mailbox channel */ 225 stub_clk->mbox = mbox_request_channel(&stub_clk->cl, 0); 226 if (IS_ERR(stub_clk->mbox)) { 227 dev_err(dev, "failed get mailbox channel\n"); 228 return PTR_ERR(stub_clk->mbox); 229 } 230 231 init.name = "acpu0"; 232 init.ops = &hi6220_stub_clk_ops; 233 init.num_parents = 0; 234 init.flags = 0; 235 236 clk = devm_clk_register(dev, &stub_clk->hw); 237 if (IS_ERR(clk)) 238 return PTR_ERR(clk); 239 240 ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); 241 if (ret) { 242 dev_err(dev, "failed to register OF clock provider\n"); 243 return ret; 244 } 245 246 /* initialize buffer to zero */ 247 regmap_write(stub_clk->dfs_map, ACPU_DFS_FLAG, 0x0); 248 regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, 0x0); 249 regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, 0x0); 250 251 dev_dbg(dev, "Registered clock '%s'\n", init.name); 252 return 0; 253} 254 255static const struct of_device_id hi6220_stub_clk_of_match[] = { 256 { .compatible = "hisilicon,hi6220-stub-clk", }, 257 {} 258}; 259 260static struct platform_driver hi6220_stub_clk_driver = { 261 .driver = { 262 .name = "hi6220-stub-clk", 263 .of_match_table = hi6220_stub_clk_of_match, 264 }, 265 .probe = hi6220_stub_clk_probe, 266}; 267 268static int __init hi6220_stub_clk_init(void) 269{ 270 return platform_driver_register(&hi6220_stub_clk_driver); 271} 272subsys_initcall(hi6220_stub_clk_init);