1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Samsung Electronics
4  * Copyright (C) 2023 Linaro Ltd.
5  *
6  * Authors:
7  *   Thomas Abraham <thomas.ab@samsung.com>
8  *   Sam Protsenko <semen.protsenko@linaro.org>
9  *
10  * This file contains the utility functions to register the pll clocks.
11  */
12 
13 #include <asm/io.h>
14 #include <div64.h>
15 #include <malloc.h>
16 #include <clk-uclass.h>
17 #include <dm/device.h>
18 #include <clk.h>
19 #include "clk.h"
20 
21 #define UBOOT_DM_CLK_SAMSUNG_PLL0822X	"samsung_clk_pll0822x"
22 #define UBOOT_DM_CLK_SAMSUNG_PLL0831X	"samsung_clk_pll0831x"
23 
24 struct samsung_clk_pll {
25 	struct clk		clk;
26 	void __iomem		*con_reg;
27 	enum samsung_pll_type	type;
28 };
29 
30 #define to_clk_pll(_clk) container_of(_clk, struct samsung_clk_pll, clk)
31 
32 /*
33  * PLL0822x Clock Type
34  */
35 
36 #define PLL0822X_MDIV_MASK		0x3ff
37 #define PLL0822X_PDIV_MASK		0x3f
38 #define PLL0822X_SDIV_MASK		0x7
39 #define PLL0822X_MDIV_SHIFT		16
40 #define PLL0822X_PDIV_SHIFT		8
41 #define PLL0822X_SDIV_SHIFT		0
42 
samsung_pll0822x_recalc_rate(struct clk * clk)43 static unsigned long samsung_pll0822x_recalc_rate(struct clk *clk)
44 {
45 	struct samsung_clk_pll *pll = to_clk_pll(clk);
46 	u32 mdiv, pdiv, sdiv, pll_con3;
47 	u64 fvco = clk_get_parent_rate(clk);
48 
49 	pll_con3 = readl_relaxed(pll->con_reg);
50 	mdiv = (pll_con3 >> PLL0822X_MDIV_SHIFT) & PLL0822X_MDIV_MASK;
51 	pdiv = (pll_con3 >> PLL0822X_PDIV_SHIFT) & PLL0822X_PDIV_MASK;
52 	sdiv = (pll_con3 >> PLL0822X_SDIV_SHIFT) & PLL0822X_SDIV_MASK;
53 
54 	fvco *= mdiv;
55 	do_div(fvco, (pdiv << sdiv));
56 	return (unsigned long)fvco;
57 }
58 
59 static const struct clk_ops samsung_pll0822x_clk_min_ops = {
60 	.get_rate = samsung_pll0822x_recalc_rate,
61 };
62 
63 /*
64  * PLL0831x Clock Type
65  */
66 
67 #define PLL0831X_KDIV_MASK		0xffff
68 #define PLL0831X_MDIV_MASK		0x1ff
69 #define PLL0831X_PDIV_MASK		0x3f
70 #define PLL0831X_SDIV_MASK		0x7
71 #define PLL0831X_MDIV_SHIFT		16
72 #define PLL0831X_PDIV_SHIFT		8
73 #define PLL0831X_SDIV_SHIFT		0
74 #define PLL0831X_KDIV_SHIFT		0
75 
samsung_pll0831x_recalc_rate(struct clk * clk)76 static unsigned long samsung_pll0831x_recalc_rate(struct clk *clk)
77 {
78 	struct samsung_clk_pll *pll = to_clk_pll(clk);
79 	u32 mdiv, pdiv, sdiv, pll_con3, pll_con5;
80 	s16 kdiv;
81 	u64 fvco = clk_get_parent_rate(clk);
82 
83 	pll_con3 = readl_relaxed(pll->con_reg);
84 	pll_con5 = readl_relaxed(pll->con_reg + 8);
85 	mdiv = (pll_con3 >> PLL0831X_MDIV_SHIFT) & PLL0831X_MDIV_MASK;
86 	pdiv = (pll_con3 >> PLL0831X_PDIV_SHIFT) & PLL0831X_PDIV_MASK;
87 	sdiv = (pll_con3 >> PLL0831X_SDIV_SHIFT) & PLL0831X_SDIV_MASK;
88 	kdiv = (s16)((pll_con5 >> PLL0831X_KDIV_SHIFT) & PLL0831X_KDIV_MASK);
89 
90 	fvco *= (mdiv << 16) + kdiv;
91 	do_div(fvco, (pdiv << sdiv));
92 	fvco >>= 16;
93 
94 	return (unsigned long)fvco;
95 }
96 
97 static const struct clk_ops samsung_pll0831x_clk_min_ops = {
98 	.get_rate = samsung_pll0831x_recalc_rate,
99 };
100 
_samsung_clk_register_pll(void __iomem * base,const struct samsung_pll_clock * pll_clk)101 static struct clk *_samsung_clk_register_pll(void __iomem *base,
102 					const struct samsung_pll_clock *pll_clk)
103 {
104 	struct samsung_clk_pll *pll;
105 	struct clk *clk;
106 	const char *drv_name;
107 	int ret;
108 
109 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
110 	if (!pll)
111 		return ERR_PTR(-ENOMEM);
112 
113 	pll->con_reg = base + pll_clk->con_offset;
114 	pll->type = pll_clk->type;
115 	clk = &pll->clk;
116 	clk->flags = pll_clk->flags;
117 
118 	switch (pll_clk->type) {
119 	case pll_0822x:
120 		drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0822X;
121 		break;
122 	case pll_0831x:
123 		drv_name = UBOOT_DM_CLK_SAMSUNG_PLL0831X;
124 		break;
125 	default:
126 		kfree(pll);
127 		return ERR_PTR(-ENODEV);
128 	}
129 
130 	ret = clk_register(clk, drv_name, pll_clk->name, pll_clk->parent_name);
131 	if (ret) {
132 		kfree(pll);
133 		return ERR_PTR(ret);
134 	}
135 
136 	return clk;
137 }
138 
samsung_clk_register_pll(void __iomem * base,unsigned int cmu_id,const struct samsung_pll_clock * clk_list,unsigned int nr_clk)139 void samsung_clk_register_pll(void __iomem *base, unsigned int cmu_id,
140 			      const struct samsung_pll_clock *clk_list,
141 			      unsigned int nr_clk)
142 {
143 	unsigned int cnt;
144 
145 	for (cnt = 0; cnt < nr_clk; cnt++) {
146 		struct clk *clk;
147 		const struct samsung_pll_clock *pll_clk;
148 		unsigned long clk_id;
149 
150 		pll_clk = &clk_list[cnt];
151 		clk = _samsung_clk_register_pll(base, pll_clk);
152 		clk_id = SAMSUNG_TO_CLK_ID(cmu_id, pll_clk->id);
153 		clk_dm(clk_id, clk);
154 	}
155 }
156 
157 U_BOOT_DRIVER(samsung_pll0822x_clk) = {
158 	.name	= UBOOT_DM_CLK_SAMSUNG_PLL0822X,
159 	.id	= UCLASS_CLK,
160 	.ops	= &samsung_pll0822x_clk_min_ops,
161 	.flags	= DM_FLAG_PRE_RELOC,
162 };
163 
164 U_BOOT_DRIVER(samsung_pll0831x_clk) = {
165 	.name	= UBOOT_DM_CLK_SAMSUNG_PLL0831X,
166 	.id	= UCLASS_CLK,
167 	.ops	= &samsung_pll0831x_clk_min_ops,
168 	.flags	= DM_FLAG_PRE_RELOC,
169 };
170