xref: /linux/drivers/clk/hisilicon/clk-hi3660-stub.c (revision 4f16f7ff3bc02f6e1845677235fea157bdc0e59c)
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