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