]> git.ipfire.org Git - people/ms/linux.git/commitdiff
riscv: Add Kendryte K210 SoC support
authorChristoph Hellwig <hch@lst.de>
Mon, 16 Mar 2020 00:47:39 +0000 (09:47 +0900)
committerPalmer Dabbelt <palmerdabbelt@google.com>
Fri, 3 Apr 2020 17:49:52 +0000 (10:49 -0700)
Add support for the Kendryte K210 RISC-V SoC. For now, this support
only provides a simple sysctl driver allowing to setup the CPU and
uart clock. This support is enabled through the new Kconfig option
SOC_KENDRYTE and defines the config option CONFIG_K210_SYSCTL
to enable the K210 SoC sysctl driver compilation.

The sysctl driver also registers an early SoC initialization function
allowing enabling the general purpose use of the 2MB of SRAM normally
reserved for the SoC AI engine. This initialization function is
automatically called before the dt early initialization using the flat
dt root node compatible property matching the value "kendryte,k210".

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
[Palmer: Add missing endmenu in Kconfig.socs]
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
arch/riscv/Kconfig.socs
drivers/soc/Kconfig
drivers/soc/Makefile
drivers/soc/kendryte/Kconfig [new file with mode: 0644]
drivers/soc/kendryte/Makefile [new file with mode: 0644]
drivers/soc/kendryte/k210-sysctl.c [new file with mode: 0644]

index d325b67d00dfcf70c4f3cf89e68e3964eec62a06..69071578e18162637d2b2a0cee8a6708dfcec71b 100644 (file)
@@ -10,4 +10,34 @@ config SOC_SIFIVE
        help
          This enables support for SiFive SoC platform hardware.
 
+config SOC_VIRT
+       bool "QEMU Virt Machine"
+       select VIRTIO_PCI
+       select VIRTIO_BALLOON
+       select VIRTIO_MMIO
+       select VIRTIO_CONSOLE
+       select VIRTIO_NET
+       select NET_9P_VIRTIO
+       select VIRTIO_BLK
+       select SCSI_VIRTIO
+       select DRM_VIRTIO_GPU
+       select HW_RANDOM_VIRTIO
+       select RPMSG_CHAR
+       select RPMSG_VIRTIO
+       select CRYPTO_DEV_VIRTIO
+       select VIRTIO_INPUT
+       select POWER_RESET_SYSCON
+       select POWER_RESET_SYSCON_POWEROFF
+       select GOLDFISH
+       select RTC_DRV_GOLDFISH
+       select SIFIVE_PLIC
+       help
+         This enables support for QEMU Virt Machine.
+
+config SOC_KENDRYTE
+       bool "Kendryte K210 SoC"
+       depends on !MMU
+       help
+         This enables support for Kendryte K210 SoC platform hardware.
+
 endmenu
index 1778f8c62861b4eb4fd7056168fbb1e4e68e2c4f..425ab6f7e375f72263ae3138e8478d659646085e 100644 (file)
@@ -22,5 +22,6 @@ source "drivers/soc/ux500/Kconfig"
 source "drivers/soc/versatile/Kconfig"
 source "drivers/soc/xilinx/Kconfig"
 source "drivers/soc/zte/Kconfig"
+source "drivers/soc/kendryte/Kconfig"
 
 endmenu
index 8b49d782a1ab7cd9fcb1e8ed121ee4b712f82081..af58063bb989f4c079cc8d98116b02eca0e91ac2 100644 (file)
@@ -28,3 +28,4 @@ obj-$(CONFIG_ARCH_U8500)      += ux500/
 obj-$(CONFIG_PLAT_VERSATILE)   += versatile/
 obj-y                          += xilinx/
 obj-$(CONFIG_ARCH_ZX)          += zte/
+obj-$(CONFIG_SOC_KENDRYTE)     += kendryte/
diff --git a/drivers/soc/kendryte/Kconfig b/drivers/soc/kendryte/Kconfig
new file mode 100644 (file)
index 0000000..49785b1
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+
+if SOC_KENDRYTE
+
+config K210_SYSCTL
+       bool "Kendryte K210 system controller"
+       default y
+       depends on RISCV
+       help
+         Enables controlling the K210 various clocks and to enable
+         general purpose use of the extra 2MB of SRAM normally
+         reserved for the AI engine.
+
+endif
diff --git a/drivers/soc/kendryte/Makefile b/drivers/soc/kendryte/Makefile
new file mode 100644 (file)
index 0000000..002d9ce
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_K210_SYSCTL)      += k210-sysctl.o
diff --git a/drivers/soc/kendryte/k210-sysctl.c b/drivers/soc/kendryte/k210-sysctl.c
new file mode 100644 (file)
index 0000000..4608fbc
--- /dev/null
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Christoph Hellwig.
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ */
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/bitfield.h>
+#include <asm/soc.h>
+
+#define K210_SYSCTL_CLK0_FREQ          26000000UL
+
+/* Registers base address */
+#define K210_SYSCTL_SYSCTL_BASE_ADDR   0x50440000ULL
+
+/* Registers */
+#define K210_SYSCTL_PLL0               0x08
+#define K210_SYSCTL_PLL1               0x0c
+/* clkr: 4bits, clkf1: 6bits, clkod: 4bits, bwadj: 4bits */
+#define   PLL_RESET            (1 << 20)
+#define   PLL_PWR              (1 << 21)
+#define   PLL_INTFB            (1 << 22)
+#define   PLL_BYPASS           (1 << 23)
+#define   PLL_TEST             (1 << 24)
+#define   PLL_OUT_EN           (1 << 25)
+#define   PLL_TEST_EN          (1 << 26)
+#define K210_SYSCTL_PLL_LOCK           0x18
+#define   PLL0_LOCK1           (1 << 0)
+#define   PLL0_LOCK2           (1 << 1)
+#define   PLL0_SLIP_CLEAR      (1 << 2)
+#define   PLL0_TEST_CLK_OUT    (1 << 3)
+#define   PLL1_LOCK1           (1 << 8)
+#define   PLL1_LOCK2           (1 << 9)
+#define   PLL1_SLIP_CLEAR      (1 << 10)
+#define   PLL1_TEST_CLK_OUT    (1 << 11)
+#define   PLL2_LOCK1           (1 << 16)
+#define   PLL2_LOCK2           (1 << 16)
+#define   PLL2_SLIP_CLEAR      (1 << 18)
+#define   PLL2_TEST_CLK_OUT    (1 << 19)
+#define K210_SYSCTL_CLKSEL0    0x20
+#define   CLKSEL_ACLK          (1 << 0)
+#define K210_SYSCTL_CLKEN_CENT         0x28
+#define   CLKEN_CPU            (1 << 0)
+#define   CLKEN_SRAM0          (1 << 1)
+#define   CLKEN_SRAM1          (1 << 2)
+#define   CLKEN_APB0           (1 << 3)
+#define   CLKEN_APB1           (1 << 4)
+#define   CLKEN_APB2           (1 << 5)
+#define K210_SYSCTL_CLKEN_PERI         0x2c
+#define   CLKEN_ROM            (1 << 0)
+#define   CLKEN_DMA            (1 << 1)
+#define   CLKEN_AI             (1 << 2)
+#define   CLKEN_DVP            (1 << 3)
+#define   CLKEN_FFT            (1 << 4)
+#define   CLKEN_GPIO           (1 << 5)
+#define   CLKEN_SPI0           (1 << 6)
+#define   CLKEN_SPI1           (1 << 7)
+#define   CLKEN_SPI2           (1 << 8)
+#define   CLKEN_SPI3           (1 << 9)
+#define   CLKEN_I2S0           (1 << 10)
+#define   CLKEN_I2S1           (1 << 11)
+#define   CLKEN_I2S2           (1 << 12)
+#define   CLKEN_I2C0           (1 << 13)
+#define   CLKEN_I2C1           (1 << 14)
+#define   CLKEN_I2C2           (1 << 15)
+#define   CLKEN_UART1          (1 << 16)
+#define   CLKEN_UART2          (1 << 17)
+#define   CLKEN_UART3          (1 << 18)
+#define   CLKEN_AES            (1 << 19)
+#define   CLKEN_FPIO           (1 << 20)
+#define   CLKEN_TIMER0         (1 << 21)
+#define   CLKEN_TIMER1         (1 << 22)
+#define   CLKEN_TIMER2         (1 << 23)
+#define   CLKEN_WDT0           (1 << 24)
+#define   CLKEN_WDT1           (1 << 25)
+#define   CLKEN_SHA            (1 << 26)
+#define   CLKEN_OTP            (1 << 27)
+#define   CLKEN_RTC            (1 << 29)
+
+struct k210_sysctl {
+       void __iomem            *regs;
+       struct clk_hw           hw;
+};
+
+static void k210_set_bits(u32 val, void __iomem *reg)
+{
+       writel(readl(reg) | val, reg);
+}
+
+static void k210_clear_bits(u32 val, void __iomem *reg)
+{
+       writel(readl(reg) & ~val, reg);
+}
+
+static void k210_pll1_enable(void __iomem *regs)
+{
+       u32 val;
+
+       val = readl(regs + K210_SYSCTL_PLL1);
+       val &= ~GENMASK(19, 0);                         /* clkr1 = 0 */
+       val |= FIELD_PREP(GENMASK(9, 4), 0x3B);         /* clkf1 = 59 */
+       val |= FIELD_PREP(GENMASK(13, 10), 0x3);        /* clkod1 = 3 */
+       val |= FIELD_PREP(GENMASK(19, 14), 0x3B);       /* bwadj1 = 59 */
+       writel(val, regs + K210_SYSCTL_PLL1);
+
+       k210_clear_bits(PLL_BYPASS, regs + K210_SYSCTL_PLL1);
+       k210_set_bits(PLL_PWR, regs + K210_SYSCTL_PLL1);
+
+       /*
+        * Reset the pll. The magic NOPs come from the Kendryte reference SDK.
+        */
+       k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
+       k210_set_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
+       nop();
+       nop();
+       k210_clear_bits(PLL_RESET, regs + K210_SYSCTL_PLL1);
+
+       for (;;) {
+               val = readl(regs + K210_SYSCTL_PLL_LOCK);
+               if (val & PLL1_LOCK2)
+                       break;
+               writel(val | PLL1_SLIP_CLEAR, regs + K210_SYSCTL_PLL_LOCK);
+       }
+
+       k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL1);
+}
+
+static unsigned long k210_sysctl_clk_recalc_rate(struct clk_hw *hw,
+               unsigned long parent_rate)
+{
+       struct k210_sysctl *s = container_of(hw, struct k210_sysctl, hw);
+       u32 clksel0, pll0;
+       u64 pll0_freq, clkr0, clkf0, clkod0;
+
+       /*
+        * If the clock selector is not set, use the base frequency.
+        * Otherwise, use PLL0 frequency with a frequency divisor.
+        */
+       clksel0 = readl(s->regs + K210_SYSCTL_CLKSEL0);
+       if (!(clksel0 & CLKSEL_ACLK))
+               return K210_SYSCTL_CLK0_FREQ;
+
+       /*
+        * Get PLL0 frequency:
+        * freq = base frequency * clkf0 / (clkr0 * clkod0)
+        */
+       pll0 = readl(s->regs + K210_SYSCTL_PLL0);
+       clkr0 = 1 + FIELD_GET(GENMASK(3, 0), pll0);
+       clkf0 = 1 + FIELD_GET(GENMASK(9, 4), pll0);
+       clkod0 = 1 + FIELD_GET(GENMASK(13, 10), pll0);
+       pll0_freq = clkf0 * K210_SYSCTL_CLK0_FREQ / (clkr0 * clkod0);
+
+       /* Get the frequency divisor from the clock selector */
+       return pll0_freq / (2ULL << FIELD_GET(0x00000006, clksel0));
+}
+
+static const struct clk_ops k210_sysctl_clk_ops = {
+       .recalc_rate    = k210_sysctl_clk_recalc_rate,
+};
+
+static const struct clk_init_data k210_clk_init_data = {
+       .name           = "k210-sysctl-pll1",
+       .ops            = &k210_sysctl_clk_ops,
+};
+
+static int k210_sysctl_probe(struct platform_device *pdev)
+{
+       struct k210_sysctl *s;
+       int error;
+
+       pr_info("Kendryte K210 SoC sysctl\n");
+
+       s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+       if (!s)
+               return -ENOMEM;
+
+       s->regs = devm_ioremap_resource(&pdev->dev,
+                       platform_get_resource(pdev, IORESOURCE_MEM, 0));
+       if (IS_ERR(s->regs))
+               return PTR_ERR(s->regs);
+
+       s->hw.init = &k210_clk_init_data;
+       error = devm_clk_hw_register(&pdev->dev, &s->hw);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register clk");
+               return error;
+       }
+
+       error = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
+                                           &s->hw);
+       if (error) {
+               dev_err(&pdev->dev, "adding clk provider failed\n");
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id k210_sysctl_of_match[] = {
+       { .compatible = "kendryte,k210-sysctl", },
+       {}
+};
+
+static struct platform_driver k210_sysctl_driver = {
+       .driver = {
+               .name           = "k210-sysctl",
+               .of_match_table = k210_sysctl_of_match,
+       },
+       .probe                  = k210_sysctl_probe,
+};
+
+static int __init k210_sysctl_init(void)
+{
+       return platform_driver_register(&k210_sysctl_driver);
+}
+core_initcall(k210_sysctl_init);
+
+/*
+ * This needs to be called very early during initialization, given that
+ * PLL1 needs to be enabled to be able to use all SRAM.
+ */
+static void __init k210_soc_early_init(const void *fdt)
+{
+       void __iomem *regs;
+
+       regs = ioremap(K210_SYSCTL_SYSCTL_BASE_ADDR, 0x1000);
+       if (!regs)
+               panic("K210 sysctl ioremap");
+
+       /* Enable PLL1 to make the KPU SRAM useable */
+       k210_pll1_enable(regs);
+
+       k210_set_bits(PLL_OUT_EN, regs + K210_SYSCTL_PLL0);
+
+       k210_set_bits(CLKEN_CPU | CLKEN_SRAM0 | CLKEN_SRAM1,
+                     regs + K210_SYSCTL_CLKEN_CENT);
+       k210_set_bits(CLKEN_ROM | CLKEN_TIMER0 | CLKEN_RTC,
+                     regs + K210_SYSCTL_CLKEN_PERI);
+
+       k210_set_bits(CLKSEL_ACLK, regs + K210_SYSCTL_CLKSEL0);
+
+       iounmap(regs);
+}
+SOC_EARLY_INIT_DECLARE(generic_k210, "kendryte,k210", k210_soc_early_init);