1*4f16f7ffSKaihua Zhong /* 2*4f16f7ffSKaihua Zhong * Hisilicon clock driver 3*4f16f7ffSKaihua Zhong * 4*4f16f7ffSKaihua Zhong * Copyright (c) 2013-2017 Hisilicon Limited. 5*4f16f7ffSKaihua Zhong * Copyright (c) 2017 Linaro Limited. 6*4f16f7ffSKaihua Zhong * 7*4f16f7ffSKaihua Zhong * Author: Kai Zhao <zhaokai1@hisilicon.com> 8*4f16f7ffSKaihua Zhong * Tao Wang <kevin.wangtao@hisilicon.com> 9*4f16f7ffSKaihua Zhong * Leo Yan <leo.yan@linaro.org> 10*4f16f7ffSKaihua Zhong * 11*4f16f7ffSKaihua Zhong * This program is free software; you can redistribute it and/or modify 12*4f16f7ffSKaihua Zhong * it under the terms of the GNU General Public License as published by 13*4f16f7ffSKaihua Zhong * the Free Software Foundation; either version 2 of the License, or 14*4f16f7ffSKaihua Zhong * (at your option) any later version. 15*4f16f7ffSKaihua Zhong * 16*4f16f7ffSKaihua Zhong * This program is distributed in the hope that it will be useful, 17*4f16f7ffSKaihua Zhong * but WITHOUT ANY WARRANTY; without even the implied warranty of 18*4f16f7ffSKaihua Zhong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19*4f16f7ffSKaihua Zhong * GNU General Public License for more details. 20*4f16f7ffSKaihua Zhong * 21*4f16f7ffSKaihua Zhong */ 22*4f16f7ffSKaihua Zhong 23*4f16f7ffSKaihua Zhong #include <linux/clk-provider.h> 24*4f16f7ffSKaihua Zhong #include <linux/device.h> 25*4f16f7ffSKaihua Zhong #include <linux/err.h> 26*4f16f7ffSKaihua Zhong #include <linux/init.h> 27*4f16f7ffSKaihua Zhong #include <linux/mailbox_client.h> 28*4f16f7ffSKaihua Zhong #include <linux/module.h> 29*4f16f7ffSKaihua Zhong #include <linux/of.h> 30*4f16f7ffSKaihua Zhong #include <linux/platform_device.h> 31*4f16f7ffSKaihua Zhong #include <dt-bindings/clock/hi3660-clock.h> 32*4f16f7ffSKaihua Zhong 33*4f16f7ffSKaihua Zhong #define HI3660_STUB_CLOCK_DATA (0x70) 34*4f16f7ffSKaihua Zhong #define MHZ (1000 * 1000) 35*4f16f7ffSKaihua Zhong 36*4f16f7ffSKaihua Zhong #define DEFINE_CLK_STUB(_id, _cmd, _name) \ 37*4f16f7ffSKaihua Zhong { \ 38*4f16f7ffSKaihua Zhong .id = (_id), \ 39*4f16f7ffSKaihua Zhong .cmd = (_cmd), \ 40*4f16f7ffSKaihua Zhong .hw.init = &(struct clk_init_data) { \ 41*4f16f7ffSKaihua Zhong .name = #_name, \ 42*4f16f7ffSKaihua Zhong .ops = &hi3660_stub_clk_ops, \ 43*4f16f7ffSKaihua Zhong .num_parents = 0, \ 44*4f16f7ffSKaihua Zhong .flags = CLK_GET_RATE_NOCACHE, \ 45*4f16f7ffSKaihua Zhong }, \ 46*4f16f7ffSKaihua Zhong }, 47*4f16f7ffSKaihua Zhong 48*4f16f7ffSKaihua Zhong #define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw) 49*4f16f7ffSKaihua Zhong 50*4f16f7ffSKaihua Zhong struct hi3660_stub_clk_chan { 51*4f16f7ffSKaihua Zhong struct mbox_client cl; 52*4f16f7ffSKaihua Zhong struct mbox_chan *mbox; 53*4f16f7ffSKaihua Zhong }; 54*4f16f7ffSKaihua Zhong 55*4f16f7ffSKaihua Zhong struct hi3660_stub_clk { 56*4f16f7ffSKaihua Zhong unsigned int id; 57*4f16f7ffSKaihua Zhong struct clk_hw hw; 58*4f16f7ffSKaihua Zhong unsigned int cmd; 59*4f16f7ffSKaihua Zhong unsigned int msg[8]; 60*4f16f7ffSKaihua Zhong unsigned int rate; 61*4f16f7ffSKaihua Zhong }; 62*4f16f7ffSKaihua Zhong 63*4f16f7ffSKaihua Zhong static void __iomem *freq_reg; 64*4f16f7ffSKaihua Zhong static struct hi3660_stub_clk_chan stub_clk_chan; 65*4f16f7ffSKaihua Zhong 66*4f16f7ffSKaihua Zhong static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, 67*4f16f7ffSKaihua Zhong unsigned long parent_rate) 68*4f16f7ffSKaihua Zhong { 69*4f16f7ffSKaihua Zhong struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 70*4f16f7ffSKaihua Zhong 71*4f16f7ffSKaihua Zhong /* 72*4f16f7ffSKaihua Zhong * LPM3 writes back the CPU frequency in shared SRAM so read 73*4f16f7ffSKaihua Zhong * back the frequency. 74*4f16f7ffSKaihua Zhong */ 75*4f16f7ffSKaihua Zhong stub_clk->rate = readl(freq_reg + (stub_clk->id << 2)) * MHZ; 76*4f16f7ffSKaihua Zhong return stub_clk->rate; 77*4f16f7ffSKaihua Zhong } 78*4f16f7ffSKaihua Zhong 79*4f16f7ffSKaihua Zhong static long hi3660_stub_clk_round_rate(struct clk_hw *hw, unsigned long rate, 80*4f16f7ffSKaihua Zhong unsigned long *prate) 81*4f16f7ffSKaihua Zhong { 82*4f16f7ffSKaihua Zhong /* 83*4f16f7ffSKaihua Zhong * LPM3 handles rate rounding so just return whatever 84*4f16f7ffSKaihua Zhong * rate is requested. 85*4f16f7ffSKaihua Zhong */ 86*4f16f7ffSKaihua Zhong return rate; 87*4f16f7ffSKaihua Zhong } 88*4f16f7ffSKaihua Zhong 89*4f16f7ffSKaihua Zhong static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, 90*4f16f7ffSKaihua Zhong unsigned long parent_rate) 91*4f16f7ffSKaihua Zhong { 92*4f16f7ffSKaihua Zhong struct hi3660_stub_clk *stub_clk = to_stub_clk(hw); 93*4f16f7ffSKaihua Zhong 94*4f16f7ffSKaihua Zhong stub_clk->msg[0] = stub_clk->cmd; 95*4f16f7ffSKaihua Zhong stub_clk->msg[1] = rate / MHZ; 96*4f16f7ffSKaihua Zhong 97*4f16f7ffSKaihua Zhong dev_dbg(stub_clk_chan.cl.dev, "set rate msg[0]=0x%x msg[1]=0x%x\n", 98*4f16f7ffSKaihua Zhong stub_clk->msg[0], stub_clk->msg[1]); 99*4f16f7ffSKaihua Zhong 100*4f16f7ffSKaihua Zhong mbox_send_message(stub_clk_chan.mbox, stub_clk->msg); 101*4f16f7ffSKaihua Zhong mbox_client_txdone(stub_clk_chan.mbox, 0); 102*4f16f7ffSKaihua Zhong 103*4f16f7ffSKaihua Zhong stub_clk->rate = rate; 104*4f16f7ffSKaihua Zhong return 0; 105*4f16f7ffSKaihua Zhong } 106*4f16f7ffSKaihua Zhong 107*4f16f7ffSKaihua Zhong static const struct clk_ops hi3660_stub_clk_ops = { 108*4f16f7ffSKaihua Zhong .recalc_rate = hi3660_stub_clk_recalc_rate, 109*4f16f7ffSKaihua Zhong .round_rate = hi3660_stub_clk_round_rate, 110*4f16f7ffSKaihua Zhong .set_rate = hi3660_stub_clk_set_rate, 111*4f16f7ffSKaihua Zhong }; 112*4f16f7ffSKaihua Zhong 113*4f16f7ffSKaihua Zhong static struct hi3660_stub_clk hi3660_stub_clks[HI3660_CLK_STUB_NUM] = { 114*4f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER0, 0x0001030A, "cpu-cluster.0") 115*4f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_CLUSTER1, 0x0002030A, "cpu-cluster.1") 116*4f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_GPU, 0x0003030A, "clk-g3d") 117*4f16f7ffSKaihua Zhong DEFINE_CLK_STUB(HI3660_CLK_STUB_DDR, 0x00040309, "clk-ddrc") 118*4f16f7ffSKaihua Zhong }; 119*4f16f7ffSKaihua Zhong 120*4f16f7ffSKaihua Zhong static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, 121*4f16f7ffSKaihua Zhong void *data) 122*4f16f7ffSKaihua Zhong { 123*4f16f7ffSKaihua Zhong unsigned int idx = clkspec->args[0]; 124*4f16f7ffSKaihua Zhong 125*4f16f7ffSKaihua Zhong if (idx >= HI3660_CLK_STUB_NUM) { 126*4f16f7ffSKaihua Zhong pr_err("%s: invalid index %u\n", __func__, idx); 127*4f16f7ffSKaihua Zhong return ERR_PTR(-EINVAL); 128*4f16f7ffSKaihua Zhong } 129*4f16f7ffSKaihua Zhong 130*4f16f7ffSKaihua Zhong return &hi3660_stub_clks[idx].hw; 131*4f16f7ffSKaihua Zhong } 132*4f16f7ffSKaihua Zhong 133*4f16f7ffSKaihua Zhong static int hi3660_stub_clk_probe(struct platform_device *pdev) 134*4f16f7ffSKaihua Zhong { 135*4f16f7ffSKaihua Zhong struct device *dev = &pdev->dev; 136*4f16f7ffSKaihua Zhong struct resource *res; 137*4f16f7ffSKaihua Zhong unsigned int i; 138*4f16f7ffSKaihua Zhong int ret; 139*4f16f7ffSKaihua Zhong 140*4f16f7ffSKaihua Zhong /* Use mailbox client without blocking */ 141*4f16f7ffSKaihua Zhong stub_clk_chan.cl.dev = dev; 142*4f16f7ffSKaihua Zhong stub_clk_chan.cl.tx_done = NULL; 143*4f16f7ffSKaihua Zhong stub_clk_chan.cl.tx_block = false; 144*4f16f7ffSKaihua Zhong stub_clk_chan.cl.knows_txdone = false; 145*4f16f7ffSKaihua Zhong 146*4f16f7ffSKaihua Zhong /* Allocate mailbox channel */ 147*4f16f7ffSKaihua Zhong stub_clk_chan.mbox = mbox_request_channel(&stub_clk_chan.cl, 0); 148*4f16f7ffSKaihua Zhong if (IS_ERR(stub_clk_chan.mbox)) 149*4f16f7ffSKaihua Zhong return PTR_ERR(stub_clk_chan.mbox); 150*4f16f7ffSKaihua Zhong 151*4f16f7ffSKaihua Zhong res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 152*4f16f7ffSKaihua Zhong freq_reg = devm_ioremap(dev, res->start, resource_size(res)); 153*4f16f7ffSKaihua Zhong if (!freq_reg) 154*4f16f7ffSKaihua Zhong return -ENOMEM; 155*4f16f7ffSKaihua Zhong 156*4f16f7ffSKaihua Zhong freq_reg += HI3660_STUB_CLOCK_DATA; 157*4f16f7ffSKaihua Zhong 158*4f16f7ffSKaihua Zhong for (i = 0; i < HI3660_CLK_STUB_NUM; i++) { 159*4f16f7ffSKaihua Zhong ret = devm_clk_hw_register(&pdev->dev, &hi3660_stub_clks[i].hw); 160*4f16f7ffSKaihua Zhong if (ret) 161*4f16f7ffSKaihua Zhong return ret; 162*4f16f7ffSKaihua Zhong } 163*4f16f7ffSKaihua Zhong 164*4f16f7ffSKaihua Zhong return devm_of_clk_add_hw_provider(&pdev->dev, hi3660_stub_clk_hw_get, 165*4f16f7ffSKaihua Zhong hi3660_stub_clks); 166*4f16f7ffSKaihua Zhong } 167*4f16f7ffSKaihua Zhong 168*4f16f7ffSKaihua Zhong static const struct of_device_id hi3660_stub_clk_of_match[] = { 169*4f16f7ffSKaihua Zhong { .compatible = "hisilicon,hi3660-stub-clk", }, 170*4f16f7ffSKaihua Zhong {} 171*4f16f7ffSKaihua Zhong }; 172*4f16f7ffSKaihua Zhong 173*4f16f7ffSKaihua Zhong static struct platform_driver hi3660_stub_clk_driver = { 174*4f16f7ffSKaihua Zhong .probe = hi3660_stub_clk_probe, 175*4f16f7ffSKaihua Zhong .driver = { 176*4f16f7ffSKaihua Zhong .name = "hi3660-stub-clk", 177*4f16f7ffSKaihua Zhong .of_match_table = hi3660_stub_clk_of_match, 178*4f16f7ffSKaihua Zhong }, 179*4f16f7ffSKaihua Zhong }; 180*4f16f7ffSKaihua Zhong 181*4f16f7ffSKaihua Zhong static int __init hi3660_stub_clk_init(void) 182*4f16f7ffSKaihua Zhong { 183*4f16f7ffSKaihua Zhong return platform_driver_register(&hi3660_stub_clk_driver); 184*4f16f7ffSKaihua Zhong } 185*4f16f7ffSKaihua Zhong subsys_initcall(hi3660_stub_clk_init); 186