aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2017-04-05 02:37:43 -0400
committerMaxime Ripard <maxime.ripard@free-electrons.com>2017-04-05 03:02:56 -0400
commit25eb035c3f22bde1eff43fe5c59b207d54a3d520 (patch)
tree3dd6609bdbcb9d740d5d2e65a7ebf467c3400972
parentcf719012b23278c65d0bca4975a7ea46e5bb75be (diff)
clk: sunxi-ng: a80: Remodel CPU cluster PLLs as N-type multiplier clocks
The CPU cluster PLLs on the A80 are NP clocks that are atypical in two ways: - The P factor is 1 bit wide, and translates to a /1 or /4 divider. - The P factor should only be used for output frequencies lower than 288 MHz. The N factor has a lower limit of 12, which likely contributed to this extra divider. According to the user manual, the clocks can only go as low as 200 MHz. The vendor BSP kernel does not even define operating points below 360 MHz for these clocks. The lower end for cpufreq in the vendor kernel is even higher. The mainline Linux kernel doesn't support cpufreq for the A80 at the moment. This means the lower frequencies are untested, and will likely remain unused. The new sunxi-ng style clocks don't support the quirks listed above. Instead of trying to work the quirks in for something of little usage, we re-model the clocks into N-type multipler clocks, with P fixed at 1. At probe time we check if P is set to 4, and fix it up if needed. This is highly unlikely though. Fixes: b8eb71dcdd08 ("clk: sunxi-ng: Add A80 CCU") Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun9i-a80.c70
1 files changed, 52 insertions, 18 deletions
diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
index e13e313ce4f5..51f6d495de5b 100644
--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
@@ -29,41 +29,41 @@
29 29
30#define CCU_SUN9I_LOCK_REG 0x09c 30#define CCU_SUN9I_LOCK_REG 0x09c
31 31
32static struct clk_div_table pll_cpux_p_div_table[] = {
33 { .val = 0, .div = 1 },
34 { .val = 1, .div = 4 },
35 { /* Sentinel */ },
36};
37
38/* 32/*
39 * The CPU PLLs are actually NP clocks, but P is /1 or /4, so here we 33 * The CPU PLLs are actually NP clocks, with P being /1 or /4. However
40 * use the NM clocks with a divider table for M. 34 * P should only be used for output frequencies lower than 228 MHz.
35 * Neither mainline Linux, U-boot, nor the vendor BSPs use these.
36 *
37 * For now we can just model it as a multiplier clock, and force P to /1.
41 */ 38 */
42static struct ccu_nm pll_c0cpux_clk = { 39#define SUN9I_A80_PLL_C0CPUX_REG 0x000
40#define SUN9I_A80_PLL_C1CPUX_REG 0x004
41
42static struct ccu_mult pll_c0cpux_clk = {
43 .enable = BIT(31), 43 .enable = BIT(31),
44 .lock = BIT(0), 44 .lock = BIT(0),
45 .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), 45 .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
46 .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table),
47 .common = { 46 .common = {
48 .reg = 0x000, 47 .reg = SUN9I_A80_PLL_C0CPUX_REG,
49 .lock_reg = CCU_SUN9I_LOCK_REG, 48 .lock_reg = CCU_SUN9I_LOCK_REG,
50 .features = CCU_FEATURE_LOCK_REG, 49 .features = CCU_FEATURE_LOCK_REG,
51 .hw.init = CLK_HW_INIT("pll-c0cpux", "osc24M", 50 .hw.init = CLK_HW_INIT("pll-c0cpux", "osc24M",
52 &ccu_nm_ops, CLK_SET_RATE_UNGATE), 51 &ccu_mult_ops,
52 CLK_SET_RATE_UNGATE),
53 }, 53 },
54}; 54};
55 55
56static struct ccu_nm pll_c1cpux_clk = { 56static struct ccu_mult pll_c1cpux_clk = {
57 .enable = BIT(31), 57 .enable = BIT(31),
58 .lock = BIT(1), 58 .lock = BIT(1),
59 .n = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0), 59 .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(8, 8, 0, 12, 0),
60 .m = _SUNXI_CCU_DIV_TABLE(16, 1, pll_cpux_p_div_table),
61 .common = { 60 .common = {
62 .reg = 0x004, 61 .reg = SUN9I_A80_PLL_C1CPUX_REG,
63 .lock_reg = CCU_SUN9I_LOCK_REG, 62 .lock_reg = CCU_SUN9I_LOCK_REG,
64 .features = CCU_FEATURE_LOCK_REG, 63 .features = CCU_FEATURE_LOCK_REG,
65 .hw.init = CLK_HW_INIT("pll-c1cpux", "osc24M", 64 .hw.init = CLK_HW_INIT("pll-c1cpux", "osc24M",
66 &ccu_nm_ops, CLK_SET_RATE_UNGATE), 65 &ccu_mult_ops,
66 CLK_SET_RATE_UNGATE),
67 }, 67 },
68}; 68};
69 69
@@ -1189,6 +1189,36 @@ static const struct sunxi_ccu_desc sun9i_a80_ccu_desc = {
1189 .num_resets = ARRAY_SIZE(sun9i_a80_ccu_resets), 1189 .num_resets = ARRAY_SIZE(sun9i_a80_ccu_resets),
1190}; 1190};
1191 1191
1192#define SUN9I_A80_PLL_P_SHIFT 16
1193#define SUN9I_A80_PLL_N_SHIFT 8
1194#define SUN9I_A80_PLL_N_WIDTH 8
1195
1196static void sun9i_a80_cpu_pll_fixup(void __iomem *reg)
1197{
1198 u32 val = readl(reg);
1199
1200 /* bail out if P divider is not used */
1201 if (!(val & BIT(SUN9I_A80_PLL_P_SHIFT)))
1202 return;
1203
1204 /*
1205 * If P is used, output should be less than 288 MHz. When we
1206 * set P to 1, we should also decrease the multiplier so the
1207 * output doesn't go out of range, but not too much such that
1208 * the multiplier stays above 12, the minimal operation value.
1209 *
1210 * To keep it simple, set the multiplier to 17, the reset value.
1211 */
1212 val &= ~GENMASK(SUN9I_A80_PLL_N_SHIFT + SUN9I_A80_PLL_N_WIDTH - 1,
1213 SUN9I_A80_PLL_N_SHIFT);
1214 val |= 17 << SUN9I_A80_PLL_N_SHIFT;
1215
1216 /* And clear P */
1217 val &= ~BIT(SUN9I_A80_PLL_P_SHIFT);
1218
1219 writel(val, reg);
1220}
1221
1192static int sun9i_a80_ccu_probe(struct platform_device *pdev) 1222static int sun9i_a80_ccu_probe(struct platform_device *pdev)
1193{ 1223{
1194 struct resource *res; 1224 struct resource *res;
@@ -1205,6 +1235,10 @@ static int sun9i_a80_ccu_probe(struct platform_device *pdev)
1205 val &= (BIT(16) & BIT(18)); 1235 val &= (BIT(16) & BIT(18));
1206 writel(val, reg + SUN9I_A80_PLL_AUDIO_REG); 1236 writel(val, reg + SUN9I_A80_PLL_AUDIO_REG);
1207 1237
1238 /* Enforce P = 1 for both CPU cluster PLLs */
1239 sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C0CPUX_REG);
1240 sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C1CPUX_REG);
1241
1208 return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc); 1242 return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc);
1209} 1243}
1210 1244