14f16f7ffSKaihua Zhong /* 24f16f7ffSKaihua Zhong * Hisilicon clock driver 34f16f7ffSKaihua Zhong * 44f16f7ffSKaihua Zhong * Copyright (c) 2013-2017 Hisilicon Limited. 54f16f7ffSKaihua Zhong * Copyright (c) 2017 Linaro Limited. 64f16f7ffSKaihua Zhong * 74f16f7ffSKaihua Zhong * Author: Kai Zhao <zhaokai1@hisilicon.com> 84f16f7ffSKaihua Zhong * Tao Wang <kevin.wangtao@hisilicon.com> 94f16f7ffSKaihua Zhong * Leo Yan <leo.yan@linaro.org> 104f16f7ffSKaihua Zhong * 114f16f7ffSKaihua Zhong * This program is free software; you can redistribute it and/or modify 124f16f7ffSKaihua Zhong * it under the terms of the GNU General Public License as published by 134f16f7ffSKaihua Zhong * the Free Software Foundation; either version 2 of the License, or 144f16f7ffSKaihua Zhong * (at your option) any later version. 154f16f7ffSKaihua Zhong * 164f16f7ffSKaihua Zhong * This program is distributed in the hope that it will be useful, 174f16f7ffSKaihua Zhong * but WITHOUT ANY WARRANTY; without even the implied warranty of 184f16f7ffSKaihua Zhong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 194f16f7ffSKaihua Zhong * GNU General Public License for more details. 204f16f7ffSKaihua Zhong * 214f16f7ffSKaihua Zhong */ 224f16f7ffSKaihua Zhong 234f16f7ffSKaihua Zhong #include <linux/clk-provider.h> 244f16f7ffSKaihua Zhong #include <linux/device.h> 254f16f7ffSKaihua Zhong #include <linux/err.h> 264f16f7ffSKaihua Zhong #include <linux/init.h> 274f16f7ffSKaihua Zhong #include <linux/mailbox_client.h> 284f16f7ffSKaihua Zhong #include <linux/module.h> 294f16f7ffSKaihua Zhong #include <linux/of.h> 304f16f7ffSKaihua Zhong #include <linux/platform_device.h> 314f16f7ffSKaihua Zhong #include <dt-bindings/clock/hi3660-clock.h> 324f16f7ffSKaihua Zhong 334f16f7ffSKaihua Zhong #define HI3660_STUB_CLOCK_DATA (0x70) 344f16f7ffSKaihua Zhong #define MHZ (1000 * 1000) 354f16f7ffSKaihua Zhong 364f16f7ffSKaihua Zhong #define DEFINE_CLK_STUB(_id, _cmd, _name) \ 374f16f7ffSKaihua Zhong { \ 384f16f7ffSKaihua Zhong .id = (_id), \ 394f16f7ffSKaihua Zhong .cmd = (_cmd), \ 404f16f7ffSKaihua Zhong .hw.init = &(struct clk_init_data) { \ 414f16f7ffSKaihua Zhong .name = #_name, \ 424f16f7ffSKaihua Zhong .ops = &hi3660_stub_clk_ops, \ 434f16f7ffSKaihua Zhong .num_parents = 0, \ 444f16f7ffSKaihua Zhong .flags = CLK_GET_RATE_NOCACHE, \ 454f16f7ffSKaihua Zhong }, \ 464f16f7ffSKaihua Zhong }, 474f16f7ffSKaihua Zhong 484f16f7ffSKaihua Zhong #define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw) 494f16f7ffSKaihua Zhong 504f16f7ffSKaihua Zhong struct hi3660_stub_clk_chan { 514f16f7ffSKaihua Zhong struct mbox_client cl; 524f16f7ffSKaihua Zhong struct mbox_chan *mbox; 534f16f7ffSKaihua Zhong }; 544f16f7ffSKaihua Zhong 554f16f7ffSKaihua Zhong struct hi3660_stub_clk { 564f16f7ffSKaihua Zhong unsigned int id; 574f16f7ffSKaihua Zhong struct clk_hw hw; 584f16f7ffSKaihua Zhong unsigned int cmd; 594f16f7ffSKaihua Zhong unsigned int msg[8]; 604f16f7ffSKaihua Zhong unsigned int rate; 614f16f7ffSKaihua Zhong }; 624f16f7ffSKaihua Zhong 634f16f7ffSKaihua Zhong static void __iomem *freq_reg; 644f16f7ffSKaihua Zhong static struct hi3660_stub_clk_chan stub_clk_chan; 654f16f7ffSKaihua Zhong 664f16f7ffSKaihua Zhong static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, 674f16f7ffSKaihua Zhong unsigned long parent_rate) 684f16f7ffSKaihua Zhong { 694f16f7ffSKaihua Zhong struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 704f16f7ffSKaihua Zhong 714f16f7ffSKaihua Zhong /* 724f16f7ffSKaihua Zhong * LPM3 writes back the CPU frequency in shared SRAM so read 734f16f7ffSKaihua Zhong * back the frequency. 744f16f7ffSKaihua Zhong */ 754f16f7ffSKaihua Zhong stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ; 764f16f7ffSKaihua Zhong return stub_clk->rate; 774f16f7ffSKaihua Zhong } 784f16f7ffSKaihua Zhong 794f16f7ffSKaihua Zhong static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, 804f16f7ffSKaihua Zhong unsigned long *prate) 814f16f7ffSKaihua Zhong { 824f16f7ffSKaihua Zhong /* 834f16f7ffSKaihua Zhong * LPM3 handles rate rounding so just return whatever 844f16f7ffSKaihua Zhong * rate is requested. 854f16f7ffSKaihua Zhong */ 864f16f7ffSKaihua Zhong return rate; 874f16f7ffSKaihua Zhong } 884f16f7ffSKaihua Zhong 894f16f7ffSKaihua Zhong static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, 904f16f7ffSKaihua Zhong unsigned long parent_rate) 914f16f7ffSKaihua Zhong { 924f16f7ffSKaihua Zhong struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 934f16f7ffSKaihua Zhong 944f16f7ffSKaihua Zhong stub_clk->msg[0] = stub_clk->cmd; 954f16f7ffSKaihua Zhong stub_clk->msg[1] = rate / MHZ; 964f16f7ffSKaihua Zhong 974f16f7ffSKaihua Zhong dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n", 984f16f7ffSKaihua Zhong stub_clk->msg[0], stub_clk->msg[1]); 994f16f7ffSKaihua Zhong 1004f16f7ffSKaihua Zhong mbox_send_message(stub_clk_chan.mbox, stub_clk->msg); 1014f16f7ffSKaihua Zhong mbox_client_txdone(stub_clk_chan.mbox, 0); 1024f16f7ffSKaihua Zhong 1034f16f7ffSKaihua Zhong stub_clk->rate = rate; 1044f16f7ffSKaihua Zhong return 0; 1054f16f7ffSKaihua Zhong } 1064f16f7ffSKaihua Zhong 1074f16f7ffSKaihua Zhong static const struct clk_ops hi3660_stub_clk_ops = { 1084f16f7ffSKaihua Zhong .recalc_rate = hi3660_stub_clk_recalc_rate, 1094f16f7ffSKaihua Zhong .round_rate = hi3660_stub_clk_round_rate, 1104f16f7ffSKaihua Zhong .set_rate = hi3660_stub_clk_set_rate, 1114f16f7ffSKaihua Zhong }; 1124f16f7ffSKaihua Zhong 1134f16f7ffSKaihua Zhong static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = { 1144f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0") 1154f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1") 1164f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d") 1174f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc") 1184f16f7ffSKaihua Zhong }; 1194f16f7ffSKaihua Zhong 1204f16f7ffSKaihua Zhong static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, 1214f16f7ffSKaihua Zhong void *data) 1224f16f7ffSKaihua Zhong { 1234f16f7ffSKaihua Zhong unsigned int idx = clkspec->args[0]; 1244f16f7ffSKaihua Zhong 1254f16f7ffSKaihua Zhong if (idx >= HI3660_CLK_STUB_NUM) { 1264f16f7ffSKaihua Zhong pr_err("%s: invalid index %u\n", __func__, idx); 1274f16f7ffSKaihua Zhong return ERR_PTR(-EINVAL); 1284f16f7ffSKaihua Zhong } 1294f16f7ffSKaihua Zhong 1304f16f7ffSKaihua Zhong return &hi3660_stub_clks[idx].hw; 1314f16f7ffSKaihua Zhong } 1324f16f7ffSKaihua Zhong 1334f16f7ffSKaihua Zhong static int hi3660_stub_clk_probe(struct platform_device *pdev) 1344f16f7ffSKaihua Zhong { 1354f16f7ffSKaihua Zhong struct device *dev = &pdev->dev; 1364f16f7ffSKaihua Zhong struct resource *res; 1374f16f7ffSKaihua Zhong unsigned int i; 1384f16f7ffSKaihua Zhong int ret; 1394f16f7ffSKaihua Zhong 1404f16f7ffSKaihua Zhong /* Use mailbox client without blocking */ 1414f16f7ffSKaihua Zhong stub_clk_chan.cl.dev = dev; 1424f16f7ffSKaihua Zhong stub_clk_chan.cl.tx_done = NULL; 1434f16f7ffSKaihua Zhong stub_clk_chan.cl.tx_block = false; 1444f16f7ffSKaihua Zhong stub_clk_chan.cl.knows_txdone = false; 1454f16f7ffSKaihua Zhong 1464f16f7ffSKaihua Zhong /* Allocate mailbox channel */ 1474f16f7ffSKaihua Zhong stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0); 1484f16f7ffSKaihua Zhong if (IS_ERR(stub_clk_chan.mbox)) 1494f16f7ffSKaihua Zhong return PTR_ERR(stub_clk_chan.mbox); 1504f16f7ffSKaihua Zhong 1514f16f7ffSKaihua Zhong res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 152*9903e41aSWei Yongjun if (!res) 153*9903e41aSWei Yongjun return -EINVAL; 1544f16f7ffSKaihua Zhong freq_reg = devm_ioremap(dev, res->start, resource_size(res)); 1554f16f7ffSKaihua Zhong if (!freq_reg) 1564f16f7ffSKaihua Zhong return -ENOMEM; 1574f16f7ffSKaihua Zhong 1584f16f7ffSKaihua Zhong freq_reg += HI3660_STUB_CLOCK_DATA; 1594f16f7ffSKaihua Zhong 1604f16f7ffSKaihua Zhong for (i = 0; i < HI3660_CLK_STUB_NUM; i++) { 1614f16f7ffSKaihua Zhong ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw); 1624f16f7ffSKaihua Zhong if (ret) 1634f16f7ffSKaihua Zhong return ret; 1644f16f7ffSKaihua Zhong } 1654f16f7ffSKaihua Zhong 1664f16f7ffSKaihua Zhong return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get, 1674f16f7ffSKaihua Zhong hi3660_stub_clks); 1684f16f7ffSKaihua Zhong } 1694f16f7ffSKaihua Zhong 1704f16f7ffSKaihua Zhong static const struct of_device_id hi3660_stub_clk_of_match[] = { 1714f16f7ffSKaihua Zhong { .compatible = "hisilicon,hi3660-stub-clk", }, 1724f16f7ffSKaihua Zhong {} 1734f16f7ffSKaihua Zhong }; 1744f16f7ffSKaihua Zhong 1754f16f7ffSKaihua Zhong static struct platform_driver hi3660_stub_clk_driver = { 1764f16f7ffSKaihua Zhong .probe = hi3660_stub_clk_probe, 1774f16f7ffSKaihua Zhong .driver = { 1784f16f7ffSKaihua Zhong .name = "hi3660-stub-clk", 1794f16f7ffSKaihua Zhong .of_match_table = hi3660_stub_clk_of_match, 1804f16f7ffSKaihua Zhong }, 1814f16f7ffSKaihua Zhong }; 1824f16f7ffSKaihua Zhong 1834f16f7ffSKaihua Zhong static int __init hi3660_stub_clk_init(void) 1844f16f7ffSKaihua Zhong { 1854f16f7ffSKaihua Zhong return platform_driver_register(&hi3660_stub_clk_driver); 1864f16f7ffSKaihua Zhong } 1874f16f7ffSKaihua Zhong subsys_initcall(hi3660_stub_clk_init); 188