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