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