1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2019 Christoph Hellwig. 4 * Copyright (c) 2019 Western Digital Corporation or its affiliates. 5 */ 6 #include <linux/types.h> 7 #include <linux/io.h> 8 #include <linux/of.h> 9 #include <linux/platform_device.h> 10 #include <linux/clk-provider.h> 11 #include <linux/clkdev.h> 12 #include <linux/bitfield.h> 13 #include <asm/soc.h> 14 15 #include <soc/canaan/k210-sysctl.h> 16 17 #define K210_SYSCTL_CLK0_FREQ 26000000UL 18 19 /* Registers base address */ 20 #define K210_SYSCTL_SYSCTL_BASE_ADDR 0x50440000ULL 21 22 /* Register bits */ 23 /* K210_SYSCTL_PLL1: clkr: 4bits, clkf1: 6bits, clkod: 4bits, bwadj: 4bits */ 24 #define PLL_RESET (1 << 20) 25 #define PLL_PWR (1 << 21) 26 #define PLL_BYPASS (1 << 23) 27 #define PLL_OUT_EN (1 << 25) 28 /* K210_SYSCTL_PLL_LOCK */ 29 #define PLL1_LOCK1 (1 << 8) 30 #define PLL1_LOCK2 (1 << 9) 31 #define PLL1_SLIP_CLEAR (1 << 10) 32 /* K210_SYSCTL_SEL0 */ 33 #define CLKSEL_ACLK (1 << 0) 34 /* K210_SYSCTL_CLKEN_CENT */ 35 #define CLKEN_CPU (1 << 0) 36 #define CLKEN_SRAM0 (1 << 1) 37 #define CLKEN_SRAM1 (1 << 2) 38 /* K210_SYSCTL_EN_PERI */ 39 #define CLKEN_ROM (1 << 0) 40 #define CLKEN_TIMER0 (1 << 21) 41 #define CLKEN_RTC (1 << 29) 42 43 struct k210_sysctl { 44 void __iomem *regs; 45 struct clk_hw hw; 46 }; 47 48 static void k210_set_bits(u32 val, void __iomem *reg) 49 { 50 writel(readl(reg) | val, reg); 51 } 52 53 static void k210_clear_bits(u32 val, void __iomem *reg) 54 { 55 writel(readl(reg) & ~val, reg); 56 } 57 58 static void k210_pll1_enable(void __iomem *regs) 59 { 60 u32 val; 61 62 val = readl(regs + K210_SYSCTL_PLL1); 63 val &= ~GENMASK(19, 0); /* clkr1 = 0 */ 64 val |= FIELD_PREP(GENMASK(9, 4), 0x3B); /* clkf1 = 59 */ 65 val |= FIELD_PREP(GENMASK(13, 10), 0x3); /* clkod1 = 3 */ 66 val |= FIELD_PREP(GENMASK(19, 14), 0x3B); /* bwadj1 = 59 */ 67 writel(val, regs + K210_SYSCTL_PLL1); 68 69 k210_clear_bits(PLL_BYPASS, regs + K210_SYSCTL_PLL1); 70 k210_set_bits(PLL_PWR, regs + K210_SYSCTL_PLL1); 71 72 /* 73 * Reset the pll. The magic NOPs come from the Kendryte reference SDK. 74 */ 75 k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 76 k210_set_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 77 nop(); 78 nop(); 79 k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 80 81 for (;;) { 82 val = readl(regs + K210_SYSCTL_PLL_LOCK); 83 if (val & PLL1_LOCK2) 84 break; 85 writel(val | PLL1_SLIP_CLEAR, regs + K210_SYSCTL_PLL_LOCK); 86 } 87 88 k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL1); 89 } 90 91 static unsigned long k210_sysctl_clk_recalc_rate(struct clk_hw *hw, 92 unsigned long parent_rate) 93 { 94 struct k210_sysctl *s = container_of(hw, struct k210_sysctl, hw); 95 u32 clksel0, pll0; 96 u64 pll0_freq, clkr0, clkf0, clkod0; 97 98 /* 99 * If the clock selector is not set, use the base frequency. 100 * Otherwise, use PLL0 frequency with a frequency divisor. 101 */ 102 clksel0 = readl(s->regs + K210_SYSCTL_SEL0); 103 if (!(clksel0 & CLKSEL_ACLK)) 104 return K210_SYSCTL_CLK0_FREQ; 105 106 /* 107 * Get PLL0 frequency: 108 * freq = base frequency * clkf0 / (clkr0 * clkod0) 109 */ 110 pll0 = readl(s->regs + K210_SYSCTL_PLL0); 111 clkr0 = 1 + FIELD_GET(GENMASK(3, 0), pll0); 112 clkf0 = 1 + FIELD_GET(GENMASK(9, 4), pll0); 113 clkod0 = 1 + FIELD_GET(GENMASK(13, 10), pll0); 114 pll0_freq = clkf0 * K210_SYSCTL_CLK0_FREQ / (clkr0 * clkod0); 115 116 /* Get the frequency divisor from the clock selector */ 117 return pll0_freq / (2ULL << FIELD_GET(0x00000006, clksel0)); 118 } 119 120 static const struct clk_ops k210_sysctl_clk_ops = { 121 .recalc_rate = k210_sysctl_clk_recalc_rate, 122 }; 123 124 static const struct clk_init_data k210_clk_init_data = { 125 .name = "k210-sysctl-pll1", 126 .ops = &k210_sysctl_clk_ops, 127 }; 128 129 static int k210_sysctl_probe(struct platform_device *pdev) 130 { 131 struct k210_sysctl *s; 132 int error; 133 134 pr_info("Kendryte K210 SoC sysctl\n"); 135 136 s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); 137 if (!s) 138 return -ENOMEM; 139 140 s->regs = devm_ioremap_resource(&pdev->dev, 141 platform_get_resource(pdev, IORESOURCE_MEM, 0)); 142 if (IS_ERR(s->regs)) 143 return PTR_ERR(s->regs); 144 145 s->hw.init = &k210_clk_init_data; 146 error = devm_clk_hw_register(&pdev->dev, &s->hw); 147 if (error) { 148 dev_err(&pdev->dev, "failed to register clk"); 149 return error; 150 } 151 152 error = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, 153 &s->hw); 154 if (error) { 155 dev_err(&pdev->dev, "adding clk provider failed\n"); 156 return error; 157 } 158 159 return 0; 160 } 161 162 static const struct of_device_id k210_sysctl_of_match[] = { 163 { .compatible = "kendryte,k210-sysctl", }, 164 {} 165 }; 166 167 static struct platform_driver k210_sysctl_driver = { 168 .driver = { 169 .name = "k210-sysctl", 170 .of_match_table = k210_sysctl_of_match, 171 }, 172 .probe = k210_sysctl_probe, 173 }; 174 175 static int __init k210_sysctl_init(void) 176 { 177 return platform_driver_register(&k210_sysctl_driver); 178 } 179 core_initcall(k210_sysctl_init); 180 181 /* 182 * This needs to be called very early during initialization, given that 183 * PLL1 needs to be enabled to be able to use all SRAM. 184 */ 185 static void __init k210_soc_early_init(const void *fdt) 186 { 187 void __iomem *regs; 188 189 regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000); 190 if (!regs) 191 panic("K210 sysctl ioremap"); 192 193 /* Enable PLL1 to make the KPU SRAM useable */ 194 k210_pll1_enable(regs); 195 196 k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL0); 197 198 k210_set_bits(CLKEN_CPU | CLKEN_SRAM0 | CLKEN_SRAM1, 199 regs + K210_SYSCTL_EN_CENT); 200 k210_set_bits(CLKEN_ROM | CLKEN_TIMER0 | CLKEN_RTC, 201 regs + K210_SYSCTL_EN_PERI); 202 203 k210_set_bits(CLKSEL_ACLK, regs + K210_SYSCTL_SEL0); 204 205 iounmap(regs); 206 } 207 SOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init); 208