xref: /OK3568_Linux_fs/kernel/drivers/clk/rockchip/clk-link.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2021 Rockchip Electronics Co., Ltd
4  */
5 
6 #include <linux/clk-provider.h>
7 #include <linux/io.h>
8 #include <linux/module.h>
9 #include <linux/of_address.h>
10 #include <linux/platform_device.h>
11 #include <linux/pm_clock.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/slab.h>
14 
15 struct rockchip_link_info {
16 	u32 shift;
17 	const char *name;
18 	const char *pname;
19 };
20 
21 struct rockchip_link {
22 	int num;
23 	const struct rockchip_link_info *info;
24 };
25 
26 struct rockchip_link_clk {
27 	void __iomem *base;
28 	struct clk_gate *gate;
29 	spinlock_t lock;
30 	u32 shift;
31 	u32 flag;
32 	const char *name;
33 	const char *pname;
34 	const char *link_name;
35 	const struct rockchip_link *link;
36 };
37 
38 #define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
39 
40 #define GATE_LINK(_name, _pname, _shift)	\
41 {						\
42 	.name = _name,				\
43 	.pname = _pname,			\
44 	.shift = (_shift),			\
45 }
46 
register_clocks(struct rockchip_link_clk * priv,struct device * dev)47 static int register_clocks(struct rockchip_link_clk *priv, struct device *dev)
48 {
49 	struct clk_gate *gate;
50 	struct clk_init_data init = {};
51 	struct clk *clk;
52 
53 	gate = devm_kzalloc(dev, sizeof(struct clk_gate), GFP_KERNEL);
54 	if (!gate)
55 		return -ENOMEM;
56 
57 	init.name = priv->name;
58 	init.ops = &clk_gate_ops;
59 	init.flags |= CLK_SET_RATE_PARENT;
60 	init.parent_names = &priv->pname;
61 	init.num_parents = 1;
62 
63 	/* struct clk_gate assignments */
64 	gate->reg = priv->base;
65 	gate->bit_idx = priv->shift;
66 	gate->flags = GFLAGS;
67 	gate->lock = &priv->lock;
68 	gate->hw.init = &init;
69 
70 	clk = devm_clk_register(dev, &gate->hw);
71 	if (IS_ERR(clk))
72 		return -EINVAL;
73 
74 	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, clk);
75 }
76 
77 static const struct rockchip_link_info rk3562_clk_gate_link_info[] = {
78 	GATE_LINK("aclk_rga_jdec", "aclk_rga_pre", 3),
79 	GATE_LINK("aclk_vdpu", "aclk_vdpu_pre", 5),
80 	GATE_LINK("aclk_vepu", "aclk_vepu_pre", 3),
81 	GATE_LINK("aclk_vi_isp", "aclk_vi", 3),
82 	GATE_LINK("aclk_vo", "aclk_vo_pre", 3),
83 	GATE_LINK("hclk_vepu", "hclk_vepu_pre", 4),
84 };
85 
86 static const struct rockchip_link rk3562_clk_gate_link = {
87 	.num = ARRAY_SIZE(rk3562_clk_gate_link_info),
88 	.info = rk3562_clk_gate_link_info,
89 };
90 
91 static const struct rockchip_link_info rk3588_clk_gate_link_info[] = {
92 	GATE_LINK("aclk_isp1_pre", "aclk_isp1_root", 6),
93 	GATE_LINK("hclk_isp1_pre", "hclk_isp1_root", 8),
94 	GATE_LINK("hclk_nvm", "hclk_nvm_root", 2),
95 	GATE_LINK("aclk_usb", "aclk_usb_root", 2),
96 	GATE_LINK("hclk_usb", "hclk_usb_root", 3),
97 	GATE_LINK("aclk_jpeg_decoder_pre", "aclk_jpeg_decoder_root", 7),
98 	GATE_LINK("aclk_vdpu_low_pre", "aclk_vdpu_low_root", 5),
99 	GATE_LINK("aclk_rkvenc1_pre", "aclk_rkvenc1_root", 3),
100 	GATE_LINK("hclk_rkvenc1_pre", "hclk_rkvenc1_root", 2),
101 	GATE_LINK("hclk_rkvdec0_pre", "hclk_rkvdec0_root", 5),
102 	GATE_LINK("aclk_rkvdec0_pre", "aclk_rkvdec0_root", 6),
103 	GATE_LINK("hclk_rkvdec1_pre", "hclk_rkvdec1_root", 4),
104 	GATE_LINK("aclk_rkvdec1_pre", "aclk_rkvdec1_root", 5),
105 	GATE_LINK("aclk_hdcp0_pre", "aclk_vo0_root", 9),
106 	GATE_LINK("hclk_vo0", "hclk_vo0_root", 5),
107 	GATE_LINK("aclk_hdcp1_pre", "aclk_hdcp1_root", 6),
108 	GATE_LINK("hclk_vo1", "hclk_vo1_root", 9),
109 	GATE_LINK("aclk_av1_pre", "aclk_av1_root", 1),
110 	GATE_LINK("pclk_av1_pre", "pclk_av1_root", 4),
111 	GATE_LINK("hclk_sdio_pre", "hclk_sdio_root", 1),
112 	GATE_LINK("pclk_vo0_grf", "pclk_vo0_root", 10),
113 	GATE_LINK("pclk_vo1_grf", "pclk_vo1_root", 12),
114 };
115 
116 static const struct rockchip_link rk3588_clk_gate_link = {
117 	.num = ARRAY_SIZE(rk3588_clk_gate_link_info),
118 	.info = rk3588_clk_gate_link_info,
119 };
120 
121 static const struct of_device_id rockchip_clk_link_of_match[] = {
122 	{
123 		.compatible = "rockchip,rk3562-clock-gate-link",
124 		.data = (void *)&rk3562_clk_gate_link,
125 	},
126 	{
127 		.compatible = "rockchip,rk3588-clock-gate-link",
128 		.data = (void *)&rk3588_clk_gate_link,
129 	},
130 	{}
131 };
132 MODULE_DEVICE_TABLE(of, rockchip_clk_link_of_match);
133 
134 static const struct rockchip_link_info *
rockchip_get_link_infos(const struct rockchip_link * link,const char * name)135 rockchip_get_link_infos(const struct rockchip_link *link, const char *name)
136 {
137 	const struct rockchip_link_info *info = link->info;
138 	int i = 0;
139 
140 	for (i = 0; i < link->num; i++) {
141 		if (strcmp(info->name, name) == 0)
142 			break;
143 		info++;
144 	}
145 	return info;
146 }
147 
rockchip_clk_link_probe(struct platform_device * pdev)148 static int rockchip_clk_link_probe(struct platform_device *pdev)
149 {
150 	struct rockchip_link_clk *priv;
151 	struct device_node *node = pdev->dev.of_node;
152 	const struct of_device_id *match;
153 	const char *clk_name;
154 	const struct rockchip_link_info *link_info;
155 	int ret;
156 
157 	match = of_match_node(rockchip_clk_link_of_match, node);
158 	if (!match)
159 		return -ENXIO;
160 
161 	priv = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_link_clk),
162 			    GFP_KERNEL);
163 	if (!priv)
164 		return -ENOMEM;
165 
166 	priv->link = match->data;
167 
168 	spin_lock_init(&priv->lock);
169 	platform_set_drvdata(pdev, priv);
170 
171 	priv->base = of_iomap(node, 0);
172 	if (IS_ERR(priv->base))
173 		return PTR_ERR(priv->base);
174 
175 	if (of_property_read_string(node, "clock-output-names", &clk_name))
176 		priv->name = node->name;
177 	else
178 		priv->name = clk_name;
179 
180 	link_info = rockchip_get_link_infos(priv->link, priv->name);
181 	priv->shift = link_info->shift;
182 	priv->pname = link_info->pname;
183 
184 	pm_runtime_enable(&pdev->dev);
185 	ret = pm_clk_create(&pdev->dev);
186 	if (ret)
187 		goto disable_pm_runtime;
188 
189 	ret = pm_clk_add(&pdev->dev, "link");
190 
191 	if (ret)
192 		goto destroy_pm_clk;
193 
194 	ret = register_clocks(priv, &pdev->dev);
195 	if (ret)
196 		goto destroy_pm_clk;
197 
198 	return 0;
199 
200 destroy_pm_clk:
201 	pm_clk_destroy(&pdev->dev);
202 disable_pm_runtime:
203 	pm_runtime_disable(&pdev->dev);
204 
205 	return ret;
206 }
207 
rockchip_clk_link_remove(struct platform_device * pdev)208 static int rockchip_clk_link_remove(struct platform_device *pdev)
209 {
210 	pm_clk_destroy(&pdev->dev);
211 	pm_runtime_disable(&pdev->dev);
212 
213 	return 0;
214 }
215 
216 static const struct dev_pm_ops rockchip_clk_link_pm_ops = {
217 	SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
218 };
219 
220 static struct platform_driver rockchip_clk_link_driver = {
221 	.driver = {
222 		.name = "clock-link",
223 		.of_match_table = of_match_ptr(rockchip_clk_link_of_match),
224 		.pm = &rockchip_clk_link_pm_ops,
225 	},
226 	.probe = rockchip_clk_link_probe,
227 	.remove = rockchip_clk_link_remove,
228 };
229 
rockchip_clk_link_drv_register(void)230 static int __init rockchip_clk_link_drv_register(void)
231 {
232 	return platform_driver_register(&rockchip_clk_link_driver);
233 }
234 postcore_initcall_sync(rockchip_clk_link_drv_register);
235 
rockchip_clk_link_drv_unregister(void)236 static void __exit rockchip_clk_link_drv_unregister(void)
237 {
238 	platform_driver_unregister(&rockchip_clk_link_driver);
239 }
240 module_exit(rockchip_clk_link_drv_unregister);
241 
242 MODULE_AUTHOR("Elaine Zhang <zhangqing@rock-chips.com>");
243 MODULE_DESCRIPTION("Clock driver for Niu Dependencies");
244 MODULE_LICENSE("GPL");
245