18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hisilicon clock driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2017 Hisilicon Limited. 68c2ecf20Sopenharmony_ci * Copyright (c) 2017 Linaro Limited. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Kai Zhao <zhaokai1@hisilicon.com> 98c2ecf20Sopenharmony_ci * Tao Wang <kevin.wangtao@hisilicon.com> 108c2ecf20Sopenharmony_ci * Leo Yan <leo.yan@linaro.org> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <dt-bindings/clock/hi3660-clock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define HI3660_STUB_CLOCK_DATA (0x70) 258c2ecf20Sopenharmony_ci#define MHZ (1000 * 1000) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DEFINE_CLK_STUB(_id, _cmd, _name) \ 288c2ecf20Sopenharmony_ci { \ 298c2ecf20Sopenharmony_ci .id = (_id), \ 308c2ecf20Sopenharmony_ci .cmd = (_cmd), \ 318c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data) { \ 328c2ecf20Sopenharmony_ci .name = #_name, \ 338c2ecf20Sopenharmony_ci .ops = &hi3660_stub_clk_ops, \ 348c2ecf20Sopenharmony_ci .num_parents = 0, \ 358c2ecf20Sopenharmony_ci .flags = CLK_GET_RATE_NOCACHE, \ 368c2ecf20Sopenharmony_ci }, \ 378c2ecf20Sopenharmony_ci }, 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct hi3660_stub_clk_chan { 428c2ecf20Sopenharmony_ci struct mbox_client cl; 438c2ecf20Sopenharmony_ci struct mbox_chan *mbox; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct hi3660_stub_clk { 478c2ecf20Sopenharmony_ci unsigned int id; 488c2ecf20Sopenharmony_ci struct clk_hw hw; 498c2ecf20Sopenharmony_ci unsigned int cmd; 508c2ecf20Sopenharmony_ci unsigned int msg[8]; 518c2ecf20Sopenharmony_ci unsigned int rate; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void __iomem *freq_reg; 558c2ecf20Sopenharmony_cistatic struct hi3660_stub_clk_chan stub_clk_chan; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, 588c2ecf20Sopenharmony_ci unsigned long parent_rate) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * LPM3 writes back the CPU frequency in shared SRAM so read 648c2ecf20Sopenharmony_ci * back the frequency. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ; 678c2ecf20Sopenharmony_ci return stub_clk->rate; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, 718c2ecf20Sopenharmony_ci unsigned long *prate) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * LPM3 handles rate rounding so just return whatever 758c2ecf20Sopenharmony_ci * rate is requested. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci return rate; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, 818c2ecf20Sopenharmony_ci unsigned long parent_rate) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci stub_clk->msg[0] = stub_clk->cmd; 868c2ecf20Sopenharmony_ci stub_clk->msg[1] = rate / MHZ; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n", 898c2ecf20Sopenharmony_ci stub_clk->msg[0], stub_clk->msg[1]); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci mbox_send_message(stub_clk_chan.mbox, stub_clk->msg); 928c2ecf20Sopenharmony_ci mbox_client_txdone(stub_clk_chan.mbox, 0); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci stub_clk->rate = rate; 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct clk_ops hi3660_stub_clk_ops = { 998c2ecf20Sopenharmony_ci .recalc_rate = hi3660_stub_clk_recalc_rate, 1008c2ecf20Sopenharmony_ci .round_rate = hi3660_stub_clk_round_rate, 1018c2ecf20Sopenharmony_ci .set_rate = hi3660_stub_clk_set_rate, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = { 1058c2ecf20Sopenharmony_ci DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0") 1068c2ecf20Sopenharmony_ci DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1") 1078c2ecf20Sopenharmony_ci DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d") 1088c2ecf20Sopenharmony_ci DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc") 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, 1128c2ecf20Sopenharmony_ci void *data) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci unsigned int idx = clkspec->args[0]; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (idx >= HI3660_CLK_STUB_NUM) { 1178c2ecf20Sopenharmony_ci pr_err("%s: invalid index %u\n", __func__, idx); 1188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return &hi3660_stub_clks[idx].hw; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int hi3660_stub_clk_probe(struct platform_device *pdev) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1278c2ecf20Sopenharmony_ci struct resource *res; 1288c2ecf20Sopenharmony_ci unsigned int i; 1298c2ecf20Sopenharmony_ci int ret; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Use mailbox client without blocking */ 1328c2ecf20Sopenharmony_ci stub_clk_chan.cl.dev = dev; 1338c2ecf20Sopenharmony_ci stub_clk_chan.cl.tx_done = NULL; 1348c2ecf20Sopenharmony_ci stub_clk_chan.cl.tx_block = false; 1358c2ecf20Sopenharmony_ci stub_clk_chan.cl.knows_txdone = false; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Allocate mailbox channel */ 1388c2ecf20Sopenharmony_ci stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0); 1398c2ecf20Sopenharmony_ci if (IS_ERR(stub_clk_chan.mbox)) 1408c2ecf20Sopenharmony_ci return PTR_ERR(stub_clk_chan.mbox); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1438c2ecf20Sopenharmony_ci if (!res) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci freq_reg = devm_ioremap(dev, res->start, resource_size(res)); 1468c2ecf20Sopenharmony_ci if (!freq_reg) 1478c2ecf20Sopenharmony_ci return -ENOMEM; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci freq_reg += HI3660_STUB_CLOCK_DATA; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (i = 0; i < HI3660_CLK_STUB_NUM; i++) { 1528c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw); 1538c2ecf20Sopenharmony_ci if (ret) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get, 1588c2ecf20Sopenharmony_ci hi3660_stub_clks); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct of_device_id hi3660_stub_clk_of_match[] = { 1628c2ecf20Sopenharmony_ci { .compatible = "hisilicon,hi3660-stub-clk", }, 1638c2ecf20Sopenharmony_ci {} 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct platform_driver hi3660_stub_clk_driver = { 1678c2ecf20Sopenharmony_ci .probe = hi3660_stub_clk_probe, 1688c2ecf20Sopenharmony_ci .driver = { 1698c2ecf20Sopenharmony_ci .name = "hi3660-stub-clk", 1708c2ecf20Sopenharmony_ci .of_match_table = hi3660_stub_clk_of_match, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int __init hi3660_stub_clk_init(void) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci return platform_driver_register(&hi3660_stub_clk_driver); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_cisubsys_initcall(hi3660_stub_clk_init); 179