18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hi6220 stub clock driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Hisilicon Limited. 68c2ecf20Sopenharmony_ci * Copyright (c) 2015 Linaro Limited. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Leo Yan <leo.yan@linaro.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Stub clocks id */ 218c2ecf20Sopenharmony_ci#define HI6220_STUB_ACPU0 0 228c2ecf20Sopenharmony_ci#define HI6220_STUB_ACPU1 1 238c2ecf20Sopenharmony_ci#define HI6220_STUB_GPU 2 248c2ecf20Sopenharmony_ci#define HI6220_STUB_DDR 5 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Mailbox message */ 278c2ecf20Sopenharmony_ci#define HI6220_MBOX_MSG_LEN 8 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define HI6220_MBOX_FREQ 0xA 308c2ecf20Sopenharmony_ci#define HI6220_MBOX_CMD_SET 0x3 318c2ecf20Sopenharmony_ci#define HI6220_MBOX_OBJ_AP 0x0 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* CPU dynamic frequency scaling */ 348c2ecf20Sopenharmony_ci#define ACPU_DFS_FREQ_MAX 0x1724 358c2ecf20Sopenharmony_ci#define ACPU_DFS_CUR_FREQ 0x17CC 368c2ecf20Sopenharmony_ci#define ACPU_DFS_FLAG 0x1B30 378c2ecf20Sopenharmony_ci#define ACPU_DFS_FREQ_REQ 0x1B34 388c2ecf20Sopenharmony_ci#define ACPU_DFS_FREQ_LMT 0x1B38 398c2ecf20Sopenharmony_ci#define ACPU_DFS_LOCK_FLAG 0xAEAEAEAE 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define to_stub_clk(hw) container_of(hw, struct hi6220_stub_clk, hw) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct hi6220_stub_clk { 448c2ecf20Sopenharmony_ci u32 id; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci struct device *dev; 478c2ecf20Sopenharmony_ci struct clk_hw hw; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci struct regmap *dfs_map; 508c2ecf20Sopenharmony_ci struct mbox_client cl; 518c2ecf20Sopenharmony_ci struct mbox_chan *mbox; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct hi6220_mbox_msg { 558c2ecf20Sopenharmony_ci unsigned char type; 568c2ecf20Sopenharmony_ci unsigned char cmd; 578c2ecf20Sopenharmony_ci unsigned char obj; 588c2ecf20Sopenharmony_ci unsigned char src; 598c2ecf20Sopenharmony_ci unsigned char para[4]; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciunion hi6220_mbox_data { 638c2ecf20Sopenharmony_ci unsigned int data[HI6220_MBOX_MSG_LEN]; 648c2ecf20Sopenharmony_ci struct hi6220_mbox_msg msg; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic unsigned int hi6220_acpu_get_freq(struct hi6220_stub_clk *stub_clk) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned int freq; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci regmap_read(stub_clk->dfs_map, ACPU_DFS_CUR_FREQ, &freq); 728c2ecf20Sopenharmony_ci return freq; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int hi6220_acpu_set_freq(struct hi6220_stub_clk *stub_clk, 768c2ecf20Sopenharmony_ci unsigned int freq) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci union hi6220_mbox_data data; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* set the frequency in sram */ 818c2ecf20Sopenharmony_ci regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, freq); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* compound mailbox message */ 848c2ecf20Sopenharmony_ci data.msg.type = HI6220_MBOX_FREQ; 858c2ecf20Sopenharmony_ci data.msg.cmd = HI6220_MBOX_CMD_SET; 868c2ecf20Sopenharmony_ci data.msg.obj = HI6220_MBOX_OBJ_AP; 878c2ecf20Sopenharmony_ci data.msg.src = HI6220_MBOX_OBJ_AP; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci mbox_send_message(stub_clk->mbox, &data); 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int hi6220_acpu_round_freq(struct hi6220_stub_clk *stub_clk, 948c2ecf20Sopenharmony_ci unsigned int freq) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned int limit_flag, limit_freq = UINT_MAX; 978c2ecf20Sopenharmony_ci unsigned int max_freq; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* check the constrained frequency */ 1008c2ecf20Sopenharmony_ci regmap_read(stub_clk->dfs_map, ACPU_DFS_FLAG, &limit_flag); 1018c2ecf20Sopenharmony_ci if (limit_flag == ACPU_DFS_LOCK_FLAG) 1028c2ecf20Sopenharmony_ci regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, &limit_freq); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* check the supported maximum frequency */ 1058c2ecf20Sopenharmony_ci regmap_read(stub_clk->dfs_map, ACPU_DFS_FREQ_MAX, &max_freq); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* calculate the real maximum frequency */ 1088c2ecf20Sopenharmony_ci max_freq = min(max_freq, limit_freq); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (WARN_ON(freq > max_freq)) 1118c2ecf20Sopenharmony_ci freq = max_freq; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return freq; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic unsigned long hi6220_stub_clk_recalc_rate(struct clk_hw *hw, 1178c2ecf20Sopenharmony_ci unsigned long parent_rate) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u32 rate = 0; 1208c2ecf20Sopenharmony_ci struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci switch (stub_clk->id) { 1238c2ecf20Sopenharmony_ci case HI6220_STUB_ACPU0: 1248c2ecf20Sopenharmony_ci rate = hi6220_acpu_get_freq(stub_clk); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* convert from kHz to Hz */ 1278c2ecf20Sopenharmony_ci rate *= 1000; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci default: 1318c2ecf20Sopenharmony_ci dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 1328c2ecf20Sopenharmony_ci __func__, stub_clk->id); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return rate; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int hi6220_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, 1408c2ecf20Sopenharmony_ci unsigned long parent_rate) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 1438c2ecf20Sopenharmony_ci unsigned long new_rate = rate / 1000; /* kHz */ 1448c2ecf20Sopenharmony_ci int ret = 0; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci switch (stub_clk->id) { 1478c2ecf20Sopenharmony_ci case HI6220_STUB_ACPU0: 1488c2ecf20Sopenharmony_ci ret = hi6220_acpu_set_freq(stub_clk, new_rate); 1498c2ecf20Sopenharmony_ci if (ret < 0) 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 1568c2ecf20Sopenharmony_ci __func__, stub_clk->id); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci pr_debug("%s: set rate=%ldkHz\n", __func__, new_rate); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic long hi6220_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, 1658c2ecf20Sopenharmony_ci unsigned long *parent_rate) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct hi6220_stub_clk *stub_clk = to_stub_clk(hw); 1688c2ecf20Sopenharmony_ci unsigned long new_rate = rate / 1000; /* kHz */ 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci switch (stub_clk->id) { 1718c2ecf20Sopenharmony_ci case HI6220_STUB_ACPU0: 1728c2ecf20Sopenharmony_ci new_rate = hi6220_acpu_round_freq(stub_clk, new_rate); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* convert from kHz to Hz */ 1758c2ecf20Sopenharmony_ci new_rate *= 1000; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci default: 1798c2ecf20Sopenharmony_ci dev_err(stub_clk->dev, "%s: un-supported clock id %d\n", 1808c2ecf20Sopenharmony_ci __func__, stub_clk->id); 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return new_rate; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct clk_ops hi6220_stub_clk_ops = { 1888c2ecf20Sopenharmony_ci .recalc_rate = hi6220_stub_clk_recalc_rate, 1898c2ecf20Sopenharmony_ci .round_rate = hi6220_stub_clk_round_rate, 1908c2ecf20Sopenharmony_ci .set_rate = hi6220_stub_clk_set_rate, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int hi6220_stub_clk_probe(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1968c2ecf20Sopenharmony_ci struct clk_init_data init; 1978c2ecf20Sopenharmony_ci struct hi6220_stub_clk *stub_clk; 1988c2ecf20Sopenharmony_ci struct clk *clk; 1998c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci stub_clk = devm_kzalloc(dev, sizeof(*stub_clk), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!stub_clk) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci stub_clk->dfs_map = syscon_regmap_lookup_by_phandle(np, 2078c2ecf20Sopenharmony_ci "hisilicon,hi6220-clk-sram"); 2088c2ecf20Sopenharmony_ci if (IS_ERR(stub_clk->dfs_map)) { 2098c2ecf20Sopenharmony_ci dev_err(dev, "failed to get sram regmap\n"); 2108c2ecf20Sopenharmony_ci return PTR_ERR(stub_clk->dfs_map); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci stub_clk->hw.init = &init; 2148c2ecf20Sopenharmony_ci stub_clk->dev = dev; 2158c2ecf20Sopenharmony_ci stub_clk->id = HI6220_STUB_ACPU0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Use mailbox client with blocking mode */ 2188c2ecf20Sopenharmony_ci stub_clk->cl.dev = dev; 2198c2ecf20Sopenharmony_ci stub_clk->cl.tx_done = NULL; 2208c2ecf20Sopenharmony_ci stub_clk->cl.tx_block = true; 2218c2ecf20Sopenharmony_ci stub_clk->cl.tx_tout = 500; 2228c2ecf20Sopenharmony_ci stub_clk->cl.knows_txdone = false; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Allocate mailbox channel */ 2258c2ecf20Sopenharmony_ci stub_clk->mbox = mbox_request_channel(&stub_clk->cl, 0); 2268c2ecf20Sopenharmony_ci if (IS_ERR(stub_clk->mbox)) { 2278c2ecf20Sopenharmony_ci dev_err(dev, "failed get mailbox channel\n"); 2288c2ecf20Sopenharmony_ci return PTR_ERR(stub_clk->mbox); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci init.name = "acpu0"; 2328c2ecf20Sopenharmony_ci init.ops = &hi6220_stub_clk_ops; 2338c2ecf20Sopenharmony_ci init.num_parents = 0; 2348c2ecf20Sopenharmony_ci init.flags = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci clk = devm_clk_register(dev, &stub_clk->hw); 2378c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 2388c2ecf20Sopenharmony_ci return PTR_ERR(clk); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); 2418c2ecf20Sopenharmony_ci if (ret) { 2428c2ecf20Sopenharmony_ci dev_err(dev, "failed to register OF clock provider\n"); 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* initialize buffer to zero */ 2478c2ecf20Sopenharmony_ci regmap_write(stub_clk->dfs_map, ACPU_DFS_FLAG, 0x0); 2488c2ecf20Sopenharmony_ci regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_REQ, 0x0); 2498c2ecf20Sopenharmony_ci regmap_write(stub_clk->dfs_map, ACPU_DFS_FREQ_LMT, 0x0); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dev_dbg(dev, "Registered clock '%s'\n", init.name); 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic const struct of_device_id hi6220_stub_clk_of_match[] = { 2568c2ecf20Sopenharmony_ci { .compatible = "hisilicon,hi6220-stub-clk", }, 2578c2ecf20Sopenharmony_ci {} 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic struct platform_driver hi6220_stub_clk_driver = { 2618c2ecf20Sopenharmony_ci .driver = { 2628c2ecf20Sopenharmony_ci .name = "hi6220-stub-clk", 2638c2ecf20Sopenharmony_ci .of_match_table = hi6220_stub_clk_of_match, 2648c2ecf20Sopenharmony_ci }, 2658c2ecf20Sopenharmony_ci .probe = hi6220_stub_clk_probe, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __init hi6220_stub_clk_init(void) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return platform_driver_register(&hi6220_stub_clk_driver); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_cisubsys_initcall(hi6220_stub_clk_init); 273