18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Christoph Hellwig. 48c2ecf20Sopenharmony_ci * Copyright (c) 2019 Western Digital Corporation or its affiliates. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 118c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 128c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 138c2ecf20Sopenharmony_ci#include <asm/soc.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLK0_FREQ 26000000UL 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Registers base address */ 188c2ecf20Sopenharmony_ci#define K210_SYSCTL_SYSCTL_BASE_ADDR 0x50440000ULL 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Registers */ 218c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL0 0x08 228c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL1 0x0c 238c2ecf20Sopenharmony_ci/* clkr: 4bits, clkf1: 6bits, clkod: 4bits, bwadj: 4bits */ 248c2ecf20Sopenharmony_ci#define PLL_RESET (1 << 20) 258c2ecf20Sopenharmony_ci#define PLL_PWR (1 << 21) 268c2ecf20Sopenharmony_ci#define PLL_INTFB (1 << 22) 278c2ecf20Sopenharmony_ci#define PLL_BYPASS (1 << 23) 288c2ecf20Sopenharmony_ci#define PLL_TEST (1 << 24) 298c2ecf20Sopenharmony_ci#define PLL_OUT_EN (1 << 25) 308c2ecf20Sopenharmony_ci#define PLL_TEST_EN (1 << 26) 318c2ecf20Sopenharmony_ci#define K210_SYSCTL_PLL_LOCK 0x18 328c2ecf20Sopenharmony_ci#define PLL0_LOCK1 (1 << 0) 338c2ecf20Sopenharmony_ci#define PLL0_LOCK2 (1 << 1) 348c2ecf20Sopenharmony_ci#define PLL0_SLIP_CLEAR (1 << 2) 358c2ecf20Sopenharmony_ci#define PLL0_TEST_CLK_OUT (1 << 3) 368c2ecf20Sopenharmony_ci#define PLL1_LOCK1 (1 << 8) 378c2ecf20Sopenharmony_ci#define PLL1_LOCK2 (1 << 9) 388c2ecf20Sopenharmony_ci#define PLL1_SLIP_CLEAR (1 << 10) 398c2ecf20Sopenharmony_ci#define PLL1_TEST_CLK_OUT (1 << 11) 408c2ecf20Sopenharmony_ci#define PLL2_LOCK1 (1 << 16) 418c2ecf20Sopenharmony_ci#define PLL2_LOCK2 (1 << 16) 428c2ecf20Sopenharmony_ci#define PLL2_SLIP_CLEAR (1 << 18) 438c2ecf20Sopenharmony_ci#define PLL2_TEST_CLK_OUT (1 << 19) 448c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKSEL0 0x20 458c2ecf20Sopenharmony_ci#define CLKSEL_ACLK (1 << 0) 468c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKEN_CENT 0x28 478c2ecf20Sopenharmony_ci#define CLKEN_CPU (1 << 0) 488c2ecf20Sopenharmony_ci#define CLKEN_SRAM0 (1 << 1) 498c2ecf20Sopenharmony_ci#define CLKEN_SRAM1 (1 << 2) 508c2ecf20Sopenharmony_ci#define CLKEN_APB0 (1 << 3) 518c2ecf20Sopenharmony_ci#define CLKEN_APB1 (1 << 4) 528c2ecf20Sopenharmony_ci#define CLKEN_APB2 (1 << 5) 538c2ecf20Sopenharmony_ci#define K210_SYSCTL_CLKEN_PERI 0x2c 548c2ecf20Sopenharmony_ci#define CLKEN_ROM (1 << 0) 558c2ecf20Sopenharmony_ci#define CLKEN_DMA (1 << 1) 568c2ecf20Sopenharmony_ci#define CLKEN_AI (1 << 2) 578c2ecf20Sopenharmony_ci#define CLKEN_DVP (1 << 3) 588c2ecf20Sopenharmony_ci#define CLKEN_FFT (1 << 4) 598c2ecf20Sopenharmony_ci#define CLKEN_GPIO (1 << 5) 608c2ecf20Sopenharmony_ci#define CLKEN_SPI0 (1 << 6) 618c2ecf20Sopenharmony_ci#define CLKEN_SPI1 (1 << 7) 628c2ecf20Sopenharmony_ci#define CLKEN_SPI2 (1 << 8) 638c2ecf20Sopenharmony_ci#define CLKEN_SPI3 (1 << 9) 648c2ecf20Sopenharmony_ci#define CLKEN_I2S0 (1 << 10) 658c2ecf20Sopenharmony_ci#define CLKEN_I2S1 (1 << 11) 668c2ecf20Sopenharmony_ci#define CLKEN_I2S2 (1 << 12) 678c2ecf20Sopenharmony_ci#define CLKEN_I2C0 (1 << 13) 688c2ecf20Sopenharmony_ci#define CLKEN_I2C1 (1 << 14) 698c2ecf20Sopenharmony_ci#define CLKEN_I2C2 (1 << 15) 708c2ecf20Sopenharmony_ci#define CLKEN_UART1 (1 << 16) 718c2ecf20Sopenharmony_ci#define CLKEN_UART2 (1 << 17) 728c2ecf20Sopenharmony_ci#define CLKEN_UART3 (1 << 18) 738c2ecf20Sopenharmony_ci#define CLKEN_AES (1 << 19) 748c2ecf20Sopenharmony_ci#define CLKEN_FPIO (1 << 20) 758c2ecf20Sopenharmony_ci#define CLKEN_TIMER0 (1 << 21) 768c2ecf20Sopenharmony_ci#define CLKEN_TIMER1 (1 << 22) 778c2ecf20Sopenharmony_ci#define CLKEN_TIMER2 (1 << 23) 788c2ecf20Sopenharmony_ci#define CLKEN_WDT0 (1 << 24) 798c2ecf20Sopenharmony_ci#define CLKEN_WDT1 (1 << 25) 808c2ecf20Sopenharmony_ci#define CLKEN_SHA (1 << 26) 818c2ecf20Sopenharmony_ci#define CLKEN_OTP (1 << 27) 828c2ecf20Sopenharmony_ci#define CLKEN_RTC (1 << 29) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct k210_sysctl { 858c2ecf20Sopenharmony_ci void __iomem *regs; 868c2ecf20Sopenharmony_ci struct clk_hw hw; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void k210_set_bits(u32 val, void __iomem *reg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci writel(readl(reg) | val, reg); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void k210_clear_bits(u32 val, void __iomem *reg) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci writel(readl(reg) & ~val, reg); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void k210_pll1_enable(void __iomem *regs) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci u32 val; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci val = readl(regs + K210_SYSCTL_PLL1); 1048c2ecf20Sopenharmony_ci val &= ~GENMASK(19, 0); /* clkr1 = 0 */ 1058c2ecf20Sopenharmony_ci val |= FIELD_PREP(GENMASK(9, 4), 0x3B); /* clkf1 = 59 */ 1068c2ecf20Sopenharmony_ci val |= FIELD_PREP(GENMASK(13, 10), 0x3); /* clkod1 = 3 */ 1078c2ecf20Sopenharmony_ci val |= FIELD_PREP(GENMASK(19, 14), 0x3B); /* bwadj1 = 59 */ 1088c2ecf20Sopenharmony_ci writel(val, regs + K210_SYSCTL_PLL1); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci k210_clear_bits(PLL_BYPASS, regs + K210_SYSCTL_PLL1); 1118c2ecf20Sopenharmony_ci k210_set_bits(PLL_PWR, regs + K210_SYSCTL_PLL1); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * Reset the pll. The magic NOPs come from the Kendryte reference SDK. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 1178c2ecf20Sopenharmony_ci k210_set_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 1188c2ecf20Sopenharmony_ci nop(); 1198c2ecf20Sopenharmony_ci nop(); 1208c2ecf20Sopenharmony_ci k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci for (;;) { 1238c2ecf20Sopenharmony_ci val = readl(regs + K210_SYSCTL_PLL_LOCK); 1248c2ecf20Sopenharmony_ci if (val & PLL1_LOCK2) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci writel(val | PLL1_SLIP_CLEAR, regs + K210_SYSCTL_PLL_LOCK); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL1); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic unsigned long k210_sysctl_clk_recalc_rate(struct clk_hw *hw, 1338c2ecf20Sopenharmony_ci unsigned long parent_rate) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct k210_sysctl *s = container_of(hw, struct k210_sysctl, hw); 1368c2ecf20Sopenharmony_ci u32 clksel0, pll0; 1378c2ecf20Sopenharmony_ci u64 pll0_freq, clkr0, clkf0, clkod0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* 1408c2ecf20Sopenharmony_ci * If the clock selector is not set, use the base frequency. 1418c2ecf20Sopenharmony_ci * Otherwise, use PLL0 frequency with a frequency divisor. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci clksel0 = readl(s->regs + K210_SYSCTL_CLKSEL0); 1448c2ecf20Sopenharmony_ci if (!(clksel0 & CLKSEL_ACLK)) 1458c2ecf20Sopenharmony_ci return K210_SYSCTL_CLK0_FREQ; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Get PLL0 frequency: 1498c2ecf20Sopenharmony_ci * freq = base frequency * clkf0 / (clkr0 * clkod0) 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci pll0 = readl(s->regs + K210_SYSCTL_PLL0); 1528c2ecf20Sopenharmony_ci clkr0 = 1 + FIELD_GET(GENMASK(3, 0), pll0); 1538c2ecf20Sopenharmony_ci clkf0 = 1 + FIELD_GET(GENMASK(9, 4), pll0); 1548c2ecf20Sopenharmony_ci clkod0 = 1 + FIELD_GET(GENMASK(13, 10), pll0); 1558c2ecf20Sopenharmony_ci pll0_freq = clkf0 * K210_SYSCTL_CLK0_FREQ / (clkr0 * clkod0); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Get the frequency divisor from the clock selector */ 1588c2ecf20Sopenharmony_ci return pll0_freq / (2ULL << FIELD_GET(0x00000006, clksel0)); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct clk_ops k210_sysctl_clk_ops = { 1628c2ecf20Sopenharmony_ci .recalc_rate = k210_sysctl_clk_recalc_rate, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const struct clk_init_data k210_clk_init_data = { 1668c2ecf20Sopenharmony_ci .name = "k210-sysctl-pll1", 1678c2ecf20Sopenharmony_ci .ops = &k210_sysctl_clk_ops, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int k210_sysctl_probe(struct platform_device *pdev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct k210_sysctl *s; 1738c2ecf20Sopenharmony_ci int error; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pr_info("Kendryte K210 SoC sysctl\n"); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); 1788c2ecf20Sopenharmony_ci if (!s) 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci s->regs = devm_ioremap_resource(&pdev->dev, 1828c2ecf20Sopenharmony_ci platform_get_resource(pdev, IORESOURCE_MEM, 0)); 1838c2ecf20Sopenharmony_ci if (IS_ERR(s->regs)) 1848c2ecf20Sopenharmony_ci return PTR_ERR(s->regs); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci s->hw.init = &k210_clk_init_data; 1878c2ecf20Sopenharmony_ci error = devm_clk_hw_register(&pdev->dev, &s->hw); 1888c2ecf20Sopenharmony_ci if (error) { 1898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register clk"); 1908c2ecf20Sopenharmony_ci return error; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci error = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, 1948c2ecf20Sopenharmony_ci &s->hw); 1958c2ecf20Sopenharmony_ci if (error) { 1968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "adding clk provider failed\n"); 1978c2ecf20Sopenharmony_ci return error; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic const struct of_device_id k210_sysctl_of_match[] = { 2048c2ecf20Sopenharmony_ci { .compatible = "kendryte,k210-sysctl", }, 2058c2ecf20Sopenharmony_ci {} 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct platform_driver k210_sysctl_driver = { 2098c2ecf20Sopenharmony_ci .driver = { 2108c2ecf20Sopenharmony_ci .name = "k210-sysctl", 2118c2ecf20Sopenharmony_ci .of_match_table = k210_sysctl_of_match, 2128c2ecf20Sopenharmony_ci }, 2138c2ecf20Sopenharmony_ci .probe = k210_sysctl_probe, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int __init k210_sysctl_init(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci return platform_driver_register(&k210_sysctl_driver); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_cicore_initcall(k210_sysctl_init); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* 2238c2ecf20Sopenharmony_ci * This needs to be called very early during initialization, given that 2248c2ecf20Sopenharmony_ci * PLL1 needs to be enabled to be able to use all SRAM. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic void __init k210_soc_early_init(const void *fdt) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci void __iomem *regs; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000); 2318c2ecf20Sopenharmony_ci if (!regs) 2328c2ecf20Sopenharmony_ci panic("K210 sysctl ioremap"); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Enable PLL1 to make the KPU SRAM useable */ 2358c2ecf20Sopenharmony_ci k210_pll1_enable(regs); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci k210_set_bits(CLKEN_CPU | CLKEN_SRAM0 | CLKEN_SRAM1, 2408c2ecf20Sopenharmony_ci regs + K210_SYSCTL_CLKEN_CENT); 2418c2ecf20Sopenharmony_ci k210_set_bits(CLKEN_ROM | CLKEN_TIMER0 | CLKEN_RTC, 2428c2ecf20Sopenharmony_ci regs + K210_SYSCTL_CLKEN_PERI); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci k210_set_bits(CLKSEL_ACLK, regs + K210_SYSCTL_CLKSEL0); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci iounmap(regs); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciSOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_KENDRYTE_K210_DTB_BUILTIN 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Generic entry for the default k210.dtb embedded DTB for boards with: 2538c2ecf20Sopenharmony_ci * - Vendor ID: 0x4B5 2548c2ecf20Sopenharmony_ci * - Arch ID: 0xE59889E6A5A04149 (= "Canaan AI" in UTF-8 encoded Chinese) 2558c2ecf20Sopenharmony_ci * - Impl ID: 0x4D41495832303030 (= "MAIX2000") 2568c2ecf20Sopenharmony_ci * These values are reported by the SiPEED MAXDUINO, SiPEED MAIX GO and 2578c2ecf20Sopenharmony_ci * SiPEED Dan dock boards. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ciSOC_BUILTIN_DTB_DECLARE(k210, 0x4B5, 0xE59889E6A5A04149, 0x4D41495832303030); 2608c2ecf20Sopenharmony_ci#endif 261