1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
4  *
5  * Authors:
6  *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
7  *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
8  *
9  * Baikal-T1 CCU PLL clocks driver
10  */
11 
12 #define pr_fmt(fmt) "bt1-ccu-pll: " fmt
13 
14 #include <linux/kernel.h>
15 #include <linux/printk.h>
16 #include <linux/slab.h>
17 #include <linux/clk-provider.h>
18 #include <linux/mfd/syscon.h>
19 #include <linux/of.h>
20 #include <linux/of_address.h>
21 #include <linux/ioport.h>
22 #include <linux/regmap.h>
23 
24 #include <dt-bindings/clock/bt1-ccu.h>
25 
26 #include "ccu-pll.h"
27 
28 #define CCU_CPU_PLL_BASE		0x000
29 #define CCU_SATA_PLL_BASE		0x008
30 #define CCU_DDR_PLL_BASE		0x010
31 #define CCU_PCIE_PLL_BASE		0x018
32 #define CCU_ETH_PLL_BASE		0x020
33 
34 #define CCU_PLL_INFO(_id, _name, _pname, _base, _flags)	\
35 	{						\
36 		.id = _id,				\
37 		.name = _name,				\
38 		.parent_name = _pname,			\
39 		.base = _base,				\
40 		.flags = _flags				\
41 	}
42 
43 #define CCU_PLL_NUM			ARRAY_SIZE(pll_info)
44 
45 struct ccu_pll_info {
46 	unsigned int id;
47 	const char *name;
48 	const char *parent_name;
49 	unsigned int base;
50 	unsigned long flags;
51 };
52 
53 /*
54  * Mark as critical all PLLs except Ethernet one. CPU and DDR PLLs are sources
55  * of CPU cores and DDR controller reference clocks, due to which they
56  * obviously shouldn't be ever gated. SATA and PCIe PLLs are the parents of
57  * APB-bus and DDR controller AXI-bus clocks. If they are gated the system will
58  * be unusable.
59  */
60 static const struct ccu_pll_info pll_info[] = {
61 	CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE,
62 		     CLK_IS_CRITICAL),
63 	CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
64 		     CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
65 	CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
66 		     CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
67 	CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
68 		     CLK_IS_CRITICAL),
69 	CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
70 		     CLK_SET_RATE_GATE)
71 };
72 
73 struct ccu_pll_data {
74 	struct device_node *np;
75 	struct regmap *sys_regs;
76 	struct ccu_pll *plls[CCU_PLL_NUM];
77 };
78 
79 static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
80 					 unsigned int clk_id)
81 {
82 	struct ccu_pll *pll;
83 	int idx;
84 
85 	for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
86 		pll = data->plls[idx];
87 		if (pll && pll->id == clk_id)
88 			return pll;
89 	}
90 
91 	return ERR_PTR(-EINVAL);
92 }
93 
94 static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np)
95 {
96 	struct ccu_pll_data *data;
97 
98 	data = kzalloc(sizeof(*data), GFP_KERNEL);
99 	if (!data)
100 		return ERR_PTR(-ENOMEM);
101 
102 	data->np = np;
103 
104 	return data;
105 }
106 
107 static void ccu_pll_free_data(struct ccu_pll_data *data)
108 {
109 	kfree(data);
110 }
111 
112 static int ccu_pll_find_sys_regs(struct ccu_pll_data *data)
113 {
114 	data->sys_regs = syscon_node_to_regmap(data->np->parent);
115 	if (IS_ERR(data->sys_regs)) {
116 		pr_err("Failed to find syscon regs for '%s'\n",
117 			of_node_full_name(data->np));
118 		return PTR_ERR(data->sys_regs);
119 	}
120 
121 	return 0;
122 }
123 
124 static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec,
125 					    void *priv)
126 {
127 	struct ccu_pll_data *data = priv;
128 	struct ccu_pll *pll;
129 	unsigned int clk_id;
130 
131 	clk_id = clkspec->args[0];
132 	pll = ccu_pll_find_desc(data, clk_id);
133 	if (IS_ERR(pll)) {
134 		pr_info("Invalid PLL clock ID %d specified\n", clk_id);
135 		return ERR_CAST(pll);
136 	}
137 
138 	return ccu_pll_get_clk_hw(pll);
139 }
140 
141 static int ccu_pll_clk_register(struct ccu_pll_data *data)
142 {
143 	int idx, ret;
144 
145 	for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
146 		const struct ccu_pll_info *info = &pll_info[idx];
147 		struct ccu_pll_init_data init = {0};
148 
149 		init.id = info->id;
150 		init.name = info->name;
151 		init.parent_name = info->parent_name;
152 		init.base = info->base;
153 		init.sys_regs = data->sys_regs;
154 		init.np = data->np;
155 		init.flags = info->flags;
156 
157 		data->plls[idx] = ccu_pll_hw_register(&init);
158 		if (IS_ERR(data->plls[idx])) {
159 			ret = PTR_ERR(data->plls[idx]);
160 			pr_err("Couldn't register PLL hw '%s'\n",
161 				init.name);
162 			goto err_hw_unregister;
163 		}
164 	}
165 
166 	ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data);
167 	if (ret) {
168 		pr_err("Couldn't register PLL provider of '%s'\n",
169 			of_node_full_name(data->np));
170 		goto err_hw_unregister;
171 	}
172 
173 	return 0;
174 
175 err_hw_unregister:
176 	for (--idx; idx >= 0; --idx)
177 		ccu_pll_hw_unregister(data->plls[idx]);
178 
179 	return ret;
180 }
181 
182 static __init void ccu_pll_init(struct device_node *np)
183 {
184 	struct ccu_pll_data *data;
185 	int ret;
186 
187 	data = ccu_pll_create_data(np);
188 	if (IS_ERR(data))
189 		return;
190 
191 	ret = ccu_pll_find_sys_regs(data);
192 	if (ret)
193 		goto err_free_data;
194 
195 	ret = ccu_pll_clk_register(data);
196 	if (ret)
197 		goto err_free_data;
198 
199 	return;
200 
201 err_free_data:
202 	ccu_pll_free_data(data);
203 }
204 CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
205