• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of
55  * CPU cores and DDR controller reference clocks, due to which they obviously
56  * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and
57  * DDR controller AXI-bus clocks. If they are gated the system will be
58  * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset
59  * of the corresponding subsystems. So until we aren't ready to re-initialize
60  * all the devices consuming those PLLs, they will be marked as critical too.
61  */
62 static const struct ccu_pll_info pll_info[] = {
63 	CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE,
64 		     CLK_IS_CRITICAL),
65 	CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
66 		     CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
67 	CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
68 		     CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
69 	CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
70 		     CLK_IS_CRITICAL),
71 	CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
72 		     CLK_IS_CRITICAL | CLK_SET_RATE_GATE)
73 };
74 
75 struct ccu_pll_data {
76 	struct device_node *np;
77 	struct regmap *sys_regs;
78 	struct ccu_pll *plls[CCU_PLL_NUM];
79 };
80 
ccu_pll_find_desc(struct ccu_pll_data * data,unsigned int clk_id)81 static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
82 					 unsigned int clk_id)
83 {
84 	struct ccu_pll *pll;
85 	int idx;
86 
87 	for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
88 		pll = data->plls[idx];
89 		if (pll && pll->id == clk_id)
90 			return pll;
91 	}
92 
93 	return ERR_PTR(-EINVAL);
94 }
95 
ccu_pll_create_data(struct device_node * np)96 static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np)
97 {
98 	struct ccu_pll_data *data;
99 
100 	data = kzalloc(sizeof(*data), GFP_KERNEL);
101 	if (!data)
102 		return ERR_PTR(-ENOMEM);
103 
104 	data->np = np;
105 
106 	return data;
107 }
108 
ccu_pll_free_data(struct ccu_pll_data * data)109 static void ccu_pll_free_data(struct ccu_pll_data *data)
110 {
111 	kfree(data);
112 }
113 
ccu_pll_find_sys_regs(struct ccu_pll_data * data)114 static int ccu_pll_find_sys_regs(struct ccu_pll_data *data)
115 {
116 	data->sys_regs = syscon_node_to_regmap(data->np->parent);
117 	if (IS_ERR(data->sys_regs)) {
118 		pr_err("Failed to find syscon regs for '%s'\n",
119 			of_node_full_name(data->np));
120 		return PTR_ERR(data->sys_regs);
121 	}
122 
123 	return 0;
124 }
125 
ccu_pll_of_clk_hw_get(struct of_phandle_args * clkspec,void * priv)126 static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec,
127 					    void *priv)
128 {
129 	struct ccu_pll_data *data = priv;
130 	struct ccu_pll *pll;
131 	unsigned int clk_id;
132 
133 	clk_id = clkspec->args[0];
134 	pll = ccu_pll_find_desc(data, clk_id);
135 	if (IS_ERR(pll)) {
136 		pr_info("Invalid PLL clock ID %d specified\n", clk_id);
137 		return ERR_CAST(pll);
138 	}
139 
140 	return ccu_pll_get_clk_hw(pll);
141 }
142 
ccu_pll_clk_register(struct ccu_pll_data * data)143 static int ccu_pll_clk_register(struct ccu_pll_data *data)
144 {
145 	int idx, ret;
146 
147 	for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
148 		const struct ccu_pll_info *info = &pll_info[idx];
149 		struct ccu_pll_init_data init = {0};
150 
151 		init.id = info->id;
152 		init.name = info->name;
153 		init.parent_name = info->parent_name;
154 		init.base = info->base;
155 		init.sys_regs = data->sys_regs;
156 		init.np = data->np;
157 		init.flags = info->flags;
158 
159 		data->plls[idx] = ccu_pll_hw_register(&init);
160 		if (IS_ERR(data->plls[idx])) {
161 			ret = PTR_ERR(data->plls[idx]);
162 			pr_err("Couldn't register PLL hw '%s'\n",
163 				init.name);
164 			goto err_hw_unregister;
165 		}
166 	}
167 
168 	ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data);
169 	if (ret) {
170 		pr_err("Couldn't register PLL provider of '%s'\n",
171 			of_node_full_name(data->np));
172 		goto err_hw_unregister;
173 	}
174 
175 	return 0;
176 
177 err_hw_unregister:
178 	for (--idx; idx >= 0; --idx)
179 		ccu_pll_hw_unregister(data->plls[idx]);
180 
181 	return ret;
182 }
183 
ccu_pll_init(struct device_node * np)184 static __init void ccu_pll_init(struct device_node *np)
185 {
186 	struct ccu_pll_data *data;
187 	int ret;
188 
189 	data = ccu_pll_create_data(np);
190 	if (IS_ERR(data))
191 		return;
192 
193 	ret = ccu_pll_find_sys_regs(data);
194 	if (ret)
195 		goto err_free_data;
196 
197 	ret = ccu_pll_clk_register(data);
198 	if (ret)
199 		goto err_free_data;
200 
201 	return;
202 
203 err_free_data:
204 	ccu_pll_free_data(data);
205 }
206 CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
207