1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  */
6 
7 #include <linux/bitfield.h>
8 #include <linux/bitops.h>
9 #include <linux/delay.h>
10 #include <linux/kernel.h>
11 #include <linux/errno.h>
12 #include <linux/io.h>
13 #include <linux/sizes.h>
14 
15 #include "pll.h"
16 
17 /* PLL type: SSC */
18 #define SC_PLLCTRL_SSC_DK_MASK		GENMASK(14, 0)
19 #define SC_PLLCTRL_SSC_EN		BIT(31)
20 #define SC_PLLCTRL2_NRSTDS		BIT(28)
21 #define SC_PLLCTRL2_SSC_JK_MASK		GENMASK(26, 0)
22 #define SC_PLLCTRL3_REGI_MASK		GENMASK(19, 16)
23 
24 /* PLL type: VPLL27 */
25 #define SC_VPLL27CTRL_WP		BIT(0)
26 #define SC_VPLL27CTRL3_K_LD		BIT(28)
27 
28 /* PLL type: DSPLL */
29 #define SC_DSPLLCTRL2_K_LD		BIT(28)
30 
uniphier_ld20_sscpll_init(unsigned long reg_base,unsigned int freq,unsigned int ssc_rate,unsigned int divn)31 int uniphier_ld20_sscpll_init(unsigned long reg_base, unsigned int freq,
32 			      unsigned int ssc_rate, unsigned int divn)
33 {
34 	void __iomem *base;
35 	u32 tmp;
36 
37 	base = ioremap(reg_base, SZ_16);
38 	if (!base)
39 		return -ENOMEM;
40 
41 	if (freq != UNIPHIER_PLL_FREQ_DEFAULT) {
42 		tmp = readl(base);	/* SSCPLLCTRL */
43 		tmp &= ~SC_PLLCTRL_SSC_DK_MASK;
44 		tmp |= FIELD_PREP(SC_PLLCTRL_SSC_DK_MASK,
45 				  DIV_ROUND_CLOSEST(487UL * freq * ssc_rate,
46 						    divn * 512));
47 		writel(tmp, base);
48 
49 		tmp = readl(base + 4);
50 		tmp &= ~SC_PLLCTRL2_SSC_JK_MASK;
51 		tmp |= FIELD_PREP(SC_PLLCTRL2_SSC_JK_MASK,
52 				  DIV_ROUND_CLOSEST(21431887UL * freq,
53 						    divn * 512));
54 		writel(tmp, base + 4);
55 
56 		udelay(50);
57 	}
58 
59 	tmp = readl(base + 4);		/* SSCPLLCTRL2 */
60 	tmp |= SC_PLLCTRL2_NRSTDS;
61 	writel(tmp, base + 4);
62 
63 	iounmap(base);
64 
65 	return 0;
66 }
67 
uniphier_ld20_sscpll_ssc_en(unsigned long reg_base)68 int uniphier_ld20_sscpll_ssc_en(unsigned long reg_base)
69 {
70 	void __iomem *base;
71 	u32 tmp;
72 
73 	base = ioremap(reg_base, SZ_16);
74 	if (!base)
75 		return -ENOMEM;
76 
77 	tmp = readl(base);	/* SSCPLLCTRL */
78 	tmp |= SC_PLLCTRL_SSC_EN;
79 	writel(tmp, base);
80 
81 	iounmap(base);
82 
83 	return 0;
84 }
85 
uniphier_ld20_sscpll_set_regi(unsigned long reg_base,unsigned regi)86 int uniphier_ld20_sscpll_set_regi(unsigned long reg_base, unsigned regi)
87 {
88 	void __iomem *base;
89 	u32 tmp;
90 
91 	base = ioremap(reg_base, SZ_16);
92 	if (!base)
93 		return -ENOMEM;
94 
95 	tmp = readl(base + 8);	/* SSCPLLCTRL3 */
96 	tmp &= ~SC_PLLCTRL3_REGI_MASK;
97 	tmp |= FIELD_PREP(SC_PLLCTRL3_REGI_MASK, regi);
98 	writel(tmp, base + 8);
99 
100 	iounmap(base);
101 
102 	return 0;
103 }
104 
uniphier_ld20_vpll27_init(unsigned long reg_base)105 int uniphier_ld20_vpll27_init(unsigned long reg_base)
106 {
107 	void __iomem *base;
108 	u32 tmp;
109 
110 	base = ioremap(reg_base, SZ_16);
111 	if (!base)
112 		return -ENOMEM;
113 
114 	tmp = readl(base);		/* VPLL27CTRL */
115 	tmp |= SC_VPLL27CTRL_WP;	/* write protect off */
116 	writel(tmp, base);
117 
118 	tmp = readl(base + 8);		/* VPLL27CTRL3 */
119 	tmp |= SC_VPLL27CTRL3_K_LD;
120 	writel(tmp, base + 8);
121 
122 	tmp = readl(base);		/* VPLL27CTRL */
123 	tmp &= ~SC_VPLL27CTRL_WP;	/* write protect on */
124 	writel(tmp, base);
125 
126 	iounmap(base);
127 
128 	return 0;
129 }
130 
uniphier_ld20_dspll_init(unsigned long reg_base)131 int uniphier_ld20_dspll_init(unsigned long reg_base)
132 {
133 	void __iomem *base;
134 	u32 tmp;
135 
136 	base = ioremap(reg_base, SZ_16);
137 	if (!base)
138 		return -ENOMEM;
139 
140 	tmp = readl(base + 4);		/* DSPLLCTRL2 */
141 	tmp |= SC_DSPLLCTRL2_K_LD;
142 	writel(tmp, base + 4);
143 
144 	iounmap(base);
145 
146 	return 0;
147 }
148