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