Merge tag 'riscv-for-linus-5.12-mw0' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 26 Feb 2021 18:28:35 +0000 (10:28 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 26 Feb 2021 18:28:35 +0000 (10:28 -0800)
Pull RISC-V updates from Palmer Dabbelt:
 "A handful of new RISC-V related patches for this merge window:

   - A check to ensure drivers are properly using uaccess. This isn't
     manifesting with any of the drivers I'm currently using, but may
     catch errors in new drivers.

   - Some preliminary support for the FU740, along with the HiFive
     Unleashed it will appear on.

   - NUMA support for RISC-V, which involves making the arm64 code
     generic.

   - Support for kasan on the vmalloc region.

   - A handful of new drivers for the Kendryte K210, along with the DT
     plumbing required to boot on a handful of K210-based boards.

   - Support for allocating ASIDs.

   - Preliminary support for kernels larger than 128MiB.

   - Various other improvements to our KASAN support, including the
     utilization of huge pages when allocating the KASAN regions.

  We may have already found a bug with the KASAN_VMALLOC code, but it's
  passing my tests. There's a fix in the works, but that will probably
  miss the merge window.

* tag 'riscv-for-linus-5.12-mw0' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux: (75 commits)
  riscv: Improve kasan population by using hugepages when possible
  riscv: Improve kasan population function
  riscv: Use KASAN_SHADOW_INIT define for kasan memory initialization
  riscv: Improve kasan definitions
  riscv: Get rid of MAX_EARLY_MAPPING_SIZE
  soc: canaan: Sort the Makefile alphabetically
  riscv: Disable KSAN_SANITIZE for vDSO
  riscv: Remove unnecessary declaration
  riscv: Add Canaan Kendryte K210 SD card defconfig
  riscv: Update Canaan Kendryte K210 defconfig
  riscv: Add Kendryte KD233 board device tree
  riscv: Add SiPeed MAIXDUINO board device tree
  riscv: Add SiPeed MAIX GO board device tree
  riscv: Add SiPeed MAIX DOCK board device tree
  riscv: Add SiPeed MAIX BiT board device tree
  riscv: Update Canaan Kendryte K210 device tree
  dt-bindings: add resets property to dw-apb-timer
  dt-bindings: fix sifive gpio properties
  dt-bindings: update sifive uart compatible string
  dt-bindings: update sifive clint compatible string
  ...

122 files changed:
Documentation/devicetree/bindings/gpio/sifive,gpio.yaml
Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml
Documentation/devicetree/bindings/mfd/canaan,k210-sysctl.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/pwm-sifive.yaml
Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/riscv/canaan.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/riscv/cpus.yaml
Documentation/devicetree/bindings/riscv/sifive-l2-cache.yaml
Documentation/devicetree/bindings/riscv/sifive.yaml
Documentation/devicetree/bindings/serial/sifive-serial.yaml
Documentation/devicetree/bindings/timer/sifive,clint.yaml
Documentation/devicetree/bindings/timer/snps,dw-apb-timer.yaml
MAINTAINERS
arch/arm64/Kconfig
arch/arm64/include/asm/numa.h
arch/arm64/kernel/acpi_numa.c
arch/arm64/mm/Makefile
arch/arm64/mm/init.c
arch/arm64/mm/numa.c [deleted file]
arch/riscv/Kconfig
arch/riscv/Kconfig.socs
arch/riscv/Makefile
arch/riscv/boot/dts/Makefile
arch/riscv/boot/dts/canaan/Makefile [new file with mode: 0644]
arch/riscv/boot/dts/canaan/canaan_kd233.dts [new file with mode: 0644]
arch/riscv/boot/dts/canaan/k210.dtsi [new file with mode: 0644]
arch/riscv/boot/dts/canaan/k210_generic.dts [new file with mode: 0644]
arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts [new file with mode: 0644]
arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts [new file with mode: 0644]
arch/riscv/boot/dts/canaan/sipeed_maix_go.dts [new file with mode: 0644]
arch/riscv/boot/dts/canaan/sipeed_maixduino.dts [new file with mode: 0644]
arch/riscv/boot/dts/kendryte/Makefile [deleted file]
arch/riscv/boot/dts/kendryte/k210.dts [deleted file]
arch/riscv/boot/dts/kendryte/k210.dtsi [deleted file]
arch/riscv/boot/dts/sifive/Makefile
arch/riscv/boot/dts/sifive/fu740-c000.dtsi [new file with mode: 0644]
arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts [new file with mode: 0644]
arch/riscv/configs/nommu_k210_defconfig
arch/riscv/configs/nommu_k210_sdcard_defconfig [new file with mode: 0644]
arch/riscv/include/asm/bug.h
arch/riscv/include/asm/csr.h
arch/riscv/include/asm/kasan.h
arch/riscv/include/asm/kprobes.h
arch/riscv/include/asm/mmu.h
arch/riscv/include/asm/mmu_context.h
arch/riscv/include/asm/mmzone.h [new file with mode: 0644]
arch/riscv/include/asm/numa.h [new file with mode: 0644]
arch/riscv/include/asm/page.h
arch/riscv/include/asm/pci.h
arch/riscv/include/asm/pgtable.h
arch/riscv/include/asm/probes.h [new file with mode: 0644]
arch/riscv/include/asm/processor.h
arch/riscv/include/asm/ptrace.h
arch/riscv/include/asm/sbi.h
arch/riscv/include/asm/set_memory.h
arch/riscv/include/asm/soc.h
arch/riscv/include/asm/stackprotector.h
arch/riscv/include/asm/stacktrace.h
arch/riscv/include/asm/thread_info.h
arch/riscv/include/asm/uprobes.h [new file with mode: 0644]
arch/riscv/kernel/Makefile
arch/riscv/kernel/asm-offsets.c
arch/riscv/kernel/ftrace.c
arch/riscv/kernel/head.S
arch/riscv/kernel/image-vars.h
arch/riscv/kernel/mcount-dyn.S
arch/riscv/kernel/patch.c
arch/riscv/kernel/probes/Makefile [new file with mode: 0644]
arch/riscv/kernel/probes/decode-insn.c [new file with mode: 0644]
arch/riscv/kernel/probes/decode-insn.h [new file with mode: 0644]
arch/riscv/kernel/probes/ftrace.c [new file with mode: 0644]
arch/riscv/kernel/probes/kprobes.c [new file with mode: 0644]
arch/riscv/kernel/probes/kprobes_trampoline.S [new file with mode: 0644]
arch/riscv/kernel/probes/simulate-insn.c [new file with mode: 0644]
arch/riscv/kernel/probes/simulate-insn.h [new file with mode: 0644]
arch/riscv/kernel/probes/uprobes.c [new file with mode: 0644]
arch/riscv/kernel/process.c
arch/riscv/kernel/ptrace.c
arch/riscv/kernel/sbi.c
arch/riscv/kernel/setup.c
arch/riscv/kernel/signal.c
arch/riscv/kernel/smpboot.c
arch/riscv/kernel/soc.c
arch/riscv/kernel/stacktrace.c
arch/riscv/kernel/traps.c
arch/riscv/kernel/vdso/Makefile
arch/riscv/lib/Makefile
arch/riscv/lib/error-inject.c [new file with mode: 0644]
arch/riscv/mm/Makefile
arch/riscv/mm/context.c
arch/riscv/mm/fault.c
arch/riscv/mm/init.c
arch/riscv/mm/kasan_init.c
drivers/base/Kconfig
drivers/base/Makefile
drivers/base/arch_numa.c [new file with mode: 0644]
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-k210.c [new file with mode: 0644]
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/pinctrl-k210.c [new file with mode: 0644]
drivers/reset/Kconfig
drivers/reset/Makefile
drivers/reset/reset-k210.c [new file with mode: 0644]
drivers/soc/Kconfig
drivers/soc/Makefile
drivers/soc/canaan/Kconfig [new file with mode: 0644]
drivers/soc/canaan/Makefile [new file with mode: 0644]
drivers/soc/canaan/k210-sysctl.c [new file with mode: 0644]
drivers/soc/kendryte/Kconfig [deleted file]
drivers/soc/kendryte/Makefile [deleted file]
drivers/soc/kendryte/k210-sysctl.c [deleted file]
drivers/soc/sifive/sifive_l2_cache.c
include/asm-generic/numa.h [new file with mode: 0644]
include/dt-bindings/clock/k210-clk.h
include/dt-bindings/pinctrl/k210-fpioa.h [new file with mode: 0644]
include/dt-bindings/reset/k210-rst.h [new file with mode: 0644]
include/linux/initrd.h
include/soc/canaan/k210-sysctl.h [new file with mode: 0644]
init/initramfs.c

index a0efd8dc25384ec7db714212c8547aa51651b362..c2902aac251452050895a2f0cfb753b5f47ce9cc 100644 (file)
@@ -13,7 +13,10 @@ maintainers:
 properties:
   compatible:
     items:
-      - const: sifive,fu540-c000-gpio
+      - enum:
+          - sifive,fu540-c000-gpio
+          - sifive,fu740-c000-gpio
+          - canaan,k210-gpiohs
       - const: sifive,gpio0
 
   reg:
@@ -21,9 +24,9 @@ properties:
 
   interrupts:
     description:
-      interrupt mapping one per GPIO. Maximum 16 GPIOs.
+      Interrupt mapping, one per GPIO. Maximum 32 GPIOs.
     minItems: 1
-    maxItems: 16
+    maxItems: 32
 
   interrupt-controller: true
 
@@ -36,6 +39,14 @@ properties:
   "#gpio-cells":
     const: 2
 
+  ngpios:
+    description:
+      The number of GPIOs available on the controller implementation.
+      It is 16 for the SiFive SoCs and 32 for the Canaan K210.
+    minimum: 1
+    maximum: 32
+    default: 16
+
   gpio-controller: true
 
 required:
@@ -44,10 +55,20 @@ required:
   - interrupts
   - interrupt-controller
   - "#interrupt-cells"
-  - clocks
   - "#gpio-cells"
   - gpio-controller
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - sifive,fu540-c000-gpio
+          - sifive,fu740-c000-gpio
+then:
+  required:
+    - clocks
+
 additionalProperties: false
 
 examples:
index b9a61c9f753082921342ee11636e21083e5ade35..08d5a57ce00ff4465eaf11b6853d4b1b03324785 100644 (file)
@@ -8,10 +8,11 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: SiFive Platform-Level Interrupt Controller (PLIC)
 
 description:
-  SiFive SOCs include an implementation of the Platform-Level Interrupt Controller
-  (PLIC) high-level specification in the RISC-V Privileged Architecture
-  specification. The PLIC connects all external interrupts in the system to all
-  hart contexts in the system, via the external interrupt source in each hart.
+  SiFive SoCs and other RISC-V SoCs include an implementation of the
+  Platform-Level Interrupt Controller (PLIC) high-level specification in
+  the RISC-V Privileged Architecture specification. The PLIC connects all
+  external interrupts in the system to all hart contexts in the system, via
+  the external interrupt source in each hart.
 
   A hart context is a privilege mode in a hardware execution thread. For example,
   in an 4 core system with 2-way SMT, you have 8 harts and probably at least two
@@ -42,7 +43,9 @@ maintainers:
 properties:
   compatible:
     items:
-      - const: sifive,fu540-c000-plic
+      - enum:
+          - sifive,fu540-c000-plic
+          - canaan,k210-plic
       - const: sifive,plic-1.0.0
 
   reg:
diff --git a/Documentation/devicetree/bindings/mfd/canaan,k210-sysctl.yaml b/Documentation/devicetree/bindings/mfd/canaan,k210-sysctl.yaml
new file mode 100644 (file)
index 0000000..c24ad45
--- /dev/null
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/canaan,k210-sysctl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Canaan Kendryte K210 System Controller Device Tree Bindings
+
+maintainers:
+  - Damien Le Moal <damien.lemoal@wdc.com>
+
+description:
+  Canaan Inc. Kendryte K210 SoC system controller which provides a
+  register map for controlling the clocks, reset signals and pin power
+  domains of the SoC.
+
+properties:
+  compatible:
+    items:
+      - const: canaan,k210-sysctl
+      - const: syscon
+      - const: simple-mfd
+
+  clocks:
+    maxItems: 1
+    description:
+      System controller Advanced Power Bus (APB) interface clock source.
+
+  clock-names:
+    items:
+      - const: pclk
+
+  reg:
+    maxItems: 1
+
+  clock-controller:
+    # Child node
+    type: object
+    $ref: "../clock/canaan,k210-clk.yaml"
+    description:
+      Clock controller for the SoC clocks. This child node definition
+      should follow the bindings specified in
+      Documentation/devicetree/bindings/clock/canaan,k210-clk.yaml.
+
+  reset-controller:
+    # Child node
+    type: object
+    $ref: "../reset/canaan,k210-rst.yaml"
+    description:
+      Reset controller for the SoC. This child node definition
+      should follow the bindings specified in
+      Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml.
+
+  syscon-reboot:
+    # Child node
+    type: object
+    $ref: "../power/reset/syscon-reboot.yaml"
+    description:
+      Reboot method for the SoC. This child node definition
+      should follow the bindings specified in
+      Documentation/devicetree/bindings/power/reset/syscon-reboot.yaml.
+
+required:
+  - compatible
+  - clocks
+  - reg
+  - clock-controller
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/k210-clk.h>
+    #include <dt-bindings/reset/k210-rst.h>
+
+    clocks {
+      in0: oscllator {
+        compatible = "fixed-clock";
+        #clock-cells = <0>;
+        clock-frequency = <26000000>;
+      };
+    };
+
+    sysctl: syscon@50440000 {
+      compatible = "canaan,k210-sysctl",
+                   "syscon", "simple-mfd";
+      reg = <0x50440000 0x100>;
+      clocks = <&sysclk K210_CLK_APB1>;
+      clock-names = "pclk";
+
+      sysclk: clock-controller {
+        #clock-cells = <1>;
+        compatible = "canaan,k210-clk";
+        clocks = <&in0>;
+      };
+
+      sysrst: reset-controller {
+        compatible = "canaan,k210-rst";
+        #reset-cells = <1>;
+      };
+
+      reboot: syscon-reboot {
+        compatible = "syscon-reboot";
+        regmap = <&sysctl>;
+        offset = <48>;
+        mask = <1>;
+        value = <1>;
+      };
+    };
diff --git a/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml b/Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml
new file mode 100644 (file)
index 0000000..46fbc73
--- /dev/null
@@ -0,0 +1,171 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/canaan,k210-fpioa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Canaan Kendryte K210 FPIOA Device Tree Bindings
+
+maintainers:
+  - Damien Le Moal <damien.lemoal@wdc.com>
+
+description:
+  The Canaan Kendryte K210 SoC Fully Programmable IO Array (FPIOA)
+  controller allows assiging any of 256 possible functions to any of
+  48 IO pins of the SoC. Pin function configuration is performed on
+  a per-pin basis.
+
+properties:
+  compatible:
+    const: canaan,k210-fpioa
+
+  reg:
+    maxItems: 1
+    description:
+      Address and length of the register set for the FPIOA controller.
+
+  clocks:
+    items:
+      - description: Controller reference clock source
+      - description: APB interface clock source
+
+  clock-names:
+    items:
+      - const: ref
+      - const: pclk
+
+  resets:
+    maxItems: 1
+
+  canaan,k210-sysctl-power:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: |
+      phandle of the K210 system controller node and offset of its
+      power domain control register.
+
+patternProperties:
+  '-pinmux$':
+    type: object
+    $ref: /schemas/pinctrl/pinmux-node.yaml
+    description:
+      FPIOA client devices use sub-nodes to define the desired pin
+      configuration. Client device sub-nodes use the pinux property
+      below.
+
+    properties:
+      pinmux:
+        description:
+          List of IO pins alternate functions. The values for each IO
+          pin is a combination of an IO pin number (0 to 47) with the
+          desired function for the IO pin. Functions are defined as
+          macros in include/dt-bindings/pinctrl/k210-fpioa.h.
+          The K210_FPIOA(IO pin, function) macro is provided to
+          facilitate the combination of IO pin numbers and functions.
+
+    required:
+      - pinmux
+
+    additionalProperties: false
+
+  '-pins$':
+    type: object
+    $ref: /schemas/pinctrl/pincfg-node.yaml
+    description:
+      FPIOA client devices use sub-nodes to define the desired
+      configuration of pins. Client device sub-nodes use the
+      properties below.
+
+    properties:
+      pins:
+        description:
+          List of IO pins affected by the properties specified in this
+          subnode. IO pins are identified using the pin names "IO_xx".
+          Pin configuration nodes can also define the power domain to
+          be used for the SoC pin groups A0 (IO pins 0-5),
+          A1 (IO pins 6-11), A2 (IO pins 12-17), B0 (IO pins 18-23),
+          B1 (IO pins 24-29), B2 (IO pins 30-35), B3 (IO pins 30-35),
+          C0 (IO pins 36-41) and C1 (IO pins 42-47) using the
+          power-source property.
+        items:
+          anyOf:
+            - pattern: "^(IO_([0-9]*))|(A[0-2])|(B[3-5])|(C[6-7])$"
+            - enum: [ IO_0, IO_1, IO_2, IO_3, IO_4, IO_5, IO_6, IO_7,
+                      IO_8, IO_9, IO_10, IO_11, IO_12, IO_13, IO_14,
+                      IO_15, IO_16, IO_17, IO_18, IO_19, IO_20, IO_21,
+                      IO_22, IO_23, IO_24, IO_25, IO_26, IO_27, IO_28,
+                      IO_29, IO_30, IO_31, IO_32, IO_33, IO_34, IO_35,
+                      IO_36, IO_37, IO_38, IO_39, IO_40, IO_41, IO_42,
+                      IO_43, IO_44, IO_45, IO_46, IO_47,
+                      A0, A1, A2, B3, B4, B5, C6, C7 ]
+      bias-disable: true
+
+      bias-pull-down: true
+
+      bias-pull-up: true
+
+      drive-strength: true
+
+      drive-strength-microamp: true
+
+      input-enable: true
+
+      input-disable: true
+
+      input-schmitt-enable: true
+
+      input-schmitt-disable: true
+
+      input-polarity-invert:
+        description:
+          Enable or disable pin input polarity inversion.
+
+      output-enable: true
+
+      output-disable: true
+
+      output-high: true
+
+      output-low: true
+
+      output-polarity-invert:
+        description:
+          Enable or disable pin output polarity inversion.
+
+      slew-rate: true
+
+      power-source: true
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - canaan,k210-sysctl-power
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/pinctrl/k210-fpioa.h>
+    #include <dt-bindings/clock/k210-clk.h>
+    #include <dt-bindings/reset/k210-rst.h>
+
+    fpioa: pinmux@502B0000 {
+      compatible = "canaan,k210-fpioa";
+      reg = <0x502B0000 0x100>;
+      clocks = <&sysclk K210_CLK_FPIOA>,
+               <&sysclk K210_CLK_APB0>;
+      clock-names = "ref", "pclk";
+      resets = <&sysrst K210_RST_FPIOA>;
+      canaan,k210-sysctl-power = <&sysctl 108>;
+      pinctrl-0 = <&jtag_pinctrl>;
+      pinctrl-names = "default";
+
+      jtag_pinctrl: jtag-pinmux {
+        pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                 <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                 <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                 <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+      };
+    };
index 5ac25275d8bffc804a984ef5f44871822e60e009..84e66913d042ef425d35592782e7dc598afeb752 100644 (file)
@@ -25,12 +25,15 @@ description:
 properties:
   compatible:
     items:
-      - const: sifive,fu540-c000-pwm
+      - enum:
+          - sifive,fu540-c000-pwm
+          - sifive,fu740-c000-pwm
       - const: sifive,pwm0
     description:
       Should be "sifive,<chip>-pwm" and "sifive,pwm<version>". Supported
-      compatible strings are "sifive,fu540-c000-pwm" for the SiFive PWM v0
-      as integrated onto the SiFive FU540 chip, and "sifive,pwm0" for the
+      compatible strings are "sifive,fu540-c000-pwm" and
+      "sifive,fu740-c000-pwm" for the SiFive PWM v0 as integrated onto the
+      SiFive FU540 and FU740 chip respectively, and "sifive,pwm0" for the
       SiFive PWM v0 IP block with no chip integration tweaks.
       Please refer to sifive-blocks-ip-versioning.txt for details.
 
diff --git a/Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml b/Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml
new file mode 100644 (file)
index 0000000..53e4ede
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reset/canaan,k210-rst.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Canaan Kendryte K210 Reset Controller Device Tree Bindings
+
+maintainers:
+  - Damien Le Moal <damien.lemoal@wdc.com>
+
+description: |
+  Canaan Kendryte K210 reset controller driver which supports the SoC
+  system controller supplied reset registers for the various peripherals
+  of the SoC. The K210 reset controller node must be defined as a child
+  node of the K210 system controller node.
+
+  See also:
+  - dt-bindings/reset/k210-rst.h
+
+properties:
+  compatible:
+    const: canaan,k210-rst
+
+  '#reset-cells':
+    const: 1
+
+required:
+  - '#reset-cells'
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/reset/k210-rst.h>
+    sysrst: reset-controller {
+      compatible = "canaan,k210-rst";
+      #reset-cells = <1>;
+    };
diff --git a/Documentation/devicetree/bindings/riscv/canaan.yaml b/Documentation/devicetree/bindings/riscv/canaan.yaml
new file mode 100644 (file)
index 0000000..f8f3f28
--- /dev/null
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/riscv/canaan.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Canaan SoC-based boards
+
+maintainers:
+  - Damien Le Moal <damien.lemoal@wdc.com>
+
+description:
+  Canaan Kendryte K210 SoC-based boards
+
+properties:
+  $nodename:
+    const: '/'
+  compatible:
+    oneOf:
+      - items:
+          - const: sipeed,maix-bit
+          - const: sipeed,maix-bitm
+          - const: canaan,kendryte-k210
+
+      - items:
+          - const: sipeed,maix-go
+          - const: canaan,kendryte-k210
+
+      - items:
+          - const: sipeed,maix-dock-m1
+          - const: sipeed,maix-dock-m1w
+          - const: canaan,kendryte-k210
+
+      - items:
+          - const: sipeed,maixduino
+          - const: canaan,kendryte-k210
+
+      - items:
+          - const: canaan,kendryte-kd233
+          - const: canaan,kendryte-k210
+
+      - items:
+          - const: canaan,kendryte-k210
+
+additionalProperties: true
+
+...
index c6925e0b16e46bb546d04fc3dd53900126ff310d..e534f6a7cfa1f2e28da5c5e191bb3c21d0716785 100644 (file)
@@ -28,11 +28,18 @@ properties:
       - items:
           - enum:
               - sifive,rocket0
+              - sifive,bullet0
               - sifive,e5
+              - sifive,e7
               - sifive,e51
+              - sifive,e71
               - sifive,u54-mc
+              - sifive,u74-mc
               - sifive,u54
+              - sifive,u74
               - sifive,u5
+              - sifive,u7
+              - canaan,k210
           - const: riscv
       - const: riscv    # Simulator only
     description:
@@ -50,6 +57,7 @@ properties:
       - riscv,sv32
       - riscv,sv39
       - riscv,sv48
+      - riscv,none
 
   riscv,isa:
     description:
index 2ece8630dc681fd76d4e23da96e905fb265077fb..23b227614366748ae69accd1bf1cadff54e292cd 100644 (file)
@@ -27,6 +27,7 @@ select:
       items:
         - enum:
             - sifive,fu540-c000-ccache
+            - sifive,fu740-c000-ccache
 
   required:
     - compatible
@@ -34,7 +35,9 @@ select:
 properties:
   compatible:
     items:
-      - const: sifive,fu540-c000-ccache
+      - enum:
+          - sifive,fu540-c000-ccache
+          - sifive,fu740-c000-ccache
       - const: cache
 
   cache-block-size:
@@ -52,10 +55,13 @@ properties:
   cache-unified: true
 
   interrupts:
-    description: |
-      Must contain entries for DirError, DataError and DataFail signals.
     minItems: 3
-    maxItems: 3
+    maxItems: 4
+    items:
+      - description: DirError interrupt
+      - description: DataError interrupt
+      - description: DataFail interrupt
+      - description: DirFail interrupt
 
   reg:
     maxItems: 1
@@ -68,6 +74,26 @@ properties:
       The reference to the reserved-memory for the L2 Loosely Integrated Memory region.
       The reserved memory node should be defined as per the bindings in reserved-memory.txt.
 
+if:
+  properties:
+    compatible:
+      contains:
+        const: sifive,fu540-c000-ccache
+
+then:
+  properties:
+    interrupts:
+      description: |
+        Must contain entries for DirError, DataError and DataFail signals.
+      maxItems: 3
+
+else:
+  properties:
+    interrupts:
+      description: |
+        Must contain entries for DirError, DataError, DataFail, DirFail signals.
+      minItems: 4
+
 additionalProperties: false
 
 required:
index 3a8647d1da4c75890f5bda47ff201a407aac0782..ee0a239af4c29667453cba37dcdbdc051c466666 100644 (file)
@@ -17,11 +17,18 @@ properties:
   $nodename:
     const: '/'
   compatible:
-    items:
-      - enum:
-          - sifive,hifive-unleashed-a00
-      - const: sifive,fu540-c000
-      - const: sifive,fu540
+    oneOf:
+      - items:
+          - enum:
+              - sifive,hifive-unleashed-a00
+          - const: sifive,fu540-c000
+          - const: sifive,fu540
+
+      - items:
+          - enum:
+              - sifive,hifive-unmatched-a00
+          - const: sifive,fu740-c000
+          - const: sifive,fu740
 
 additionalProperties: true
 
index 3ac5c7ff27586428ff0f52f26f8fb1a27196c9f5..5fa94dacbba97b4d62b3e43696106a8d1e0dbdee 100644 (file)
@@ -20,6 +20,7 @@ properties:
       - enum:
           - sifive,fu540-c000-uart
           - sifive,fu740-c000-uart
+          - canaan,k210-uarths
       - const: sifive,uart0
 
     description:
index 2a0e9cd9fbcf0ffded1fb1d2809c9a6094c7dfaa..a35952f487426988641dcde96981904a761e05ab 100644 (file)
@@ -23,15 +23,19 @@ description:
 properties:
   compatible:
     items:
-      - const: sifive,fu540-c000-clint
+      - enum:
+          - sifive,fu540-c000-clint
+          - canaan,k210-clint
       - const: sifive,clint0
 
     description:
-      Should be "sifive,<chip>-clint" and "sifive,clint<version>".
+      Should be "<vendor>,<chip>-clint" and "sifive,clint<version>".
       Supported compatible strings are -
       "sifive,fu540-c000-clint" for the SiFive CLINT v0 as integrated
-      onto the SiFive FU540 chip, and "sifive,clint0" for the SiFive
-      CLINT v0 IP block with no chip integration tweaks.
+      onto the SiFive FU540 chip, "canaan,k210-clint" for the SiFive
+      CLINT v0 as integrated onto the Canaan Kendryte K210 chip, and
+      "sifive,clint0" for the SiFive CLINT v0 IP block with no chip
+      integration tweaks.
       Please refer to sifive-blocks-ip-versioning.txt for details
 
   reg:
index d65faf289a833cb6479a612badeed35eff3b3f18..d33c9205a9093c1da564d4d508f9ea7261486c52 100644 (file)
@@ -24,6 +24,9 @@ properties:
   interrupts:
     maxItems: 1
 
+  resets:
+    maxItems: 1
+
   clocks:
     minItems: 1
     items:
index 498cc779e354b5acceeff547cfbeb83935994029..d92f85ca831d30d658fef768575ba538be4d4b93 100644 (file)
@@ -3855,6 +3855,29 @@ W:       https://github.com/Cascoda/ca8210-linux.git
 F:     Documentation/devicetree/bindings/net/ieee802154/ca8210.txt
 F:     drivers/net/ieee802154/ca8210.c
 
+CANAAN/KENDRYTE K210 SOC FPIOA DRIVER
+M:     Damien Le Moal <damien.lemoal@wdc.com>
+L:     linux-riscv@lists.infradead.org
+L:     linux-gpio@vger.kernel.org (pinctrl driver)
+F:     Documentation/devicetree/bindings/pinctrl/canaan,k210-fpioa.yaml
+F:     drivers/pinctrl/pinctrl-k210.c
+
+CANAAN/KENDRYTE K210 SOC RESET CONTROLLER DRIVER
+M:     Damien Le Moal <damien.lemoal@wdc.com>
+L:     linux-kernel@vger.kernel.org
+L:     linux-riscv@lists.infradead.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/reset/canaan,k210-rst.yaml
+F:     drivers/reset/reset-k210.c
+
+CANAAN/KENDRYTE K210 SOC SYSTEM CONTROLLER DRIVER
+M:     Damien Le Moal <damien.lemoal@wdc.com>
+L:     linux-riscv@lists.infradead.org
+S:     Maintained
+F:      Documentation/devicetree/bindings/mfd/canaan,k210-sysctl.yaml
+F:     drivers/soc/canaan/
+F:     include/soc/canaan/
+
 CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS
 M:     David Howells <dhowells@redhat.com>
 L:     linux-cachefs@redhat.com (moderated for non-subscribers)
index a254f4871683f0c62156662a1e4b01137bf2c1b6..1f212b47a48a1033d1799c946f0bb0b9dca96fd2 100644 (file)
@@ -999,6 +999,7 @@ config HOTPLUG_CPU
 # Common NUMA Features
 config NUMA
        bool "NUMA Memory Allocation and Scheduler Support"
+       select GENERIC_ARCH_NUMA
        select ACPI_NUMA if ACPI
        select OF_NUMA
        help
index dd870390d639f2b7361fe1c0626f4a7dace0e26c..8c8cf4297cc321ace630d4d1a78049d75b70e524 100644 (file)
@@ -3,52 +3,6 @@
 #define __ASM_NUMA_H
 
 #include <asm/topology.h>
-
-#ifdef CONFIG_NUMA
-
-#define NR_NODE_MEMBLKS                (MAX_NUMNODES * 2)
-
-int __node_distance(int from, int to);
-#define node_distance(a, b) __node_distance(a, b)
-
-extern nodemask_t numa_nodes_parsed __initdata;
-
-extern bool numa_off;
-
-/* Mappings between node number and cpus on that node. */
-extern cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
-void numa_clear_node(unsigned int cpu);
-
-#ifdef CONFIG_DEBUG_PER_CPU_MAPS
-const struct cpumask *cpumask_of_node(int node);
-#else
-/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
-static inline const struct cpumask *cpumask_of_node(int node)
-{
-       if (node == NUMA_NO_NODE)
-               return cpu_all_mask;
-
-       return node_to_cpumask_map[node];
-}
-#endif
-
-void __init arm64_numa_init(void);
-int __init numa_add_memblk(int nodeid, u64 start, u64 end);
-void __init numa_set_distance(int from, int to, int distance);
-void __init numa_free_distance(void);
-void __init early_map_cpu_to_node(unsigned int cpu, int nid);
-void numa_store_cpu_info(unsigned int cpu);
-void numa_add_cpu(unsigned int cpu);
-void numa_remove_cpu(unsigned int cpu);
-
-#else  /* CONFIG_NUMA */
-
-static inline void numa_store_cpu_info(unsigned int cpu) { }
-static inline void numa_add_cpu(unsigned int cpu) { }
-static inline void numa_remove_cpu(unsigned int cpu) { }
-static inline void arm64_numa_init(void) { }
-static inline void early_map_cpu_to_node(unsigned int cpu, int nid) { }
-
-#endif /* CONFIG_NUMA */
+#include <asm-generic/numa.h>
 
 #endif /* __ASM_NUMA_H */
index 7ff800045434602507a4a9aefaf50230cee4b289..fdfecf0991ced52e1b9216462e5618be33061bdb 100644 (file)
@@ -118,15 +118,3 @@ void __init acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa)
        node_set(node, numa_nodes_parsed);
 }
 
-int __init arm64_acpi_numa_init(void)
-{
-       int ret;
-
-       ret = acpi_numa_init();
-       if (ret) {
-               pr_info("Failed to initialise from firmware\n");
-               return ret;
-       }
-
-       return srat_disabled() ? -EINVAL : 0;
-}
index 77222d92667a7222d3fd715d048f2adfeffd7440..f188c9092696308c9ae75ad0505af66ee0712bf8 100644 (file)
@@ -7,7 +7,6 @@ obj-$(CONFIG_HUGETLB_PAGE)      += hugetlbpage.o
 obj-$(CONFIG_PTDUMP_CORE)      += ptdump.o
 obj-$(CONFIG_PTDUMP_DEBUGFS)   += ptdump_debugfs.o
 obj-$(CONFIG_TRANS_TABLE)      += trans_pgd.o
-obj-$(CONFIG_NUMA)             += numa.o
 obj-$(CONFIG_DEBUG_VIRTUAL)    += physaddr.o
 obj-$(CONFIG_ARM64_MTE)                += mteswap.o
 KASAN_SANITIZE_physaddr.o      += n
index 709d98fea90cc1f8b08c85864fbaa6fd6a0b7d42..0ace5e68efba0ea05b32c989ac6ae987364fc89c 100644 (file)
@@ -416,10 +416,10 @@ void __init bootmem_init(void)
        max_pfn = max_low_pfn = max;
        min_low_pfn = min;
 
-       arm64_numa_init();
+       arch_numa_init();
 
        /*
-        * must be done after arm64_numa_init() which calls numa_init() to
+        * must be done after arch_numa_init() which calls numa_init() to
         * initialize node_online_map that gets used in hugetlb_cma_reserve()
         * while allocating required CMA size across online nodes.
         */
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
deleted file mode 100644 (file)
index a8303bc..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * NUMA support, based on the x86 implementation.
- *
- * Copyright (C) 2015 Cavium Inc.
- * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
- */
-
-#define pr_fmt(fmt) "NUMA: " fmt
-
-#include <linux/acpi.h>
-#include <linux/memblock.h>
-#include <linux/module.h>
-#include <linux/of.h>
-
-#include <asm/acpi.h>
-#include <asm/sections.h>
-
-struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
-EXPORT_SYMBOL(node_data);
-nodemask_t numa_nodes_parsed __initdata;
-static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
-
-static int numa_distance_cnt;
-static u8 *numa_distance;
-bool numa_off;
-
-static __init int numa_parse_early_param(char *opt)
-{
-       if (!opt)
-               return -EINVAL;
-       if (str_has_prefix(opt, "off"))
-               numa_off = true;
-
-       return 0;
-}
-early_param("numa", numa_parse_early_param);
-
-cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
-EXPORT_SYMBOL(node_to_cpumask_map);
-
-#ifdef CONFIG_DEBUG_PER_CPU_MAPS
-
-/*
- * Returns a pointer to the bitmask of CPUs on Node 'node'.
- */
-const struct cpumask *cpumask_of_node(int node)
-{
-
-       if (node == NUMA_NO_NODE)
-               return cpu_all_mask;
-
-       if (WARN_ON(node < 0 || node >= nr_node_ids))
-               return cpu_none_mask;
-
-       if (WARN_ON(node_to_cpumask_map[node] == NULL))
-               return cpu_online_mask;
-
-       return node_to_cpumask_map[node];
-}
-EXPORT_SYMBOL(cpumask_of_node);
-
-#endif
-
-static void numa_update_cpu(unsigned int cpu, bool remove)
-{
-       int nid = cpu_to_node(cpu);
-
-       if (nid == NUMA_NO_NODE)
-               return;
-
-       if (remove)
-               cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]);
-       else
-               cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
-}
-
-void numa_add_cpu(unsigned int cpu)
-{
-       numa_update_cpu(cpu, false);
-}
-
-void numa_remove_cpu(unsigned int cpu)
-{
-       numa_update_cpu(cpu, true);
-}
-
-void numa_clear_node(unsigned int cpu)
-{
-       numa_remove_cpu(cpu);
-       set_cpu_numa_node(cpu, NUMA_NO_NODE);
-}
-
-/*
- * Allocate node_to_cpumask_map based on number of available nodes
- * Requires node_possible_map to be valid.
- *
- * Note: cpumask_of_node() is not valid until after this is done.
- * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
- */
-static void __init setup_node_to_cpumask_map(void)
-{
-       int node;
-
-       /* setup nr_node_ids if not done yet */
-       if (nr_node_ids == MAX_NUMNODES)
-               setup_nr_node_ids();
-
-       /* allocate and clear the mapping */
-       for (node = 0; node < nr_node_ids; node++) {
-               alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
-               cpumask_clear(node_to_cpumask_map[node]);
-       }
-
-       /* cpumask_of_node() will now work */
-       pr_debug("Node to cpumask map for %u nodes\n", nr_node_ids);
-}
-
-/*
- * Set the cpu to node and mem mapping
- */
-void numa_store_cpu_info(unsigned int cpu)
-{
-       set_cpu_numa_node(cpu, cpu_to_node_map[cpu]);
-}
-
-void __init early_map_cpu_to_node(unsigned int cpu, int nid)
-{
-       /* fallback to node 0 */
-       if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
-               nid = 0;
-
-       cpu_to_node_map[cpu] = nid;
-
-       /*
-        * We should set the numa node of cpu0 as soon as possible, because it
-        * has already been set up online before. cpu_to_node(0) will soon be
-        * called.
-        */
-       if (!cpu)
-               set_cpu_numa_node(cpu, nid);
-}
-
-#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
-unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
-EXPORT_SYMBOL(__per_cpu_offset);
-
-static int __init early_cpu_to_node(int cpu)
-{
-       return cpu_to_node_map[cpu];
-}
-
-static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
-{
-       return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
-}
-
-static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
-                                      size_t align)
-{
-       int nid = early_cpu_to_node(cpu);
-
-       return  memblock_alloc_try_nid(size, align,
-                       __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, nid);
-}
-
-static void __init pcpu_fc_free(void *ptr, size_t size)
-{
-       memblock_free_early(__pa(ptr), size);
-}
-
-void __init setup_per_cpu_areas(void)
-{
-       unsigned long delta;
-       unsigned int cpu;
-       int rc;
-
-       /*
-        * Always reserve area for module percpu variables.  That's
-        * what the legacy allocator did.
-        */
-       rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
-                                   PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
-                                   pcpu_cpu_distance,
-                                   pcpu_fc_alloc, pcpu_fc_free);
-       if (rc < 0)
-               panic("Failed to initialize percpu areas.");
-
-       delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
-       for_each_possible_cpu(cpu)
-               __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
-}
-#endif
-
-/**
- * numa_add_memblk() - Set node id to memblk
- * @nid: NUMA node ID of the new memblk
- * @start: Start address of the new memblk
- * @end:  End address of the new memblk
- *
- * RETURNS:
- * 0 on success, -errno on failure.
- */
-int __init numa_add_memblk(int nid, u64 start, u64 end)
-{
-       int ret;
-
-       ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
-       if (ret < 0) {
-               pr_err("memblock [0x%llx - 0x%llx] failed to add on node %d\n",
-                       start, (end - 1), nid);
-               return ret;
-       }
-
-       node_set(nid, numa_nodes_parsed);
-       return ret;
-}
-
-/*
- * Initialize NODE_DATA for a node on the local memory
- */
-static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
-{
-       const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
-       u64 nd_pa;
-       void *nd;
-       int tnid;
-
-       if (start_pfn >= end_pfn)
-               pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
-
-       nd_pa = memblock_phys_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
-       if (!nd_pa)
-               panic("Cannot allocate %zu bytes for node %d data\n",
-                     nd_size, nid);
-
-       nd = __va(nd_pa);
-
-       /* report and initialize */
-       pr_info("NODE_DATA [mem %#010Lx-%#010Lx]\n",
-               nd_pa, nd_pa + nd_size - 1);
-       tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
-       if (tnid != nid)
-               pr_info("NODE_DATA(%d) on node %d\n", nid, tnid);
-
-       node_data[nid] = nd;
-       memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
-       NODE_DATA(nid)->node_id = nid;
-       NODE_DATA(nid)->node_start_pfn = start_pfn;
-       NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
-}
-
-/*
- * numa_free_distance
- *
- * The current table is freed.
- */
-void __init numa_free_distance(void)
-{
-       size_t size;
-
-       if (!numa_distance)
-               return;
-
-       size = numa_distance_cnt * numa_distance_cnt *
-               sizeof(numa_distance[0]);
-
-       memblock_free(__pa(numa_distance), size);
-       numa_distance_cnt = 0;
-       numa_distance = NULL;
-}
-
-/*
- * Create a new NUMA distance table.
- */
-static int __init numa_alloc_distance(void)
-{
-       size_t size;
-       u64 phys;
-       int i, j;
-
-       size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
-       phys = memblock_find_in_range(0, PFN_PHYS(max_pfn),
-                                     size, PAGE_SIZE);
-       if (WARN_ON(!phys))
-               return -ENOMEM;
-
-       memblock_reserve(phys, size);
-
-       numa_distance = __va(phys);
-       numa_distance_cnt = nr_node_ids;
-
-       /* fill with the default distances */
-       for (i = 0; i < numa_distance_cnt; i++)
-               for (j = 0; j < numa_distance_cnt; j++)
-                       numa_distance[i * numa_distance_cnt + j] = i == j ?
-                               LOCAL_DISTANCE : REMOTE_DISTANCE;
-
-       pr_debug("Initialized distance table, cnt=%d\n", numa_distance_cnt);
-
-       return 0;
-}
-
-/**
- * numa_set_distance() - Set inter node NUMA distance from node to node.
- * @from: the 'from' node to set distance
- * @to: the 'to'  node to set distance
- * @distance: NUMA distance
- *
- * Set the distance from node @from to @to to @distance.
- * If distance table doesn't exist, a warning is printed.
- *
- * If @from or @to is higher than the highest known node or lower than zero
- * or @distance doesn't make sense, the call is ignored.
- */
-void __init numa_set_distance(int from, int to, int distance)
-{
-       if (!numa_distance) {
-               pr_warn_once("Warning: distance table not allocated yet\n");
-               return;
-       }
-
-       if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
-                       from < 0 || to < 0) {
-               pr_warn_once("Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
-                           from, to, distance);
-               return;
-       }
-
-       if ((u8)distance != distance ||
-           (from == to && distance != LOCAL_DISTANCE)) {
-               pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
-                            from, to, distance);
-               return;
-       }
-
-       numa_distance[from * numa_distance_cnt + to] = distance;
-}
-
-/*
- * Return NUMA distance @from to @to
- */
-int __node_distance(int from, int to)
-{
-       if (from >= numa_distance_cnt || to >= numa_distance_cnt)
-               return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
-       return numa_distance[from * numa_distance_cnt + to];
-}
-EXPORT_SYMBOL(__node_distance);
-
-static int __init numa_register_nodes(void)
-{
-       int nid;
-       struct memblock_region *mblk;
-
-       /* Check that valid nid is set to memblks */
-       for_each_mem_region(mblk) {
-               int mblk_nid = memblock_get_region_node(mblk);
-
-               if (mblk_nid == NUMA_NO_NODE || mblk_nid >= MAX_NUMNODES) {
-                       pr_warn("Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n",
-                               mblk_nid, mblk->base,
-                               mblk->base + mblk->size - 1);
-                       return -EINVAL;
-               }
-       }
-
-       /* Finally register nodes. */
-       for_each_node_mask(nid, numa_nodes_parsed) {
-               unsigned long start_pfn, end_pfn;
-
-               get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
-               setup_node_data(nid, start_pfn, end_pfn);
-               node_set_online(nid);
-       }
-
-       /* Setup online nodes to actual nodes*/
-       node_possible_map = numa_nodes_parsed;
-
-       return 0;
-}
-
-static int __init numa_init(int (*init_func)(void))
-{
-       int ret;
-
-       nodes_clear(numa_nodes_parsed);
-       nodes_clear(node_possible_map);
-       nodes_clear(node_online_map);
-
-       ret = numa_alloc_distance();
-       if (ret < 0)
-               return ret;
-
-       ret = init_func();
-       if (ret < 0)
-               goto out_free_distance;
-
-       if (nodes_empty(numa_nodes_parsed)) {
-               pr_info("No NUMA configuration found\n");
-               ret = -EINVAL;
-               goto out_free_distance;
-       }
-
-       ret = numa_register_nodes();
-       if (ret < 0)
-               goto out_free_distance;
-
-       setup_node_to_cpumask_map();
-
-       return 0;
-out_free_distance:
-       numa_free_distance();
-       return ret;
-}
-
-/**
- * dummy_numa_init() - Fallback dummy NUMA init
- *
- * Used if there's no underlying NUMA architecture, NUMA initialization
- * fails, or NUMA is disabled on the command line.
- *
- * Must online at least one node (node 0) and add memory blocks that cover all
- * allowed memory. It is unlikely that this function fails.
- *
- * Return: 0 on success, -errno on failure.
- */
-static int __init dummy_numa_init(void)
-{
-       phys_addr_t start = memblock_start_of_DRAM();
-       phys_addr_t end = memblock_end_of_DRAM();
-       int ret;
-
-       if (numa_off)
-               pr_info("NUMA disabled\n"); /* Forced off on command line. */
-       pr_info("Faking a node at [mem %#018Lx-%#018Lx]\n", start, end - 1);
-
-       ret = numa_add_memblk(0, start, end);
-       if (ret) {
-               pr_err("NUMA init failed\n");
-               return ret;
-       }
-
-       numa_off = true;
-       return 0;
-}
-
-/**
- * arm64_numa_init() - Initialize NUMA
- *
- * Try each configured NUMA initialization method until one succeeds. The
- * last fallback is dummy single node config encompassing whole memory.
- */
-void __init arm64_numa_init(void)
-{
-       if (!numa_off) {
-               if (!acpi_disabled && !numa_init(arm64_acpi_numa_init))
-                       return;
-               if (acpi_disabled && !numa_init(of_numa_init))
-                       return;
-       }
-
-       numa_init(dummy_numa_init);
-}
index e0a34eb5ed3b37a199d44fd661a4dabd57be603b..a998babc1237c1e8c6b3bc5ef3c7b47ed54d71da 100644 (file)
@@ -57,6 +57,7 @@ config RISCV
        select HAVE_ARCH_JUMP_LABEL
        select HAVE_ARCH_JUMP_LABEL_RELATIVE
        select HAVE_ARCH_KASAN if MMU && 64BIT
+       select HAVE_ARCH_KASAN_VMALLOC if MMU && 64BIT
        select HAVE_ARCH_KGDB
        select HAVE_ARCH_KGDB_QXFER_PKT
        select HAVE_ARCH_MMAP_RND_BITS if MMU
@@ -67,14 +68,19 @@ config RISCV
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DMA_CONTIGUOUS if MMU
        select HAVE_EBPF_JIT if MMU
+       select HAVE_FUNCTION_ERROR_INJECTION
        select HAVE_FUTEX_CMPXCHG if FUTEX
        select HAVE_GCC_PLUGINS
        select HAVE_GENERIC_VDSO if MMU && 64BIT
        select HAVE_IRQ_TIME_ACCOUNTING
+       select HAVE_KPROBES
+       select HAVE_KPROBES_ON_FTRACE
+       select HAVE_KRETPROBES
        select HAVE_PCI
        select HAVE_PERF_EVENTS
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
+       select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_STACKPROTECTOR
        select HAVE_SYSCALL_TRACEPOINTS
        select IRQ_DOMAIN
@@ -143,7 +149,7 @@ config PAGE_OFFSET
        default 0xffffffe000000000 if 64BIT && MAXPHYSMEM_128GB
 
 config ARCH_FLATMEM_ENABLE
-       def_bool y
+       def_bool !NUMA
 
 config ARCH_SPARSEMEM_ENABLE
        def_bool y
@@ -156,6 +162,9 @@ config ARCH_SELECT_MEMORY_MODEL
 config ARCH_WANT_GENERAL_HUGETLB
        def_bool y
 
+config ARCH_SUPPORTS_UPROBES
+       def_bool y
+
 config SYS_SUPPORTS_HUGETLBFS
        depends on MMU
        def_bool y
@@ -302,6 +311,35 @@ config TUNE_GENERIC
 
 endchoice
 
+# Common NUMA Features
+config NUMA
+       bool "NUMA Memory Allocation and Scheduler Support"
+       select GENERIC_ARCH_NUMA
+       select OF_NUMA
+       select ARCH_SUPPORTS_NUMA_BALANCING
+       help
+         Enable NUMA (Non-Uniform Memory Access) support.
+
+         The kernel will try to allocate memory used by a CPU on the
+         local memory of the CPU and add some more NUMA awareness to the kernel.
+
+config NODES_SHIFT
+       int "Maximum NUMA Nodes (as a power of 2)"
+       range 1 10
+       default "2"
+       depends on NEED_MULTIPLE_NODES
+       help
+         Specify the maximum number of NUMA Nodes available on the target
+         system.  Increases memory reserved to accommodate various tables.
+
+config USE_PERCPU_NUMA_NODE_ID
+       def_bool y
+       depends on NUMA
+
+config NEED_PER_CPU_EMBED_FIRST_CHUNK
+       def_bool y
+       depends on NUMA
+
 config RISCV_ISA_C
        bool "Emit compressed instructions when building Linux"
        default y
@@ -416,11 +454,17 @@ config EFI
          allow the kernel to be booted as an EFI application. This
          is only useful on systems that have UEFI firmware.
 
+config CC_HAVE_STACKPROTECTOR_TLS
+       def_bool $(cc-option,-mstack-protector-guard=tls -mstack-protector-guard-reg=tp -mstack-protector-guard-offset=0)
+
+config STACKPROTECTOR_PER_TASK
+       def_bool y
+       depends on STACKPROTECTOR && CC_HAVE_STACKPROTECTOR_TLS
+
 endmenu
 
 config BUILTIN_DTB
        def_bool n
-       depends on RISCV_M_MODE
        depends on OF
 
 menu "Power management options"
index 3284d5c291be541f0cc7d7316b9938b66e4c4ec3..7efcece8896cfe7dd361f97b9850c1da9f4665cb 100644 (file)
@@ -22,30 +22,41 @@ config SOC_VIRT
        help
          This enables support for QEMU Virt Machine.
 
-config SOC_KENDRYTE
-       bool "Kendryte K210 SoC"
+config SOC_CANAAN
+       bool "Canaan Kendryte K210 SoC"
        depends on !MMU
        select CLINT_TIMER if RISCV_M_MODE
        select SERIAL_SIFIVE if TTY
        select SERIAL_SIFIVE_CONSOLE if TTY
        select SIFIVE_PLIC
+       select ARCH_HAS_RESET_CONTROLLER
+       select PINCTRL
        help
-         This enables support for Kendryte K210 SoC platform hardware.
+         This enables support for Canaan Kendryte K210 SoC platform hardware.
 
-config SOC_KENDRYTE_K210_DTB
-       def_bool y
-       depends on SOC_KENDRYTE_K210_DTB_BUILTIN
+if SOC_CANAAN
 
-config SOC_KENDRYTE_K210_DTB_BUILTIN
-       bool "Builtin device tree for the Kendryte K210"
-       depends on SOC_KENDRYTE
+config SOC_CANAAN_K210_DTB_BUILTIN
+       bool "Builtin device tree for the Canaan Kendryte K210"
+       depends on SOC_CANAAN
        default y
        select OF
        select BUILTIN_DTB
-       select SOC_KENDRYTE_K210_DTB
        help
-         Builds a device tree for the Kendryte K210 into the Linux image.
+         Build a device tree for the Kendryte K210 into the Linux image.
          This option should be selected if no bootloader is being used.
          If unsure, say Y.
 
+config SOC_CANAAN_K210_DTB_SOURCE
+       string "Source file for the Canaan Kendryte K210 builtin DTB"
+       depends on SOC_CANAAN
+       depends on SOC_CANAAN_K210_DTB_BUILTIN
+       default "k210_generic"
+       help
+         Base name (without suffix, relative to arch/riscv/boot/dts/canaan)
+         for the DTS file that will be used to produce the DTB linked into the
+         kernel.
+
+endif
+
 endmenu
index 8c29e553ef7fd945a13b80a11e69b9f98d4ab932..1368d943f1f398089b2b7a1501f2f76671a41da7 100644 (file)
@@ -12,6 +12,8 @@ OBJCOPYFLAGS    := -O binary
 LDFLAGS_vmlinux :=
 ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
        LDFLAGS_vmlinux := --no-relax
+       KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
+       CC_FLAGS_FTRACE := -fpatchable-function-entry=8
 endif
 
 ifeq ($(CONFIG_64BIT)$(CONFIG_CMODEL_MEDLOW),yy)
@@ -65,6 +67,16 @@ KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)
 # architectures.  It's faster to have GCC emit only aligned accesses.
 KBUILD_CFLAGS += $(call cc-option,-mstrict-align)
 
+ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
+prepare: stack_protector_prepare
+stack_protector_prepare: prepare0
+       $(eval KBUILD_CFLAGS += -mstack-protector-guard=tls               \
+                               -mstack-protector-guard-reg=tp            \
+                               -mstack-protector-guard-offset=$(shell    \
+                       awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}' \
+                                       include/generated/asm-offsets.h))
+endif
+
 # arch specific predefines for sparse
 CHECKFLAGS += -D__riscv -D__riscv_xlen=$(BITS)
 
@@ -83,7 +95,7 @@ PHONY += vdso_install
 vdso_install:
        $(Q)$(MAKE) $(build)=arch/riscv/kernel/vdso $@
 
-ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_SOC_KENDRYTE),yy)
+ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_SOC_CANAAN),yy)
 KBUILD_IMAGE := $(boot)/loader.bin
 else
 KBUILD_IMAGE := $(boot)/Image.gz
index ca1f8cbd78c0e3a44bf17753237d7eb517256148..7ffd502e3e7b9d5ea5a1a78dc796748155c8f49f 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 subdir-y += sifive
-subdir-y += kendryte
+subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
 
 obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y))
diff --git a/arch/riscv/boot/dts/canaan/Makefile b/arch/riscv/boot/dts/canaan/Makefile
new file mode 100644 (file)
index 0000000..9ee7156
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+ifneq ($(CONFIG_SOC_CANAAN_K210_DTB_SOURCE),"")
+dtb-y += $(strip $(shell echo $(CONFIG_SOC_CANAAN_K210_DTB_SOURCE))).dtb
+obj-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += $(addsuffix .o, $(dtb-y))
+endif
diff --git a/arch/riscv/boot/dts/canaan/canaan_kd233.dts b/arch/riscv/boot/dts/canaan/canaan_kd233.dts
new file mode 100644 (file)
index 0000000..039b92a
--- /dev/null
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+       model = "Kendryte KD233";
+       compatible = "canaan,kendryte-kd233", "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+
+       gpio-leds {
+               compatible = "gpio-leds";
+
+               led0 {
+                       gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+               };
+
+               led1 {
+                       gpios = <&gpio0 9 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+
+               key0 {
+                       label = "KEY0";
+                       linux,code = <BTN_0>;
+                       gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+               };
+       };
+};
+
+&fpioa {
+       pinctrl-0 = <&jtag_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+
+       jtag_pinctrl: jtag-pinmux {
+               pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                        <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                        <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                        <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+       };
+
+       uarths_pinctrl: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>,
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>;
+       };
+
+       spi0_pinctrl: spi0-pinmux {
+               pinmux = <K210_FPIOA(6, K210_PCF_GPIOHS20)>,  /* cs */
+                        <K210_FPIOA(7, K210_PCF_SPI0_SCLK)>, /* wr */
+                        <K210_FPIOA(8, K210_PCF_GPIOHS21)>;  /* dc */
+       };
+
+       dvp_pinctrl: dvp-pinmux {
+               pinmux = <K210_FPIOA(9, K210_PCF_SCCB_SCLK)>,
+                        <K210_FPIOA(10, K210_PCF_SCCB_SDA)>,
+                        <K210_FPIOA(11, K210_PCF_DVP_RST)>,
+                        <K210_FPIOA(12, K210_PCF_DVP_VSYNC)>,
+                        <K210_FPIOA(13, K210_PCF_DVP_PWDN)>,
+                        <K210_FPIOA(14, K210_PCF_DVP_XCLK)>,
+                        <K210_FPIOA(15, K210_PCF_DVP_PCLK)>,
+                        <K210_FPIOA(17, K210_PCF_DVP_HSYNC)>;
+       };
+
+       gpiohs_pinctrl: gpiohs-pinmux {
+               pinmux = <K210_FPIOA(16, K210_PCF_GPIOHS0)>,
+                        <K210_FPIOA(20, K210_PCF_GPIOHS4)>, /* Rot. dip sw line 8 */
+                        <K210_FPIOA(21, K210_PCF_GPIOHS5)>, /* Rot. dip sw line 4 */
+                        <K210_FPIOA(22, K210_PCF_GPIOHS6)>, /* Rot. dip sw line 2 */
+                        <K210_FPIOA(23, K210_PCF_GPIOHS7)>, /* Rot. dip sw line 1 */
+                        <K210_FPIOA(24, K210_PCF_GPIOHS8)>,
+                        <K210_FPIOA(25, K210_PCF_GPIOHS9)>,
+                        <K210_FPIOA(26, K210_PCF_GPIOHS10)>;
+       };
+
+       spi1_pinctrl: spi1-pinmux {
+               pinmux = <K210_FPIOA(29, K210_PCF_SPI1_SCLK)>,
+                        <K210_FPIOA(30, K210_PCF_SPI1_D0)>,
+                        <K210_FPIOA(31, K210_PCF_SPI1_D1)>,
+                        <K210_FPIOA(32, K210_PCF_GPIOHS16)>; /* cs */
+       };
+
+       i2s0_pinctrl: i2s0-pinmux {
+               pinmux = <K210_FPIOA(33, K210_PCF_I2S0_IN_D0)>,
+                        <K210_FPIOA(34, K210_PCF_I2S0_WS)>,
+                        <K210_FPIOA(35, K210_PCF_I2S0_SCLK)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio0 {
+       pinctrl-0 = <&gpiohs_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+       pinctrl-0 = <&i2s0_pinctrl>;
+       pinctrl-names = "default";
+};
+
+&spi0 {
+       pinctrl-0 = <&spi0_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
+
+       panel@0 {
+               compatible = "ilitek,ili9341";
+               reg = <0>;
+               dc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
+               spi-max-frequency = <15000000>;
+               status = "disabled";
+       };
+};
+
+&spi1 {
+       pinctrl-0 = <&spi1_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       slot@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               voltage-ranges = <3300 3300>;
+               spi-max-frequency = <25000000>;
+               broken-cd;
+       };
+};
diff --git a/arch/riscv/boot/dts/canaan/k210.dtsi b/arch/riscv/boot/dts/canaan/k210.dtsi
new file mode 100644 (file)
index 0000000..5e8ca81
--- /dev/null
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+#include <dt-bindings/clock/k210-clk.h>
+#include <dt-bindings/pinctrl/k210-fpioa.h>
+#include <dt-bindings/reset/k210-rst.h>
+
+/ {
+       /*
+        * Although the K210 is a 64-bit CPU, the address bus is only 32-bits
+        * wide, and the upper half of all addresses is ignored.
+        */
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "canaan,kendryte-k210";
+
+       aliases {
+               serial0 = &uarths0;
+               serial1 = &uart1;
+               serial2 = &uart2;
+               serial3 = &uart3;
+       };
+
+       /*
+        * The K210 has an sv39 MMU following the privileged specification v1.9.
+        * Since this is a non-ratified draft specification, the kernel does not
+        * support it and the K210 support enabled only for the !MMU case.
+        * Be consistent with this by setting the CPUs MMU type to "none".
+        */
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               timebase-frequency = <7800000>;
+               cpu0: cpu@0 {
+                       device_type = "cpu";
+                       compatible = "canaan,k210", "riscv";
+                       reg = <0>;
+                       riscv,isa = "rv64imafdc";
+                       mmu-type = "riscv,none";
+                       i-cache-block-size = <64>;
+                       i-cache-size = <0x8000>;
+                       d-cache-block-size = <64>;
+                       d-cache-size = <0x8000>;
+                       cpu0_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               interrupt-controller;
+                               compatible = "riscv,cpu-intc";
+                       };
+               };
+               cpu1: cpu@1 {
+                       device_type = "cpu";
+                       compatible = "canaan,k210", "riscv";
+                       reg = <1>;
+                       riscv,isa = "rv64imafdc";
+                       mmu-type = "riscv,none";
+                       i-cache-block-size = <64>;
+                       i-cache-size = <0x8000>;
+                       d-cache-block-size = <64>;
+                       d-cache-size = <0x8000>;
+                       cpu1_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               interrupt-controller;
+                               compatible = "riscv,cpu-intc";
+                       };
+               };
+       };
+
+       sram: memory@80000000 {
+               device_type = "memory";
+               compatible = "canaan,k210-sram";
+               reg = <0x80000000 0x400000>,
+                     <0x80400000 0x200000>,
+                     <0x80600000 0x200000>;
+               reg-names = "sram0", "sram1", "aisram";
+               clocks = <&sysclk K210_CLK_SRAM0>,
+                        <&sysclk K210_CLK_SRAM1>,
+                        <&sysclk K210_CLK_AI>;
+               clock-names = "sram0", "sram1", "aisram";
+       };
+
+       clocks {
+               in0: oscillator {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <26000000>;
+               };
+       };
+
+       soc {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "simple-bus";
+               ranges;
+               interrupt-parent = <&plic0>;
+
+               rom0: nvmem@1000 {
+                       reg = <0x1000 0x1000>;
+                       read-only;
+               };
+
+               clint0: timer@2000000 {
+                       compatible = "canaan,k210-clint", "sifive,clint0";
+                       reg = <0x2000000 0xC000>;
+                       interrupts-extended = <&cpu0_intc 3 &cpu0_intc 7
+                                             &cpu1_intc 3 &cpu1_intc 7>;
+               };
+
+               plic0: interrupt-controller@c000000 {
+                       #interrupt-cells = <1>;
+                       #address-cells = <0>;
+                       compatible = "canaan,k210-plic", "sifive,plic-1.0.0";
+                       reg = <0xC000000 0x4000000>;
+                       interrupt-controller;
+                       interrupts-extended = <&cpu0_intc 11 &cpu1_intc 11>;
+                       riscv,ndev = <65>;
+               };
+
+               uarths0: serial@38000000 {
+                       compatible = "canaan,k210-uarths", "sifive,uart0";
+                       reg = <0x38000000 0x1000>;
+                       interrupts = <33>;
+                       clocks = <&sysclk K210_CLK_CPU>;
+               };
+
+               gpio0: gpio-controller@38001000 {
+                       #interrupt-cells = <2>;
+                       #gpio-cells = <2>;
+                       compatible = "canaan,k210-gpiohs", "sifive,gpio0";
+                       reg = <0x38001000 0x1000>;
+                       interrupt-controller;
+                       interrupts = <34 35 36 37 38 39 40 41
+                                     42 43 44 45 46 47 48 49
+                                     50 51 52 53 54 55 56 57
+                                     58 59 60 61 62 63 64 65>;
+                       gpio-controller;
+                       ngpios = <32>;
+               };
+
+               dmac0: dma-controller@50000000 {
+                       compatible = "snps,axi-dma-1.01a";
+                       reg = <0x50000000 0x1000>;
+                       interrupts = <27 28 29 30 31 32>;
+                       #dma-cells = <1>;
+                       clocks = <&sysclk K210_CLK_DMA>, <&sysclk K210_CLK_DMA>;
+                       clock-names = "core-clk", "cfgr-clk";
+                       resets = <&sysrst K210_RST_DMA>;
+                       dma-channels = <6>;
+                       snps,dma-masters = <2>;
+                       snps,priority = <0 1 2 3 4 5>;
+                       snps,data-width = <5>;
+                       snps,block-size = <0x200000 0x200000 0x200000
+                                          0x200000 0x200000 0x200000>;
+                       snps,axi-max-burst-len = <256>;
+               };
+
+               apb0: bus@50200000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB0>;
+
+                       gpio1: gpio@50200000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "snps,dw-apb-gpio";
+                               reg = <0x50200000 0x80>;
+                               clocks = <&sysclk K210_CLK_APB0>,
+                                        <&sysclk K210_CLK_GPIO>;
+                               clock-names = "bus", "db";
+                               resets = <&sysrst K210_RST_GPIO>;
+
+                               gpio1_0: gpio-port@0 {
+                                       #gpio-cells = <2>;
+                                       #interrupt-cells = <2>;
+                                       compatible = "snps,dw-apb-gpio-port";
+                                       reg = <0>;
+                                       interrupt-controller;
+                                       interrupts = <23>;
+                                       gpio-controller;
+                                       ngpios = <8>;
+                               };
+                       };
+
+                       uart1: serial@50210000 {
+                               compatible = "snps,dw-apb-uart";
+                               reg = <0x50210000 0x100>;
+                               interrupts = <11>;
+                               clocks = <&sysclk K210_CLK_UART1>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "baudclk", "apb_pclk";
+                               resets = <&sysrst K210_RST_UART1>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                       };
+
+                       uart2: serial@50220000 {
+                               compatible = "snps,dw-apb-uart";
+                               reg = <0x50220000 0x100>;
+                               interrupts = <12>;
+                               clocks = <&sysclk K210_CLK_UART2>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "baudclk", "apb_pclk";
+                               resets = <&sysrst K210_RST_UART2>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                       };
+
+                       uart3: serial@50230000 {
+                               compatible = "snps,dw-apb-uart";
+                               reg = <0x50230000 0x100>;
+                               interrupts = <13>;
+                               clocks = <&sysclk K210_CLK_UART3>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "baudclk", "apb_pclk";
+                               resets = <&sysrst K210_RST_UART3>;
+                               reg-io-width = <4>;
+                               reg-shift = <2>;
+                               dcd-override;
+                               dsr-override;
+                               cts-override;
+                               ri-override;
+                       };
+
+                       spi2: spi@50240000 {
+                               compatible = "canaan,k210-spi";
+                               spi-slave;
+                               reg = <0x50240000 0x100>;
+                               #address-cells = <0>;
+                               #size-cells = <0>;
+                               interrupts = <3>;
+                               clocks = <&sysclk K210_CLK_SPI2>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "ssi_clk", "pclk";
+                               resets = <&sysrst K210_RST_SPI2>;
+                               spi-max-frequency = <25000000>;
+                       };
+
+                       i2s0: i2s@50250000 {
+                               compatible = "snps,designware-i2s";
+                               reg = <0x50250000 0x200>;
+                               interrupts = <5>;
+                               clocks = <&sysclk K210_CLK_I2S0>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S0>;
+                       };
+
+                       i2s1: i2s@50260000 {
+                               compatible = "snps,designware-i2s";
+                               reg = <0x50260000 0x200>;
+                               interrupts = <6>;
+                               clocks = <&sysclk K210_CLK_I2S1>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S1>;
+                       };
+
+                       i2s2: i2s@50270000 {
+                               compatible = "snps,designware-i2s";
+                               reg = <0x50270000 0x200>;
+                               interrupts = <7>;
+                               clocks = <&sysclk K210_CLK_I2S2>;
+                               clock-names = "i2sclk";
+                               resets = <&sysrst K210_RST_I2S2>;
+                       };
+
+                       i2c0: i2c@50280000 {
+                               compatible = "snps,designware-i2c";
+                               reg = <0x50280000 0x100>;
+                               interrupts = <8>;
+                               clocks = <&sysclk K210_CLK_I2C0>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "ref", "pclk";
+                               resets = <&sysrst K210_RST_I2C0>;
+                       };
+
+                       i2c1: i2c@50290000 {
+                               compatible = "snps,designware-i2c";
+                               reg = <0x50290000 0x100>;
+                               interrupts = <9>;
+                               clocks = <&sysclk K210_CLK_I2C1>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "ref", "pclk";
+                               resets = <&sysrst K210_RST_I2C1>;
+                       };
+
+                       i2c2: i2c@502a0000 {
+                               compatible = "snps,designware-i2c";
+                               reg = <0x502A0000 0x100>;
+                               interrupts = <10>;
+                               clocks = <&sysclk K210_CLK_I2C2>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "ref", "pclk";
+                               resets = <&sysrst K210_RST_I2C2>;
+                       };
+
+                       fpioa: pinmux@502b0000 {
+                               compatible = "canaan,k210-fpioa";
+                               reg = <0x502B0000 0x100>;
+                               clocks = <&sysclk K210_CLK_FPIOA>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "ref", "pclk";
+                               resets = <&sysrst K210_RST_FPIOA>;
+                               canaan,k210-sysctl-power = <&sysctl 108>;
+                       };
+
+                       timer0: timer@502d0000 {
+                               compatible = "snps,dw-apb-timer";
+                               reg = <0x502D0000 0x100>;
+                               interrupts = <14 15>;
+                               clocks = <&sysclk K210_CLK_TIMER0>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "timer", "pclk";
+                               resets = <&sysrst K210_RST_TIMER0>;
+                       };
+
+                       timer1: timer@502e0000 {
+                               compatible = "snps,dw-apb-timer";
+                               reg = <0x502E0000 0x100>;
+                               interrupts = <16 17>;
+                               clocks = <&sysclk K210_CLK_TIMER1>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "timer", "pclk";
+                               resets = <&sysrst K210_RST_TIMER1>;
+                       };
+
+                       timer2: timer@502f0000 {
+                               compatible = "snps,dw-apb-timer";
+                               reg = <0x502F0000 0x100>;
+                               interrupts = <18 19>;
+                               clocks = <&sysclk K210_CLK_TIMER2>,
+                                        <&sysclk K210_CLK_APB0>;
+                               clock-names = "timer", "pclk";
+                               resets = <&sysrst K210_RST_TIMER2>;
+                       };
+               };
+
+               apb1: bus@50400000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB1>;
+
+                       wdt0: watchdog@50400000 {
+                               compatible = "snps,dw-wdt";
+                               reg = <0x50400000 0x100>;
+                               interrupts = <21>;
+                               clocks = <&sysclk K210_CLK_WDT0>,
+                                        <&sysclk K210_CLK_APB1>;
+                               clock-names = "tclk", "pclk";
+                               resets = <&sysrst K210_RST_WDT0>;
+                       };
+
+                       wdt1: watchdog@50410000 {
+                               compatible = "snps,dw-wdt";
+                               reg = <0x50410000 0x100>;
+                               interrupts = <22>;
+                               clocks = <&sysclk K210_CLK_WDT1>,
+                                        <&sysclk K210_CLK_APB1>;
+                               clock-names = "tclk", "pclk";
+                               resets = <&sysrst K210_RST_WDT1>;
+                       };
+
+                       sysctl: syscon@50440000 {
+                               compatible = "canaan,k210-sysctl",
+                                            "syscon", "simple-mfd";
+                               reg = <0x50440000 0x100>;
+                               clocks = <&sysclk K210_CLK_APB1>;
+                               clock-names = "pclk";
+
+                               sysclk: clock-controller {
+                                       #clock-cells = <1>;
+                                       compatible = "canaan,k210-clk";
+                                       clocks = <&in0>;
+                               };
+
+                               sysrst: reset-controller {
+                                       compatible = "canaan,k210-rst";
+                                       #reset-cells = <1>;
+                               };
+
+                               reboot: syscon-reboot {
+                                       compatible = "syscon-reboot";
+                                       regmap = <&sysctl>;
+                                       offset = <48>;
+                                       mask = <1>;
+                                       value = <1>;
+                               };
+                       };
+               };
+
+               apb2: bus@52000000 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "simple-pm-bus";
+                       ranges;
+                       clocks = <&sysclk K210_CLK_APB2>;
+
+                       spi0: spi@52000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "canaan,k210-spi";
+                               reg = <0x52000000 0x100>;
+                               interrupts = <1>;
+                               clocks = <&sysclk K210_CLK_SPI0>,
+                                        <&sysclk K210_CLK_APB2>;
+                               clock-names = "ssi_clk", "pclk";
+                               resets = <&sysrst K210_RST_SPI0>;
+                               reset-names = "spi";
+                               spi-max-frequency = <25000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                       };
+
+                       spi1: spi@53000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "canaan,k210-spi";
+                               reg = <0x53000000 0x100>;
+                               interrupts = <2>;
+                               clocks = <&sysclk K210_CLK_SPI1>,
+                                        <&sysclk K210_CLK_APB2>;
+                               clock-names = "ssi_clk", "pclk";
+                               resets = <&sysrst K210_RST_SPI1>;
+                               reset-names = "spi";
+                               spi-max-frequency = <25000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                       };
+
+                       spi3: spi@54000000 {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               compatible = "snps,dwc-ssi-1.01a";
+                               reg = <0x54000000 0x200>;
+                               interrupts = <4>;
+                               clocks = <&sysclk K210_CLK_SPI3>,
+                                        <&sysclk K210_CLK_APB2>;
+                               clock-names = "ssi_clk", "pclk";
+                               resets = <&sysrst K210_RST_SPI3>;
+                               reset-names = "spi";
+                               /* Could possibly go up to 200 MHz */
+                               spi-max-frequency = <100000000>;
+                               num-cs = <4>;
+                               reg-io-width = <4>;
+                       };
+               };
+       };
+};
diff --git a/arch/riscv/boot/dts/canaan/k210_generic.dts b/arch/riscv/boot/dts/canaan/k210_generic.dts
new file mode 100644 (file)
index 0000000..396c8ca
--- /dev/null
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+       model = "Kendryte K210 generic";
+       compatible = "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+};
+
+&fpioa {
+       pinctrl-0 = <&jtag_pins>;
+       pinctrl-names = "default";
+       status = "okay";
+
+       jtag_pins: jtag-pinmux {
+               pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                        <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                        <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                        <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+       };
+
+       uarths_pins: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>,
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pins>;
+       pinctrl-names = "default";
+       status = "okay";
+};
diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_bit.dts
new file mode 100644 (file)
index 0000000..0bcaf35
--- /dev/null
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+       model = "SiPeed MAIX BiT";
+       compatible = "sipeed,maix-bit", "sipeed,maix-bitm",
+                    "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+
+       gpio-leds {
+               compatible = "gpio-leds";
+
+               led0 {
+                       color = <LED_COLOR_ID_GREEN>;
+                       label = "green";
+                       gpios = <&gpio1_0 4 GPIO_ACTIVE_LOW>;
+               };
+
+               led1 {
+                       color = <LED_COLOR_ID_RED>;
+                       label = "red";
+                       gpios = <&gpio1_0 5 GPIO_ACTIVE_LOW>;
+               };
+
+               led2 {
+                       color = <LED_COLOR_ID_BLUE>;
+                       label = "blue";
+                       gpios = <&gpio1_0 6 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+
+               boot {
+                       label = "BOOT";
+                       linux,code = <BTN_0>;
+                       gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+               };
+       };
+};
+
+&fpioa {
+       pinctrl-names = "default";
+       pinctrl-0 = <&jtag_pinctrl>;
+       status = "okay";
+
+       jtag_pinctrl: jtag-pinmux {
+               pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                        <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                        <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                        <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+       };
+
+       uarths_pinctrl: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>,
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>;
+       };
+
+       gpio_pinctrl: gpio-pinmux {
+               pinmux = <K210_FPIOA(8, K210_PCF_GPIO0)>,
+                        <K210_FPIOA(9, K210_PCF_GPIO1)>,
+                        <K210_FPIOA(10, K210_PCF_GPIO2)>,
+                        <K210_FPIOA(11, K210_PCF_GPIO3)>,
+                        <K210_FPIOA(12, K210_PCF_GPIO4)>,
+                        <K210_FPIOA(13, K210_PCF_GPIO5)>,
+                        <K210_FPIOA(14, K210_PCF_GPIO6)>,
+                        <K210_FPIOA(15, K210_PCF_GPIO7)>;
+       };
+
+       gpiohs_pinctrl: gpiohs-pinmux {
+               pinmux = <K210_FPIOA(16, K210_PCF_GPIOHS0)>,
+                        <K210_FPIOA(17, K210_PCF_GPIOHS1)>,
+                        <K210_FPIOA(21, K210_PCF_GPIOHS5)>,
+                        <K210_FPIOA(22, K210_PCF_GPIOHS6)>,
+                        <K210_FPIOA(23, K210_PCF_GPIOHS7)>,
+                        <K210_FPIOA(24, K210_PCF_GPIOHS8)>,
+                        <K210_FPIOA(25, K210_PCF_GPIOHS9)>,
+                        <K210_FPIOA(32, K210_PCF_GPIOHS16)>,
+                        <K210_FPIOA(33, K210_PCF_GPIOHS17)>,
+                        <K210_FPIOA(34, K210_PCF_GPIOHS18)>,
+                        <K210_FPIOA(35, K210_PCF_GPIOHS19)>;
+       };
+
+       i2s0_pinctrl: i2s0-pinmux {
+               pinmux = <K210_FPIOA(18, K210_PCF_I2S0_SCLK)>,
+                        <K210_FPIOA(19, K210_PCF_I2S0_WS)>,
+                        <K210_FPIOA(20, K210_PCF_I2S0_IN_D0)>;
+       };
+
+       dvp_pinctrl: dvp-pinmux {
+               pinmux = <K210_FPIOA(40, K210_PCF_SCCB_SDA)>,
+                        <K210_FPIOA(41, K210_PCF_SCCB_SCLK)>,
+                        <K210_FPIOA(42, K210_PCF_DVP_RST)>,
+                        <K210_FPIOA(43, K210_PCF_DVP_VSYNC)>,
+                        <K210_FPIOA(44, K210_PCF_DVP_PWDN)>,
+                        <K210_FPIOA(45, K210_PCF_DVP_HSYNC)>,
+                        <K210_FPIOA(46, K210_PCF_DVP_XCLK)>,
+                        <K210_FPIOA(47, K210_PCF_DVP_PCLK)>;
+       };
+
+       spi0_pinctrl: spi0-pinmux {
+               pinmux = <K210_FPIOA(36, K210_PCF_GPIOHS20)>,  /* cs */
+                        <K210_FPIOA(37, K210_PCF_GPIOHS21)>,  /* rst */
+                        <K210_FPIOA(38, K210_PCF_GPIOHS22)>,  /* dc */
+                        <K210_FPIOA(39, K210_PCF_SPI0_SCLK)>; /* wr */
+       };
+
+       spi1_pinctrl: spi1-pinmux {
+               pinmux = <K210_FPIOA(26, K210_PCF_SPI1_D1)>,
+                        <K210_FPIOA(27, K210_PCF_SPI1_SCLK)>,
+                        <K210_FPIOA(28, K210_PCF_SPI1_D0)>,
+                        <K210_FPIOA(29, K210_PCF_GPIOHS13)>; /* cs */
+       };
+
+       i2c1_pinctrl: i2c1-pinmux {
+               pinmux = <K210_FPIOA(30, K210_PCF_I2C1_SCLK)>,
+                        <K210_FPIOA(31, K210_PCF_I2C1_SDA)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio0 {
+       pinctrl-0 = <&gpiohs_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio1 {
+       pinctrl-0 = <&gpio_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+       pinctrl-0 = <&i2s0_pinctrl>;
+       pinctrl-names = "default";
+};
+
+&i2c1 {
+       pinctrl-0 = <&i2c1_pinctrl>;
+       pinctrl-names = "default";
+       clock-frequency = <400000>;
+       status = "okay";
+};
+
+&spi0 {
+       pinctrl-0 = <&spi0_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
+
+       panel@0 {
+               compatible = "sitronix,st7789v";
+               reg = <0>;
+               reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+               dc-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
+               spi-max-frequency = <15000000>;
+               spi-cs-high;
+               status = "disabled";
+       };
+};
+
+&spi1 {
+       pinctrl-0 = <&spi1_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       slot@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               voltage-ranges = <3300 3300>;
+               spi-max-frequency = <25000000>;
+               broken-cd;
+       };
+};
+
+&spi3 {
+       spi-flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+               m25p,fast-read;
+               broken-flash-reset;
+       };
+};
diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_dock.dts
new file mode 100644 (file)
index 0000000..ac8a03f
--- /dev/null
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+       model = "SiPeed MAIX Dock";
+       compatible = "sipeed,maix-dock-m1", "sipeed,maix-dock-m1w",
+                    "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+
+       gpio-leds {
+               compatible = "gpio-leds";
+
+               /*
+                * Note: the board wiring drawing documents green on
+                * gpio #4, red on gpio #5 and blue on gpio #6. However,
+                * the board is actually wired differently as defined here.
+                */
+               led0 {
+                       color = <LED_COLOR_ID_BLUE>;
+                       label = "blue";
+                       gpios = <&gpio1_0 4 GPIO_ACTIVE_LOW>;
+               };
+
+               led1 {
+                       color = <LED_COLOR_ID_GREEN>;
+                       label = "green";
+                       gpios = <&gpio1_0 5 GPIO_ACTIVE_LOW>;
+               };
+
+               led2 {
+                       color = <LED_COLOR_ID_RED>;
+                       label = "red";
+                       gpios = <&gpio1_0 6 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+
+               boot {
+                       label = "BOOT";
+                       linux,code = <BTN_0>;
+                       gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+               };
+       };
+};
+
+&fpioa {
+       pinctrl-0 = <&jtag_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+
+       jtag_pinctrl: jtag-pinmux {
+               pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                        <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                        <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                        <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+       };
+
+       uarths_pinctrl: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>,
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>;
+       };
+
+       gpio_pinctrl: gpio-pinmux {
+               pinmux = <K210_FPIOA(8, K210_PCF_GPIO0)>,
+                        <K210_FPIOA(11, K210_PCF_GPIO3)>,
+                        <K210_FPIOA(12, K210_PCF_GPIO4)>,
+                        <K210_FPIOA(13, K210_PCF_GPIO5)>,
+                        <K210_FPIOA(14, K210_PCF_GPIO6)>,
+                        <K210_FPIOA(15, K210_PCF_GPIO7)>;
+       };
+
+       gpiohs_pinctrl: gpiohs-pinmux {
+               pinmux = <K210_FPIOA(16, K210_PCF_GPIOHS0)>,
+                        <K210_FPIOA(17, K210_PCF_GPIOHS1)>,
+                        <K210_FPIOA(21, K210_PCF_GPIOHS5)>,
+                        <K210_FPIOA(22, K210_PCF_GPIOHS6)>,
+                        <K210_FPIOA(23, K210_PCF_GPIOHS7)>,
+                        <K210_FPIOA(24, K210_PCF_GPIOHS8)>,
+                        <K210_FPIOA(25, K210_PCF_GPIOHS9)>,
+                        <K210_FPIOA(32, K210_PCF_GPIOHS16)>,
+                        <K210_FPIOA(33, K210_PCF_GPIOHS17)>,
+                        <K210_FPIOA(34, K210_PCF_GPIOHS18)>,
+                        <K210_FPIOA(35, K210_PCF_GPIOHS19)>;
+       };
+
+       i2s0_pinctrl: i2s0-pinmux {
+               pinmux = <K210_FPIOA(18, K210_PCF_I2S0_SCLK)>,
+                        <K210_FPIOA(19, K210_PCF_I2S0_WS)>,
+                        <K210_FPIOA(20, K210_PCF_I2S0_IN_D0)>;
+       };
+
+       dvp_pinctrl: dvp-pinmux {
+               pinmux = <K210_FPIOA(40, K210_PCF_SCCB_SDA)>,
+                        <K210_FPIOA(41, K210_PCF_SCCB_SCLK)>,
+                        <K210_FPIOA(42, K210_PCF_DVP_RST)>,
+                        <K210_FPIOA(43, K210_PCF_DVP_VSYNC)>,
+                        <K210_FPIOA(44, K210_PCF_DVP_PWDN)>,
+                        <K210_FPIOA(45, K210_PCF_DVP_HSYNC)>,
+                        <K210_FPIOA(46, K210_PCF_DVP_XCLK)>,
+                        <K210_FPIOA(47, K210_PCF_DVP_PCLK)>;
+       };
+
+       spi0_pinctrl: spi0-pinmux {
+               pinmux = <K210_FPIOA(36, K210_PCF_GPIOHS20)>,  /* cs */
+                        <K210_FPIOA(37, K210_PCF_GPIOHS21)>,  /* rst */
+                        <K210_FPIOA(38, K210_PCF_GPIOHS22)>,  /* dc */
+                        <K210_FPIOA(39, K210_PCF_SPI0_SCLK)>; /* wr */
+       };
+
+       spi1_pinctrl: spi1-pinmux {
+               pinmux = <K210_FPIOA(26, K210_PCF_SPI1_D1)>,
+                        <K210_FPIOA(27, K210_PCF_SPI1_SCLK)>,
+                        <K210_FPIOA(28, K210_PCF_SPI1_D0)>,
+                        <K210_FPIOA(29, K210_PCF_GPIOHS13)>; /* cs */
+       };
+
+       i2c1_pinctrl: i2c1-pinmux {
+               pinmux = <K210_FPIOA(9, K210_PCF_I2C1_SCLK)>,
+                        <K210_FPIOA(10, K210_PCF_I2C1_SDA)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio0 {
+       pinctrl-0 = <&gpiohs_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio1 {
+       pinctrl-0 = <&gpio_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+       pinctrl-0 = <&i2s0_pinctrl>;
+       pinctrl-names = "default";
+};
+
+&i2c1 {
+       pinctrl-0 = <&i2c1_pinctrl>;
+       pinctrl-names = "default";
+       clock-frequency = <400000>;
+       status = "okay";
+};
+
+&spi0 {
+       pinctrl-0 = <&spi0_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
+
+       panel@0 {
+               compatible = "sitronix,st7789v";
+               reg = <0>;
+               reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+               dc-gpios = <&gpio0 22 0>;
+               spi-max-frequency = <15000000>;
+               status = "disabled";
+       };
+};
+
+&spi1 {
+       pinctrl-0 = <&spi1_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       slot@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               voltage-ranges = <3300 3300>;
+               spi-max-frequency = <25000000>;
+               broken-cd;
+       };
+};
+
+&spi3 {
+       spi-flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+               m25p,fast-read;
+               broken-flash-reset;
+       };
+};
diff --git a/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts b/arch/riscv/boot/dts/canaan/sipeed_maix_go.dts
new file mode 100644 (file)
index 0000000..6239981
--- /dev/null
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+       model = "SiPeed MAIX GO";
+       compatible = "sipeed,maix-go", "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+
+       gpio-leds {
+               compatible = "gpio-leds";
+
+               led0 {
+                       color = <LED_COLOR_ID_GREEN>;
+                       label = "green";
+                       gpios = <&gpio1_0 4 GPIO_ACTIVE_LOW>;
+               };
+
+               led1 {
+                       color = <LED_COLOR_ID_RED>;
+                       label = "red";
+                       gpios = <&gpio1_0 5 GPIO_ACTIVE_LOW>;
+               };
+
+               led2 {
+                       color = <LED_COLOR_ID_BLUE>;
+                       label = "blue";
+                       gpios = <&gpio1_0 6 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+
+               up {
+                       label = "UP";
+                       linux,code = <BTN_1>;
+                       gpios = <&gpio1_0 7 GPIO_ACTIVE_LOW>;
+               };
+
+               press {
+                       label = "PRESS";
+                       linux,code = <BTN_0>;
+                       gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+               };
+
+               down {
+                       label = "DOWN";
+                       linux,code = <BTN_2>;
+                       gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
+               };
+       };
+};
+
+&fpioa {
+       pinctrl-0 = <&jtag_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+
+       jtag_pinctrl: jtag-pinmux {
+               pinmux = <K210_FPIOA(0, K210_PCF_JTAG_TCLK)>,
+                        <K210_FPIOA(1, K210_PCF_JTAG_TDI)>,
+                        <K210_FPIOA(2, K210_PCF_JTAG_TMS)>,
+                        <K210_FPIOA(3, K210_PCF_JTAG_TDO)>;
+       };
+
+       uarths_pinctrl: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>,
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>;
+       };
+
+       gpio_pinctrl: gpio-pinmux {
+               pinmux = <K210_FPIOA(8, K210_PCF_GPIO0)>,
+                        <K210_FPIOA(9, K210_PCF_GPIO1)>,
+                        <K210_FPIOA(10, K210_PCF_GPIO2)>,
+                        <K210_FPIOA(11, K210_PCF_GPIO3)>,
+                        <K210_FPIOA(12, K210_PCF_GPIO4)>,
+                        <K210_FPIOA(13, K210_PCF_GPIO5)>,
+                        <K210_FPIOA(14, K210_PCF_GPIO6)>,
+                        <K210_FPIOA(15, K210_PCF_GPIO7)>;
+       };
+
+       gpiohs_pinctrl: gpiohs-pinmux {
+               pinmux = <K210_FPIOA(16, K210_PCF_GPIOHS0)>,
+                        <K210_FPIOA(17, K210_PCF_GPIOHS1)>,
+                        <K210_FPIOA(21, K210_PCF_GPIOHS5)>,
+                        <K210_FPIOA(22, K210_PCF_GPIOHS6)>,
+                        <K210_FPIOA(23, K210_PCF_GPIOHS7)>,
+                        <K210_FPIOA(24, K210_PCF_GPIOHS8)>,
+                        <K210_FPIOA(25, K210_PCF_GPIOHS9)>,
+                        <K210_FPIOA(32, K210_PCF_GPIOHS16)>,
+                        <K210_FPIOA(33, K210_PCF_GPIOHS17)>,
+                        <K210_FPIOA(34, K210_PCF_GPIOHS18)>,
+                        <K210_FPIOA(35, K210_PCF_GPIOHS19)>;
+       };
+
+       i2s0_pinctrl: i2s0-pinmux {
+               pinmux = <K210_FPIOA(18, K210_PCF_I2S0_SCLK)>,
+                        <K210_FPIOA(19, K210_PCF_I2S0_WS)>,
+                        <K210_FPIOA(20, K210_PCF_I2S0_IN_D0)>;
+       };
+
+       dvp_pinctrl: dvp-pinmux {
+               pinmux = <K210_FPIOA(40, K210_PCF_SCCB_SDA)>,
+                        <K210_FPIOA(41, K210_PCF_SCCB_SCLK)>,
+                        <K210_FPIOA(42, K210_PCF_DVP_RST)>,
+                        <K210_FPIOA(43, K210_PCF_DVP_VSYNC)>,
+                        <K210_FPIOA(44, K210_PCF_DVP_PWDN)>,
+                        <K210_FPIOA(45, K210_PCF_DVP_HSYNC)>,
+                        <K210_FPIOA(46, K210_PCF_DVP_XCLK)>,
+                        <K210_FPIOA(47, K210_PCF_DVP_PCLK)>;
+       };
+
+       spi0_pinctrl: spi0-pinmux {
+               pinmux = <K210_FPIOA(36, K210_PCF_GPIOHS20)>,  /* cs */
+                        <K210_FPIOA(37, K210_PCF_GPIOHS21)>,  /* rst */
+                        <K210_FPIOA(38, K210_PCF_GPIOHS22)>,  /* dc */
+                        <K210_FPIOA(39, K210_PCF_SPI0_SCLK)>; /* wr */
+       };
+
+       spi1_pinctrl: spi1-pinmux {
+               pinmux = <K210_FPIOA(26, K210_PCF_SPI1_D1)>,
+                        <K210_FPIOA(27, K210_PCF_SPI1_SCLK)>,
+                        <K210_FPIOA(28, K210_PCF_SPI1_D0)>,
+                        <K210_FPIOA(29, K210_PCF_GPIOHS13)>; /* cs */
+       };
+
+       i2c1_pinctrl: i2c1-pinmux {
+               pinmux = <K210_FPIOA(30, K210_PCF_I2C1_SCLK)>,
+                        <K210_FPIOA(31, K210_PCF_I2C1_SDA)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio0 {
+       pinctrl-0 = <&gpiohs_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio1 {
+       pinctrl-0 = <&gpio_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+       pinctrl-0 = <&i2s0_pinctrl>;
+       pinctrl-names = "default";
+};
+
+&i2c1 {
+       pinctrl-0 = <&i2c1_pinctrl>;
+       pinctrl-names = "default";
+       clock-frequency = <400000>;
+       status = "okay";
+};
+
+&spi0 {
+       pinctrl-0 = <&spi0_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
+
+       panel@0 {
+               compatible = "sitronix,st7789v";
+               reg = <0>;
+               reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+               dc-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
+               spi-max-frequency = <15000000>;
+               status = "disabled";
+       };
+};
+
+&spi1 {
+       pinctrl-0 = <&spi1_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       slot@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               voltage-ranges = <3300 3300>;
+               spi-max-frequency = <25000000>;
+               broken-cd;
+       };
+};
+
+&spi3 {
+       spi-flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+               m25p,fast-read;
+               broken-flash-reset;
+       };
+};
diff --git a/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts b/arch/riscv/boot/dts/canaan/sipeed_maixduino.dts
new file mode 100644 (file)
index 0000000..cf605ba
--- /dev/null
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+
+/dts-v1/;
+
+#include "k210.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+       model = "SiPeed MAIXDUINO";
+       compatible = "sipeed,maixduino", "canaan,kendryte-k210";
+
+       chosen {
+               bootargs = "earlycon console=ttySIF0";
+               stdout-path = "serial0:115200n8";
+       };
+
+       gpio-keys {
+               compatible = "gpio-keys";
+
+               boot {
+                       label = "BOOT";
+                       linux,code = <BTN_0>;
+                       gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       vcc_3v3: regulator-3v3 {
+               compatible = "regulator-fixed";
+               regulator-name = "3v3";
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+       };
+};
+
+&fpioa {
+       status = "okay";
+
+       uarths_pinctrl: uarths-pinmux {
+               pinmux = <K210_FPIOA(4, K210_PCF_UARTHS_RX)>, /* Header "0" */
+                        <K210_FPIOA(5, K210_PCF_UARTHS_TX)>; /* Header "1" */
+       };
+
+       gpio_pinctrl: gpio-pinmux {
+               pinmux = <K210_FPIOA(8, K210_PCF_GPIO0)>,
+                        <K210_FPIOA(9, K210_PCF_GPIO1)>;
+       };
+
+       gpiohs_pinctrl: gpiohs-pinmux {
+               pinmux = <K210_FPIOA(16, K210_PCF_GPIOHS0)>,  /* BOOT */
+                        <K210_FPIOA(21, K210_PCF_GPIOHS2)>,  /* Header "2" */
+                        <K210_FPIOA(22, K210_PCF_GPIOHS3)>,  /* Header "3" */
+                        <K210_FPIOA(23, K210_PCF_GPIOHS4)>,  /* Header "4" */
+                        <K210_FPIOA(24, K210_PCF_GPIOHS5)>,  /* Header "5" */
+                        <K210_FPIOA(32, K210_PCF_GPIOHS6)>,  /* Header "6" */
+                        <K210_FPIOA(15, K210_PCF_GPIOHS7)>,  /* Header "7" */
+                        <K210_FPIOA(14, K210_PCF_GPIOHS8)>,  /* Header "8" */
+                        <K210_FPIOA(13, K210_PCF_GPIOHS9)>,  /* Header "9" */
+                        <K210_FPIOA(12, K210_PCF_GPIOHS10)>, /* Header "10" */
+                        <K210_FPIOA(11, K210_PCF_GPIOHS11)>, /* Header "11" */
+                        <K210_FPIOA(10, K210_PCF_GPIOHS12)>, /* Header "12" */
+                        <K210_FPIOA(3,  K210_PCF_GPIOHS13)>; /* Header "13" */
+       };
+
+       i2s0_pinctrl: i2s0-pinmux {
+               pinmux = <K210_FPIOA(18, K210_PCF_I2S0_SCLK)>,
+                        <K210_FPIOA(19, K210_PCF_I2S0_WS)>,
+                        <K210_FPIOA(20, K210_PCF_I2S0_IN_D0)>;
+       };
+
+       spi1_pinctrl: spi1-pinmux {
+               pinmux = <K210_FPIOA(26, K210_PCF_SPI1_D1)>,
+                        <K210_FPIOA(27, K210_PCF_SPI1_SCLK)>,
+                        <K210_FPIOA(28, K210_PCF_SPI1_D0)>,
+                        <K210_FPIOA(29, K210_PCF_GPIO2)>; /* cs */
+       };
+
+       i2c1_pinctrl: i2c1-pinmux {
+               pinmux = <K210_FPIOA(30, K210_PCF_I2C1_SCLK)>, /* Header "scl" */
+                        <K210_FPIOA(31, K210_PCF_I2C1_SDA)>;  /* Header "sda" */
+       };
+
+       i2s1_pinctrl: i2s1-pinmux {
+               pinmux = <K210_FPIOA(33, K210_PCF_I2S1_WS)>,
+                        <K210_FPIOA(34, K210_PCF_I2S1_IN_D0)>,
+                        <K210_FPIOA(35, K210_PCF_I2S1_SCLK)>;
+       };
+
+       spi0_pinctrl: spi0-pinmux {
+               pinmux = <K210_FPIOA(36, K210_PCF_GPIOHS20)>,  /* cs */
+                        <K210_FPIOA(37, K210_PCF_GPIOHS21)>,  /* rst */
+                        <K210_FPIOA(38, K210_PCF_GPIOHS22)>,  /* dc */
+                        <K210_FPIOA(39, K210_PCF_SPI0_SCLK)>; /* wr */
+       };
+
+       dvp_pinctrl: dvp-pinmux {
+               pinmux = <K210_FPIOA(40, K210_PCF_SCCB_SDA)>,
+                        <K210_FPIOA(41, K210_PCF_SCCB_SCLK)>,
+                        <K210_FPIOA(42, K210_PCF_DVP_RST)>,
+                        <K210_FPIOA(43, K210_PCF_DVP_VSYNC)>,
+                        <K210_FPIOA(44, K210_PCF_DVP_PWDN)>,
+                        <K210_FPIOA(45, K210_PCF_DVP_HSYNC)>,
+                        <K210_FPIOA(46, K210_PCF_DVP_XCLK)>,
+                        <K210_FPIOA(47, K210_PCF_DVP_PCLK)>;
+       };
+};
+
+&uarths0 {
+       pinctrl-0 = <&uarths_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio0 {
+       pinctrl-0 = <&gpiohs_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&gpio1 {
+       pinctrl-0 = <&gpio_pinctrl>;
+       pinctrl-names = "default";
+       status = "okay";
+};
+
+&i2s0 {
+       #sound-dai-cells = <1>;
+       pinctrl-0 = <&i2s0_pinctrl>;
+       pinctrl-names = "default";
+};
+
+&i2c1 {
+       pinctrl-0 = <&i2c1_pinctrl>;
+       pinctrl-names = "default";
+       clock-frequency = <400000>;
+       status = "okay";
+};
+
+&spi0 {
+       pinctrl-0 = <&spi0_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio0 20 GPIO_ACTIVE_HIGH>;
+
+       panel@0 {
+               compatible = "sitronix,st7789v";
+               reg = <0>;
+               reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>;
+               dc-gpios = <&gpio0 22 0>;
+               spi-max-frequency = <15000000>;
+               power-supply = <&vcc_3v3>;
+       };
+};
+
+&spi1 {
+       pinctrl-0 = <&spi1_pinctrl>;
+       pinctrl-names = "default";
+       num-cs = <1>;
+       cs-gpios = <&gpio1_0 2 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       slot@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               voltage-ranges = <3300 3300>;
+               spi-max-frequency = <25000000>;
+               broken-cd;
+       };
+};
+
+&spi3 {
+       spi-flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+               m25p,fast-read;
+               broken-flash-reset;
+       };
+};
diff --git a/arch/riscv/boot/dts/kendryte/Makefile b/arch/riscv/boot/dts/kendryte/Makefile
deleted file mode 100644 (file)
index 1a88e61..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-dtb-$(CONFIG_SOC_KENDRYTE_K210_DTB) += k210.dtb
-
-obj-$(CONFIG_SOC_KENDRYTE_K210_DTB_BUILTIN) += $(addsuffix .o, $(dtb-y))
diff --git a/arch/riscv/boot/dts/kendryte/k210.dts b/arch/riscv/boot/dts/kendryte/k210.dts
deleted file mode 100644 (file)
index 0d1f28f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2020 Western Digital Corporation or its affiliates.
- */
-
-/dts-v1/;
-
-#include "k210.dtsi"
-
-/ {
-       model = "Kendryte K210 generic";
-       compatible = "kendryte,k210";
-
-       chosen {
-               bootargs = "earlycon console=ttySIF0";
-               stdout-path = "serial0";
-       };
-};
-
-&uarths0 {
-       status = "okay";
-};
-
diff --git a/arch/riscv/boot/dts/kendryte/k210.dtsi b/arch/riscv/boot/dts/kendryte/k210.dtsi
deleted file mode 100644 (file)
index d2d0ff6..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
- * Copyright (C) 2020 Western Digital Corporation or its affiliates.
- */
-#include <dt-bindings/clock/k210-clk.h>
-
-/ {
-       /*
-        * Although the K210 is a 64-bit CPU, the address bus is only 32-bits
-        * wide, and the upper half of all addresses is ignored.
-        */
-       #address-cells = <1>;
-       #size-cells = <1>;
-       compatible = "kendryte,k210";
-
-       aliases {
-               serial0 = &uarths0;
-       };
-
-       /*
-        * The K210 has an sv39 MMU following the priviledge specification v1.9.
-        * Since this is a non-ratified draft specification, the kernel does not
-        * support it and the K210 support enabled only for the !MMU case.
-        * Be consistent with this by setting the CPUs MMU type to "none".
-        */
-       cpus {
-               #address-cells = <1>;
-               #size-cells = <0>;
-               timebase-frequency = <7800000>;
-               cpu0: cpu@0 {
-                       device_type = "cpu";
-                       reg = <0>;
-                       compatible = "kendryte,k210", "sifive,rocket0", "riscv";
-                       riscv,isa = "rv64imafdc";
-                       mmu-type = "none";
-                       i-cache-size = <0x8000>;
-                       i-cache-block-size = <64>;
-                       d-cache-size = <0x8000>;
-                       d-cache-block-size = <64>;
-                       clocks = <&sysctl K210_CLK_CPU>;
-                       clock-frequency = <390000000>;
-                       cpu0_intc: interrupt-controller {
-                               #interrupt-cells = <1>;
-                               interrupt-controller;
-                               compatible = "riscv,cpu-intc";
-                       };
-               };
-               cpu1: cpu@1 {
-                       device_type = "cpu";
-                       reg = <1>;
-                       compatible = "kendryte,k210", "sifive,rocket0", "riscv";
-                       riscv,isa = "rv64imafdc";
-                       mmu-type = "none";
-                       i-cache-size = <0x8000>;
-                       i-cache-block-size = <64>;
-                       d-cache-size = <0x8000>;
-                       d-cache-block-size = <64>;
-                       clocks = <&sysctl K210_CLK_CPU>;
-                       clock-frequency = <390000000>;
-                       cpu1_intc: interrupt-controller {
-                               #interrupt-cells = <1>;
-                               interrupt-controller;
-                               compatible = "riscv,cpu-intc";
-                       };
-               };
-       };
-
-       sram: memory@80000000 {
-               device_type = "memory";
-               reg = <0x80000000 0x400000>,
-                     <0x80400000 0x200000>,
-                     <0x80600000 0x200000>;
-               reg-names = "sram0", "sram1", "aisram";
-       };
-
-       clocks {
-               in0: oscillator {
-                       compatible = "fixed-clock";
-                       #clock-cells = <0>;
-                       clock-frequency = <26000000>;
-               };
-       };
-
-       soc {
-               #address-cells = <1>;
-               #size-cells = <1>;
-               compatible = "kendryte,k210-soc", "simple-bus";
-               ranges;
-               interrupt-parent = <&plic0>;
-
-               sysctl: sysctl@50440000 {
-                       compatible = "kendryte,k210-sysctl", "simple-mfd";
-                       reg = <0x50440000 0x1000>;
-                       #clock-cells = <1>;
-               };
-
-               clint0: clint@2000000 {
-                       #interrupt-cells = <1>;
-                       compatible = "riscv,clint0";
-                       reg = <0x2000000 0xC000>;
-                       interrupts-extended =  <&cpu0_intc 3 &cpu0_intc 7
-                                               &cpu1_intc 3 &cpu1_intc 7>;
-                       clocks = <&sysctl K210_CLK_ACLK>;
-               };
-
-               plic0: interrupt-controller@c000000 {
-                       #interrupt-cells = <1>;
-                       interrupt-controller;
-                       compatible = "kendryte,k210-plic0", "riscv,plic0";
-                       reg = <0xC000000 0x4000000>;
-                       interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 0xffffffff>,
-                                             <&cpu1_intc 11>, <&cpu1_intc 0xffffffff>;
-                       riscv,ndev = <65>;
-                       riscv,max-priority = <7>;
-               };
-
-               uarths0: serial@38000000 {
-                       compatible = "kendryte,k210-uarths", "sifive,uart0";
-                       reg = <0x38000000 0x1000>;
-                       interrupts = <33>;
-                       clocks = <&sysctl K210_CLK_CPU>;
-               };
-       };
-};
index 6d6189e6e4af4d8e906e573677e1b4b6728cbf8b..74c47fe9fc22e037c6eb441855fa030fa89a7f4f 100644 (file)
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
-dtb-$(CONFIG_SOC_SIFIVE) += hifive-unleashed-a00.dtb
+dtb-$(CONFIG_SOC_SIFIVE) += hifive-unleashed-a00.dtb \
+                           hifive-unmatched-a00.dtb
diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
new file mode 100644 (file)
index 0000000..eeb4f8c
--- /dev/null
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Copyright (c) 2020 SiFive, Inc */
+
+/dts-v1/;
+
+#include <dt-bindings/clock/sifive-fu740-prci.h>
+
+/ {
+       #address-cells = <2>;
+       #size-cells = <2>;
+       compatible = "sifive,fu740-c000", "sifive,fu740";
+
+       aliases {
+               serial0 = &uart0;
+               serial1 = &uart1;
+               ethernet0 = &eth0;
+       };
+
+       chosen {
+       };
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               cpu0: cpu@0 {
+                       compatible = "sifive,bullet0", "riscv";
+                       device_type = "cpu";
+                       i-cache-block-size = <64>;
+                       i-cache-sets = <128>;
+                       i-cache-size = <16384>;
+                       next-level-cache = <&ccache>;
+                       reg = <0x0>;
+                       riscv,isa = "rv64imac";
+                       status = "disabled";
+                       cpu0_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               compatible = "riscv,cpu-intc";
+                               interrupt-controller;
+                       };
+               };
+               cpu1: cpu@1 {
+                       compatible = "sifive,bullet0", "riscv";
+                       d-cache-block-size = <64>;
+                       d-cache-sets = <64>;
+                       d-cache-size = <32768>;
+                       d-tlb-sets = <1>;
+                       d-tlb-size = <40>;
+                       device_type = "cpu";
+                       i-cache-block-size = <64>;
+                       i-cache-sets = <128>;
+                       i-cache-size = <32768>;
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <40>;
+                       mmu-type = "riscv,sv39";
+                       next-level-cache = <&ccache>;
+                       reg = <0x1>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+                       cpu1_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               compatible = "riscv,cpu-intc";
+                               interrupt-controller;
+                       };
+               };
+               cpu2: cpu@2 {
+                       compatible = "sifive,bullet0", "riscv";
+                       d-cache-block-size = <64>;
+                       d-cache-sets = <64>;
+                       d-cache-size = <32768>;
+                       d-tlb-sets = <1>;
+                       d-tlb-size = <40>;
+                       device_type = "cpu";
+                       i-cache-block-size = <64>;
+                       i-cache-sets = <128>;
+                       i-cache-size = <32768>;
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <40>;
+                       mmu-type = "riscv,sv39";
+                       next-level-cache = <&ccache>;
+                       reg = <0x2>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+                       cpu2_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               compatible = "riscv,cpu-intc";
+                               interrupt-controller;
+                       };
+               };
+               cpu3: cpu@3 {
+                       compatible = "sifive,bullet0", "riscv";
+                       d-cache-block-size = <64>;
+                       d-cache-sets = <64>;
+                       d-cache-size = <32768>;
+                       d-tlb-sets = <1>;
+                       d-tlb-size = <40>;
+                       device_type = "cpu";
+                       i-cache-block-size = <64>;
+                       i-cache-sets = <128>;
+                       i-cache-size = <32768>;
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <40>;
+                       mmu-type = "riscv,sv39";
+                       next-level-cache = <&ccache>;
+                       reg = <0x3>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+                       cpu3_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               compatible = "riscv,cpu-intc";
+                               interrupt-controller;
+                       };
+               };
+               cpu4: cpu@4 {
+                       compatible = "sifive,bullet0", "riscv";
+                       d-cache-block-size = <64>;
+                       d-cache-sets = <64>;
+                       d-cache-size = <32768>;
+                       d-tlb-sets = <1>;
+                       d-tlb-size = <40>;
+                       device_type = "cpu";
+                       i-cache-block-size = <64>;
+                       i-cache-sets = <128>;
+                       i-cache-size = <32768>;
+                       i-tlb-sets = <1>;
+                       i-tlb-size = <40>;
+                       mmu-type = "riscv,sv39";
+                       next-level-cache = <&ccache>;
+                       reg = <0x4>;
+                       riscv,isa = "rv64imafdc";
+                       tlb-split;
+                       cpu4_intc: interrupt-controller {
+                               #interrupt-cells = <1>;
+                               compatible = "riscv,cpu-intc";
+                               interrupt-controller;
+                       };
+               };
+       };
+       soc {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               compatible = "simple-bus";
+               ranges;
+               plic0: interrupt-controller@c000000 {
+                       #interrupt-cells = <1>;
+                       #address-cells = <0>;
+                       compatible = "sifive,fu540-c000-plic", "sifive,plic-1.0.0";
+                       reg = <0x0 0xc000000 0x0 0x4000000>;
+                       riscv,ndev = <69>;
+                       interrupt-controller;
+                       interrupts-extended = <
+                               &cpu0_intc 0xffffffff
+                               &cpu1_intc 0xffffffff &cpu1_intc 9
+                               &cpu2_intc 0xffffffff &cpu2_intc 9
+                               &cpu3_intc 0xffffffff &cpu3_intc 9
+                               &cpu4_intc 0xffffffff &cpu4_intc 9>;
+               };
+               prci: clock-controller@10000000 {
+                       compatible = "sifive,fu740-c000-prci";
+                       reg = <0x0 0x10000000 0x0 0x1000>;
+                       clocks = <&hfclk>, <&rtcclk>;
+                       #clock-cells = <1>;
+               };
+               uart0: serial@10010000 {
+                       compatible = "sifive,fu740-c000-uart", "sifive,uart0";
+                       reg = <0x0 0x10010000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <39>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       status = "disabled";
+               };
+               uart1: serial@10011000 {
+                       compatible = "sifive,fu740-c000-uart", "sifive,uart0";
+                       reg = <0x0 0x10011000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <40>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       status = "disabled";
+               };
+               i2c0: i2c@10030000 {
+                       compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
+                       reg = <0x0 0x10030000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <52>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       reg-shift = <2>;
+                       reg-io-width = <1>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               i2c1: i2c@10031000 {
+                       compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
+                       reg = <0x0 0x10031000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <53>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       reg-shift = <2>;
+                       reg-io-width = <1>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               qspi0: spi@10040000 {
+                       compatible = "sifive,fu740-c000-spi", "sifive,spi0";
+                       reg = <0x0 0x10040000 0x0 0x1000>,
+                             <0x0 0x20000000 0x0 0x10000000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <41>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               qspi1: spi@10041000 {
+                       compatible = "sifive,fu740-c000-spi", "sifive,spi0";
+                       reg = <0x0 0x10041000 0x0 0x1000>,
+                             <0x0 0x30000000 0x0 0x10000000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <42>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               spi0: spi@10050000 {
+                       compatible = "sifive,fu740-c000-spi", "sifive,spi0";
+                       reg = <0x0 0x10050000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <43>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               eth0: ethernet@10090000 {
+                       compatible = "sifive,fu540-c000-gem";
+                       interrupt-parent = <&plic0>;
+                       interrupts = <55>;
+                       reg = <0x0 0x10090000 0x0 0x2000>,
+                             <0x0 0x100a0000 0x0 0x1000>;
+                       local-mac-address = [00 00 00 00 00 00];
+                       clock-names = "pclk", "hclk";
+                       clocks = <&prci PRCI_CLK_GEMGXLPLL>,
+                                <&prci PRCI_CLK_GEMGXLPLL>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+               };
+               pwm0: pwm@10020000 {
+                       compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
+                       reg = <0x0 0x10020000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <44>, <45>, <46>, <47>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       #pwm-cells = <3>;
+                       status = "disabled";
+               };
+               pwm1: pwm@10021000 {
+                       compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
+                       reg = <0x0 0x10021000 0x0 0x1000>;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <48>, <49>, <50>, <51>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       #pwm-cells = <3>;
+                       status = "disabled";
+               };
+               ccache: cache-controller@2010000 {
+                       compatible = "sifive,fu740-c000-ccache", "cache";
+                       cache-block-size = <64>;
+                       cache-level = <2>;
+                       cache-sets = <2048>;
+                       cache-size = <2097152>;
+                       cache-unified;
+                       interrupt-parent = <&plic0>;
+                       interrupts = <19 20 21 22>;
+                       reg = <0x0 0x2010000 0x0 0x1000>;
+               };
+               gpio: gpio@10060000 {
+                       compatible = "sifive,fu740-c000-gpio", "sifive,gpio0";
+                       interrupt-parent = <&plic0>;
+                       interrupts = <23>, <24>, <25>, <26>, <27>, <28>, <29>,
+                                    <30>, <31>, <32>, <33>, <34>, <35>, <36>,
+                                    <37>, <38>;
+                       reg = <0x0 0x10060000 0x0 0x1000>;
+                       gpio-controller;
+                       #gpio-cells = <2>;
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
+                       clocks = <&prci PRCI_CLK_PCLK>;
+                       status = "disabled";
+               };
+       };
+};
diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
new file mode 100644 (file)
index 0000000..b1c3c59
--- /dev/null
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Copyright (c) 2020 SiFive, Inc */
+
+#include "fu740-c000.dtsi"
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/* Clock frequency (in Hz) of the PCB crystal for rtcclk */
+#define RTCCLK_FREQ            1000000
+
+/ {
+       #address-cells = <2>;
+       #size-cells = <2>;
+       model = "SiFive HiFive Unmatched A00";
+       compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000",
+                    "sifive,fu740";
+
+       chosen {
+               stdout-path = "serial0";
+       };
+
+       cpus {
+               timebase-frequency = <RTCCLK_FREQ>;
+       };
+
+       memory@80000000 {
+               device_type = "memory";
+               reg = <0x0 0x80000000 0x2 0x00000000>;
+       };
+
+       soc {
+       };
+
+       hfclk: hfclk {
+               #clock-cells = <0>;
+               compatible = "fixed-clock";
+               clock-frequency = <26000000>;
+               clock-output-names = "hfclk";
+       };
+
+       rtcclk: rtcclk {
+               #clock-cells = <0>;
+               compatible = "fixed-clock";
+               clock-frequency = <RTCCLK_FREQ>;
+               clock-output-names = "rtcclk";
+       };
+};
+
+&uart0 {
+       status = "okay";
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&i2c0 {
+       status = "okay";
+
+       temperature-sensor@4c {
+               compatible = "ti,tmp451";
+               reg = <0x4c>;
+               interrupt-parent = <&gpio>;
+               interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
+       };
+
+       pmic@58 {
+               compatible = "dlg,da9063";
+               reg = <0x58>;
+               interrupt-parent = <&gpio>;
+               interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
+               interrupt-controller;
+
+               regulators {
+                       vdd_bcore1: bcore1 {
+                               regulator-min-microvolt = <900000>;
+                               regulator-max-microvolt = <900000>;
+                               regulator-min-microamp = <5000000>;
+                               regulator-max-microamp = <5000000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_bcore2: bcore2 {
+                               regulator-min-microvolt = <900000>;
+                               regulator-max-microvolt = <900000>;
+                               regulator-min-microamp = <5000000>;
+                               regulator-max-microamp = <5000000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_bpro: bpro {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <2500000>;
+                               regulator-max-microamp = <2500000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_bperi: bperi {
+                               regulator-min-microvolt = <1050000>;
+                               regulator-max-microvolt = <1050000>;
+                               regulator-min-microamp = <1500000>;
+                               regulator-max-microamp = <1500000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_bmem: bmem {
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                               regulator-min-microamp = <3000000>;
+                               regulator-max-microamp = <3000000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_bio: bio {
+                               regulator-min-microvolt = <1200000>;
+                               regulator-max-microvolt = <1200000>;
+                               regulator-min-microamp = <3000000>;
+                               regulator-max-microamp = <3000000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo1: ldo1 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <100000>;
+                               regulator-max-microamp = <100000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo2: ldo2 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo3: ldo3 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo4: ldo4 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo5: ldo5 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <100000>;
+                               regulator-max-microamp = <100000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo6: ldo6 {
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo7: ldo7 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ldo8: ldo8 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_ld09: ldo9 {
+                               regulator-min-microvolt = <1050000>;
+                               regulator-max-microvolt = <1050000>;
+                               regulator-min-microamp = <200000>;
+                               regulator-max-microamp = <200000>;
+                       };
+
+                       vdd_ldo10: ldo10 {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <1000000>;
+                               regulator-min-microamp = <300000>;
+                               regulator-max-microamp = <300000>;
+                       };
+
+                       vdd_ldo11: ldo11 {
+                               regulator-min-microvolt = <2500000>;
+                               regulator-max-microvolt = <2500000>;
+                               regulator-min-microamp = <300000>;
+                               regulator-max-microamp = <300000>;
+                               regulator-always-on;
+                       };
+               };
+       };
+};
+
+&qspi0 {
+       status = "okay";
+       flash@0 {
+               compatible = "issi,is25wp256", "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <50000000>;
+               m25p,fast-read;
+               spi-tx-bus-width = <4>;
+               spi-rx-bus-width = <4>;
+       };
+};
+
+&spi0 {
+       status = "okay";
+       mmc@0 {
+               compatible = "mmc-spi-slot";
+               reg = <0>;
+               spi-max-frequency = <20000000>;
+               voltage-ranges = <3300 3300>;
+               disable-wp;
+       };
+};
+
+&eth0 {
+       status = "okay";
+       phy-mode = "gmii";
+       phy-handle = <&phy0>;
+       phy0: ethernet-phy@0 {
+               reg = <0>;
+       };
+};
+
+&pwm0 {
+       status = "okay";
+};
+
+&pwm1 {
+       status = "okay";
+};
+
+&gpio {
+       status = "okay";
+};
index cd1df62b13c7271789d917fa3159d8a4ebe7721e..b16a2a12c82a8b06ae8ecf8384ee46af181ffb3e 100644 (file)
@@ -1,17 +1,19 @@
 # CONFIG_CPU_ISOLATION is not set
-CONFIG_LOG_BUF_SHIFT=15
+CONFIG_LOG_BUF_SHIFT=13
 CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12
 CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_FORCE=y
+# CONFIG_RD_GZIP is not set
 # CONFIG_RD_BZIP2 is not set
 # CONFIG_RD_LZMA is not set
 # CONFIG_RD_XZ is not set
 # CONFIG_RD_LZO is not set
 # CONFIG_RD_LZ4 is not set
+# CONFIG_RD_ZSTD is not set
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 # CONFIG_SYSFS_SYSCALL is not set
 # CONFIG_FHANDLE is not set
 # CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
 # CONFIG_EPOLL is not set
 # CONFIG_SIGNALFD is not set
 # CONFIG_TIMERFD is not set
@@ -25,15 +27,17 @@ CONFIG_EMBEDDED=y
 # CONFIG_VM_EVENT_COUNTERS is not set
 # CONFIG_COMPAT_BRK is not set
 CONFIG_SLOB=y
-# CONFIG_SLAB_MERGE_DEFAULT is not set
 # CONFIG_MMU is not set
-CONFIG_SOC_KENDRYTE=y
+CONFIG_SOC_CANAAN=y
+CONFIG_SOC_CANAAN_K210_DTB_SOURCE="k210_generic"
 CONFIG_MAXPHYSMEM_2GB=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=2
 CONFIG_CMDLINE="earlycon console=ttySIF0"
 CONFIG_CMDLINE_FORCE=y
-CONFIG_JUMP_LABEL=y
+# CONFIG_SECCOMP is not set
+# CONFIG_STACKPROTECTOR is not set
+# CONFIG_GCC_PLUGINS is not set
 # CONFIG_BLOCK is not set
 CONFIG_BINFMT_FLAT=y
 # CONFIG_COREDUMP is not set
@@ -41,23 +45,47 @@ CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
 # CONFIG_FW_LOADER is not set
 # CONFIG_ALLOW_DEV_COREDUMP is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT is not set
 # CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_UNIX98_PTYS is not set
 # CONFIG_LEGACY_PTYS is not set
 # CONFIG_LDISC_AUTOLOAD is not set
 # CONFIG_HW_RANDOM is not set
 # CONFIG_DEVMEM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_SPI=y
+# CONFIG_SPI_MEM is not set
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_MMIO=y
+# CONFIG_GPIO_CDEV_V1 is not set
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_SIFIVE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
 # CONFIG_HWMON is not set
-# CONFIG_VGA_CONSOLE is not set
-# CONFIG_HID is not set
 # CONFIG_USB_SUPPORT is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_USER=y
 # CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
+# CONFIG_SURFACE_PLATFORMS is not set
+# CONFIG_FILE_LOCKING is not set
 # CONFIG_DNOTIFY is not set
 # CONFIG_INOTIFY_USER is not set
 # CONFIG_MISC_FILESYSTEMS is not set
 CONFIG_LSM="[]"
 CONFIG_PRINTK_TIME=y
+# CONFIG_SYMBOLIC_ERRNAME is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
+# CONFIG_FRAME_POINTER is not set
 # CONFIG_DEBUG_MISC is not set
 CONFIG_PANIC_ON_OOPS=y
 # CONFIG_SCHED_DEBUG is not set
diff --git a/arch/riscv/configs/nommu_k210_sdcard_defconfig b/arch/riscv/configs/nommu_k210_sdcard_defconfig
new file mode 100644 (file)
index 0000000..61f887f
--- /dev/null
@@ -0,0 +1,92 @@
+# CONFIG_CPU_ISOLATION is not set
+CONFIG_LOG_BUF_SHIFT=13
+CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_SYSFS_SYSCALL is not set
+# CONFIG_FHANDLE is not set
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_AIO is not set
+# CONFIG_IO_URING is not set
+# CONFIG_ADVISE_SYSCALLS is not set
+# CONFIG_MEMBARRIER is not set
+# CONFIG_KALLSYMS is not set
+CONFIG_EMBEDDED=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLOB=y
+# CONFIG_MMU is not set
+CONFIG_SOC_CANAAN=y
+CONFIG_SOC_CANAAN_K210_DTB_SOURCE="k210_generic"
+CONFIG_MAXPHYSMEM_2GB=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_CMDLINE="earlycon console=ttySIF0 rootdelay=2 root=/dev/mmcblk0p1 ro"
+CONFIG_CMDLINE_FORCE=y
+# CONFIG_SECCOMP is not set
+# CONFIG_STACKPROTECTOR is not set
+# CONFIG_GCC_PLUGINS is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+CONFIG_BINFMT_FLAT=y
+# CONFIG_COREDUMP is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+# CONFIG_BLK_DEV is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_LDISC_AUTOLOAD is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_DEVMEM is not set
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_SPI=y
+# CONFIG_SPI_MEM is not set
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_MMIO=y
+# CONFIG_GPIO_CDEV_V1 is not set
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_SIFIVE=y
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
+CONFIG_MMC_SPI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_USER=y
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_VHOST_MENU is not set
+# CONFIG_SURFACE_PLATFORMS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_FILE_LOCKING is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_LSM="[]"
+CONFIG_PRINTK_TIME=y
+# CONFIG_SYMBOLIC_ERRNAME is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_DEBUG_MISC is not set
+CONFIG_PANIC_ON_OOPS=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_FTRACE is not set
+# CONFIG_RUNTIME_TESTING_MENU is not set
index d6f1ec08d97b0875ee4f222fc86f448471c92a4d..d3804a2f9aad33fb8089691ca5f5ccd847452b51 100644 (file)
@@ -85,6 +85,7 @@ do {                                                          \
 struct pt_regs;
 struct task_struct;
 
+void __show_regs(struct pt_regs *regs);
 void die(struct pt_regs *regs, const char *str);
 void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr);
 
index cec462e198ced58c82ecf530115fb079d06b03b1..caadfc1d7487e6969877234bdbce864f06d263fd 100644 (file)
 #define SATP_PPN       _AC(0x003FFFFF, UL)
 #define SATP_MODE_32   _AC(0x80000000, UL)
 #define SATP_MODE      SATP_MODE_32
+#define SATP_ASID_BITS 9
+#define SATP_ASID_SHIFT        22
+#define SATP_ASID_MASK _AC(0x1FF, UL)
 #else
 #define SATP_PPN       _AC(0x00000FFFFFFFFFFF, UL)
 #define SATP_MODE_39   _AC(0x8000000000000000, UL)
 #define SATP_MODE      SATP_MODE_39
+#define SATP_ASID_BITS 16
+#define SATP_ASID_SHIFT        44
+#define SATP_ASID_MASK _AC(0xFFFF, UL)
 #endif
 
 /* Exception cause high bit - is an interrupt if set */
index b04028c6218c9f39fc086fe5a40f94459d58f0cd..a2b3d9cdbc8687ee5f20b547e33684013026bd42 100644 (file)
@@ -8,12 +8,28 @@
 
 #ifdef CONFIG_KASAN
 
+/*
+ * The following comment was copied from arm64:
+ * KASAN_SHADOW_START: beginning of the kernel virtual addresses.
+ * KASAN_SHADOW_END: KASAN_SHADOW_START + 1/N of kernel virtual addresses,
+ * where N = (1 << KASAN_SHADOW_SCALE_SHIFT).
+ *
+ * KASAN_SHADOW_OFFSET:
+ * This value is used to map an address to the corresponding shadow
+ * address by the following formula:
+ *     shadow_addr = (address >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET
+ *
+ * (1 << (64 - KASAN_SHADOW_SCALE_SHIFT)) shadow addresses that lie in range
+ * [KASAN_SHADOW_OFFSET, KASAN_SHADOW_END) cover all 64-bits of virtual
+ * addresses. So KASAN_SHADOW_OFFSET should satisfy the following equation:
+ *      KASAN_SHADOW_OFFSET = KASAN_SHADOW_END -
+ *                              (1ULL << (64 - KASAN_SHADOW_SCALE_SHIFT))
+ */
 #define KASAN_SHADOW_SCALE_SHIFT       3
 
-#define KASAN_SHADOW_SIZE      (UL(1) << (38 - KASAN_SHADOW_SCALE_SHIFT))
-#define KASAN_SHADOW_START     KERN_VIRT_START /* 2^64 - 2^38 */
+#define KASAN_SHADOW_SIZE      (UL(1) << ((CONFIG_VA_BITS - 1) - KASAN_SHADOW_SCALE_SHIFT))
+#define KASAN_SHADOW_START     KERN_VIRT_START
 #define KASAN_SHADOW_END       (KASAN_SHADOW_START + KASAN_SHADOW_SIZE)
-
 #define KASAN_SHADOW_OFFSET    (KASAN_SHADOW_END - (1ULL << \
                                        (64 - KASAN_SHADOW_SCALE_SHIFT)))
 
index 56a98ea3073145dec12a7d9a16fff7bb1a757d30..4647d38018f6a6a310a34bc2c74d1c13fbbcb406 100644 (file)
 
 #include <asm-generic/kprobes.h>
 
+#ifdef CONFIG_KPROBES
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/percpu.h>
+
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define MAX_INSN_SIZE                  2
+
+#define flush_insn_slot(p)             do { } while (0)
+#define kretprobe_blacklist_size       0
+
+#include <asm/probes.h>
+
+struct prev_kprobe {
+       struct kprobe *kp;
+       unsigned int status;
+};
+
+/* Single step context for kprobe */
+struct kprobe_step_ctx {
+       unsigned long ss_pending;
+       unsigned long match_addr;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+       unsigned int kprobe_status;
+       unsigned long saved_status;
+       struct prev_kprobe prev_kprobe;
+       struct kprobe_step_ctx ss_ctx;
+};
+
+void arch_remove_kprobe(struct kprobe *p);
+int kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr);
+bool kprobe_breakpoint_handler(struct pt_regs *regs);
+bool kprobe_single_step_handler(struct pt_regs *regs);
+void kretprobe_trampoline(void);
+void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
+
+#endif /* CONFIG_KPROBES */
 #endif /* _ASM_RISCV_KPROBES_H */
index dabcf2cfb3dc762011662c31c3b36d61db8dc859..0099dc1161683ddd1e3c45460309e331b7e6b0a7 100644 (file)
@@ -12,6 +12,8 @@
 typedef struct {
 #ifndef CONFIG_MMU
        unsigned long   end_brk;
+#else
+       atomic_long_t id;
 #endif
        void *vdso;
 #ifdef CONFIG_SMP
index 250defa06f3ad156de74cf1d73afd12d6c32159e..b0659413a080fc4f5702866b744957fc515437fd 100644 (file)
@@ -23,6 +23,16 @@ static inline void activate_mm(struct mm_struct *prev,
        switch_mm(prev, next, NULL);
 }
 
+#define init_new_context init_new_context
+static inline int init_new_context(struct task_struct *tsk,
+                       struct mm_struct *mm)
+{
+#ifdef CONFIG_MMU
+       atomic_long_set(&mm->context.id, 0);
+#endif
+       return 0;
+}
+
 #include <asm-generic/mmu_context.h>
 
 #endif /* _ASM_RISCV_MMU_CONTEXT_H */
diff --git a/arch/riscv/include/asm/mmzone.h b/arch/riscv/include/asm/mmzone.h
new file mode 100644 (file)
index 0000000..fa17e01
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MMZONE_H
+#define __ASM_MMZONE_H
+
+#ifdef CONFIG_NUMA
+
+#include <asm/numa.h>
+
+extern struct pglist_data *node_data[];
+#define NODE_DATA(nid)         (node_data[(nid)])
+
+#endif /* CONFIG_NUMA */
+#endif /* __ASM_MMZONE_H */
diff --git a/arch/riscv/include/asm/numa.h b/arch/riscv/include/asm/numa.h
new file mode 100644 (file)
index 0000000..8c8cf42
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_NUMA_H
+#define __ASM_NUMA_H
+
+#include <asm/topology.h>
+#include <asm-generic/numa.h>
+
+#endif /* __ASM_NUMA_H */
index 64a675c5c30acb2c9806645ba9f3f5e7297cef21..adc9d26f3d752dd02e434f0be04958a871459a0c 100644 (file)
@@ -97,9 +97,6 @@ extern unsigned long pfn_base;
 #define ARCH_PFN_OFFSET                (PAGE_OFFSET >> PAGE_SHIFT)
 #endif /* CONFIG_MMU */
 
-extern unsigned long max_low_pfn;
-extern unsigned long min_low_pfn;
-
 #define __pa_to_va_nodebug(x)  ((void *)((unsigned long) (x) + va_pa_offset))
 #define __va_to_pa_nodebug(x)  ((unsigned long)(x) - va_pa_offset)
 
index 1c473a1bd9862b9a75d27e8c59292f28ff419bbc..658e112c3ce73bcbce10e627400426ec41c8daec 100644 (file)
@@ -32,6 +32,20 @@ static inline int pci_proc_domain(struct pci_bus *bus)
        /* always show the domain in /proc */
        return 1;
 }
+
+#ifdef CONFIG_NUMA
+
+static inline int pcibus_to_node(struct pci_bus *bus)
+{
+       return dev_to_node(&bus->dev);
+}
+#ifndef cpumask_of_pcibus
+#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ?            \
+                                cpu_all_mask :                         \
+                                cpumask_of_node(pcibus_to_node(bus)))
+#endif
+#endif /* CONFIG_NUMA */
+
 #endif  /* CONFIG_PCI */
 
 #endif  /* _ASM_RISCV_PCI_H */
index 251e1db088fa2d8508762bcc5e459f2e7b120684..ebf817c1bdf4b3d955875670494d1828bd042d26 100644 (file)
@@ -186,6 +186,11 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
        return (unsigned long)pfn_to_virt(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
 }
 
+static inline pte_t pmd_pte(pmd_t pmd)
+{
+       return __pte(pmd_val(pmd));
+}
+
 /* Yields the page frame number (PFN) of a page table entry */
 static inline unsigned long pte_pfn(pte_t pte)
 {
@@ -289,6 +294,21 @@ static inline pte_t pte_mkhuge(pte_t pte)
        return pte;
 }
 
+#ifdef CONFIG_NUMA_BALANCING
+/*
+ * See the comment in include/asm-generic/pgtable.h
+ */
+static inline int pte_protnone(pte_t pte)
+{
+       return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_PROT_NONE)) == _PAGE_PROT_NONE;
+}
+
+static inline int pmd_protnone(pmd_t pmd)
+{
+       return pte_protnone(pmd_pte(pmd));
+}
+#endif
+
 /* Modify page protection bits */
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
@@ -468,6 +488,7 @@ extern void *dtb_early_va;
 extern uintptr_t dtb_early_pa;
 void setup_bootmem(void);
 void paging_init(void);
+void misc_mem_init(void);
 
 #define FIRST_USER_ADDRESS  0
 
diff --git a/arch/riscv/include/asm/probes.h b/arch/riscv/include/asm/probes.h
new file mode 100644 (file)
index 0000000..a787e6d
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_RISCV_PROBES_H
+#define _ASM_RISCV_PROBES_H
+
+typedef u32 probe_opcode_t;
+typedef bool (probes_handler_t) (u32 opcode, unsigned long addr, struct pt_regs *);
+
+/* architecture specific copy of original instruction */
+struct arch_probe_insn {
+       probe_opcode_t *insn;
+       probes_handler_t *handler;
+       /* restore address after simulation */
+       unsigned long restore;
+};
+
+#ifdef CONFIG_KPROBES
+typedef u32 kprobe_opcode_t;
+struct arch_specific_insn {
+       struct arch_probe_insn api;
+};
+#endif
+
+#endif /* _ASM_RISCV_PROBES_H */
index bdddcd5c1b71d905536f2774c0506ed8efb4ec3c..3a240037bde26e1c3a135f373d17805d0b471679 100644 (file)
@@ -34,6 +34,7 @@ struct thread_struct {
        unsigned long sp;       /* Kernel mode stack */
        unsigned long s[12];    /* s[0]: frame pointer */
        struct __riscv_d_ext_state fstate;
+       unsigned long bad_cause;
 };
 
 #define INIT_THREAD {                                  \
index ee49f80c95337f948a3e6fbbb3b42fc20b8709a1..cb4abb639e8d6876ca77b2d255e9875f1946945b 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <uapi/asm/ptrace.h>
 #include <asm/csr.h>
+#include <linux/compiler.h>
 
 #ifndef __ASSEMBLY__
 
@@ -60,6 +61,7 @@ struct pt_regs {
 
 #define user_mode(regs) (((regs)->status & SR_PP) == 0)
 
+#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0)
 
 /* Helpers for working with the instruction pointer */
 static inline unsigned long instruction_pointer(struct pt_regs *regs)
@@ -85,6 +87,12 @@ static inline void user_stack_pointer_set(struct pt_regs *regs,
        regs->sp =  val;
 }
 
+/* Valid only for Kernel mode traps. */
+static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+{
+       return regs->sp;
+}
+
 /* Helpers for working with the frame pointer */
 static inline unsigned long frame_pointer(struct pt_regs *regs)
 {
@@ -101,6 +109,33 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
        return regs->a0;
 }
 
+static inline void regs_set_return_value(struct pt_regs *regs,
+                                        unsigned long val)
+{
+       regs->a0 = val;
+}
+
+extern int regs_query_register_offset(const char *name);
+extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
+                                              unsigned int n);
+
+/**
+ * regs_get_register() - get register value from its offset
+ * @regs:      pt_regs from which register value is gotten
+ * @offset:    offset of the register.
+ *
+ * regs_get_register returns the value of a register whose offset from @regs.
+ * The @offset is the offset of the register in struct pt_regs.
+ * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
+ */
+static inline unsigned long regs_get_register(struct pt_regs *regs,
+                                             unsigned int offset)
+{
+       if (unlikely(offset > MAX_REG_OFFSET))
+               return 0;
+
+       return *(unsigned long *)((unsigned long)regs + offset);
+}
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_RISCV_PTRACE_H */
index 653edb25d4957db90da638159508a58dccb8a619..99895d9c3bddb06469b690b062cdb76631cb0547 100644 (file)
@@ -89,7 +89,7 @@ struct sbiret {
        long value;
 };
 
-int sbi_init(void);
+void sbi_init(void);
 struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
                        unsigned long arg1, unsigned long arg2,
                        unsigned long arg3, unsigned long arg4,
@@ -100,13 +100,13 @@ int sbi_console_getchar(void);
 void sbi_set_timer(uint64_t stime_value);
 void sbi_shutdown(void);
 void sbi_clear_ipi(void);
-void sbi_send_ipi(const unsigned long *hart_mask);
-void sbi_remote_fence_i(const unsigned long *hart_mask);
-void sbi_remote_sfence_vma(const unsigned long *hart_mask,
+int sbi_send_ipi(const unsigned long *hart_mask);
+int sbi_remote_fence_i(const unsigned long *hart_mask);
+int sbi_remote_sfence_vma(const unsigned long *hart_mask,
                           unsigned long start,
                           unsigned long size);
 
-void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
+int sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
                                unsigned long start,
                                unsigned long size,
                                unsigned long asid);
@@ -147,11 +147,7 @@ static inline unsigned long sbi_minor_version(void)
 
 int sbi_err_map_linux_errno(int err);
 #else /* CONFIG_RISCV_SBI */
-/* stubs for code that is only reachable under IS_ENABLED(CONFIG_RISCV_SBI): */
-void sbi_set_timer(uint64_t stime_value);
-void sbi_clear_ipi(void);
-void sbi_send_ipi(const unsigned long *hart_mask);
-void sbi_remote_fence_i(const unsigned long *hart_mask);
-void sbi_init(void);
+static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; }
+static inline void sbi_init(void) {}
 #endif /* CONFIG_RISCV_SBI */
 #endif /* _ASM_RISCV_SBI_H */
index 8b80c80c7f1acad9d9e1b3cb29d28a005fd83cd3..6887b3d9f3712bb5f4558b5c40b91b9559ad7335 100644 (file)
@@ -22,7 +22,7 @@ static inline int set_memory_ro(unsigned long addr, int numpages) { return 0; }
 static inline int set_memory_rw(unsigned long addr, int numpages) { return 0; }
 static inline int set_memory_x(unsigned long addr, int numpages) { return 0; }
 static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; }
-static inline void protect_kernel_text_data(void) {};
+static inline void protect_kernel_text_data(void) {}
 static inline int set_memory_rw_nx(unsigned long addr, int numpages) { return 0; }
 #endif
 
index 6c8363b1f3272ea669ce9761783dc30436e6656f..f494066051a2c10a122c17ecf855f230571c6f98 100644 (file)
@@ -21,42 +21,4 @@ void soc_early_init(void);
 extern unsigned long __soc_early_init_table_start;
 extern unsigned long __soc_early_init_table_end;
 
-/*
- * Allows Linux to provide a device tree, which is necessary for SOCs that
- * don't provide a useful one on their own.
- */
-struct soc_builtin_dtb {
-       unsigned long vendor_id;
-       unsigned long arch_id;
-       unsigned long imp_id;
-       void *(*dtb_func)(void);
-};
-
-/*
- * The argument name must specify a valid DTS file name without the dts
- * extension.
- */
-#define SOC_BUILTIN_DTB_DECLARE(name, vendor, arch, impl)              \
-       extern void *__dtb_##name##_begin;                              \
-                                                                       \
-       static __init __used                                            \
-       void *__soc_builtin_dtb_f__##name(void)                         \
-       {                                                               \
-               return (void *)&__dtb_##name##_begin;                   \
-       }                                                               \
-                                                                       \
-       static const struct soc_builtin_dtb __soc_builtin_dtb__##name   \
-               __used __section("__soc_builtin_dtb_table") =           \
-       {                                                               \
-               .vendor_id = vendor,                                    \
-               .arch_id   = arch,                                      \
-               .imp_id    = impl,                                      \
-               .dtb_func  = __soc_builtin_dtb_f__##name,               \
-       }
-
-extern unsigned long __soc_builtin_dtb_table_start;
-extern unsigned long __soc_builtin_dtb_table_end;
-
-void *soc_lookup_builtin_dtb(void);
-
 #endif
index 5962f8891f06f92ebe6223861942a408e0257232..09093af46565e5674a34e21e59808d4c3f36e5fc 100644 (file)
@@ -24,6 +24,7 @@ static __always_inline void boot_init_stack_canary(void)
        canary &= CANARY_MASK;
 
        current->stack_canary = canary;
-       __stack_chk_guard = current->stack_canary;
+       if (!IS_ENABLED(CONFIG_STACKPROTECTOR_PER_TASK))
+               __stack_chk_guard = current->stack_canary;
 }
 #endif /* _ASM_RISCV_STACKPROTECTOR_H */
index 470a65c4ccdccddb868d4692d85398e0990dcd39..3450c1912afdf2111543ad5d83973fba5ea7538a 100644 (file)
@@ -13,5 +13,7 @@ struct stackframe {
 
 extern void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
                                    bool (*fn)(void *, unsigned long), void *arg);
+extern void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
+                          const char *loglvl);
 
 #endif /* _ASM_RISCV_STACKTRACE_H */
index 97bf5a1575d2ce191e22acf6ea87def9ee014164..0e549a3089b3512c7337508c7ff891d8f38bbf64 100644 (file)
@@ -75,6 +75,7 @@ struct thread_info {
 #define TIF_SYSCALL_AUDIT      7       /* syscall auditing */
 #define TIF_SECCOMP            8       /* syscall secure computing */
 #define TIF_NOTIFY_SIGNAL      9       /* signal notifications exist */
+#define TIF_UPROBE             10      /* uprobe breakpoint or singlestep */
 
 #define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME     (1 << TIF_NOTIFY_RESUME)
@@ -84,10 +85,11 @@ struct thread_info {
 #define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SECCOMP           (1 << TIF_SECCOMP)
 #define _TIF_NOTIFY_SIGNAL     (1 << TIF_NOTIFY_SIGNAL)
+#define _TIF_UPROBE            (1 << TIF_UPROBE)
 
 #define _TIF_WORK_MASK \
        (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \
-        _TIF_NOTIFY_SIGNAL)
+        _TIF_NOTIFY_SIGNAL | _TIF_UPROBE)
 
 #define _TIF_SYSCALL_WORK \
        (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT | \
diff --git a/arch/riscv/include/asm/uprobes.h b/arch/riscv/include/asm/uprobes.h
new file mode 100644 (file)
index 0000000..f2183e0
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ASM_RISCV_UPROBES_H
+#define _ASM_RISCV_UPROBES_H
+
+#include <asm/probes.h>
+#include <asm/patch.h>
+#include <asm/bug.h>
+
+#define MAX_UINSN_BYTES                8
+
+#ifdef CONFIG_RISCV_ISA_C
+#define UPROBE_SWBP_INSN       __BUG_INSN_16
+#define UPROBE_SWBP_INSN_SIZE  2
+#else
+#define UPROBE_SWBP_INSN       __BUG_INSN_32
+#define UPROBE_SWBP_INSN_SIZE  4
+#endif
+#define UPROBE_XOL_SLOT_BYTES  MAX_UINSN_BYTES
+
+typedef u32 uprobe_opcode_t;
+
+struct arch_uprobe_task {
+       unsigned long   saved_cause;
+};
+
+struct arch_uprobe {
+       union {
+               u8 insn[MAX_UINSN_BYTES];
+               u8 ixol[MAX_UINSN_BYTES];
+       };
+       struct arch_probe_insn api;
+       unsigned long insn_size;
+       bool simulate;
+};
+
+bool uprobe_breakpoint_handler(struct pt_regs *regs);
+bool uprobe_single_step_handler(struct pt_regs *regs);
+
+#endif /* _ASM_RISCV_UPROBES_H */
index f6caf4d9ca150c7a134e4d0b964dc210bfaf6d63..3dc0abde988a27cb89f55b68d2e0d00207b346d7 100644 (file)
@@ -4,8 +4,9 @@
 #
 
 ifdef CONFIG_FTRACE
-CFLAGS_REMOVE_ftrace.o = -pg
-CFLAGS_REMOVE_patch.o  = -pg
+CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_patch.o  = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_sbi.o    = $(CC_FLAGS_FTRACE)
 endif
 
 extra-y += head.o
@@ -29,6 +30,7 @@ obj-y += riscv_ksyms.o
 obj-y  += stacktrace.o
 obj-y  += cacheinfo.o
 obj-y  += patch.o
+obj-y  += probes/
 obj-$(CONFIG_MMU) += vdso.o vdso/
 
 obj-$(CONFIG_RISCV_M_MODE)     += traps_misaligned.o
index b79ffa3561fd7245eacec78a0277e5555b80bbe9..9ef33346853c283f878b4fbf1a81a2bbb5e2511a 100644 (file)
@@ -68,6 +68,9 @@ void asm_offsets(void)
        OFFSET(TASK_THREAD_F30, task_struct, thread.fstate.f[30]);
        OFFSET(TASK_THREAD_F31, task_struct, thread.fstate.f[31]);
        OFFSET(TASK_THREAD_FCSR, task_struct, thread.fstate.fcsr);
+#ifdef CONFIG_STACKPROTECTOR
+       OFFSET(TSK_STACK_CANARY, task_struct, stack_canary);
+#endif
 
        DEFINE(PT_SIZE, sizeof(struct pt_regs));
        OFFSET(PT_EPC, pt_regs, epc);
index 765b62434f303d2c99f63b82be3799725b82e5dc..7f1e5203de886bdf85873159eb4bb9be95fc7e40 100644 (file)
@@ -72,29 +72,56 @@ static int __ftrace_modify_call(unsigned long hook_pos, unsigned long target,
        return 0;
 }
 
+/*
+ * Put 5 instructions with 16 bytes at the front of function within
+ * patchable function entry nops' area.
+ *
+ * 0: REG_S  ra, -SZREG(sp)
+ * 1: auipc  ra, 0x?
+ * 2: jalr   -?(ra)
+ * 3: REG_L  ra, -SZREG(sp)
+ *
+ * So the opcodes is:
+ * 0: 0xfe113c23 (sd)/0xfe112e23 (sw)
+ * 1: 0x???????? -> auipc
+ * 2: 0x???????? -> jalr
+ * 3: 0xff813083 (ld)/0xffc12083 (lw)
+ */
+#if __riscv_xlen == 64
+#define INSN0  0xfe113c23
+#define INSN3  0xff813083
+#elif __riscv_xlen == 32
+#define INSN0  0xfe112e23
+#define INSN3  0xffc12083
+#endif
+
+#define FUNC_ENTRY_SIZE        16
+#define FUNC_ENTRY_JMP 4
+
 int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
 {
-       int ret = ftrace_check_current_call(rec->ip, NULL);
+       unsigned int call[4] = {INSN0, 0, 0, INSN3};
+       unsigned long target = addr;
+       unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
 
-       if (ret)
-               return ret;
+       call[1] = to_auipc_insn((unsigned int)(target - caller));
+       call[2] = to_jalr_insn((unsigned int)(target - caller));
 
-       return __ftrace_modify_call(rec->ip, addr, true);
+       if (patch_text_nosync((void *)rec->ip, call, FUNC_ENTRY_SIZE))
+               return -EPERM;
+
+       return 0;
 }
 
 int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
                    unsigned long addr)
 {
-       unsigned int call[2];
-       int ret;
+       unsigned int nops[4] = {NOP4, NOP4, NOP4, NOP4};
 
-       make_call(rec->ip, addr, call);
-       ret = ftrace_check_current_call(rec->ip, call);
-
-       if (ret)
-               return ret;
+       if (patch_text_nosync((void *)rec->ip, nops, FUNC_ENTRY_SIZE))
+               return -EPERM;
 
-       return __ftrace_modify_call(rec->ip, addr, false);
+       return 0;
 }
 
 
@@ -139,15 +166,16 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
                       unsigned long addr)
 {
        unsigned int call[2];
+       unsigned long caller = rec->ip + FUNC_ENTRY_JMP;
        int ret;
 
-       make_call(rec->ip, old_addr, call);
-       ret = ftrace_check_current_call(rec->ip, call);
+       make_call(caller, old_addr, call);
+       ret = ftrace_check_current_call(caller, call);
 
        if (ret)
                return ret;
 
-       return __ftrace_modify_call(rec->ip, addr, true);
+       return __ftrace_modify_call(caller, addr, true);
 }
 #endif
 
@@ -176,53 +204,30 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 extern void ftrace_graph_call(void);
+extern void ftrace_graph_regs_call(void);
 int ftrace_enable_ftrace_graph_caller(void)
 {
-       unsigned int call[2];
-       static int init_graph = 1;
        int ret;
 
-       make_call(&ftrace_graph_call, &ftrace_stub, call);
-
-       /*
-        * When enabling graph tracer for the first time, ftrace_graph_call
-        * should contains a call to ftrace_stub.  Once it has been disabled,
-        * the 8-bytes at the position becomes NOPs.
-        */
-       if (init_graph) {
-               ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
-                                               call);
-               init_graph = 0;
-       } else {
-               ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
-                                               NULL);
-       }
-
+       ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+                                   (unsigned long)&prepare_ftrace_return, true);
        if (ret)
                return ret;
 
-       return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+       return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
                                    (unsigned long)&prepare_ftrace_return, true);
 }
 
 int ftrace_disable_ftrace_graph_caller(void)
 {
-       unsigned int call[2];
        int ret;
 
-       make_call(&ftrace_graph_call, &prepare_ftrace_return, call);
-
-       /*
-        * This is to make sure that ftrace_enable_ftrace_graph_caller
-        * did the right thing.
-        */
-       ret = ftrace_check_current_call((unsigned long)&ftrace_graph_call,
-                                       call);
-
+       ret = __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+                                   (unsigned long)&prepare_ftrace_return, false);
        if (ret)
                return ret;
 
-       return __ftrace_modify_call((unsigned long)&ftrace_graph_call,
+       return __ftrace_modify_call((unsigned long)&ftrace_graph_regs_call,
                                    (unsigned long)&prepare_ftrace_return, false);
 }
 #endif /* CONFIG_DYNAMIC_FTRACE */
index 16e9941900c4d0c9a9acca6e4209d9c615711dee..f5a9bad86e5831890441e9c7c216d406664b2540 100644 (file)
@@ -260,7 +260,11 @@ clear_bss_done:
 
        /* Initialize page tables and relocate to virtual addresses */
        la sp, init_thread_union + THREAD_SIZE
+#ifdef CONFIG_BUILTIN_DTB
+       la a0, __dtb_start
+#else
        mv a0, s1
+#endif /* CONFIG_BUILTIN_DTB */
        call setup_vm
 #ifdef CONFIG_MMU
        la a0, early_pg_dir
index 8c212efb37a64cf0ff33a4fa98a2ac74f625174a..71a76a62325778c4e030b83f24e80d18c49f62bb 100644 (file)
@@ -3,7 +3,7 @@
  * Copyright (C) 2020 Western Digital Corporation or its affiliates.
  * Linker script variables to be set after section resolution, as
  * ld.lld does not like variables assigned before SECTIONS is processed.
- * Based on arch/arm64/kerne/image-vars.h
+ * Based on arch/arm64/kernel/image-vars.h
  */
 #ifndef __RISCV_KERNEL_IMAGE_VARS_H
 #define __RISCV_KERNEL_IMAGE_VARS_H
index 35a6ed76cb8b74e7806d211173fdb77dc25f9298..d171eca623b6fc744cb5f349dd563d6e583dd788 100644 (file)
 
        .text
 
-       .macro SAVE_ABI_STATE
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       addi    sp, sp, -48
-       sd      s0, 32(sp)
-       sd      ra, 40(sp)
-       addi    s0, sp, 48
-       sd      t0, 24(sp)
-       sd      t1, 16(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
-       sd      t2, 8(sp)
-#endif
-#else
-       addi    sp, sp, -16
-       sd      s0, 0(sp)
-       sd      ra, 8(sp)
-       addi    s0, sp, 16
-#endif
+#define FENTRY_RA_OFFSET       12
+#define ABI_SIZE_ON_STACK      72
+#define ABI_A0                 0
+#define ABI_A1                 8
+#define ABI_A2                 16
+#define ABI_A3                 24
+#define ABI_A4                 32
+#define ABI_A5                 40
+#define ABI_A6                 48
+#define ABI_A7                 56
+#define ABI_RA                 64
+
+       .macro SAVE_ABI
+       addi    sp, sp, -SZREG
+       addi    sp, sp, -ABI_SIZE_ON_STACK
+
+       REG_S   a0, ABI_A0(sp)
+       REG_S   a1, ABI_A1(sp)
+       REG_S   a2, ABI_A2(sp)
+       REG_S   a3, ABI_A3(sp)
+       REG_S   a4, ABI_A4(sp)
+       REG_S   a5, ABI_A5(sp)
+       REG_S   a6, ABI_A6(sp)
+       REG_S   a7, ABI_A7(sp)
+       REG_S   ra, ABI_RA(sp)
        .endm
 
-       .macro RESTORE_ABI_STATE
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       ld      s0, 32(sp)
-       ld      ra, 40(sp)
-       addi    sp, sp, 48
-#else
-       ld      ra, 8(sp)
-       ld      s0, 0(sp)
-       addi    sp, sp, 16
-#endif
+       .macro RESTORE_ABI
+       REG_L   a0, ABI_A0(sp)
+       REG_L   a1, ABI_A1(sp)
+       REG_L   a2, ABI_A2(sp)
+       REG_L   a3, ABI_A3(sp)
+       REG_L   a4, ABI_A4(sp)
+       REG_L   a5, ABI_A5(sp)
+       REG_L   a6, ABI_A6(sp)
+       REG_L   a7, ABI_A7(sp)
+       REG_L   ra, ABI_RA(sp)
+
+       addi    sp, sp, ABI_SIZE_ON_STACK
+       addi    sp, sp, SZREG
        .endm
 
-       .macro RESTORE_GRAPH_ARGS
-       ld      a0, 24(sp)
-       ld      a1, 16(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
-       ld      a2, 8(sp)
-#endif
+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
+       .macro SAVE_ALL
+       addi    sp, sp, -SZREG
+       addi    sp, sp, -PT_SIZE_ON_STACK
+
+       REG_S x1,  PT_EPC(sp)
+       addi    sp, sp, PT_SIZE_ON_STACK
+       REG_L x1,  (sp)
+       addi    sp, sp, -PT_SIZE_ON_STACK
+       REG_S x1,  PT_RA(sp)
+       REG_L x1,  PT_EPC(sp)
+
+       REG_S x2,  PT_SP(sp)
+       REG_S x3,  PT_GP(sp)
+       REG_S x4,  PT_TP(sp)
+       REG_S x5,  PT_T0(sp)
+       REG_S x6,  PT_T1(sp)
+       REG_S x7,  PT_T2(sp)
+       REG_S x8,  PT_S0(sp)
+       REG_S x9,  PT_S1(sp)
+       REG_S x10, PT_A0(sp)
+       REG_S x11, PT_A1(sp)
+       REG_S x12, PT_A2(sp)
+       REG_S x13, PT_A3(sp)
+       REG_S x14, PT_A4(sp)
+       REG_S x15, PT_A5(sp)
+       REG_S x16, PT_A6(sp)
+       REG_S x17, PT_A7(sp)
+       REG_S x18, PT_S2(sp)
+       REG_S x19, PT_S3(sp)
+       REG_S x20, PT_S4(sp)
+       REG_S x21, PT_S5(sp)
+       REG_S x22, PT_S6(sp)
+       REG_S x23, PT_S7(sp)
+       REG_S x24, PT_S8(sp)
+       REG_S x25, PT_S9(sp)
+       REG_S x26, PT_S10(sp)
+       REG_S x27, PT_S11(sp)
+       REG_S x28, PT_T3(sp)
+       REG_S x29, PT_T4(sp)
+       REG_S x30, PT_T5(sp)
+       REG_S x31, PT_T6(sp)
        .endm
 
-ENTRY(ftrace_graph_caller)
-       addi    sp, sp, -16
-       sd      s0, 0(sp)
-       sd      ra, 8(sp)
-       addi    s0, sp, 16
-ftrace_graph_call:
-       .global ftrace_graph_call
-       /*
-        * Calling ftrace_enable/disable_ftrace_graph_caller would overwrite the
-        * call below.  Check ftrace_modify_all_code for details.
-        */
-       call    ftrace_stub
-       ld      ra, 8(sp)
-       ld      s0, 0(sp)
-       addi    sp, sp, 16
-       ret
-ENDPROC(ftrace_graph_caller)
+       .macro RESTORE_ALL
+       REG_L x1,  PT_RA(sp)
+       addi    sp, sp, PT_SIZE_ON_STACK
+       REG_S x1,  (sp)
+       addi    sp, sp, -PT_SIZE_ON_STACK
+       REG_L x1,  PT_EPC(sp)
+       REG_L x2,  PT_SP(sp)
+       REG_L x3,  PT_GP(sp)
+       REG_L x4,  PT_TP(sp)
+       REG_L x5,  PT_T0(sp)
+       REG_L x6,  PT_T1(sp)
+       REG_L x7,  PT_T2(sp)
+       REG_L x8,  PT_S0(sp)
+       REG_L x9,  PT_S1(sp)
+       REG_L x10, PT_A0(sp)
+       REG_L x11, PT_A1(sp)
+       REG_L x12, PT_A2(sp)
+       REG_L x13, PT_A3(sp)
+       REG_L x14, PT_A4(sp)
+       REG_L x15, PT_A5(sp)
+       REG_L x16, PT_A6(sp)
+       REG_L x17, PT_A7(sp)
+       REG_L x18, PT_S2(sp)
+       REG_L x19, PT_S3(sp)
+       REG_L x20, PT_S4(sp)
+       REG_L x21, PT_S5(sp)
+       REG_L x22, PT_S6(sp)
+       REG_L x23, PT_S7(sp)
+       REG_L x24, PT_S8(sp)
+       REG_L x25, PT_S9(sp)
+       REG_L x26, PT_S10(sp)
+       REG_L x27, PT_S11(sp)
+       REG_L x28, PT_T3(sp)
+       REG_L x29, PT_T4(sp)
+       REG_L x30, PT_T5(sp)
+       REG_L x31, PT_T6(sp)
+
+       addi    sp, sp, PT_SIZE_ON_STACK
+       addi    sp, sp, SZREG
+       .endm
+#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
 
 ENTRY(ftrace_caller)
-       /*
-        * a0: the address in the caller when calling ftrace_caller
-        * a1: the caller's return address
-        * a2: the address of global variable function_trace_op
-        */
-       ld      a1, -8(s0)
-       addi    a0, ra, -MCOUNT_INSN_SIZE
-       la      t5, function_trace_op
-       ld      a2, 0(t5)
+       SAVE_ABI
 
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       /*
-        * the graph tracer (specifically, prepare_ftrace_return) needs these
-        * arguments but for now the function tracer occupies the regs, so we
-        * save them in temporary regs to recover later.
-        */
-       addi    t0, s0, -8
-       mv      t1, a0
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
-       ld      t2, -16(s0)
-#endif
-#endif
+       addi    a0, ra, -FENTRY_RA_OFFSET
+       la      a1, function_trace_op
+       REG_L   a2, 0(a1)
+       REG_L   a1, ABI_SIZE_ON_STACK(sp)
+       mv      a3, sp
 
-       SAVE_ABI_STATE
 ftrace_call:
        .global ftrace_call
-       /*
-        * For the dynamic ftrace to work, here we should reserve at least
-        * 8 bytes for a functional auipc-jalr pair.  The following call
-        * serves this purpose.
-        *
-        * Calling ftrace_update_ftrace_func would overwrite the nops below.
-        * Check ftrace_modify_all_code for details.
-        */
        call    ftrace_stub
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       RESTORE_GRAPH_ARGS
-       call    ftrace_graph_caller
+       addi    a0, sp, ABI_SIZE_ON_STACK
+       REG_L   a1, ABI_RA(sp)
+       addi    a1, a1, -FENTRY_RA_OFFSET
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+       mv      a2, s0
 #endif
-
-       RESTORE_ABI_STATE
+ftrace_graph_call:
+       .global ftrace_graph_call
+       call    ftrace_stub
+#endif
+       RESTORE_ABI
        ret
 ENDPROC(ftrace_caller)
 
 #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
-       .macro SAVE_ALL
-       addi    sp, sp, -(PT_SIZE_ON_STACK+16)
-       sd      s0, (PT_SIZE_ON_STACK)(sp)
-       sd      ra, (PT_SIZE_ON_STACK+8)(sp)
-       addi    s0, sp, (PT_SIZE_ON_STACK+16)
-
-       sd x1,  PT_RA(sp)
-       sd x2,  PT_SP(sp)
-       sd x3,  PT_GP(sp)
-       sd x4,  PT_TP(sp)
-       sd x5,  PT_T0(sp)
-       sd x6,  PT_T1(sp)
-       sd x7,  PT_T2(sp)
-       sd x8,  PT_S0(sp)
-       sd x9,  PT_S1(sp)
-       sd x10, PT_A0(sp)
-       sd x11, PT_A1(sp)
-       sd x12, PT_A2(sp)
-       sd x13, PT_A3(sp)
-       sd x14, PT_A4(sp)
-       sd x15, PT_A5(sp)
-       sd x16, PT_A6(sp)
-       sd x17, PT_A7(sp)
-       sd x18, PT_S2(sp)
-       sd x19, PT_S3(sp)
-       sd x20, PT_S4(sp)
-       sd x21, PT_S5(sp)
-       sd x22, PT_S6(sp)
-       sd x23, PT_S7(sp)
-       sd x24, PT_S8(sp)
-       sd x25, PT_S9(sp)
-       sd x26, PT_S10(sp)
-       sd x27, PT_S11(sp)
-       sd x28, PT_T3(sp)
-       sd x29, PT_T4(sp)
-       sd x30, PT_T5(sp)
-       sd x31, PT_T6(sp)
-       .endm
-
-       .macro RESTORE_ALL
-       ld x1,  PT_RA(sp)
-       ld x2,  PT_SP(sp)
-       ld x3,  PT_GP(sp)
-       ld x4,  PT_TP(sp)
-       ld x5,  PT_T0(sp)
-       ld x6,  PT_T1(sp)
-       ld x7,  PT_T2(sp)
-       ld x8,  PT_S0(sp)
-       ld x9,  PT_S1(sp)
-       ld x10, PT_A0(sp)
-       ld x11, PT_A1(sp)
-       ld x12, PT_A2(sp)
-       ld x13, PT_A3(sp)
-       ld x14, PT_A4(sp)
-       ld x15, PT_A5(sp)
-       ld x16, PT_A6(sp)
-       ld x17, PT_A7(sp)
-       ld x18, PT_S2(sp)
-       ld x19, PT_S3(sp)
-       ld x20, PT_S4(sp)
-       ld x21, PT_S5(sp)
-       ld x22, PT_S6(sp)
-       ld x23, PT_S7(sp)
-       ld x24, PT_S8(sp)
-       ld x25, PT_S9(sp)
-       ld x26, PT_S10(sp)
-       ld x27, PT_S11(sp)
-       ld x28, PT_T3(sp)
-       ld x29, PT_T4(sp)
-       ld x30, PT_T5(sp)
-       ld x31, PT_T6(sp)
-
-       ld      s0, (PT_SIZE_ON_STACK)(sp)
-       ld      ra, (PT_SIZE_ON_STACK+8)(sp)
-       addi    sp, sp, (PT_SIZE_ON_STACK+16)
-       .endm
-
-       .macro RESTORE_GRAPH_REG_ARGS
-       ld      a0, PT_T0(sp)
-       ld      a1, PT_T1(sp)
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
-       ld      a2, PT_T2(sp)
-#endif
-       .endm
-
-/*
- * Most of the contents are the same as ftrace_caller.
- */
 ENTRY(ftrace_regs_caller)
-       /*
-        * a3: the address of all registers in the stack
-        */
-       ld      a1, -8(s0)
-       addi    a0, ra, -MCOUNT_INSN_SIZE
-       la      t5, function_trace_op
-       ld      a2, 0(t5)
-       addi    a3, sp, -(PT_SIZE_ON_STACK+16)
-
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       addi    t0, s0, -8
-       mv      t1, a0
-#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
-       ld      t2, -16(s0)
-#endif
-#endif
        SAVE_ALL
 
+       addi    a0, ra, -FENTRY_RA_OFFSET
+       la      a1, function_trace_op
+       REG_L   a2, 0(a1)
+       REG_L   a1, PT_SIZE_ON_STACK(sp)
+       mv      a3, sp
+
 ftrace_regs_call:
        .global ftrace_regs_call
        call    ftrace_stub
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       RESTORE_GRAPH_REG_ARGS
-       call    ftrace_graph_caller
+       addi    a0, sp, PT_RA
+       REG_L   a1, PT_EPC(sp)
+       addi    a1, a1, -FENTRY_RA_OFFSET
+#ifdef HAVE_FUNCTION_GRAPH_FP_TEST
+       mv      a2, s0
+#endif
+ftrace_graph_regs_call:
+       .global ftrace_graph_regs_call
+       call    ftrace_stub
 #endif
 
        RESTORE_ALL
index 3fe7a5296aa5897c4867a019e66315c1dfbe5888..0b552873a5778b397a3b25ca8e64124c57d4a5c3 100644 (file)
@@ -20,7 +20,12 @@ struct patch_insn {
 };
 
 #ifdef CONFIG_MMU
-static void *patch_map(void *addr, int fixmap)
+/*
+ * The fix_to_virt(, idx) needs a const value (not a dynamic variable of
+ * reg-a0) or BUILD_BUG_ON failed with "idx >= __end_of_fixed_addresses".
+ * So use '__always_inline' and 'const unsigned int fixmap' here.
+ */
+static __always_inline void *patch_map(void *addr, const unsigned int fixmap)
 {
        uintptr_t uintaddr = (uintptr_t) addr;
        struct page *page;
@@ -37,7 +42,6 @@ static void *patch_map(void *addr, int fixmap)
        return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
                                         (uintaddr & ~PAGE_MASK));
 }
-NOKPROBE_SYMBOL(patch_map);
 
 static void patch_unmap(int fixmap)
 {
diff --git a/arch/riscv/kernel/probes/Makefile b/arch/riscv/kernel/probes/Makefile
new file mode 100644 (file)
index 0000000..7f0840d
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_KPROBES)          += kprobes.o decode-insn.o simulate-insn.o
+obj-$(CONFIG_KPROBES)          += kprobes_trampoline.o
+obj-$(CONFIG_KPROBES_ON_FTRACE)        += ftrace.o
+obj-$(CONFIG_UPROBES)          += uprobes.o decode-insn.o simulate-insn.o
+CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
diff --git a/arch/riscv/kernel/probes/decode-insn.c b/arch/riscv/kernel/probes/decode-insn.c
new file mode 100644 (file)
index 0000000..0ed043a
--- /dev/null
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <asm/sections.h>
+
+#include "decode-insn.h"
+#include "simulate-insn.h"
+
+/* Return:
+ *   INSN_REJECTED     If instruction is one not allowed to kprobe,
+ *   INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ */
+enum probe_insn __kprobes
+riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
+{
+       probe_opcode_t insn = *addr;
+
+       /*
+        * Reject instructions list:
+        */
+       RISCV_INSN_REJECTED(system,             insn);
+       RISCV_INSN_REJECTED(fence,              insn);
+
+       /*
+        * Simulate instructions list:
+        * TODO: the REJECTED ones below need to be implemented
+        */
+#ifdef CONFIG_RISCV_ISA_C
+       RISCV_INSN_REJECTED(c_j,                insn);
+       RISCV_INSN_REJECTED(c_jr,               insn);
+       RISCV_INSN_REJECTED(c_jal,              insn);
+       RISCV_INSN_REJECTED(c_jalr,             insn);
+       RISCV_INSN_REJECTED(c_beqz,             insn);
+       RISCV_INSN_REJECTED(c_bnez,             insn);
+       RISCV_INSN_REJECTED(c_ebreak,           insn);
+#endif
+
+       RISCV_INSN_REJECTED(auipc,              insn);
+       RISCV_INSN_REJECTED(branch,             insn);
+
+       RISCV_INSN_SET_SIMULATE(jal,            insn);
+       RISCV_INSN_SET_SIMULATE(jalr,           insn);
+
+       return INSN_GOOD;
+}
diff --git a/arch/riscv/kernel/probes/decode-insn.h b/arch/riscv/kernel/probes/decode-insn.h
new file mode 100644 (file)
index 0000000..42269a7
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _RISCV_KERNEL_KPROBES_DECODE_INSN_H
+#define _RISCV_KERNEL_KPROBES_DECODE_INSN_H
+
+#include <asm/sections.h>
+#include <asm/kprobes.h>
+
+enum probe_insn {
+       INSN_REJECTED,
+       INSN_GOOD_NO_SLOT,
+       INSN_GOOD,
+};
+
+enum probe_insn __kprobes
+riscv_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);
+
+#endif /* _RISCV_KERNEL_KPROBES_DECODE_INSN_H */
diff --git a/arch/riscv/kernel/probes/ftrace.c b/arch/riscv/kernel/probes/ftrace.c
new file mode 100644 (file)
index 0000000..e637249
--- /dev/null
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kprobes.h>
+
+/* Ftrace callback handler for kprobes -- called under preepmt disabed */
+void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+                          struct ftrace_ops *ops, struct ftrace_regs *regs)
+{
+       struct kprobe *p;
+       struct kprobe_ctlblk *kcb;
+
+       p = get_kprobe((kprobe_opcode_t *)ip);
+       if (unlikely(!p) || kprobe_disabled(p))
+               return;
+
+       kcb = get_kprobe_ctlblk();
+       if (kprobe_running()) {
+               kprobes_inc_nmissed_count(p);
+       } else {
+               unsigned long orig_ip = instruction_pointer(&(regs->regs));
+
+               instruction_pointer_set(&(regs->regs), ip);
+
+               __this_cpu_write(current_kprobe, p);
+               kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+               if (!p->pre_handler || !p->pre_handler(p, &(regs->regs))) {
+                       /*
+                        * Emulate singlestep (and also recover regs->pc)
+                        * as if there is a nop
+                        */
+                       instruction_pointer_set(&(regs->regs),
+                               (unsigned long)p->addr + MCOUNT_INSN_SIZE);
+                       if (unlikely(p->post_handler)) {
+                               kcb->kprobe_status = KPROBE_HIT_SSDONE;
+                               p->post_handler(p, &(regs->regs), 0);
+                       }
+                       instruction_pointer_set(&(regs->regs), orig_ip);
+               }
+
+               /*
+                * If pre_handler returns !0, it changes regs->pc. We have to
+                * skip emulating post_handler.
+                */
+               __this_cpu_write(current_kprobe, NULL);
+       }
+}
+NOKPROBE_SYMBOL(kprobe_ftrace_handler);
+
+int arch_prepare_kprobe_ftrace(struct kprobe *p)
+{
+       p->ainsn.api.insn = NULL;
+       return 0;
+}
diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
new file mode 100644 (file)
index 0000000..a2ec186
--- /dev/null
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/kprobes.h>
+#include <linux/extable.h>
+#include <linux/slab.h>
+#include <linux/stop_machine.h>
+#include <asm/ptrace.h>
+#include <linux/uaccess.h>
+#include <asm/sections.h>
+#include <asm/cacheflush.h>
+#include <asm/bug.h>
+#include <asm/patch.h>
+
+#include "decode-insn.h"
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *);
+
+static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
+{
+       unsigned long offset = GET_INSN_LENGTH(p->opcode);
+
+       p->ainsn.api.restore = (unsigned long)p->addr + offset;
+
+       patch_text(p->ainsn.api.insn, p->opcode);
+       patch_text((void *)((unsigned long)(p->ainsn.api.insn) + offset),
+                  __BUG_INSN_32);
+}
+
+static void __kprobes arch_prepare_simulate(struct kprobe *p)
+{
+       p->ainsn.api.restore = 0;
+}
+
+static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       if (p->ainsn.api.handler)
+               p->ainsn.api.handler((u32)p->opcode,
+                                       (unsigned long)p->addr, regs);
+
+       post_kprobe_handler(kcb, regs);
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+       unsigned long probe_addr = (unsigned long)p->addr;
+
+       if (probe_addr & 0x1) {
+               pr_warn("Address not aligned.\n");
+
+               return -EINVAL;
+       }
+
+       /* copy instruction */
+       p->opcode = *p->addr;
+
+       /* decode instruction */
+       switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
+       case INSN_REJECTED:     /* insn not supported */
+               return -EINVAL;
+
+       case INSN_GOOD_NO_SLOT: /* insn need simulation */
+               p->ainsn.api.insn = NULL;
+               break;
+
+       case INSN_GOOD: /* instruction uses slot */
+               p->ainsn.api.insn = get_insn_slot();
+               if (!p->ainsn.api.insn)
+                       return -ENOMEM;
+               break;
+       }
+
+       /* prepare the instruction */
+       if (p->ainsn.api.insn)
+               arch_prepare_ss_slot(p);
+       else
+               arch_prepare_simulate(p);
+
+       return 0;
+}
+
+/* install breakpoint in text */
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+       if ((p->opcode & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
+               patch_text(p->addr, __BUG_INSN_32);
+       else
+               patch_text(p->addr, __BUG_INSN_16);
+}
+
+/* remove breakpoint from text */
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+       patch_text(p->addr, p->opcode);
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+       kcb->prev_kprobe.kp = kprobe_running();
+       kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+       __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
+       kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+       __this_cpu_write(current_kprobe, p);
+}
+
+/*
+ * Interrupts need to be disabled before single-step mode is set, and not
+ * reenabled until after single-step mode ends.
+ * Without disabling interrupt on local CPU, there is a chance of
+ * interrupt occurrence in the period of exception return and  start of
+ * out-of-line single-step, that result in wrongly single stepping
+ * into the interrupt handler.
+ */
+static void __kprobes kprobes_save_local_irqflag(struct kprobe_ctlblk *kcb,
+                                               struct pt_regs *regs)
+{
+       kcb->saved_status = regs->status;
+       regs->status &= ~SR_SPIE;
+}
+
+static void __kprobes kprobes_restore_local_irqflag(struct kprobe_ctlblk *kcb,
+                                               struct pt_regs *regs)
+{
+       regs->status = kcb->saved_status;
+}
+
+static void __kprobes
+set_ss_context(struct kprobe_ctlblk *kcb, unsigned long addr, struct kprobe *p)
+{
+       unsigned long offset = GET_INSN_LENGTH(p->opcode);
+
+       kcb->ss_ctx.ss_pending = true;
+       kcb->ss_ctx.match_addr = addr + offset;
+}
+
+static void __kprobes clear_ss_context(struct kprobe_ctlblk *kcb)
+{
+       kcb->ss_ctx.ss_pending = false;
+       kcb->ss_ctx.match_addr = 0;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p,
+                                      struct pt_regs *regs,
+                                      struct kprobe_ctlblk *kcb, int reenter)
+{
+       unsigned long slot;
+
+       if (reenter) {
+               save_previous_kprobe(kcb);
+               set_current_kprobe(p);
+               kcb->kprobe_status = KPROBE_REENTER;
+       } else {
+               kcb->kprobe_status = KPROBE_HIT_SS;
+       }
+
+       if (p->ainsn.api.insn) {
+               /* prepare for single stepping */
+               slot = (unsigned long)p->ainsn.api.insn;
+
+               set_ss_context(kcb, slot, p);   /* mark pending ss */
+
+               /* IRQs and single stepping do not mix well. */
+               kprobes_save_local_irqflag(kcb, regs);
+
+               instruction_pointer_set(regs, slot);
+       } else {
+               /* insn simulation */
+               arch_simulate_insn(p, regs);
+       }
+}
+
+static int __kprobes reenter_kprobe(struct kprobe *p,
+                                   struct pt_regs *regs,
+                                   struct kprobe_ctlblk *kcb)
+{
+       switch (kcb->kprobe_status) {
+       case KPROBE_HIT_SSDONE:
+       case KPROBE_HIT_ACTIVE:
+               kprobes_inc_nmissed_count(p);
+               setup_singlestep(p, regs, kcb, 1);
+               break;
+       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
+               pr_warn("Unrecoverable kprobe detected.\n");
+               dump_kprobe(p);
+               BUG();
+               break;
+       default:
+               WARN_ON(1);
+               return 0;
+       }
+
+       return 1;
+}
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *kcb, struct pt_regs *regs)
+{
+       struct kprobe *cur = kprobe_running();
+
+       if (!cur)
+               return;
+
+       /* return addr restore if non-branching insn */
+       if (cur->ainsn.api.restore != 0)
+               regs->epc = cur->ainsn.api.restore;
+
+       /* restore back original saved kprobe variables and continue */
+       if (kcb->kprobe_status == KPROBE_REENTER) {
+               restore_previous_kprobe(kcb);
+               return;
+       }
+
+       /* call post handler */
+       kcb->kprobe_status = KPROBE_HIT_SSDONE;
+       if (cur->post_handler)  {
+               /* post_handler can hit breakpoint and single step
+                * again, so we enable D-flag for recursive exception.
+                */
+               cur->post_handler(cur, regs, 0);
+       }
+
+       reset_current_kprobe();
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr)
+{
+       struct kprobe *cur = kprobe_running();
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       switch (kcb->kprobe_status) {
+       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
+               /*
+                * We are here because the instruction being single
+                * stepped caused a page fault. We reset the current
+                * kprobe and the ip points back to the probe address
+                * and allow the page fault handler to continue as a
+                * normal page fault.
+                */
+               regs->epc = (unsigned long) cur->addr;
+               if (!instruction_pointer(regs))
+                       BUG();
+
+               if (kcb->kprobe_status == KPROBE_REENTER)
+                       restore_previous_kprobe(kcb);
+               else
+                       reset_current_kprobe();
+
+               break;
+       case KPROBE_HIT_ACTIVE:
+       case KPROBE_HIT_SSDONE:
+               /*
+                * We increment the nmissed count for accounting,
+                * we can also use npre/npostfault count for accounting
+                * these specific fault cases.
+                */
+               kprobes_inc_nmissed_count(cur);
+
+               /*
+                * We come here because instructions in the pre/post
+                * handler caused the page_fault, this could happen
+                * if handler tries to access user space by
+                * copy_from_user(), get_user() etc. Let the
+                * user-specified handler try to fix it first.
+                */
+               if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+                       return 1;
+
+               /*
+                * In case the user-specified fault handler returned
+                * zero, try to fix up.
+                */
+               if (fixup_exception(regs))
+                       return 1;
+       }
+       return 0;
+}
+
+bool __kprobes
+kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+       struct kprobe *p, *cur_kprobe;
+       struct kprobe_ctlblk *kcb;
+       unsigned long addr = instruction_pointer(regs);
+
+       kcb = get_kprobe_ctlblk();
+       cur_kprobe = kprobe_running();
+
+       p = get_kprobe((kprobe_opcode_t *) addr);
+
+       if (p) {
+               if (cur_kprobe) {
+                       if (reenter_kprobe(p, regs, kcb))
+                               return true;
+               } else {
+                       /* Probe hit */
+                       set_current_kprobe(p);
+                       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+                       /*
+                        * If we have no pre-handler or it returned 0, we
+                        * continue with normal processing.  If we have a
+                        * pre-handler and it returned non-zero, it will
+                        * modify the execution path and no need to single
+                        * stepping. Let's just reset current kprobe and exit.
+                        *
+                        * pre_handler can hit a breakpoint and can step thru
+                        * before return.
+                        */
+                       if (!p->pre_handler || !p->pre_handler(p, regs))
+                               setup_singlestep(p, regs, kcb, 0);
+                       else
+                               reset_current_kprobe();
+               }
+               return true;
+       }
+
+       /*
+        * The breakpoint instruction was removed right
+        * after we hit it.  Another cpu has removed
+        * either a probepoint or a debugger breakpoint
+        * at this address.  In either case, no further
+        * handling of this interrupt is appropriate.
+        * Return back to original instruction, and continue.
+        */
+       return false;
+}
+
+bool __kprobes
+kprobe_single_step_handler(struct pt_regs *regs)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       if ((kcb->ss_ctx.ss_pending)
+           && (kcb->ss_ctx.match_addr == instruction_pointer(regs))) {
+               clear_ss_context(kcb);  /* clear pending ss */
+
+               kprobes_restore_local_irqflag(kcb, regs);
+
+               post_kprobe_handler(kcb, regs);
+               return true;
+       }
+       return false;
+}
+
+/*
+ * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
+ * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
+ */
+int __init arch_populate_kprobe_blacklist(void)
+{
+       int ret;
+
+       ret = kprobe_add_area_blacklist((unsigned long)__irqentry_text_start,
+                                       (unsigned long)__irqentry_text_end);
+       return ret;
+}
+
+void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
+{
+       return (void *)kretprobe_trampoline_handler(regs, &kretprobe_trampoline, NULL);
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+                                     struct pt_regs *regs)
+{
+       ri->ret_addr = (kprobe_opcode_t *)regs->ra;
+       ri->fp = NULL;
+       regs->ra = (unsigned long) &kretprobe_trampoline;
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+       return 0;
+}
+
+int __init arch_init_kprobes(void)
+{
+       return 0;
+}
diff --git a/arch/riscv/kernel/probes/kprobes_trampoline.S b/arch/riscv/kernel/probes/kprobes_trampoline.S
new file mode 100644 (file)
index 0000000..6e85d02
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Author: Patrick Stählin <me@packi.ch>
+ */
+#include <linux/linkage.h>
+
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+
+       .text
+       .altmacro
+
+       .macro save_all_base_regs
+       REG_S x1,  PT_RA(sp)
+       REG_S x3,  PT_GP(sp)
+       REG_S x4,  PT_TP(sp)
+       REG_S x5,  PT_T0(sp)
+       REG_S x6,  PT_T1(sp)
+       REG_S x7,  PT_T2(sp)
+       REG_S x8,  PT_S0(sp)
+       REG_S x9,  PT_S1(sp)
+       REG_S x10, PT_A0(sp)
+       REG_S x11, PT_A1(sp)
+       REG_S x12, PT_A2(sp)
+       REG_S x13, PT_A3(sp)
+       REG_S x14, PT_A4(sp)
+       REG_S x15, PT_A5(sp)
+       REG_S x16, PT_A6(sp)
+       REG_S x17, PT_A7(sp)
+       REG_S x18, PT_S2(sp)
+       REG_S x19, PT_S3(sp)
+       REG_S x20, PT_S4(sp)
+       REG_S x21, PT_S5(sp)
+       REG_S x22, PT_S6(sp)
+       REG_S x23, PT_S7(sp)
+       REG_S x24, PT_S8(sp)
+       REG_S x25, PT_S9(sp)
+       REG_S x26, PT_S10(sp)
+       REG_S x27, PT_S11(sp)
+       REG_S x28, PT_T3(sp)
+       REG_S x29, PT_T4(sp)
+       REG_S x30, PT_T5(sp)
+       REG_S x31, PT_T6(sp)
+       .endm
+
+       .macro restore_all_base_regs
+       REG_L x3,  PT_GP(sp)
+       REG_L x4,  PT_TP(sp)
+       REG_L x5,  PT_T0(sp)
+       REG_L x6,  PT_T1(sp)
+       REG_L x7,  PT_T2(sp)
+       REG_L x8,  PT_S0(sp)
+       REG_L x9,  PT_S1(sp)
+       REG_L x10, PT_A0(sp)
+       REG_L x11, PT_A1(sp)
+       REG_L x12, PT_A2(sp)
+       REG_L x13, PT_A3(sp)
+       REG_L x14, PT_A4(sp)
+       REG_L x15, PT_A5(sp)
+       REG_L x16, PT_A6(sp)
+       REG_L x17, PT_A7(sp)
+       REG_L x18, PT_S2(sp)
+       REG_L x19, PT_S3(sp)
+       REG_L x20, PT_S4(sp)
+       REG_L x21, PT_S5(sp)
+       REG_L x22, PT_S6(sp)
+       REG_L x23, PT_S7(sp)
+       REG_L x24, PT_S8(sp)
+       REG_L x25, PT_S9(sp)
+       REG_L x26, PT_S10(sp)
+       REG_L x27, PT_S11(sp)
+       REG_L x28, PT_T3(sp)
+       REG_L x29, PT_T4(sp)
+       REG_L x30, PT_T5(sp)
+       REG_L x31, PT_T6(sp)
+       .endm
+
+ENTRY(kretprobe_trampoline)
+       addi sp, sp, -(PT_SIZE_ON_STACK)
+       save_all_base_regs
+
+       move a0, sp /* pt_regs */
+
+       call trampoline_probe_handler
+
+       /* use the result as the return-address */
+       move ra, a0
+
+       restore_all_base_regs
+       addi sp, sp, PT_SIZE_ON_STACK
+
+       ret
+ENDPROC(kretprobe_trampoline)
diff --git a/arch/riscv/kernel/probes/simulate-insn.c b/arch/riscv/kernel/probes/simulate-insn.c
new file mode 100644 (file)
index 0000000..2519ce2
--- /dev/null
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "decode-insn.h"
+#include "simulate-insn.h"
+
+static inline bool rv_insn_reg_get_val(struct pt_regs *regs, u32 index,
+                                      unsigned long *ptr)
+{
+       if (index == 0)
+               *ptr = 0;
+       else if (index <= 31)
+               *ptr = *((unsigned long *)regs + index);
+       else
+               return false;
+
+       return true;
+}
+
+static inline bool rv_insn_reg_set_val(struct pt_regs *regs, u32 index,
+                                      unsigned long val)
+{
+       if (index == 0)
+               return false;
+       else if (index <= 31)
+               *((unsigned long *)regs + index) = val;
+       else
+               return false;
+
+       return true;
+}
+
+bool __kprobes simulate_jal(u32 opcode, unsigned long addr, struct pt_regs *regs)
+{
+       /*
+        *     31    30       21    20     19        12 11 7 6      0
+        * imm [20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode
+        *     1         10          1           8       5    JAL/J
+        */
+       bool ret;
+       u32 imm;
+       u32 index = (opcode >> 7) & 0x1f;
+
+       ret = rv_insn_reg_set_val(regs, index, addr + 4);
+       if (!ret)
+               return ret;
+
+       imm  = ((opcode >> 21) & 0x3ff) << 1;
+       imm |= ((opcode >> 20) & 0x1)   << 11;
+       imm |= ((opcode >> 12) & 0xff)  << 12;
+       imm |= ((opcode >> 31) & 0x1)   << 20;
+
+       instruction_pointer_set(regs, addr + sign_extend32((imm), 20));
+
+       return ret;
+}
+
+bool __kprobes simulate_jalr(u32 opcode, unsigned long addr, struct pt_regs *regs)
+{
+       /*
+        * 31          20 19 15 14 12 11 7 6      0
+        *  offset[11:0] | rs1 | 010 | rd | opcode
+        *      12         5      3    5    JALR/JR
+        */
+       bool ret;
+       unsigned long base_addr;
+       u32 imm = (opcode >> 20) & 0xfff;
+       u32 rd_index = (opcode >> 7) & 0x1f;
+       u32 rs1_index = (opcode >> 15) & 0x1f;
+
+       ret = rv_insn_reg_set_val(regs, rd_index, addr + 4);
+       if (!ret)
+               return ret;
+
+       ret = rv_insn_reg_get_val(regs, rs1_index, &base_addr);
+       if (!ret)
+               return ret;
+
+       instruction_pointer_set(regs, (base_addr + sign_extend32((imm), 11))&~1);
+
+       return ret;
+}
diff --git a/arch/riscv/kernel/probes/simulate-insn.h b/arch/riscv/kernel/probes/simulate-insn.h
new file mode 100644 (file)
index 0000000..cb6ff7d
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
+#define _RISCV_KERNEL_PROBES_SIMULATE_INSN_H
+
+#define __RISCV_INSN_FUNCS(name, mask, val)                            \
+static __always_inline bool riscv_insn_is_##name(probe_opcode_t code)  \
+{                                                                      \
+       BUILD_BUG_ON(~(mask) & (val));                                  \
+       return (code & (mask)) == (val);                                \
+}                                                                      \
+bool simulate_##name(u32 opcode, unsigned long addr,                   \
+                    struct pt_regs *regs)
+
+#define RISCV_INSN_REJECTED(name, code)                                        \
+       do {                                                            \
+               if (riscv_insn_is_##name(code)) {                       \
+                       return INSN_REJECTED;                           \
+               }                                                       \
+       } while (0)
+
+__RISCV_INSN_FUNCS(system,     0x7f, 0x73);
+__RISCV_INSN_FUNCS(fence,      0x7f, 0x0f);
+
+#define RISCV_INSN_SET_SIMULATE(name, code)                            \
+       do {                                                            \
+               if (riscv_insn_is_##name(code)) {                       \
+                       api->handler = simulate_##name;                 \
+                       return INSN_GOOD_NO_SLOT;                       \
+               }                                                       \
+       } while (0)
+
+__RISCV_INSN_FUNCS(c_j,                0xe003, 0xa001);
+__RISCV_INSN_FUNCS(c_jr,       0xf007, 0x8002);
+__RISCV_INSN_FUNCS(c_jal,      0xe003, 0x2001);
+__RISCV_INSN_FUNCS(c_jalr,     0xf007, 0x9002);
+__RISCV_INSN_FUNCS(c_beqz,     0xe003, 0xc001);
+__RISCV_INSN_FUNCS(c_bnez,     0xe003, 0xe001);
+__RISCV_INSN_FUNCS(c_ebreak,   0xffff, 0x9002);
+
+__RISCV_INSN_FUNCS(auipc,      0x7f, 0x17);
+__RISCV_INSN_FUNCS(branch,     0x7f, 0x63);
+
+__RISCV_INSN_FUNCS(jal,                0x7f, 0x6f);
+__RISCV_INSN_FUNCS(jalr,       0x707f, 0x67);
+
+#endif /* _RISCV_KERNEL_PROBES_SIMULATE_INSN_H */
diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c
new file mode 100644 (file)
index 0000000..7a057b5
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/highmem.h>
+#include <linux/ptrace.h>
+#include <linux/uprobes.h>
+
+#include "decode-insn.h"
+
+#define UPROBE_TRAP_NR UINT_MAX
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+#ifdef CONFIG_RISCV_ISA_C
+       return (*insn & 0xffff) == UPROBE_SWBP_INSN;
+#else
+       return *insn == UPROBE_SWBP_INSN;
+#endif
+}
+
+unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
+{
+       return instruction_pointer(regs);
+}
+
+int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+                            unsigned long addr)
+{
+       probe_opcode_t opcode;
+
+       opcode = *(probe_opcode_t *)(&auprobe->insn[0]);
+
+       auprobe->insn_size = GET_INSN_LENGTH(opcode);
+
+       switch (riscv_probe_decode_insn(&opcode, &auprobe->api)) {
+       case INSN_REJECTED:
+               return -EINVAL;
+
+       case INSN_GOOD_NO_SLOT:
+               auprobe->simulate = true;
+               break;
+
+       case INSN_GOOD:
+               auprobe->simulate = false;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       utask->autask.saved_cause = current->thread.bad_cause;
+       current->thread.bad_cause = UPROBE_TRAP_NR;
+
+       instruction_pointer_set(regs, utask->xol_vaddr);
+
+       regs->status &= ~SR_SPIE;
+
+       return 0;
+}
+
+int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       WARN_ON_ONCE(current->thread.bad_cause != UPROBE_TRAP_NR);
+
+       instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size);
+
+       regs->status |= SR_SPIE;
+
+       return 0;
+}
+
+bool arch_uprobe_xol_was_trapped(struct task_struct *t)
+{
+       if (t->thread.bad_cause != UPROBE_TRAP_NR)
+               return true;
+
+       return false;
+}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       probe_opcode_t insn;
+       unsigned long addr;
+
+       if (!auprobe->simulate)
+               return false;
+
+       insn = *(probe_opcode_t *)(&auprobe->insn[0]);
+       addr = instruction_pointer(regs);
+
+       if (auprobe->api.handler)
+               auprobe->api.handler(insn, addr, regs);
+
+       return true;
+}
+
+void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+       struct uprobe_task *utask = current->utask;
+
+       /*
+        * Task has received a fatal signal, so reset back to probbed
+        * address.
+        */
+       instruction_pointer_set(regs, utask->vaddr);
+
+       regs->status &= ~SR_SPIE;
+}
+
+bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
+               struct pt_regs *regs)
+{
+       if (ctx == RP_CHECK_CHAIN_CALL)
+               return regs->sp <= ret->stack;
+       else
+               return regs->sp < ret->stack;
+}
+
+unsigned long
+arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
+                                 struct pt_regs *regs)
+{
+       unsigned long ra;
+
+       ra = regs->ra;
+
+       regs->ra = trampoline_vaddr;
+
+       return ra;
+}
+
+int arch_uprobe_exception_notify(struct notifier_block *self,
+                                unsigned long val, void *data)
+{
+       return NOTIFY_DONE;
+}
+
+bool uprobe_breakpoint_handler(struct pt_regs *regs)
+{
+       if (uprobe_pre_sstep_notifier(regs))
+               return true;
+
+       return false;
+}
+
+bool uprobe_single_step_handler(struct pt_regs *regs)
+{
+       if (uprobe_post_sstep_notifier(regs))
+               return true;
+
+       return false;
+}
+
+void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
+                          void *src, unsigned long len)
+{
+       /* Initialize the slot */
+       void *kaddr = kmap_atomic(page);
+       void *dst = kaddr + (vaddr & ~PAGE_MASK);
+
+       memcpy(dst, src, len);
+
+       /* Add ebreak behind opcode to simulate singlestep */
+       if (vaddr) {
+               dst += GET_INSN_LENGTH(*(probe_opcode_t *)src);
+               *(uprobe_opcode_t *)dst = __BUG_INSN_32;
+       }
+
+       kunmap_atomic(kaddr);
+
+       /*
+        * We probably need flush_icache_user_page() but it needs vma.
+        * This should work on most of architectures by default. If
+        * architecture needs to do something different it can define
+        * its own version of the function.
+        */
+       flush_dcache_page(page);
+}
index dd5f985b1f40e0418f4bdac872819275a0766cd0..19f4688f2f36704701cc357b31d7886257effafe 100644 (file)
 #include <asm/unistd.h>
 #include <asm/processor.h>
 #include <asm/csr.h>
+#include <asm/stacktrace.h>
 #include <asm/string.h>
 #include <asm/switch_to.h>
 #include <asm/thread_info.h>
 
 register unsigned long gp_in_global __asm__("gp");
 
-#ifdef CONFIG_STACKPROTECTOR
+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
 #include <linux/stackprotector.h>
 unsigned long __stack_chk_guard __read_mostly;
 EXPORT_SYMBOL(__stack_chk_guard);
@@ -39,11 +40,16 @@ void arch_cpu_idle(void)
        raw_local_irq_enable();
 }
 
-void show_regs(struct pt_regs *regs)
+void __show_regs(struct pt_regs *regs)
 {
        show_regs_print_info(KERN_DEFAULT);
 
-       pr_cont("epc: " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n",
+       if (!user_mode(regs)) {
+               pr_cont("epc : %pS\n", (void *)regs->epc);
+               pr_cont(" ra : %pS\n", (void *)regs->ra);
+       }
+
+       pr_cont("epc : " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n",
                regs->epc, regs->ra, regs->sp);
        pr_cont(" gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT "\n",
                regs->gp, regs->tp, regs->t0);
@@ -69,6 +75,12 @@ void show_regs(struct pt_regs *regs)
        pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT "\n",
                regs->status, regs->badaddr, regs->cause);
 }
+void show_regs(struct pt_regs *regs)
+{
+       __show_regs(regs);
+       if (!user_mode(regs))
+               dump_backtrace(regs, NULL, KERN_DEFAULT);
+}
 
 void start_thread(struct pt_regs *regs, unsigned long pc,
        unsigned long sp)
index 2d6395f5ad54f0c93cc162671fd97e6acfe72771..1a85305720e848033fcbd0fb7e4c7b45883e5d52 100644 (file)
@@ -114,6 +114,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
        return &riscv_user_native_view;
 }
 
+struct pt_regs_offset {
+       const char *name;
+       int offset;
+};
+
+#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+
+static const struct pt_regs_offset regoffset_table[] = {
+       REG_OFFSET_NAME(epc),
+       REG_OFFSET_NAME(ra),
+       REG_OFFSET_NAME(sp),
+       REG_OFFSET_NAME(gp),
+       REG_OFFSET_NAME(tp),
+       REG_OFFSET_NAME(t0),
+       REG_OFFSET_NAME(t1),
+       REG_OFFSET_NAME(t2),
+       REG_OFFSET_NAME(s0),
+       REG_OFFSET_NAME(s1),
+       REG_OFFSET_NAME(a0),
+       REG_OFFSET_NAME(a1),
+       REG_OFFSET_NAME(a2),
+       REG_OFFSET_NAME(a3),
+       REG_OFFSET_NAME(a4),
+       REG_OFFSET_NAME(a5),
+       REG_OFFSET_NAME(a6),
+       REG_OFFSET_NAME(a7),
+       REG_OFFSET_NAME(s2),
+       REG_OFFSET_NAME(s3),
+       REG_OFFSET_NAME(s4),
+       REG_OFFSET_NAME(s5),
+       REG_OFFSET_NAME(s6),
+       REG_OFFSET_NAME(s7),
+       REG_OFFSET_NAME(s8),
+       REG_OFFSET_NAME(s9),
+       REG_OFFSET_NAME(s10),
+       REG_OFFSET_NAME(s11),
+       REG_OFFSET_NAME(t3),
+       REG_OFFSET_NAME(t4),
+       REG_OFFSET_NAME(t5),
+       REG_OFFSET_NAME(t6),
+       REG_OFFSET_NAME(status),
+       REG_OFFSET_NAME(badaddr),
+       REG_OFFSET_NAME(cause),
+       REG_OFFSET_NAME(orig_a0),
+       REG_OFFSET_END,
+};
+
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name:      the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+       const struct pt_regs_offset *roff;
+
+       for (roff = regoffset_table; roff->name != NULL; roff++)
+               if (!strcmp(roff->name, name))
+                       return roff->offset;
+       return -EINVAL;
+}
+
+/**
+ * regs_within_kernel_stack() - check the address in the stack
+ * @regs:      pt_regs which contains kernel stack pointer.
+ * @addr:      address which is checked.
+ *
+ * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
+ * If @addr is within the kernel stack, it returns true. If not, returns false.
+ */
+static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+{
+       return (addr & ~(THREAD_SIZE - 1))  ==
+               (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1));
+}
+
+/**
+ * regs_get_kernel_stack_nth() - get Nth entry of the stack
+ * @regs:      pt_regs which contains kernel stack pointer.
+ * @n:         stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
+ * is specified by @regs. If the @n th entry is NOT in the kernel stack,
+ * this returns 0.
+ */
+unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+{
+       unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
+
+       addr += n;
+       if (regs_within_kernel_stack(regs, (unsigned long)addr))
+               return *addr;
+       else
+               return 0;
+}
+
 void ptrace_disable(struct task_struct *child)
 {
        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
index 226ccce0f9e0734a08ff3da652c898ed21e378ce..f4a7db3d309e69f4928b423532da1b96b762275d 100644 (file)
@@ -351,7 +351,7 @@ static int __sbi_rfence_v02(int fid, const unsigned long *hart_mask,
  * sbi_set_timer() - Program the timer for next timer event.
  * @stime_value: The value after which next timer event should fire.
  *
- * Return: None
+ * Return: None.
  */
 void sbi_set_timer(uint64_t stime_value)
 {
@@ -362,11 +362,11 @@ void sbi_set_timer(uint64_t stime_value)
  * sbi_send_ipi() - Send an IPI to any hart.
  * @hart_mask: A cpu mask containing all the target harts.
  *
- * Return: None
+ * Return: 0 on success, appropriate linux error code otherwise.
  */
-void sbi_send_ipi(const unsigned long *hart_mask)
+int sbi_send_ipi(const unsigned long *hart_mask)
 {
-       __sbi_send_ipi(hart_mask);
+       return __sbi_send_ipi(hart_mask);
 }
 EXPORT_SYMBOL(sbi_send_ipi);
 
@@ -374,12 +374,12 @@ EXPORT_SYMBOL(sbi_send_ipi);
  * sbi_remote_fence_i() - Execute FENCE.I instruction on given remote harts.
  * @hart_mask: A cpu mask containing all the target harts.
  *
- * Return: None
+ * Return: 0 on success, appropriate linux error code otherwise.
  */
-void sbi_remote_fence_i(const unsigned long *hart_mask)
+int sbi_remote_fence_i(const unsigned long *hart_mask)
 {
-       __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I,
-                    hart_mask, 0, 0, 0, 0);
+       return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I,
+                           hart_mask, 0, 0, 0, 0);
 }
 EXPORT_SYMBOL(sbi_remote_fence_i);
 
@@ -390,14 +390,14 @@ EXPORT_SYMBOL(sbi_remote_fence_i);
  * @start: Start of the virtual address
  * @size: Total size of the virtual address range.
  *
- * Return: None
+ * Return: 0 on success, appropriate linux error code otherwise.
  */
-void sbi_remote_sfence_vma(const unsigned long *hart_mask,
+int sbi_remote_sfence_vma(const unsigned long *hart_mask,
                           unsigned long start,
                           unsigned long size)
 {
-       __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
-                    hart_mask, start, size, 0, 0);
+       return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
+                           hart_mask, start, size, 0, 0);
 }
 EXPORT_SYMBOL(sbi_remote_sfence_vma);
 
@@ -410,15 +410,15 @@ EXPORT_SYMBOL(sbi_remote_sfence_vma);
  * @size: Total size of the virtual address range.
  * @asid: The value of address space identifier (ASID).
  *
- * Return: None
+ * Return: 0 on success, appropriate linux error code otherwise.
  */
-void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
+int sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
                                unsigned long start,
                                unsigned long size,
                                unsigned long asid)
 {
-       __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
-                    hart_mask, start, size, asid, 0);
+       return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
+                           hart_mask, start, size, asid, 0);
 }
 EXPORT_SYMBOL(sbi_remote_sfence_vma_asid);
 
@@ -560,7 +560,7 @@ static struct riscv_ipi_ops sbi_ipi_ops = {
        .ipi_inject = sbi_send_cpumask_ipi
 };
 
-int __init sbi_init(void)
+void __init sbi_init(void)
 {
        int ret;
 
@@ -600,6 +600,4 @@ int __init sbi_init(void)
        }
 
        riscv_set_ipi_ops(&sbi_ipi_ops);
-
-       return 0;
 }
index c7c0655dd45b0f617a34d89b315e5829638c43af..e85bacff1b5075ee3b704f7a65e40d102d0a9de3 100644 (file)
@@ -216,8 +216,15 @@ static void __init init_resources(void)
 static void __init parse_dtb(void)
 {
        /* Early scan of device tree from init memory */
-       if (early_init_dt_scan(dtb_early_va))
+       if (early_init_dt_scan(dtb_early_va)) {
+               const char *name = of_flat_dt_get_machine_name();
+
+               if (name) {
+                       pr_info("Machine model: %s\n", name);
+                       dump_stack_set_arch_desc("%s (DT)", name);
+               }
                return;
+       }
 
        pr_err("No DTB passed to the kernel\n");
 #ifdef CONFIG_CMDLINE_FORCE
@@ -252,9 +259,9 @@ void __init setup_arch(char **cmdline_p)
        else
                pr_err("No DTB found in kernel mappings\n");
 #endif
+       misc_mem_init();
 
-       if (IS_ENABLED(CONFIG_RISCV_SBI))
-               sbi_init();
+       sbi_init();
 
        if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
                protect_kernel_text_data();
@@ -275,13 +282,19 @@ void __init setup_arch(char **cmdline_p)
 
 static int __init topology_init(void)
 {
-       int i;
+       int i, ret;
+
+       for_each_online_node(i)
+               register_one_node(i);
 
        for_each_possible_cpu(i) {
                struct cpu *cpu = &per_cpu(cpu_devices, i);
 
                cpu->hotpluggable = cpu_has_hotplug(i);
-               register_cpu(cpu, i);
+               ret = register_cpu(cpu, i);
+               if (unlikely(ret))
+                       pr_warn("Warning: %s: register_cpu %d failed (%d)\n",
+                              __func__, i, ret);
        }
 
        return 0;
index 469aef8ed922e862edd172e218b74590888e858c..65942b3748b41bb2eed79d2e284e06e5c20f8dee 100644 (file)
@@ -309,6 +309,9 @@ static void do_signal(struct pt_regs *regs)
 asmlinkage __visible void do_notify_resume(struct pt_regs *regs,
                                           unsigned long thread_info_flags)
 {
+       if (thread_info_flags & _TIF_UPROBE)
+               uprobe_notify_resume(regs);
+
        /* Handle pending signal delivery */
        if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
                do_signal(regs);
index 96167d55ed9845a23debad56fcbeb009c600c7d1..5e276c25646fc0b583d2783f4508918af1bb83b6 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/cpu_ops.h>
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
+#include <asm/numa.h>
 #include <asm/tlbflush.h>
 #include <asm/sections.h>
 #include <asm/sbi.h>
@@ -45,13 +46,18 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
 {
        int cpuid;
        int ret;
+       unsigned int curr_cpuid;
+
+       curr_cpuid = smp_processor_id();
+       numa_store_cpu_info(curr_cpuid);
+       numa_add_cpu(curr_cpuid);
 
        /* This covers non-smp usecase mandated by "nosmp" option */
        if (max_cpus == 0)
                return;
 
        for_each_possible_cpu(cpuid) {
-               if (cpuid == smp_processor_id())
+               if (cpuid == curr_cpuid)
                        continue;
                if (cpu_ops[cpuid]->cpu_prepare) {
                        ret = cpu_ops[cpuid]->cpu_prepare(cpuid);
@@ -59,6 +65,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
                                continue;
                }
                set_cpu_present(cpuid, true);
+               numa_store_cpu_info(cpuid);
        }
 }
 
@@ -79,6 +86,7 @@ void __init setup_smp(void)
                if (hart == cpuid_to_hartid_map(0)) {
                        BUG_ON(found_boot_cpu);
                        found_boot_cpu = 1;
+                       early_map_cpu_to_node(0, of_node_to_nid(dn));
                        continue;
                }
                if (cpuid >= NR_CPUS) {
@@ -88,6 +96,7 @@ void __init setup_smp(void)
                }
 
                cpuid_to_hartid_map(cpuid) = hart;
+               early_map_cpu_to_node(cpuid, of_node_to_nid(dn));
                cpuid++;
        }
 
@@ -153,6 +162,7 @@ asmlinkage __visible void smp_callin(void)
        current->active_mm = mm;
 
        notify_cpu_starting(curr_cpuid);
+       numa_add_cpu(curr_cpuid);
        update_siblings_masks(curr_cpuid);
        set_cpu_online(curr_cpuid, 1);
 
index c7b0a73e382e920bbfb1a1d6b6488cf906bf0cfa..a0516172a33c84f0f0ce3b58afd6f984aaa5e38d 100644 (file)
@@ -26,30 +26,3 @@ void __init soc_early_init(void)
                }
        }
 }
-
-static bool soc_builtin_dtb_match(unsigned long vendor_id,
-                               unsigned long arch_id, unsigned long imp_id,
-                               const struct soc_builtin_dtb *entry)
-{
-       return entry->vendor_id == vendor_id &&
-              entry->arch_id == arch_id &&
-              entry->imp_id == imp_id;
-}
-
-void * __init soc_lookup_builtin_dtb(void)
-{
-       unsigned long vendor_id, arch_id, imp_id;
-       const struct soc_builtin_dtb *s;
-
-       __asm__ ("csrr %0, mvendorid" : "=r"(vendor_id));
-       __asm__ ("csrr %0, marchid" : "=r"(arch_id));
-       __asm__ ("csrr %0, mimpid" : "=r"(imp_id));
-
-       for (s = (void *)&__soc_builtin_dtb_table_start;
-            (void *)s < (void *)&__soc_builtin_dtb_table_end; s++) {
-               if (soc_builtin_dtb_match(vendor_id, arch_id, imp_id, s))
-                       return s->dtb_func();
-       }
-
-       return NULL;
-}
index df5d2da7c40be3fa9ec41e464911277b98321db3..3f893c9d9d85a37ab08b0e2a3e447902156dc6bc 100644 (file)
@@ -53,9 +53,15 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
                /* Unwind stack frame */
                frame = (struct stackframe *)fp - 1;
                sp = fp;
-               fp = frame->fp;
-               pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
-                                          (unsigned long *)(fp - 8));
+               if (regs && (regs->epc == pc) && (frame->fp & 0x7)) {
+                       fp = frame->ra;
+                       pc = regs->ra;
+               } else {
+                       fp = frame->fp;
+                       pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
+                                                  (unsigned long *)(fp - 8));
+               }
+
        }
 }
 
@@ -100,10 +106,16 @@ static bool print_trace_address(void *arg, unsigned long pc)
        return true;
 }
 
+void dump_backtrace(struct pt_regs *regs, struct task_struct *task,
+                   const char *loglvl)
+{
+       pr_cont("%sCall Trace:\n", loglvl);
+       walk_stackframe(task, regs, print_trace_address, (void *)loglvl);
+}
+
 void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
 {
-       pr_cont("Call Trace:\n");
-       walk_stackframe(task, NULL, print_trace_address, (void *)loglvl);
+       dump_backtrace(NULL, task, loglvl);
 }
 
 static bool save_wchan(void *arg, unsigned long pc)
index ad14f4466d92421c9fff2a5c6cb973d7d98016cf..3ed2c23601a02959fe15230c192e70bb3d686a5d 100644 (file)
 #include <linux/signal.h>
 #include <linux/kdebug.h>
 #include <linux/uaccess.h>
+#include <linux/kprobes.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/irq.h>
 
+#include <asm/bug.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/csr.h>
@@ -66,7 +68,7 @@ void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
                        tsk->comm, task_pid_nr(tsk), signo, code, addr);
                print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
                pr_cont("\n");
-               show_regs(regs);
+               __show_regs(regs);
        }
 
        force_sig_fault(signo, code, (void __user *)addr);
@@ -75,6 +77,8 @@ void do_trap(struct pt_regs *regs, int signo, int code, unsigned long addr)
 static void do_trap_error(struct pt_regs *regs, int signo, int code,
        unsigned long addr, const char *str)
 {
+       current->thread.bad_cause = regs->cause;
+
        if (user_mode(regs)) {
                do_trap(regs, signo, code, addr);
        } else {
@@ -145,6 +149,22 @@ static inline unsigned long get_break_insn_length(unsigned long pc)
 
 asmlinkage __visible void do_trap_break(struct pt_regs *regs)
 {
+#ifdef CONFIG_KPROBES
+       if (kprobe_single_step_handler(regs))
+               return;
+
+       if (kprobe_breakpoint_handler(regs))
+               return;
+#endif
+#ifdef CONFIG_UPROBES
+       if (uprobe_single_step_handler(regs))
+               return;
+
+       if (uprobe_breakpoint_handler(regs))
+               return;
+#endif
+       current->thread.bad_cause = regs->cause;
+
        if (user_mode(regs))
                force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->epc);
 #ifdef CONFIG_KGDB
index 0cfd6da784f84187487a369b479a6dabd41dbbc8..71a315e73cbe7444031db9898ef08445a742a303 100644 (file)
@@ -32,9 +32,10 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
 # Disable -pg to prevent insert call site
 CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os
 
-# Disable gcov profiling for VDSO code
+# Disable profiling and instrumentation for VDSO code
 GCOV_PROFILE := n
 KCOV_INSTRUMENT := n
+KASAN_SANITIZE := n
 
 # Force dependency
 $(obj)/vdso.o: $(obj)/vdso.so
index ac6171e9c19e308aeaac2b69f961363bd8a045a5..25d5c9664e57e4f20e7c0c36ef47c1e1d096ac1c 100644 (file)
@@ -5,3 +5,5 @@ lib-y                   += memset.o
 lib-y                  += memmove.o
 lib-$(CONFIG_MMU)      += uaccess.o
 lib-$(CONFIG_64BIT)    += tishift.o
+
+obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
diff --git a/arch/riscv/lib/error-inject.c b/arch/riscv/lib/error-inject.c
new file mode 100644 (file)
index 0000000..d667ade
--- /dev/null
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/error-injection.h>
+#include <linux/kprobes.h>
+
+void override_function_with_return(struct pt_regs *regs)
+{
+       instruction_pointer_set(regs, regs->ra);
+}
+NOKPROBE_SYMBOL(override_function_with_return);
index c0185e556ca51b944e276267352119809ac1290e..7ebaef10ea1b69e1557c9d08fec4d288ac31e842 100644 (file)
@@ -2,7 +2,8 @@
 
 CFLAGS_init.o := -mcmodel=medany
 ifdef CONFIG_FTRACE
-CFLAGS_REMOVE_init.o = -pg
+CFLAGS_REMOVE_init.o = $(CC_FLAGS_FTRACE)
+CFLAGS_REMOVE_cacheflush.o = $(CC_FLAGS_FTRACE)
 endif
 
 KCOV_INSTRUMENT_init.o := n
index 613ec81a8979adc837390f3d676a56f78309563f..68aa312fc352458193b849a876781a6cf9c194e3 100644 (file)
 /*
  * Copyright (C) 2012 Regents of the University of California
  * Copyright (C) 2017 SiFive
+ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
  */
 
+#include <linux/bitops.h>
+#include <linux/cpumask.h>
 #include <linux/mm.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/static_key.h>
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 
+#ifdef CONFIG_MMU
+
+static DEFINE_STATIC_KEY_FALSE(use_asid_allocator);
+
+static unsigned long asid_bits;
+static unsigned long num_asids;
+static unsigned long asid_mask;
+
+static atomic_long_t current_version;
+
+static DEFINE_RAW_SPINLOCK(context_lock);
+static cpumask_t context_tlb_flush_pending;
+static unsigned long *context_asid_map;
+
+static DEFINE_PER_CPU(atomic_long_t, active_context);
+static DEFINE_PER_CPU(unsigned long, reserved_context);
+
+static bool check_update_reserved_context(unsigned long cntx,
+                                         unsigned long newcntx)
+{
+       int cpu;
+       bool hit = false;
+
+       /*
+        * Iterate over the set of reserved CONTEXT looking for a match.
+        * If we find one, then we can update our mm to use new CONTEXT
+        * (i.e. the same CONTEXT in the current_version) but we can't
+        * exit the loop early, since we need to ensure that all copies
+        * of the old CONTEXT are updated to reflect the mm. Failure to do
+        * so could result in us missing the reserved CONTEXT in a future
+        * version.
+        */
+       for_each_possible_cpu(cpu) {
+               if (per_cpu(reserved_context, cpu) == cntx) {
+                       hit = true;
+                       per_cpu(reserved_context, cpu) = newcntx;
+               }
+       }
+
+       return hit;
+}
+
+static void __flush_context(void)
+{
+       int i;
+       unsigned long cntx;
+
+       /* Must be called with context_lock held */
+       lockdep_assert_held(&context_lock);
+
+       /* Update the list of reserved ASIDs and the ASID bitmap. */
+       bitmap_clear(context_asid_map, 0, num_asids);
+
+       /* Mark already active ASIDs as used */
+       for_each_possible_cpu(i) {
+               cntx = atomic_long_xchg_relaxed(&per_cpu(active_context, i), 0);
+               /*
+                * If this CPU has already been through a rollover, but
+                * hasn't run another task in the meantime, we must preserve
+                * its reserved CONTEXT, as this is the only trace we have of
+                * the process it is still running.
+                */
+               if (cntx == 0)
+                       cntx = per_cpu(reserved_context, i);
+
+               __set_bit(cntx & asid_mask, context_asid_map);
+               per_cpu(reserved_context, i) = cntx;
+       }
+
+       /* Mark ASID #0 as used because it is used at boot-time */
+       __set_bit(0, context_asid_map);
+
+       /* Queue a TLB invalidation for each CPU on next context-switch */
+       cpumask_setall(&context_tlb_flush_pending);
+}
+
+static unsigned long __new_context(struct mm_struct *mm)
+{
+       static u32 cur_idx = 1;
+       unsigned long cntx = atomic_long_read(&mm->context.id);
+       unsigned long asid, ver = atomic_long_read(&current_version);
+
+       /* Must be called with context_lock held */
+       lockdep_assert_held(&context_lock);
+
+       if (cntx != 0) {
+               unsigned long newcntx = ver | (cntx & asid_mask);
+
+               /*
+                * If our current CONTEXT was active during a rollover, we
+                * can continue to use it and this was just a false alarm.
+                */
+               if (check_update_reserved_context(cntx, newcntx))
+                       return newcntx;
+
+               /*
+                * We had a valid CONTEXT in a previous life, so try to
+                * re-use it if possible.
+                */
+               if (!__test_and_set_bit(cntx & asid_mask, context_asid_map))
+                       return newcntx;
+       }
+
+       /*
+        * Allocate a free ASID. If we can't find one then increment
+        * current_version and flush all ASIDs.
+        */
+       asid = find_next_zero_bit(context_asid_map, num_asids, cur_idx);
+       if (asid != num_asids)
+               goto set_asid;
+
+       /* We're out of ASIDs, so increment current_version */
+       ver = atomic_long_add_return_relaxed(num_asids, &current_version);
+
+       /* Flush everything  */
+       __flush_context();
+
+       /* We have more ASIDs than CPUs, so this will always succeed */
+       asid = find_next_zero_bit(context_asid_map, num_asids, 1);
+
+set_asid:
+       __set_bit(asid, context_asid_map);
+       cur_idx = asid;
+       return asid | ver;
+}
+
+static void set_mm_asid(struct mm_struct *mm, unsigned int cpu)
+{
+       unsigned long flags;
+       bool need_flush_tlb = false;
+       unsigned long cntx, old_active_cntx;
+
+       cntx = atomic_long_read(&mm->context.id);
+
+       /*
+        * If our active_context is non-zero and the context matches the
+        * current_version, then we update the active_context entry with a
+        * relaxed cmpxchg.
+        *
+        * Following is how we handle racing with a concurrent rollover:
+        *
+        * - We get a zero back from the cmpxchg and end up waiting on the
+        *   lock. Taking the lock synchronises with the rollover and so
+        *   we are forced to see the updated verion.
+        *
+        * - We get a valid context back from the cmpxchg then we continue
+        *   using old ASID because __flush_context() would have marked ASID
+        *   of active_context as used and next context switch we will
+        *   allocate new context.
+        */
+       old_active_cntx = atomic_long_read(&per_cpu(active_context, cpu));
+       if (old_active_cntx &&
+           ((cntx & ~asid_mask) == atomic_long_read(&current_version)) &&
+           atomic_long_cmpxchg_relaxed(&per_cpu(active_context, cpu),
+                                       old_active_cntx, cntx))
+               goto switch_mm_fast;
+
+       raw_spin_lock_irqsave(&context_lock, flags);
+
+       /* Check that our ASID belongs to the current_version. */
+       cntx = atomic_long_read(&mm->context.id);
+       if ((cntx & ~asid_mask) != atomic_long_read(&current_version)) {
+               cntx = __new_context(mm);
+               atomic_long_set(&mm->context.id, cntx);
+       }
+
+       if (cpumask_test_and_clear_cpu(cpu, &context_tlb_flush_pending))
+               need_flush_tlb = true;
+
+       atomic_long_set(&per_cpu(active_context, cpu), cntx);
+
+       raw_spin_unlock_irqrestore(&context_lock, flags);
+
+switch_mm_fast:
+       csr_write(CSR_SATP, virt_to_pfn(mm->pgd) |
+                 ((cntx & asid_mask) << SATP_ASID_SHIFT) |
+                 SATP_MODE);
+
+       if (need_flush_tlb)
+               local_flush_tlb_all();
+}
+
+static void set_mm_noasid(struct mm_struct *mm)
+{
+       /* Switch the page table and blindly nuke entire local TLB */
+       csr_write(CSR_SATP, virt_to_pfn(mm->pgd) | SATP_MODE);
+       local_flush_tlb_all();
+}
+
+static inline void set_mm(struct mm_struct *mm, unsigned int cpu)
+{
+       if (static_branch_unlikely(&use_asid_allocator))
+               set_mm_asid(mm, cpu);
+       else
+               set_mm_noasid(mm);
+}
+
+static int asids_init(void)
+{
+       unsigned long old;
+
+       /* Figure-out number of ASID bits in HW */
+       old = csr_read(CSR_SATP);
+       asid_bits = old | (SATP_ASID_MASK << SATP_ASID_SHIFT);
+       csr_write(CSR_SATP, asid_bits);
+       asid_bits = (csr_read(CSR_SATP) >> SATP_ASID_SHIFT)  & SATP_ASID_MASK;
+       asid_bits = fls_long(asid_bits);
+       csr_write(CSR_SATP, old);
+
+       /*
+        * In the process of determining number of ASID bits (above)
+        * we polluted the TLB of current HART so let's do TLB flushed
+        * to remove unwanted TLB enteries.
+        */
+       local_flush_tlb_all();
+
+       /* Pre-compute ASID details */
+       num_asids = 1 << asid_bits;
+       asid_mask = num_asids - 1;
+
+       /*
+        * Use ASID allocator only if number of HW ASIDs are
+        * at-least twice more than CPUs
+        */
+       if (num_asids > (2 * num_possible_cpus())) {
+               atomic_long_set(&current_version, num_asids);
+
+               context_asid_map = kcalloc(BITS_TO_LONGS(num_asids),
+                                  sizeof(*context_asid_map), GFP_KERNEL);
+               if (!context_asid_map)
+                       panic("Failed to allocate bitmap for %lu ASIDs\n",
+                             num_asids);
+
+               __set_bit(0, context_asid_map);
+
+               static_branch_enable(&use_asid_allocator);
+
+               pr_info("ASID allocator using %lu bits (%lu entries)\n",
+                       asid_bits, num_asids);
+       } else {
+               pr_info("ASID allocator disabled\n");
+       }
+
+       return 0;
+}
+early_initcall(asids_init);
+#else
+static inline void set_mm(struct mm_struct *mm, unsigned int cpu)
+{
+       /* Nothing to do here when there is no MMU */
+}
+#endif
+
 /*
  * When necessary, performs a deferred icache flush for the given MM context,
  * on the local CPU.  RISC-V has no direct mechanism for instruction cache
@@ -58,10 +318,7 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
        cpumask_clear_cpu(cpu, mm_cpumask(prev));
        cpumask_set_cpu(cpu, mm_cpumask(next));
 
-#ifdef CONFIG_MMU
-       csr_write(CSR_SATP, virt_to_pfn(next->pgd) | SATP_MODE);
-       local_flush_tlb_all();
-#endif
+       set_mm(next, cpu);
 
        flush_icache_deferred(next);
 }
index 3c8b9e433c673754e29444ffe2e75883c8ce0e99..8f17519208c756b7a08bf089ec53004c8a5b0c89 100644 (file)
 #include <linux/perf_event.h>
 #include <linux/signal.h>
 #include <linux/uaccess.h>
+#include <linux/kprobes.h>
 
 #include <asm/ptrace.h>
 #include <asm/tlbflush.h>
 
 #include "../kernel/head.h"
 
+static void die_kernel_fault(const char *msg, unsigned long addr,
+               struct pt_regs *regs)
+{
+       bust_spinlocks(1);
+
+       pr_alert("Unable to handle kernel %s at virtual address " REG_FMT "\n", msg,
+               addr);
+
+       bust_spinlocks(0);
+       die(regs, "Oops");
+       do_exit(SIGKILL);
+}
+
 static inline void no_context(struct pt_regs *regs, unsigned long addr)
 {
+       const char *msg;
+
        /* Are we prepared to handle this kernel fault? */
        if (fixup_exception(regs))
                return;
@@ -29,12 +45,8 @@ static inline void no_context(struct pt_regs *regs, unsigned long addr)
         * Oops. The kernel tried to access some bad page. We'll have to
         * terminate things with extreme prejudice.
         */
-       bust_spinlocks(1);
-       pr_alert("Unable to handle kernel %s at virtual address " REG_FMT "\n",
-               (addr < PAGE_SIZE) ? "NULL pointer dereference" :
-               "paging request", addr);
-       die(regs, "Oops");
-       do_exit(SIGKILL);
+       msg = (addr < PAGE_SIZE) ? "NULL pointer dereference" : "paging request";
+       die_kernel_fault(msg, addr, regs);
 }
 
 static inline void mm_fault_error(struct pt_regs *regs, unsigned long addr, vm_fault_t fault)
@@ -202,6 +214,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
        tsk = current;
        mm = tsk->mm;
 
+       if (kprobe_page_fault(regs, cause))
+               return;
+
        /*
         * Fault-in kernel-space virtual memory on-demand.
         * The 'reference' page table is init_mm.pgd.
@@ -225,6 +240,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
         * in an atomic region, then we must not take the fault.
         */
        if (unlikely(faulthandler_disabled() || !mm)) {
+               tsk->thread.bad_cause = cause;
                no_context(regs, addr);
                return;
        }
@@ -232,6 +248,11 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
        if (user_mode(regs))
                flags |= FAULT_FLAG_USER;
 
+       if (!user_mode(regs) && addr < TASK_SIZE &&
+                       unlikely(!(regs->status & SR_SUM)))
+               die_kernel_fault("access to user memory without uaccess routines",
+                               addr, regs);
+
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
 
        if (cause == EXC_STORE_PAGE_FAULT)
@@ -242,16 +263,19 @@ retry:
        mmap_read_lock(mm);
        vma = find_vma(mm, addr);
        if (unlikely(!vma)) {
+               tsk->thread.bad_cause = cause;
                bad_area(regs, mm, code, addr);
                return;
        }
        if (likely(vma->vm_start <= addr))
                goto good_area;
        if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
+               tsk->thread.bad_cause = cause;
                bad_area(regs, mm, code, addr);
                return;
        }
        if (unlikely(expand_stack(vma, addr))) {
+               tsk->thread.bad_cause = cause;
                bad_area(regs, mm, code, addr);
                return;
        }
@@ -264,6 +288,7 @@ good_area:
        code = SEGV_ACCERR;
 
        if (unlikely(access_error(cause, vma))) {
+               tsk->thread.bad_cause = cause;
                bad_area(regs, mm, code, addr);
                return;
        }
@@ -297,6 +322,7 @@ good_area:
        mmap_read_unlock(mm);
 
        if (unlikely(fault & VM_FAULT_ERROR)) {
+               tsk->thread.bad_cause = cause;
                mm_fault_error(regs, addr, fault);
                return;
        }
index f9f9568d689ef53e215c44de8b4468ae3033cfa9..9dfb8b2dffd6ebf255c7a9b03d32e8026313d94f 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/soc.h>
 #include <asm/io.h>
 #include <asm/ptdump.h>
+#include <asm/numa.h>
 
 #include "../kernel/head.h"
 
@@ -105,55 +106,6 @@ void __init mem_init(void)
        print_vm_layout();
 }
 
-#ifdef CONFIG_BLK_DEV_INITRD
-static void __init setup_initrd(void)
-{
-       phys_addr_t start;
-       unsigned long size;
-
-       /* Ignore the virtul address computed during device tree parsing */
-       initrd_start = initrd_end = 0;
-
-       if (!phys_initrd_size)
-               return;
-       /*
-        * Round the memory region to page boundaries as per free_initrd_mem()
-        * This allows us to detect whether the pages overlapping the initrd
-        * are in use, but more importantly, reserves the entire set of pages
-        * as we don't want these pages allocated for other purposes.
-        */
-       start = round_down(phys_initrd_start, PAGE_SIZE);
-       size = phys_initrd_size + (phys_initrd_start - start);
-       size = round_up(size, PAGE_SIZE);
-
-       if (!memblock_is_region_memory(start, size)) {
-               pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region",
-                      (u64)start, size);
-               goto disable;
-       }
-
-       if (memblock_is_region_reserved(start, size)) {
-               pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region\n",
-                      (u64)start, size);
-               goto disable;
-       }
-
-       memblock_reserve(start, size);
-       /* Now convert initrd to virtual addresses */
-       initrd_start = (unsigned long)__va(phys_initrd_start);
-       initrd_end = initrd_start + phys_initrd_size;
-       initrd_below_start_ok = 1;
-
-       pr_info("Initial ramdisk at: 0x%p (%lu bytes)\n",
-               (void *)(initrd_start), size);
-       return;
-disable:
-       pr_cont(" - disabling initrd\n");
-       initrd_start = 0;
-       initrd_end = 0;
-}
-#endif /* CONFIG_BLK_DEV_INITRD */
-
 void __init setup_bootmem(void)
 {
        phys_addr_t mem_start = 0;
@@ -198,20 +150,19 @@ void __init setup_bootmem(void)
        dma32_phys_limit = min(4UL * SZ_1G, (unsigned long)PFN_PHYS(max_low_pfn));
        set_max_mapnr(max_low_pfn - ARCH_PFN_OFFSET);
 
-#ifdef CONFIG_BLK_DEV_INITRD
-       setup_initrd();
-#endif /* CONFIG_BLK_DEV_INITRD */
-
+       reserve_initrd_mem();
        /*
-        * Avoid using early_init_fdt_reserve_self() since __pa() does
+        * If DTB is built in, no need to reserve its memblock.
+        * Otherwise, do reserve it but avoid using
+        * early_init_fdt_reserve_self() since __pa() does
         * not work for DTB pointers that are fixmap addresses
         */
-       memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va));
+       if (!IS_ENABLED(CONFIG_BUILTIN_DTB))
+               memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va));
 
        early_init_fdt_scan_reserved_mem();
        dma_contiguous_reserve(dma32_phys_limit);
        memblock_allow_resize();
-       memblock_dump_all();
 }
 
 #ifdef CONFIG_MMU
@@ -226,8 +177,6 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned_bss;
 pgd_t trampoline_pg_dir[PTRS_PER_PGD] __page_aligned_bss;
 pte_t fixmap_pte[PTRS_PER_PTE] __page_aligned_bss;
 
-#define MAX_EARLY_MAPPING_SIZE SZ_128M
-
 pgd_t early_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);
 
 void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot)
@@ -302,13 +251,7 @@ static void __init create_pte_mapping(pte_t *ptep,
 
 pmd_t trampoline_pmd[PTRS_PER_PMD] __page_aligned_bss;
 pmd_t fixmap_pmd[PTRS_PER_PMD] __page_aligned_bss;
-
-#if MAX_EARLY_MAPPING_SIZE < PGDIR_SIZE
-#define NUM_EARLY_PMDS         1UL
-#else
-#define NUM_EARLY_PMDS         (1UL + MAX_EARLY_MAPPING_SIZE / PGDIR_SIZE)
-#endif
-pmd_t early_pmd[PTRS_PER_PMD * NUM_EARLY_PMDS] __initdata __aligned(PAGE_SIZE);
+pmd_t early_pmd[PTRS_PER_PMD] __initdata __aligned(PAGE_SIZE);
 pmd_t early_dtb_pmd[PTRS_PER_PMD] __initdata __aligned(PAGE_SIZE);
 
 static pmd_t *__init get_pmd_virt_early(phys_addr_t pa)
@@ -330,11 +273,9 @@ static pmd_t *get_pmd_virt_late(phys_addr_t pa)
 
 static phys_addr_t __init alloc_pmd_early(uintptr_t va)
 {
-       uintptr_t pmd_num;
+       BUG_ON((va - PAGE_OFFSET) >> PGDIR_SHIFT);
 
-       pmd_num = (va - PAGE_OFFSET) >> PGDIR_SHIFT;
-       BUG_ON(pmd_num >= NUM_EARLY_PMDS);
-       return (uintptr_t)&early_pmd[pmd_num * PTRS_PER_PMD];
+       return (uintptr_t)early_pmd;
 }
 
 static phys_addr_t __init alloc_pmd_fixmap(uintptr_t va)
@@ -452,7 +393,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
        uintptr_t va, pa, end_va;
        uintptr_t load_pa = (uintptr_t)(&_start);
        uintptr_t load_sz = (uintptr_t)(&_end) - load_pa;
-       uintptr_t map_size = best_map_size(load_pa, MAX_EARLY_MAPPING_SIZE);
+       uintptr_t map_size;
 #ifndef __PAGETABLE_PMD_FOLDED
        pmd_t fix_bmap_spmd, fix_bmap_epmd;
 #endif
@@ -464,12 +405,11 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
         * Enforce boot alignment requirements of RV32 and
         * RV64 by only allowing PMD or PGD mappings.
         */
-       BUG_ON(map_size == PAGE_SIZE);
+       map_size = PMD_SIZE;
 
        /* Sanity check alignment and size */
        BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0);
        BUG_ON((load_pa % map_size) != 0);
-       BUG_ON(load_sz > MAX_EARLY_MAPPING_SIZE);
 
        pt_ops.alloc_pte = alloc_pte_early;
        pt_ops.get_pte_virt = get_pte_virt_early;
@@ -511,6 +451,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
        /* Setup early PMD for DTB */
        create_pgd_mapping(early_pg_dir, DTB_EARLY_BASE_VA,
                           (uintptr_t)early_dtb_pmd, PGDIR_SIZE, PAGE_TABLE);
+#ifndef CONFIG_BUILTIN_DTB
        /* Create two consecutive PMD mappings for FDT early scan */
        pa = dtb_pa & ~(PMD_SIZE - 1);
        create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA,
@@ -518,7 +459,11 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
        create_pmd_mapping(early_dtb_pmd, DTB_EARLY_BASE_VA + PMD_SIZE,
                           pa + PMD_SIZE, PMD_SIZE, PAGE_KERNEL);
        dtb_early_va = (void *)DTB_EARLY_BASE_VA + (dtb_pa & (PMD_SIZE - 1));
+#else /* CONFIG_BUILTIN_DTB */
+       dtb_early_va = __va(dtb_pa);
+#endif /* CONFIG_BUILTIN_DTB */
 #else
+#ifndef CONFIG_BUILTIN_DTB
        /* Create two consecutive PGD mappings for FDT early scan */
        pa = dtb_pa & ~(PGDIR_SIZE - 1);
        create_pgd_mapping(early_pg_dir, DTB_EARLY_BASE_VA,
@@ -526,6 +471,9 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
        create_pgd_mapping(early_pg_dir, DTB_EARLY_BASE_VA + PGDIR_SIZE,
                           pa + PGDIR_SIZE, PGDIR_SIZE, PAGE_KERNEL);
        dtb_early_va = (void *)DTB_EARLY_BASE_VA + (dtb_pa & (PGDIR_SIZE - 1));
+#else /* CONFIG_BUILTIN_DTB */
+       dtb_early_va = __va(dtb_pa);
+#endif /* CONFIG_BUILTIN_DTB */
 #endif
        dtb_early_pa = dtb_pa;
 
@@ -616,15 +564,7 @@ static void __init setup_vm_final(void)
 #else
 asmlinkage void __init setup_vm(uintptr_t dtb_pa)
 {
-#ifdef CONFIG_BUILTIN_DTB
-       dtb_early_va = soc_lookup_builtin_dtb();
-       if (!dtb_early_va) {
-               /* Fallback to first available DTS */
-               dtb_early_va = (void *) __dtb_start;
-       }
-#else
        dtb_early_va = (void *)dtb_pa;
-#endif
        dtb_early_pa = dtb_pa;
 }
 
@@ -665,9 +605,15 @@ void mark_rodata_ro(void)
 void __init paging_init(void)
 {
        setup_vm_final();
-       sparse_init();
        setup_zero_page();
+}
+
+void __init misc_mem_init(void)
+{
+       arch_numa_init();
+       sparse_init();
        zone_sizes_init();
+       memblock_dump_all();
 }
 
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
index a8a2ffd9114aaa22c60661c7cd2d80023747cb2e..3fc18f469efbc6f1e3b05f8fbba71bc9345315fa 100644 (file)
@@ -9,6 +9,19 @@
 #include <linux/pgtable.h>
 #include <asm/tlbflush.h>
 #include <asm/fixmap.h>
+#include <asm/pgalloc.h>
+
+static __init void *early_alloc(size_t size, int node)
+{
+       void *ptr = memblock_alloc_try_nid(size, size,
+               __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, node);
+
+       if (!ptr)
+               panic("%pS: Failed to allocate %zu bytes align=%zx nid=%d from=%llx\n",
+                       __func__, size, size, node, (u64)__pa(MAX_DMA_ADDRESS));
+
+       return ptr;
+}
 
 extern pgd_t early_pg_dir[PTRS_PER_PGD];
 asmlinkage void __init kasan_early_init(void)
@@ -47,40 +60,133 @@ asmlinkage void __init kasan_early_init(void)
        local_flush_tlb_all();
 }
 
-static void __init populate(void *start, void *end)
+static void kasan_populate_pte(pmd_t *pmd, unsigned long vaddr, unsigned long end)
+{
+       phys_addr_t phys_addr;
+       pte_t *ptep, *base_pte;
+
+       if (pmd_none(*pmd))
+               base_pte = memblock_alloc(PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE);
+       else
+               base_pte = (pte_t *)pmd_page_vaddr(*pmd);
+
+       ptep = base_pte + pte_index(vaddr);
+
+       do {
+               if (pte_none(*ptep)) {
+                       phys_addr = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
+                       set_pte(ptep, pfn_pte(PFN_DOWN(phys_addr), PAGE_KERNEL));
+               }
+       } while (ptep++, vaddr += PAGE_SIZE, vaddr != end);
+
+       set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(base_pte)), PAGE_TABLE));
+}
+
+static void kasan_populate_pmd(pgd_t *pgd, unsigned long vaddr, unsigned long end)
+{
+       phys_addr_t phys_addr;
+       pmd_t *pmdp, *base_pmd;
+       unsigned long next;
+
+       base_pmd = (pmd_t *)pgd_page_vaddr(*pgd);
+       if (base_pmd == lm_alias(kasan_early_shadow_pmd))
+               base_pmd = memblock_alloc(PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE);
+
+       pmdp = base_pmd + pmd_index(vaddr);
+
+       do {
+               next = pmd_addr_end(vaddr, end);
+
+               if (pmd_none(*pmdp) && IS_ALIGNED(vaddr, PMD_SIZE) && (next - vaddr) >= PMD_SIZE) {
+                       phys_addr = memblock_phys_alloc(PMD_SIZE, PMD_SIZE);
+                       if (phys_addr) {
+                               set_pmd(pmdp, pfn_pmd(PFN_DOWN(phys_addr), PAGE_KERNEL));
+                               continue;
+                       }
+               }
+
+               kasan_populate_pte(pmdp, vaddr, next);
+       } while (pmdp++, vaddr = next, vaddr != end);
+
+       /*
+        * Wait for the whole PGD to be populated before setting the PGD in
+        * the page table, otherwise, if we did set the PGD before populating
+        * it entirely, memblock could allocate a page at a physical address
+        * where KASAN is not populated yet and then we'd get a page fault.
+        */
+       set_pgd(pgd, pfn_pgd(PFN_DOWN(__pa(base_pmd)), PAGE_TABLE));
+}
+
+static void kasan_populate_pgd(unsigned long vaddr, unsigned long end)
+{
+       phys_addr_t phys_addr;
+       pgd_t *pgdp = pgd_offset_k(vaddr);
+       unsigned long next;
+
+       do {
+               next = pgd_addr_end(vaddr, end);
+
+               /*
+                * pgdp can't be none since kasan_early_init initialized all KASAN
+                * shadow region with kasan_early_shadow_pmd: if this is stillthe case,
+                * that means we can try to allocate a hugepage as a replacement.
+                */
+               if (pgd_page_vaddr(*pgdp) == (unsigned long)lm_alias(kasan_early_shadow_pmd) &&
+                   IS_ALIGNED(vaddr, PGDIR_SIZE) && (next - vaddr) >= PGDIR_SIZE) {
+                       phys_addr = memblock_phys_alloc(PGDIR_SIZE, PGDIR_SIZE);
+                       if (phys_addr) {
+                               set_pgd(pgdp, pfn_pgd(PFN_DOWN(phys_addr), PAGE_KERNEL));
+                               continue;
+                       }
+               }
+
+               kasan_populate_pmd(pgdp, vaddr, next);
+       } while (pgdp++, vaddr = next, vaddr != end);
+}
+
+static void __init kasan_populate(void *start, void *end)
 {
-       unsigned long i, offset;
        unsigned long vaddr = (unsigned long)start & PAGE_MASK;
        unsigned long vend = PAGE_ALIGN((unsigned long)end);
-       unsigned long n_pages = (vend - vaddr) / PAGE_SIZE;
-       unsigned long n_ptes =
-           ((n_pages + PTRS_PER_PTE) & -PTRS_PER_PTE) / PTRS_PER_PTE;
-       unsigned long n_pmds =
-           ((n_ptes + PTRS_PER_PMD) & -PTRS_PER_PMD) / PTRS_PER_PMD;
-
-       pte_t *pte =
-           memblock_alloc(n_ptes * PTRS_PER_PTE * sizeof(pte_t), PAGE_SIZE);
-       pmd_t *pmd =
-           memblock_alloc(n_pmds * PTRS_PER_PMD * sizeof(pmd_t), PAGE_SIZE);
-       pgd_t *pgd = pgd_offset_k(vaddr);
-
-       for (i = 0; i < n_pages; i++) {
-               phys_addr_t phys = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
-               set_pte(&pte[i], pfn_pte(PHYS_PFN(phys), PAGE_KERNEL));
-       }
-
-       for (i = 0, offset = 0; i < n_ptes; i++, offset += PTRS_PER_PTE)
-               set_pmd(&pmd[i],
-                       pfn_pmd(PFN_DOWN(__pa(&pte[offset])),
-                               __pgprot(_PAGE_TABLE)));
 
-       for (i = 0, offset = 0; i < n_pmds; i++, offset += PTRS_PER_PMD)
-               set_pgd(&pgd[i],
-                       pfn_pgd(PFN_DOWN(__pa(&pmd[offset])),
-                               __pgprot(_PAGE_TABLE)));
+       kasan_populate_pgd(vaddr, vend);
 
        local_flush_tlb_all();
-       memset(start, 0, end - start);
+       memset(start, KASAN_SHADOW_INIT, end - start);
+}
+
+void __init kasan_shallow_populate(void *start, void *end)
+{
+       unsigned long vaddr = (unsigned long)start & PAGE_MASK;
+       unsigned long vend = PAGE_ALIGN((unsigned long)end);
+       unsigned long pfn;
+       int index;
+       void *p;
+       pud_t *pud_dir, *pud_k;
+       pgd_t *pgd_dir, *pgd_k;
+       p4d_t *p4d_dir, *p4d_k;
+
+       while (vaddr < vend) {
+               index = pgd_index(vaddr);
+               pfn = csr_read(CSR_SATP) & SATP_PPN;
+               pgd_dir = (pgd_t *)pfn_to_virt(pfn) + index;
+               pgd_k = init_mm.pgd + index;
+               pgd_dir = pgd_offset_k(vaddr);
+               set_pgd(pgd_dir, *pgd_k);
+
+               p4d_dir = p4d_offset(pgd_dir, vaddr);
+               p4d_k  = p4d_offset(pgd_k, vaddr);
+
+               vaddr = (vaddr + PUD_SIZE) & PUD_MASK;
+               pud_dir = pud_offset(p4d_dir, vaddr);
+               pud_k = pud_offset(p4d_k, vaddr);
+
+               if (pud_present(*pud_dir)) {
+                       p = early_alloc(PAGE_SIZE, NUMA_NO_NODE);
+                       pud_populate(&init_mm, pud_dir, p);
+               }
+               vaddr += PAGE_SIZE;
+       }
 }
 
 void __init kasan_init(void)
@@ -90,7 +196,15 @@ void __init kasan_init(void)
 
        kasan_populate_early_shadow((void *)KASAN_SHADOW_START,
                                    (void *)kasan_mem_to_shadow((void *)
-                                                               VMALLOC_END));
+                                                               VMEMMAP_END));
+       if (IS_ENABLED(CONFIG_KASAN_VMALLOC))
+               kasan_shallow_populate(
+                       (void *)kasan_mem_to_shadow((void *)VMALLOC_START),
+                       (void *)kasan_mem_to_shadow((void *)VMALLOC_END));
+       else
+               kasan_populate_early_shadow(
+                       (void *)kasan_mem_to_shadow((void *)VMALLOC_START),
+                       (void *)kasan_mem_to_shadow((void *)VMALLOC_END));
 
        for_each_mem_range(i, &_start, &_end) {
                void *start = (void *)__va(_start);
@@ -99,7 +213,7 @@ void __init kasan_init(void)
                if (start >= end)
                        break;
 
-               populate(kasan_mem_to_shadow(start), kasan_mem_to_shadow(end));
+               kasan_populate(kasan_mem_to_shadow(start), kasan_mem_to_shadow(end));
        };
 
        for (i = 0; i < PTRS_PER_PTE; i++)
@@ -108,6 +222,6 @@ void __init kasan_init(void)
                               __pgprot(_PAGE_PRESENT | _PAGE_READ |
                                        _PAGE_ACCESSED)));
 
-       memset(kasan_early_shadow_page, 0, PAGE_SIZE);
+       memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
        init_task.kasan_depth = 0;
 }
index 324aa03fed3cf597249a107e48dfe54b6f77f60a..ffcbe2bc460eb5effddb4ce546ba0d375e313e08 100644 (file)
@@ -213,4 +213,10 @@ config GENERIC_ARCH_TOPOLOGY
          appropriate scaling, sysfs interface for reading capacity values at
          runtime.
 
+config GENERIC_ARCH_NUMA
+       bool
+       help
+         Enable support for generic NUMA implementation. Currently, RISC-V
+         and ARM64 use it.
+
 endmenu
index 5e7bf9669a81f8c21d8347ddfb182f0171e4e3f9..8b93a7f291ecd736d3decb878bcc90c34b543484 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o
 obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
 obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o
 obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
+obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
 
 obj-y                  += test/
 
diff --git a/drivers/base/arch_numa.c b/drivers/base/arch_numa.c
new file mode 100644 (file)
index 0000000..4cc4e11
--- /dev/null
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NUMA support, based on the x86 implementation.
+ *
+ * Copyright (C) 2015 Cavium Inc.
+ * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
+ */
+
+#define pr_fmt(fmt) "NUMA: " fmt
+
+#include <linux/acpi.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <asm/sections.h>
+
+struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
+EXPORT_SYMBOL(node_data);
+nodemask_t numa_nodes_parsed __initdata;
+static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
+
+static int numa_distance_cnt;
+static u8 *numa_distance;
+bool numa_off;
+
+static __init int numa_parse_early_param(char *opt)
+{
+       if (!opt)
+               return -EINVAL;
+       if (str_has_prefix(opt, "off"))
+               numa_off = true;
+
+       return 0;
+}
+early_param("numa", numa_parse_early_param);
+
+cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
+EXPORT_SYMBOL(node_to_cpumask_map);
+
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+
+/*
+ * Returns a pointer to the bitmask of CPUs on Node 'node'.
+ */
+const struct cpumask *cpumask_of_node(int node)
+{
+
+       if (node == NUMA_NO_NODE)
+               return cpu_all_mask;
+
+       if (WARN_ON(node < 0 || node >= nr_node_ids))
+               return cpu_none_mask;
+
+       if (WARN_ON(node_to_cpumask_map[node] == NULL))
+               return cpu_online_mask;
+
+       return node_to_cpumask_map[node];
+}
+EXPORT_SYMBOL(cpumask_of_node);
+
+#endif
+
+static void numa_update_cpu(unsigned int cpu, bool remove)
+{
+       int nid = cpu_to_node(cpu);
+
+       if (nid == NUMA_NO_NODE)
+               return;
+
+       if (remove)
+               cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]);
+       else
+               cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
+}
+
+void numa_add_cpu(unsigned int cpu)
+{
+       numa_update_cpu(cpu, false);
+}
+
+void numa_remove_cpu(unsigned int cpu)
+{
+       numa_update_cpu(cpu, true);
+}
+
+void numa_clear_node(unsigned int cpu)
+{
+       numa_remove_cpu(cpu);
+       set_cpu_numa_node(cpu, NUMA_NO_NODE);
+}
+
+/*
+ * Allocate node_to_cpumask_map based on number of available nodes
+ * Requires node_possible_map to be valid.
+ *
+ * Note: cpumask_of_node() is not valid until after this is done.
+ * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
+ */
+static void __init setup_node_to_cpumask_map(void)
+{
+       int node;
+
+       /* setup nr_node_ids if not done yet */
+       if (nr_node_ids == MAX_NUMNODES)
+               setup_nr_node_ids();
+
+       /* allocate and clear the mapping */
+       for (node = 0; node < nr_node_ids; node++) {
+               alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
+               cpumask_clear(node_to_cpumask_map[node]);
+       }
+
+       /* cpumask_of_node() will now work */
+       pr_debug("Node to cpumask map for %u nodes\n", nr_node_ids);
+}
+
+/*
+ * Set the cpu to node and mem mapping
+ */
+void numa_store_cpu_info(unsigned int cpu)
+{
+       set_cpu_numa_node(cpu, cpu_to_node_map[cpu]);
+}
+
+void __init early_map_cpu_to_node(unsigned int cpu, int nid)
+{
+       /* fallback to node 0 */
+       if (nid < 0 || nid >= MAX_NUMNODES || numa_off)
+               nid = 0;
+
+       cpu_to_node_map[cpu] = nid;
+
+       /*
+        * We should set the numa node of cpu0 as soon as possible, because it
+        * has already been set up online before. cpu_to_node(0) will soon be
+        * called.
+        */
+       if (!cpu)
+               set_cpu_numa_node(cpu, nid);
+}
+
+#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
+unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(__per_cpu_offset);
+
+static int __init early_cpu_to_node(int cpu)
+{
+       return cpu_to_node_map[cpu];
+}
+
+static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
+{
+       return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
+}
+
+static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
+                                      size_t align)
+{
+       int nid = early_cpu_to_node(cpu);
+
+       return  memblock_alloc_try_nid(size, align,
+                       __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, nid);
+}
+
+static void __init pcpu_fc_free(void *ptr, size_t size)
+{
+       memblock_free_early(__pa(ptr), size);
+}
+
+void __init setup_per_cpu_areas(void)
+{
+       unsigned long delta;
+       unsigned int cpu;
+       int rc;
+
+       /*
+        * Always reserve area for module percpu variables.  That's
+        * what the legacy allocator did.
+        */
+       rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
+                                   PERCPU_DYNAMIC_RESERVE, PAGE_SIZE,
+                                   pcpu_cpu_distance,
+                                   pcpu_fc_alloc, pcpu_fc_free);
+       if (rc < 0)
+               panic("Failed to initialize percpu areas.");
+
+       delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
+       for_each_possible_cpu(cpu)
+               __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
+}
+#endif
+
+/**
+ * numa_add_memblk() - Set node id to memblk
+ * @nid: NUMA node ID of the new memblk
+ * @start: Start address of the new memblk
+ * @end:  End address of the new memblk
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int __init numa_add_memblk(int nid, u64 start, u64 end)
+{
+       int ret;
+
+       ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
+       if (ret < 0) {
+               pr_err("memblock [0x%llx - 0x%llx] failed to add on node %d\n",
+                       start, (end - 1), nid);
+               return ret;
+       }
+
+       node_set(nid, numa_nodes_parsed);
+       return ret;
+}
+
+/*
+ * Initialize NODE_DATA for a node on the local memory
+ */
+static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
+{
+       const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
+       u64 nd_pa;
+       void *nd;
+       int tnid;
+
+       if (start_pfn >= end_pfn)
+               pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
+
+       nd_pa = memblock_phys_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
+       if (!nd_pa)
+               panic("Cannot allocate %zu bytes for node %d data\n",
+                     nd_size, nid);
+
+       nd = __va(nd_pa);
+
+       /* report and initialize */
+       pr_info("NODE_DATA [mem %#010Lx-%#010Lx]\n",
+               nd_pa, nd_pa + nd_size - 1);
+       tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
+       if (tnid != nid)
+               pr_info("NODE_DATA(%d) on node %d\n", nid, tnid);
+
+       node_data[nid] = nd;
+       memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
+       NODE_DATA(nid)->node_id = nid;
+       NODE_DATA(nid)->node_start_pfn = start_pfn;
+       NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
+}
+
+/*
+ * numa_free_distance
+ *
+ * The current table is freed.
+ */
+void __init numa_free_distance(void)
+{
+       size_t size;
+
+       if (!numa_distance)
+               return;
+
+       size = numa_distance_cnt * numa_distance_cnt *
+               sizeof(numa_distance[0]);
+
+       memblock_free(__pa(numa_distance), size);
+       numa_distance_cnt = 0;
+       numa_distance = NULL;
+}
+
+/*
+ * Create a new NUMA distance table.
+ */
+static int __init numa_alloc_distance(void)
+{
+       size_t size;
+       u64 phys;
+       int i, j;
+
+       size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
+       phys = memblock_find_in_range(0, PFN_PHYS(max_pfn),
+                                     size, PAGE_SIZE);
+       if (WARN_ON(!phys))
+               return -ENOMEM;
+
+       memblock_reserve(phys, size);
+
+       numa_distance = __va(phys);
+       numa_distance_cnt = nr_node_ids;
+
+       /* fill with the default distances */
+       for (i = 0; i < numa_distance_cnt; i++)
+               for (j = 0; j < numa_distance_cnt; j++)
+                       numa_distance[i * numa_distance_cnt + j] = i == j ?
+                               LOCAL_DISTANCE : REMOTE_DISTANCE;
+
+       pr_debug("Initialized distance table, cnt=%d\n", numa_distance_cnt);
+
+       return 0;
+}
+
+/**
+ * numa_set_distance() - Set inter node NUMA distance from node to node.
+ * @from: the 'from' node to set distance
+ * @to: the 'to'  node to set distance
+ * @distance: NUMA distance
+ *
+ * Set the distance from node @from to @to to @distance.
+ * If distance table doesn't exist, a warning is printed.
+ *
+ * If @from or @to is higher than the highest known node or lower than zero
+ * or @distance doesn't make sense, the call is ignored.
+ */
+void __init numa_set_distance(int from, int to, int distance)
+{
+       if (!numa_distance) {
+               pr_warn_once("Warning: distance table not allocated yet\n");
+               return;
+       }
+
+       if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
+                       from < 0 || to < 0) {
+               pr_warn_once("Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
+                           from, to, distance);
+               return;
+       }
+
+       if ((u8)distance != distance ||
+           (from == to && distance != LOCAL_DISTANCE)) {
+               pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
+                            from, to, distance);
+               return;
+       }
+
+       numa_distance[from * numa_distance_cnt + to] = distance;
+}
+
+/*
+ * Return NUMA distance @from to @to
+ */
+int __node_distance(int from, int to)
+{
+       if (from >= numa_distance_cnt || to >= numa_distance_cnt)
+               return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
+       return numa_distance[from * numa_distance_cnt + to];
+}
+EXPORT_SYMBOL(__node_distance);
+
+static int __init numa_register_nodes(void)
+{
+       int nid;
+       struct memblock_region *mblk;
+
+       /* Check that valid nid is set to memblks */
+       for_each_mem_region(mblk) {
+               int mblk_nid = memblock_get_region_node(mblk);
+               phys_addr_t start = mblk->base;
+               phys_addr_t end = mblk->base + mblk->size - 1;
+
+               if (mblk_nid == NUMA_NO_NODE || mblk_nid >= MAX_NUMNODES) {
+                       pr_warn("Warning: invalid memblk node %d [mem %pap-%pap]\n",
+                               mblk_nid, &start, &end);
+                       return -EINVAL;
+               }
+       }
+
+       /* Finally register nodes. */
+       for_each_node_mask(nid, numa_nodes_parsed) {
+               unsigned long start_pfn, end_pfn;
+
+               get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+               setup_node_data(nid, start_pfn, end_pfn);
+               node_set_online(nid);
+       }
+
+       /* Setup online nodes to actual nodes*/
+       node_possible_map = numa_nodes_parsed;
+
+       return 0;
+}
+
+static int __init numa_init(int (*init_func)(void))
+{
+       int ret;
+
+       nodes_clear(numa_nodes_parsed);
+       nodes_clear(node_possible_map);
+       nodes_clear(node_online_map);
+
+       ret = numa_alloc_distance();
+       if (ret < 0)
+               return ret;
+
+       ret = init_func();
+       if (ret < 0)
+               goto out_free_distance;
+
+       if (nodes_empty(numa_nodes_parsed)) {
+               pr_info("No NUMA configuration found\n");
+               ret = -EINVAL;
+               goto out_free_distance;
+       }
+
+       ret = numa_register_nodes();
+       if (ret < 0)
+               goto out_free_distance;
+
+       setup_node_to_cpumask_map();
+
+       return 0;
+out_free_distance:
+       numa_free_distance();
+       return ret;
+}
+
+/**
+ * dummy_numa_init() - Fallback dummy NUMA init
+ *
+ * Used if there's no underlying NUMA architecture, NUMA initialization
+ * fails, or NUMA is disabled on the command line.
+ *
+ * Must online at least one node (node 0) and add memory blocks that cover all
+ * allowed memory. It is unlikely that this function fails.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+static int __init dummy_numa_init(void)
+{
+       phys_addr_t start = memblock_start_of_DRAM();
+       phys_addr_t end = memblock_end_of_DRAM() - 1;
+       int ret;
+
+       if (numa_off)
+               pr_info("NUMA disabled\n"); /* Forced off on command line. */
+       pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end);
+
+       ret = numa_add_memblk(0, start, end + 1);
+       if (ret) {
+               pr_err("NUMA init failed\n");
+               return ret;
+       }
+
+       numa_off = true;
+       return 0;
+}
+
+#ifdef CONFIG_ACPI_NUMA
+static int __init arch_acpi_numa_init(void)
+{
+       int ret;
+
+       ret = acpi_numa_init();
+       if (ret) {
+               pr_info("Failed to initialise from firmware\n");
+               return ret;
+       }
+
+       return srat_disabled() ? -EINVAL : 0;
+}
+#else
+static int __init arch_acpi_numa_init(void)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
+/**
+ * arch_numa_init() - Initialize NUMA
+ *
+ * Try each configured NUMA initialization method until one succeeds. The
+ * last fallback is dummy single node config encompassing whole memory.
+ */
+void __init arch_numa_init(void)
+{
+       if (!numa_off) {
+               if (!acpi_disabled && !numa_init(arch_acpi_numa_init))
+                       return;
+               if (acpi_disabled && !numa_init(of_numa_init))
+                       return;
+       }
+
+       numa_init(dummy_numa_init);
+}
index 4b47170dbe2eecb046e60fbda6ab875a5f105b90..a588d56502d40a07ae65654752161fbf58dc33ab 100644 (file)
@@ -369,6 +369,13 @@ config COMMON_CLK_FIXED_MMIO
        help
          Support for Memory Mapped IO Fixed clocks
 
+config COMMON_CLK_K210
+       bool "Clock driver for the Canaan Kendryte K210 SoC"
+       depends on OF && RISCV && SOC_CANAAN
+       default SOC_CANAAN
+       help
+         Support for the Canaan Kendryte K210 RISC-V SoC clocks.
+
 source "drivers/clk/actions/Kconfig"
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/baikal-t1/Kconfig"
index 71c1fa24b5f0380d6f9d70cad55d47defade2042..b22ae4f81e0b33c375e1c28f9caa8247b662512a 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_COMMON_CLK_ASPEED)               += clk-aspeed.o
 obj-$(CONFIG_MACH_ASPEED_G6)           += clk-ast2600.o
 obj-$(CONFIG_ARCH_HIGHBANK)            += clk-highbank.o
 obj-$(CONFIG_CLK_HSDK)                 += clk-hsdk-pll.o
+obj-$(CONFIG_COMMON_CLK_K210)          += clk-k210.o
 obj-$(CONFIG_COMMON_CLK_LOCHNAGAR)     += clk-lochnagar.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)      += clk-max77686.o
 obj-$(CONFIG_COMMON_CLK_MAX9485)       += clk-max9485.o
diff --git a/drivers/clk/clk-k210.c b/drivers/clk/clk-k210.c
new file mode 100644 (file)
index 0000000..6c84abf
--- /dev/null
@@ -0,0 +1,1007 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ */
+#define pr_fmt(fmt)     "k210-clk: " fmt
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk-provider.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <soc/canaan/k210-sysctl.h>
+
+#include <dt-bindings/clock/k210-clk.h>
+
+struct k210_sysclk;
+
+struct k210_clk {
+       int id;
+       struct k210_sysclk *ksc;
+       struct clk_hw hw;
+};
+
+struct k210_clk_cfg {
+       const char *name;
+       u8 gate_reg;
+       u8 gate_bit;
+       u8 div_reg;
+       u8 div_shift;
+       u8 div_width;
+       u8 div_type;
+       u8 mux_reg;
+       u8 mux_bit;
+};
+
+enum k210_clk_div_type {
+       K210_DIV_NONE,
+       K210_DIV_ONE_BASED,
+       K210_DIV_DOUBLE_ONE_BASED,
+       K210_DIV_POWER_OF_TWO,
+};
+
+#define K210_GATE(_reg, _bit)  \
+       .gate_reg = (_reg),     \
+       .gate_bit = (_bit)
+
+#define K210_DIV(_reg, _shift, _width, _type)  \
+       .div_reg = (_reg),                      \
+       .div_shift = (_shift),                  \
+       .div_width = (_width),                  \
+       .div_type = (_type)
+
+#define K210_MUX(_reg, _bit)   \
+       .mux_reg = (_reg),      \
+       .mux_bit = (_bit)
+
+static struct k210_clk_cfg k210_clk_cfgs[K210_NUM_CLKS] = {
+       /* Gated clocks, no mux, no divider */
+       [K210_CLK_CPU] = {
+               .name = "cpu",
+               K210_GATE(K210_SYSCTL_EN_CENT, 0)
+       },
+       [K210_CLK_DMA] = {
+               .name = "dma",
+               K210_GATE(K210_SYSCTL_EN_PERI, 1)
+       },
+       [K210_CLK_FFT] = {
+               .name = "fft",
+               K210_GATE(K210_SYSCTL_EN_PERI, 4)
+       },
+       [K210_CLK_GPIO] = {
+               .name = "gpio",
+               K210_GATE(K210_SYSCTL_EN_PERI, 5)
+       },
+       [K210_CLK_UART1] = {
+               .name = "uart1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 16)
+       },
+       [K210_CLK_UART2] = {
+               .name = "uart2",
+               K210_GATE(K210_SYSCTL_EN_PERI, 17)
+       },
+       [K210_CLK_UART3] = {
+               .name = "uart3",
+               K210_GATE(K210_SYSCTL_EN_PERI, 18)
+       },
+       [K210_CLK_FPIOA] = {
+               .name = "fpioa",
+               K210_GATE(K210_SYSCTL_EN_PERI, 20)
+       },
+       [K210_CLK_SHA] = {
+               .name = "sha",
+               K210_GATE(K210_SYSCTL_EN_PERI, 26)
+       },
+       [K210_CLK_AES] = {
+               .name = "aes",
+               K210_GATE(K210_SYSCTL_EN_PERI, 19)
+       },
+       [K210_CLK_OTP] = {
+               .name = "otp",
+               K210_GATE(K210_SYSCTL_EN_PERI, 27)
+       },
+       [K210_CLK_RTC] = {
+               .name = "rtc",
+               K210_GATE(K210_SYSCTL_EN_PERI, 29)
+       },
+
+       /* Gated divider clocks */
+       [K210_CLK_SRAM0] = {
+               .name = "sram0",
+               K210_GATE(K210_SYSCTL_EN_CENT, 1),
+               K210_DIV(K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_SRAM1] = {
+               .name = "sram1",
+               K210_GATE(K210_SYSCTL_EN_CENT, 2),
+               K210_DIV(K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_ROM] = {
+               .name = "rom",
+               K210_GATE(K210_SYSCTL_EN_PERI, 0),
+               K210_DIV(K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_DVP] = {
+               .name = "dvp",
+               K210_GATE(K210_SYSCTL_EN_PERI, 3),
+               K210_DIV(K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_APB0] = {
+               .name = "apb0",
+               K210_GATE(K210_SYSCTL_EN_CENT, 3),
+               K210_DIV(K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_APB1] = {
+               .name = "apb1",
+               K210_GATE(K210_SYSCTL_EN_CENT, 4),
+               K210_DIV(K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_APB2] = {
+               .name = "apb2",
+               K210_GATE(K210_SYSCTL_EN_CENT, 5),
+               K210_DIV(K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_AI] = {
+               .name = "ai",
+               K210_GATE(K210_SYSCTL_EN_PERI, 2),
+               K210_DIV(K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE_BASED)
+       },
+       [K210_CLK_SPI0] = {
+               .name = "spi0",
+               K210_GATE(K210_SYSCTL_EN_PERI, 6),
+               K210_DIV(K210_SYSCTL_THR1, 0, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_SPI1] = {
+               .name = "spi1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 7),
+               K210_DIV(K210_SYSCTL_THR1, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_SPI2] = {
+               .name = "spi2",
+               K210_GATE(K210_SYSCTL_EN_PERI, 8),
+               K210_DIV(K210_SYSCTL_THR1, 16, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2C0] = {
+               .name = "i2c0",
+               K210_GATE(K210_SYSCTL_EN_PERI, 13),
+               K210_DIV(K210_SYSCTL_THR5, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2C1] = {
+               .name = "i2c1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 14),
+               K210_DIV(K210_SYSCTL_THR5, 16, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2C2] = {
+               .name = "i2c2",
+               K210_GATE(K210_SYSCTL_EN_PERI, 15),
+               K210_DIV(K210_SYSCTL_THR5, 24, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_WDT0] = {
+               .name = "wdt0",
+               K210_GATE(K210_SYSCTL_EN_PERI, 24),
+               K210_DIV(K210_SYSCTL_THR6, 0, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_WDT1] = {
+               .name = "wdt1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 25),
+               K210_DIV(K210_SYSCTL_THR6, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2S0] = {
+               .name = "i2s0",
+               K210_GATE(K210_SYSCTL_EN_PERI, 10),
+               K210_DIV(K210_SYSCTL_THR3, 0, 16, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2S1] = {
+               .name = "i2s1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 11),
+               K210_DIV(K210_SYSCTL_THR3, 16, 16, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2S2] = {
+               .name = "i2s2",
+               K210_GATE(K210_SYSCTL_EN_PERI, 12),
+               K210_DIV(K210_SYSCTL_THR4, 0, 16, K210_DIV_DOUBLE_ONE_BASED)
+       },
+
+       /* Divider clocks, no gate, no mux */
+       [K210_CLK_I2S0_M] = {
+               .name = "i2s0_m",
+               K210_DIV(K210_SYSCTL_THR4, 16, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2S1_M] = {
+               .name = "i2s1_m",
+               K210_DIV(K210_SYSCTL_THR4, 24, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+       [K210_CLK_I2S2_M] = {
+               .name = "i2s2_m",
+               K210_DIV(K210_SYSCTL_THR4, 0, 8, K210_DIV_DOUBLE_ONE_BASED)
+       },
+
+       /* Muxed gated divider clocks */
+       [K210_CLK_SPI3] = {
+               .name = "spi3",
+               K210_GATE(K210_SYSCTL_EN_PERI, 9),
+               K210_DIV(K210_SYSCTL_THR1, 24, 8, K210_DIV_DOUBLE_ONE_BASED),
+               K210_MUX(K210_SYSCTL_SEL0, 12)
+       },
+       [K210_CLK_TIMER0] = {
+               .name = "timer0",
+               K210_GATE(K210_SYSCTL_EN_PERI, 21),
+               K210_DIV(K210_SYSCTL_THR2,  0, 8, K210_DIV_DOUBLE_ONE_BASED),
+               K210_MUX(K210_SYSCTL_SEL0, 13)
+       },
+       [K210_CLK_TIMER1] = {
+               .name = "timer1",
+               K210_GATE(K210_SYSCTL_EN_PERI, 22),
+               K210_DIV(K210_SYSCTL_THR2, 8, 8, K210_DIV_DOUBLE_ONE_BASED),
+               K210_MUX(K210_SYSCTL_SEL0, 14)
+       },
+       [K210_CLK_TIMER2] = {
+               .name = "timer2",
+               K210_GATE(K210_SYSCTL_EN_PERI, 23),
+               K210_DIV(K210_SYSCTL_THR2, 16, 8, K210_DIV_DOUBLE_ONE_BASED),
+               K210_MUX(K210_SYSCTL_SEL0, 15)
+       },
+};
+
+/*
+ * PLL control register bits.
+ */
+#define K210_PLL_CLKR          GENMASK(3, 0)
+#define K210_PLL_CLKF          GENMASK(9, 4)
+#define K210_PLL_CLKOD         GENMASK(13, 10)
+#define K210_PLL_BWADJ         GENMASK(19, 14)
+#define K210_PLL_RESET         (1 << 20)
+#define K210_PLL_PWRD          (1 << 21)
+#define K210_PLL_INTFB         (1 << 22)
+#define K210_PLL_BYPASS                (1 << 23)
+#define K210_PLL_TEST          (1 << 24)
+#define K210_PLL_EN            (1 << 25)
+#define K210_PLL_SEL           GENMASK(27, 26) /* PLL2 only */
+
+/*
+ * PLL lock register bits.
+ */
+#define K210_PLL_LOCK          0
+#define K210_PLL_CLEAR_SLIP    2
+#define K210_PLL_TEST_OUT      3
+
+/*
+ * Clock selector register bits.
+ */
+#define K210_ACLK_SEL          BIT(0)
+#define K210_ACLK_DIV          GENMASK(2, 1)
+
+/*
+ * PLLs.
+ */
+enum k210_pll_id {
+       K210_PLL0, K210_PLL1, K210_PLL2, K210_PLL_NUM
+};
+
+struct k210_pll {
+       enum k210_pll_id id;
+       struct k210_sysclk *ksc;
+       void __iomem *base;
+       void __iomem *reg;
+       void __iomem *lock;
+       u8 lock_shift;
+       u8 lock_width;
+       struct clk_hw hw;
+};
+#define to_k210_pll(_hw)       container_of(_hw, struct k210_pll, hw)
+
+/*
+ * PLLs configuration: by default PLL0 runs at 780 MHz and PLL1 at 299 MHz.
+ * The first 2 SRAM banks depend on ACLK/CPU clock which is by default PLL0
+ * rate divided by 2. Set PLL1 to 390 MHz so that the third SRAM bank has the
+ * same clock as the first 2.
+ */
+struct k210_pll_cfg {
+       u32 reg;
+       u8 lock_shift;
+       u8 lock_width;
+       u32 r;
+       u32 f;
+       u32 od;
+       u32 bwadj;
+};
+
+static struct k210_pll_cfg k210_plls_cfg[] = {
+       { K210_SYSCTL_PLL0,  0, 2, 0, 59, 1, 59 }, /* 780 MHz */
+       { K210_SYSCTL_PLL1,  8, 1, 0, 59, 3, 59 }, /* 390 MHz */
+       { K210_SYSCTL_PLL2, 16, 1, 0, 22, 1, 22 }, /* 299 MHz */
+};
+
+/**
+ * struct k210_sysclk - sysclk driver data
+ * @regs: system controller registers start address
+ * @clk_lock: clock setting spinlock
+ * @plls: SoC PLLs descriptors
+ * @aclk: ACLK clock
+ * @clks: All other clocks
+ */
+struct k210_sysclk {
+       void __iomem                    *regs;
+       spinlock_t                      clk_lock;
+       struct k210_pll                 plls[K210_PLL_NUM];
+       struct clk_hw                   aclk;
+       struct k210_clk                 clks[K210_NUM_CLKS];
+};
+
+#define to_k210_sysclk(_hw)    container_of(_hw, struct k210_sysclk, aclk)
+
+/*
+ * Set ACLK parent selector: 0 for IN0, 1 for PLL0.
+ */
+static void k210_aclk_set_selector(void __iomem *regs, u8 sel)
+{
+       u32 reg = readl(regs + K210_SYSCTL_SEL0);
+
+       if (sel)
+               reg |= K210_ACLK_SEL;
+       else
+               reg &= K210_ACLK_SEL;
+       writel(reg, regs + K210_SYSCTL_SEL0);
+}
+
+static void k210_init_pll(void __iomem *regs, enum k210_pll_id pllid,
+                         struct k210_pll *pll)
+{
+       pll->id = pllid;
+       pll->reg = regs + k210_plls_cfg[pllid].reg;
+       pll->lock = regs + K210_SYSCTL_PLL_LOCK;
+       pll->lock_shift = k210_plls_cfg[pllid].lock_shift;
+       pll->lock_width = k210_plls_cfg[pllid].lock_width;
+}
+
+static void k210_pll_wait_for_lock(struct k210_pll *pll)
+{
+       u32 reg, mask = GENMASK(pll->lock_shift + pll->lock_width - 1,
+                               pll->lock_shift);
+
+       while (true) {
+               reg = readl(pll->lock);
+               if ((reg & mask) == mask)
+                       break;
+
+               reg |= BIT(pll->lock_shift + K210_PLL_CLEAR_SLIP);
+               writel(reg, pll->lock);
+       }
+}
+
+static bool k210_pll_hw_is_enabled(struct k210_pll *pll)
+{
+       u32 reg = readl(pll->reg);
+       u32 mask = K210_PLL_PWRD | K210_PLL_EN;
+
+       if (reg & K210_PLL_RESET)
+               return false;
+
+       return (reg & mask) == mask;
+}
+
+static void k210_pll_enable_hw(void __iomem *regs, struct k210_pll *pll)
+{
+       struct k210_pll_cfg *pll_cfg = &k210_plls_cfg[pll->id];
+       u32 reg;
+
+       if (k210_pll_hw_is_enabled(pll))
+               return;
+
+       /*
+        * For PLL0, we need to re-parent ACLK to IN0 to keep the CPU cores and
+        * SRAM running.
+        */
+       if (pll->id == K210_PLL0)
+               k210_aclk_set_selector(regs, 0);
+
+       /* Set PLL factors */
+       reg = readl(pll->reg);
+       reg &= ~GENMASK(19, 0);
+       reg |= FIELD_PREP(K210_PLL_CLKR, pll_cfg->r);
+       reg |= FIELD_PREP(K210_PLL_CLKF, pll_cfg->f);
+       reg |= FIELD_PREP(K210_PLL_CLKOD, pll_cfg->od);
+       reg |= FIELD_PREP(K210_PLL_BWADJ, pll_cfg->bwadj);
+       reg |= K210_PLL_PWRD;
+       writel(reg, pll->reg);
+
+       /*
+        * Reset the PLL: ensure reset is low before asserting it.
+        * The magic NOPs come from the Kendryte reference SDK.
+        */
+       reg &= ~K210_PLL_RESET;
+       writel(reg, pll->reg);
+       reg |= K210_PLL_RESET;
+       writel(reg, pll->reg);
+       nop();
+       nop();
+       reg &= ~K210_PLL_RESET;
+       writel(reg, pll->reg);
+
+       k210_pll_wait_for_lock(pll);
+
+       reg &= ~K210_PLL_BYPASS;
+       reg |= K210_PLL_EN;
+       writel(reg, pll->reg);
+
+       if (pll->id == K210_PLL0)
+               k210_aclk_set_selector(regs, 1);
+}
+
+static int k210_pll_enable(struct clk_hw *hw)
+{
+       struct k210_pll *pll = to_k210_pll(hw);
+       struct k210_sysclk *ksc = pll->ksc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+
+       k210_pll_enable_hw(ksc->regs, pll);
+
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return 0;
+}
+
+static void k210_pll_disable(struct clk_hw *hw)
+{
+       struct k210_pll *pll = to_k210_pll(hw);
+       struct k210_sysclk *ksc = pll->ksc;
+       unsigned long flags;
+       u32 reg;
+
+       /*
+        * Bypassing before powering off is important so child clocks do not
+        * stop working. This is especially important for pll0, the indirect
+        * parent of the cpu clock.
+        */
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+       reg = readl(pll->reg);
+       reg |= K210_PLL_BYPASS;
+       writel(reg, pll->reg);
+
+       reg &= ~K210_PLL_PWRD;
+       reg &= ~K210_PLL_EN;
+       writel(reg, pll->reg);
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+}
+
+static int k210_pll_is_enabled(struct clk_hw *hw)
+{
+       return k210_pll_hw_is_enabled(to_k210_pll(hw));
+}
+
+static unsigned long k210_pll_get_rate(struct clk_hw *hw,
+                                      unsigned long parent_rate)
+{
+       struct k210_pll *pll = to_k210_pll(hw);
+       u32 reg = readl(pll->reg);
+       u32 r, f, od;
+
+       if (reg & K210_PLL_BYPASS)
+               return parent_rate;
+
+       if (!(reg & K210_PLL_PWRD))
+               return 0;
+
+       r = FIELD_GET(K210_PLL_CLKR, reg) + 1;
+       f = FIELD_GET(K210_PLL_CLKF, reg) + 1;
+       od = FIELD_GET(K210_PLL_CLKOD, reg) + 1;
+
+       return (u64)parent_rate * f / (r * od);
+}
+
+static const struct clk_ops k210_pll_ops = {
+       .enable         = k210_pll_enable,
+       .disable        = k210_pll_disable,
+       .is_enabled     = k210_pll_is_enabled,
+       .recalc_rate    = k210_pll_get_rate,
+};
+
+static int k210_pll2_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct k210_pll *pll = to_k210_pll(hw);
+       struct k210_sysclk *ksc = pll->ksc;
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+
+       reg = readl(pll->reg);
+       reg &= ~K210_PLL_SEL;
+       reg |= FIELD_PREP(K210_PLL_SEL, index);
+       writel(reg, pll->reg);
+
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return 0;
+}
+
+static u8 k210_pll2_get_parent(struct clk_hw *hw)
+{
+       struct k210_pll *pll = to_k210_pll(hw);
+       u32 reg = readl(pll->reg);
+
+       return FIELD_GET(K210_PLL_SEL, reg);
+}
+
+static const struct clk_ops k210_pll2_ops = {
+       .enable         = k210_pll_enable,
+       .disable        = k210_pll_disable,
+       .is_enabled     = k210_pll_is_enabled,
+       .recalc_rate    = k210_pll_get_rate,
+       .set_parent     = k210_pll2_set_parent,
+       .get_parent     = k210_pll2_get_parent,
+};
+
+static int __init k210_register_pll(struct device_node *np,
+                                   struct k210_sysclk *ksc,
+                                   enum k210_pll_id pllid, const char *name,
+                                   int num_parents, const struct clk_ops *ops)
+{
+       struct k210_pll *pll = &ksc->plls[pllid];
+       struct clk_init_data init = {};
+       const struct clk_parent_data parent_data[] = {
+               { /* .index = 0 for in0 */ },
+               { .hw = &ksc->plls[K210_PLL0].hw },
+               { .hw = &ksc->plls[K210_PLL1].hw },
+       };
+
+       init.name = name;
+       init.parent_data = parent_data;
+       init.num_parents = num_parents;
+       init.ops = ops;
+
+       pll->hw.init = &init;
+       pll->ksc = ksc;
+
+       return of_clk_hw_register(np, &pll->hw);
+}
+
+static int __init k210_register_plls(struct device_node *np,
+                                    struct k210_sysclk *ksc)
+{
+       int i, ret;
+
+       for (i = 0; i < K210_PLL_NUM; i++)
+               k210_init_pll(ksc->regs, i, &ksc->plls[i]);
+
+       /* PLL0 and PLL1 only have IN0 as parent */
+       ret = k210_register_pll(np, ksc, K210_PLL0, "pll0", 1, &k210_pll_ops);
+       if (ret) {
+               pr_err("%pOFP: register PLL0 failed\n", np);
+               return ret;
+       }
+       ret = k210_register_pll(np, ksc, K210_PLL1, "pll1", 1, &k210_pll_ops);
+       if (ret) {
+               pr_err("%pOFP: register PLL1 failed\n", np);
+               return ret;
+       }
+
+       /* PLL2 has IN0, PLL0 and PLL1 as parents */
+       ret = k210_register_pll(np, ksc, K210_PLL2, "pll2", 3, &k210_pll2_ops);
+       if (ret) {
+               pr_err("%pOFP: register PLL2 failed\n", np);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int k210_aclk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct k210_sysclk *ksc = to_k210_sysclk(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+
+       k210_aclk_set_selector(ksc->regs, index);
+
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return 0;
+}
+
+static u8 k210_aclk_get_parent(struct clk_hw *hw)
+{
+       struct k210_sysclk *ksc = to_k210_sysclk(hw);
+       u32 sel;
+
+       sel = readl(ksc->regs + K210_SYSCTL_SEL0) & K210_ACLK_SEL;
+
+       return sel ? 1 : 0;
+}
+
+static unsigned long k210_aclk_get_rate(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct k210_sysclk *ksc = to_k210_sysclk(hw);
+       u32 reg = readl(ksc->regs + K210_SYSCTL_SEL0);
+       unsigned int shift;
+
+       if (!(reg & 0x1))
+               return parent_rate;
+
+       shift = FIELD_GET(K210_ACLK_DIV, reg);
+
+       return parent_rate / (2UL << shift);
+}
+
+static const struct clk_ops k210_aclk_ops = {
+       .set_parent     = k210_aclk_set_parent,
+       .get_parent     = k210_aclk_get_parent,
+       .recalc_rate    = k210_aclk_get_rate,
+};
+
+/*
+ * ACLK has IN0 and PLL0 as parents.
+ */
+static int __init k210_register_aclk(struct device_node *np,
+                                    struct k210_sysclk *ksc)
+{
+       struct clk_init_data init = {};
+       const struct clk_parent_data parent_data[] = {
+               { /* .index = 0 for in0 */ },
+               { .hw = &ksc->plls[K210_PLL0].hw },
+       };
+       int ret;
+
+       init.name = "aclk";
+       init.parent_data = parent_data;
+       init.num_parents = 2;
+       init.ops = &k210_aclk_ops;
+       ksc->aclk.init = &init;
+
+       ret = of_clk_hw_register(np, &ksc->aclk);
+       if (ret) {
+               pr_err("%pOFP: register aclk failed\n", np);
+               return ret;
+       }
+
+       return 0;
+}
+
+#define to_k210_clk(_hw)       container_of(_hw, struct k210_clk, hw)
+
+static int k210_clk_enable(struct clk_hw *hw)
+{
+       struct k210_clk *kclk = to_k210_clk(hw);
+       struct k210_sysclk *ksc = kclk->ksc;
+       struct k210_clk_cfg *cfg = &k210_clk_cfgs[kclk->id];
+       unsigned long flags;
+       u32 reg;
+
+       if (!cfg->gate_reg)
+               return 0;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+       reg = readl(ksc->regs + cfg->gate_reg);
+       reg |= BIT(cfg->gate_bit);
+       writel(reg, ksc->regs + cfg->gate_reg);
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return 0;
+}
+
+static void k210_clk_disable(struct clk_hw *hw)
+{
+       struct k210_clk *kclk = to_k210_clk(hw);
+       struct k210_sysclk *ksc = kclk->ksc;
+       struct k210_clk_cfg *cfg = &k210_clk_cfgs[kclk->id];
+       unsigned long flags;
+       u32 reg;
+
+       if (!cfg->gate_reg)
+               return;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+       reg = readl(ksc->regs + cfg->gate_reg);
+       reg &= ~BIT(cfg->gate_bit);
+       writel(reg, ksc->regs + cfg->gate_reg);
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+}
+
+static int k210_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct k210_clk *kclk = to_k210_clk(hw);
+       struct k210_sysclk *ksc = kclk->ksc;
+       struct k210_clk_cfg *cfg = &k210_clk_cfgs[kclk->id];
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+       reg = readl(ksc->regs + cfg->mux_reg);
+       if (index)
+               reg |= BIT(cfg->mux_bit);
+       else
+               reg &= ~BIT(cfg->mux_bit);
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return 0;
+}
+
+static u8 k210_clk_get_parent(struct clk_hw *hw)
+{
+       struct k210_clk *kclk = to_k210_clk(hw);
+       struct k210_sysclk *ksc = kclk->ksc;
+       struct k210_clk_cfg *cfg = &k210_clk_cfgs[kclk->id];
+       unsigned long flags;
+       u32 reg, idx;
+
+       spin_lock_irqsave(&ksc->clk_lock, flags);
+       reg = readl(ksc->regs + cfg->mux_reg);
+       idx = (reg & BIT(cfg->mux_bit)) ? 1 : 0;
+       spin_unlock_irqrestore(&ksc->clk_lock, flags);
+
+       return idx;
+}
+
+static unsigned long k210_clk_get_rate(struct clk_hw *hw,
+                                      unsigned long parent_rate)
+{
+       struct k210_clk *kclk = to_k210_clk(hw);
+       struct k210_sysclk *ksc = kclk->ksc;
+       struct k210_clk_cfg *cfg = &k210_clk_cfgs[kclk->id];
+       u32 reg, div_val;
+
+       if (!cfg->div_reg)
+               return parent_rate;
+
+       reg = readl(ksc->regs + cfg->div_reg);
+       div_val = (reg >> cfg->div_shift) & GENMASK(cfg->div_width - 1, 0);
+
+       switch (cfg->div_type) {
+       case K210_DIV_ONE_BASED:
+               return parent_rate / (div_val + 1);
+       case K210_DIV_DOUBLE_ONE_BASED:
+               return parent_rate / ((div_val + 1) * 2);
+       case K210_DIV_POWER_OF_TWO:
+               return parent_rate / (2UL << div_val);
+       case K210_DIV_NONE:
+       default:
+               return 0;
+       }
+}
+
+static const struct clk_ops k210_clk_mux_ops = {
+       .enable         = k210_clk_enable,
+       .disable        = k210_clk_disable,
+       .set_parent     = k210_clk_set_parent,
+       .get_parent     = k210_clk_get_parent,
+       .recalc_rate    = k210_clk_get_rate,
+};
+
+static const struct clk_ops k210_clk_ops = {
+       .enable         = k210_clk_enable,
+       .disable        = k210_clk_disable,
+       .recalc_rate    = k210_clk_get_rate,
+};
+
+static void __init k210_register_clk(struct device_node *np,
+                                    struct k210_sysclk *ksc, int id,
+                                    const struct clk_parent_data *parent_data,
+                                    int num_parents, unsigned long flags)
+{
+       struct k210_clk *kclk = &ksc->clks[id];
+       struct clk_init_data init = {};
+       int ret;
+
+       init.name = k210_clk_cfgs[id].name;
+       init.flags = flags;
+       init.parent_data = parent_data;
+       init.num_parents = num_parents;
+       if (num_parents > 1)
+               init.ops = &k210_clk_mux_ops;
+       else
+               init.ops = &k210_clk_ops;
+
+       kclk->id = id;
+       kclk->ksc = ksc;
+       kclk->hw.init = &init;
+
+       ret = of_clk_hw_register(np, &kclk->hw);
+       if (ret) {
+               pr_err("%pOFP: register clock %s failed\n",
+                      np, k210_clk_cfgs[id].name);
+               kclk->id = -1;
+       }
+}
+
+/*
+ * All muxed clocks have IN0 and PLL0 as parents.
+ */
+static inline void __init k210_register_mux_clk(struct device_node *np,
+                                               struct k210_sysclk *ksc, int id)
+{
+       const struct clk_parent_data parent_data[2] = {
+               { /* .index = 0 for in0 */ },
+               { .hw = &ksc->plls[K210_PLL0].hw }
+       };
+
+       k210_register_clk(np, ksc, id, parent_data, 2, 0);
+}
+
+static inline void __init k210_register_in0_child(struct device_node *np,
+                                               struct k210_sysclk *ksc, int id)
+{
+       const struct clk_parent_data parent_data = {
+               /* .index = 0 for in0 */
+       };
+
+       k210_register_clk(np, ksc, id, &parent_data, 1, 0);
+}
+
+static inline void __init k210_register_pll_child(struct device_node *np,
+                                               struct k210_sysclk *ksc, int id,
+                                               enum k210_pll_id pllid,
+                                               unsigned long flags)
+{
+       const struct clk_parent_data parent_data = {
+               .hw = &ksc->plls[pllid].hw,
+       };
+
+       k210_register_clk(np, ksc, id, &parent_data, 1, flags);
+}
+
+static inline void __init k210_register_aclk_child(struct device_node *np,
+                                               struct k210_sysclk *ksc, int id,
+                                               unsigned long flags)
+{
+       const struct clk_parent_data parent_data = {
+               .hw = &ksc->aclk,
+       };
+
+       k210_register_clk(np, ksc, id, &parent_data, 1, flags);
+}
+
+static inline void __init k210_register_clk_child(struct device_node *np,
+                                               struct k210_sysclk *ksc, int id,
+                                               int parent_id)
+{
+       const struct clk_parent_data parent_data = {
+               .hw = &ksc->clks[parent_id].hw,
+       };
+
+       k210_register_clk(np, ksc, id, &parent_data, 1, 0);
+}
+
+static struct clk_hw *k210_clk_hw_onecell_get(struct of_phandle_args *clkspec,
+                                             void *data)
+{
+       struct k210_sysclk *ksc = data;
+       unsigned int idx = clkspec->args[0];
+
+       if (idx >= K210_NUM_CLKS)
+               return ERR_PTR(-EINVAL);
+
+       return &ksc->clks[idx].hw;
+}
+
+static void __init k210_clk_init(struct device_node *np)
+{
+       struct device_node *sysctl_np;
+       struct k210_sysclk *ksc;
+       int i, ret;
+
+       ksc = kzalloc(sizeof(*ksc), GFP_KERNEL);
+       if (!ksc)
+               return;
+
+       spin_lock_init(&ksc->clk_lock);
+       sysctl_np = of_get_parent(np);
+       ksc->regs = of_iomap(sysctl_np, 0);
+       of_node_put(sysctl_np);
+       if (!ksc->regs) {
+               pr_err("%pOFP: failed to map registers\n", np);
+               return;
+       }
+
+       ret = k210_register_plls(np, ksc);
+       if (ret)
+               return;
+
+       ret = k210_register_aclk(np, ksc);
+       if (ret)
+               return;
+
+       /*
+        * Critical clocks: there are no consumers of the SRAM clocks,
+        * including the AI clock for the third SRAM bank. The CPU clock
+        * is only referenced by the uarths serial device and so would be
+        * disabled if the serial console is disabled to switch to another
+        * console. Mark all these clocks as critical so that they are never
+        * disabled by the core clock management.
+        */
+       k210_register_aclk_child(np, ksc, K210_CLK_CPU, CLK_IS_CRITICAL);
+       k210_register_aclk_child(np, ksc, K210_CLK_SRAM0, CLK_IS_CRITICAL);
+       k210_register_aclk_child(np, ksc, K210_CLK_SRAM1, CLK_IS_CRITICAL);
+       k210_register_pll_child(np, ksc, K210_CLK_AI, K210_PLL1,
+                               CLK_IS_CRITICAL);
+
+       /* Clocks with aclk as source */
+       k210_register_aclk_child(np, ksc, K210_CLK_DMA, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_FFT, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_ROM, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_DVP, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_APB0, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_APB1, 0);
+       k210_register_aclk_child(np, ksc, K210_CLK_APB2, 0);
+
+       /* Clocks with PLL0 as source */
+       k210_register_pll_child(np, ksc, K210_CLK_SPI0, K210_PLL0, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_SPI1, K210_PLL0, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_SPI2, K210_PLL0, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2C0, K210_PLL0, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2C1, K210_PLL0, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2C2, K210_PLL0, 0);
+
+       /* Clocks with PLL2 as source */
+       k210_register_pll_child(np, ksc, K210_CLK_I2S0, K210_PLL2, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2S1, K210_PLL2, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2S2, K210_PLL2, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2S0_M, K210_PLL2, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2S1_M, K210_PLL2, 0);
+       k210_register_pll_child(np, ksc, K210_CLK_I2S2_M, K210_PLL2, 0);
+
+       /* Clocks with IN0 as source */
+       k210_register_in0_child(np, ksc, K210_CLK_WDT0);
+       k210_register_in0_child(np, ksc, K210_CLK_WDT1);
+       k210_register_in0_child(np, ksc, K210_CLK_RTC);
+
+       /* Clocks with APB0 as source */
+       k210_register_clk_child(np, ksc, K210_CLK_GPIO, K210_CLK_APB0);
+       k210_register_clk_child(np, ksc, K210_CLK_UART1, K210_CLK_APB0);
+       k210_register_clk_child(np, ksc, K210_CLK_UART2, K210_CLK_APB0);
+       k210_register_clk_child(np, ksc, K210_CLK_UART3, K210_CLK_APB0);
+       k210_register_clk_child(np, ksc, K210_CLK_FPIOA, K210_CLK_APB0);
+       k210_register_clk_child(np, ksc, K210_CLK_SHA, K210_CLK_APB0);
+
+       /* Clocks with APB1 as source */
+       k210_register_clk_child(np, ksc, K210_CLK_AES, K210_CLK_APB1);
+       k210_register_clk_child(np, ksc, K210_CLK_OTP, K210_CLK_APB1);
+
+       /* Mux clocks with in0 or pll0 as source */
+       k210_register_mux_clk(np, ksc, K210_CLK_SPI3);
+       k210_register_mux_clk(np, ksc, K210_CLK_TIMER0);
+       k210_register_mux_clk(np, ksc, K210_CLK_TIMER1);
+       k210_register_mux_clk(np, ksc, K210_CLK_TIMER2);
+
+       /* Check for registration errors */
+       for (i = 0; i < K210_NUM_CLKS; i++) {
+               if (ksc->clks[i].id != i)
+                       return;
+       }
+
+       ret = of_clk_add_hw_provider(np, k210_clk_hw_onecell_get, ksc);
+       if (ret) {
+               pr_err("%pOFP: add clock provider failed %d\n", np, ret);
+               return;
+       }
+
+       pr_info("%pOFP: CPU running at %lu MHz\n",
+               np, clk_hw_get_rate(&ksc->clks[K210_CLK_CPU].hw) / 1000000);
+}
+
+CLK_OF_DECLARE(k210_clk, "canaan,k210-clk", k210_clk_init);
+
+/*
+ * Enable PLL1 to be able to use the AI SRAM.
+ */
+void __init k210_clk_early_init(void __iomem *regs)
+{
+       struct k210_pll pll1;
+
+       /* Make sure ACLK selector is set to PLL0 */
+       k210_aclk_set_selector(regs, 1);
+
+       /* Startup PLL1 to enable the aisram bank for general memory use */
+       k210_init_pll(regs, K210_PLL1, &pll1);
+       k210_pll_enable_hw(regs, &pll1);
+}
index 03c62e1cb395fc0c22a1f668c5f5aedea914d1f8..b7675cce0027b3f9bc5713f662049bc1511170ca 100644 (file)
@@ -370,6 +370,19 @@ config PINCTRL_MICROCHIP_SGPIO
          connect control signals from SFP modules and to act as an
          LED controller.
 
+config PINCTRL_K210
+       bool "Pinctrl driver for the Canaan Kendryte K210 SoC"
+       depends on RISCV && SOC_CANAAN && OF
+       select GENERIC_PINMUX_FUNCTIONS
+       select GENERIC_PINCONF
+       select GPIOLIB
+       select OF_GPIO
+       select REGMAP_MMIO
+       default SOC_CANAAN
+       help
+         Add support for the Canaan Kendryte K210 RISC-V SOC Field
+         Programmable IO Array (FPIOA) controller.
+
 source "drivers/pinctrl/actions/Kconfig"
 source "drivers/pinctrl/aspeed/Kconfig"
 source "drivers/pinctrl/bcm/Kconfig"
index efc96f25c8db4a8e6ce6dc883ee40dcbeb88b3da..8bf459c32a7631002f8943ab566f0f4928dd3764 100644 (file)
@@ -45,6 +45,7 @@ obj-$(CONFIG_PINCTRL_RK805)   += pinctrl-rk805.o
 obj-$(CONFIG_PINCTRL_OCELOT)   += pinctrl-ocelot.o
 obj-$(CONFIG_PINCTRL_MICROCHIP_SGPIO)  += pinctrl-microchip-sgpio.o
 obj-$(CONFIG_PINCTRL_EQUILIBRIUM)   += pinctrl-equilibrium.o
+obj-$(CONFIG_PINCTRL_K210)     += pinctrl-k210.o
 
 obj-y                          += actions/
 obj-$(CONFIG_ARCH_ASPEED)      += aspeed/
diff --git a/drivers/pinctrl/pinctrl-k210.c b/drivers/pinctrl/pinctrl-k210.c
new file mode 100644 (file)
index 0000000..8a733cf
--- /dev/null
@@ -0,0 +1,985 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/io.h>
+
+#include <dt-bindings/pinctrl/k210-fpioa.h>
+
+#include "core.h"
+#include "pinconf.h"
+#include "pinctrl-utils.h"
+
+/*
+ * The K210 only implements 8 drive levels, even though
+ * there is register space for 16
+ */
+#define K210_PC_DRIVE_MASK     GENMASK(11, 8)
+#define K210_PC_DRIVE_SHIFT    8
+#define K210_PC_DRIVE_0                (0 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_1                (1 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_2                (2 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_3                (3 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_4                (4 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_5                (5 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_6                (6 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_7                (7 << K210_PC_DRIVE_SHIFT)
+#define K210_PC_DRIVE_MAX      7
+#define K210_PC_MODE_MASK      GENMASK(23, 12)
+
+/*
+ * output enabled == PC_OE & (PC_OE_INV ^ FUNCTION_OE)
+ * where FUNCTION_OE is a physical signal from the function.
+ */
+#define K210_PC_OE             BIT(12) /* Output Enable */
+#define K210_PC_OE_INV         BIT(13) /* INVert Output Enable */
+#define K210_PC_DO_OE          BIT(14) /* set Data Out to Output Enable sig */
+#define K210_PC_DO_INV         BIT(15) /* INVert final Data Output */
+#define K210_PC_PU             BIT(16) /* Pull Up */
+#define K210_PC_PD             BIT(17) /* Pull Down */
+/* Strong pull up not implemented on K210 */
+#define K210_PC_SL             BIT(19) /* reduce SLew rate */
+/* Same semantics as OE above */
+#define K210_PC_IE             BIT(20) /* Input Enable */
+#define K210_PC_IE_INV         BIT(21) /* INVert Input Enable */
+#define K210_PC_DI_INV         BIT(22) /* INVert Data Input */
+#define K210_PC_ST             BIT(23) /* Schmitt Trigger */
+#define K210_PC_DI             BIT(31) /* raw Data Input */
+
+#define K210_PC_BIAS_MASK      (K210_PC_PU & K210_PC_PD)
+
+#define K210_PC_MODE_IN                (K210_PC_IE | K210_PC_ST)
+#define K210_PC_MODE_OUT       (K210_PC_DRIVE_7 | K210_PC_OE)
+#define K210_PC_MODE_I2C       (K210_PC_MODE_IN | K210_PC_SL | \
+                                K210_PC_OE | K210_PC_PU)
+#define K210_PC_MODE_SCCB      (K210_PC_MODE_I2C | \
+                                K210_PC_OE_INV | K210_PC_IE_INV)
+#define K210_PC_MODE_SPI       (K210_PC_MODE_IN | K210_PC_IE_INV | \
+                                K210_PC_MODE_OUT | K210_PC_OE_INV)
+#define K210_PC_MODE_GPIO      (K210_PC_MODE_IN | K210_PC_MODE_OUT)
+
+#define K210_PG_FUNC           GENMASK(7, 0)
+#define K210_PG_DO             BIT(8)
+#define K210_PG_PIN            GENMASK(22, 16)
+
+/*
+ * struct k210_fpioa: Kendryte K210 FPIOA memory mapped registers
+ * @pins: 48 32-bits IO pin registers
+ * @tie_en: 256 (one per function) input tie enable bits
+ * @tie_val: 256 (one per function) input tie value bits
+ */
+struct k210_fpioa {
+       u32 pins[48];
+       u32 tie_en[8];
+       u32 tie_val[8];
+};
+
+struct k210_fpioa_data {
+
+       struct device *dev;
+       struct pinctrl_dev *pctl;
+
+       struct k210_fpioa __iomem *fpioa;
+       struct regmap *sysctl_map;
+       u32 power_offset;
+       struct clk *clk;
+       struct clk *pclk;
+};
+
+#define K210_PIN_NAME(i)       ("IO_" #i)
+#define K210_PIN(i)            [(i)] = PINCTRL_PIN((i), K210_PIN_NAME(i))
+
+static const struct pinctrl_pin_desc k210_pins[] = {
+       K210_PIN(0),  K210_PIN(1),  K210_PIN(2),
+       K210_PIN(3),  K210_PIN(4),  K210_PIN(5),
+       K210_PIN(6),  K210_PIN(7),  K210_PIN(8),
+       K210_PIN(9),  K210_PIN(10), K210_PIN(11),
+       K210_PIN(12), K210_PIN(13), K210_PIN(14),
+       K210_PIN(15), K210_PIN(16), K210_PIN(17),
+       K210_PIN(18), K210_PIN(19), K210_PIN(20),
+       K210_PIN(21), K210_PIN(22), K210_PIN(23),
+       K210_PIN(24), K210_PIN(25), K210_PIN(26),
+       K210_PIN(27), K210_PIN(28), K210_PIN(29),
+       K210_PIN(30), K210_PIN(31), K210_PIN(32),
+       K210_PIN(33), K210_PIN(34), K210_PIN(35),
+       K210_PIN(36), K210_PIN(37), K210_PIN(38),
+       K210_PIN(39), K210_PIN(40), K210_PIN(41),
+       K210_PIN(42), K210_PIN(43), K210_PIN(44),
+       K210_PIN(45), K210_PIN(46), K210_PIN(47)
+};
+
+#define K210_NPINS ARRAY_SIZE(k210_pins)
+
+/*
+ * Pin groups: each of the 48 programmable pins is a group.
+ * To this are added 8 power domain groups, which for the purposes of
+ * the pin subsystem, contain no pins. The power domain groups only exist
+ * to set the power level. The id should never be used (since there are
+ * no pins 48-55).
+ */
+static const char *const k210_group_names[] = {
+       /* The first 48 groups are for pins, one each */
+       K210_PIN_NAME(0),  K210_PIN_NAME(1),  K210_PIN_NAME(2),
+       K210_PIN_NAME(3),  K210_PIN_NAME(4),  K210_PIN_NAME(5),
+       K210_PIN_NAME(6),  K210_PIN_NAME(7),  K210_PIN_NAME(8),
+       K210_PIN_NAME(9),  K210_PIN_NAME(10), K210_PIN_NAME(11),
+       K210_PIN_NAME(12), K210_PIN_NAME(13), K210_PIN_NAME(14),
+       K210_PIN_NAME(15), K210_PIN_NAME(16), K210_PIN_NAME(17),
+       K210_PIN_NAME(18), K210_PIN_NAME(19), K210_PIN_NAME(20),
+       K210_PIN_NAME(21), K210_PIN_NAME(22), K210_PIN_NAME(23),
+       K210_PIN_NAME(24), K210_PIN_NAME(25), K210_PIN_NAME(26),
+       K210_PIN_NAME(27), K210_PIN_NAME(28), K210_PIN_NAME(29),
+       K210_PIN_NAME(30), K210_PIN_NAME(31), K210_PIN_NAME(32),
+       K210_PIN_NAME(33), K210_PIN_NAME(34), K210_PIN_NAME(35),
+       K210_PIN_NAME(36), K210_PIN_NAME(37), K210_PIN_NAME(38),
+       K210_PIN_NAME(39), K210_PIN_NAME(40), K210_PIN_NAME(41),
+       K210_PIN_NAME(42), K210_PIN_NAME(43), K210_PIN_NAME(44),
+       K210_PIN_NAME(45), K210_PIN_NAME(46), K210_PIN_NAME(47),
+       [48] = "A0", [49] = "A1", [50] = "A2",
+       [51] = "B3", [52] = "B4", [53] = "B5",
+       [54] = "C6", [55] = "C7"
+};
+
+#define K210_NGROUPS   ARRAY_SIZE(k210_group_names)
+
+enum k210_pinctrl_mode_id {
+       K210_PC_DEFAULT_DISABLED,
+       K210_PC_DEFAULT_IN,
+       K210_PC_DEFAULT_IN_TIE,
+       K210_PC_DEFAULT_OUT,
+       K210_PC_DEFAULT_I2C,
+       K210_PC_DEFAULT_SCCB,
+       K210_PC_DEFAULT_SPI,
+       K210_PC_DEFAULT_GPIO,
+       K210_PC_DEFAULT_INT13,
+};
+
+#define K210_PC_DEFAULT(mode) \
+       [K210_PC_DEFAULT_##mode] = K210_PC_MODE_##mode
+
+static const u32 k210_pinconf_mode_id_to_mode[] = {
+       [K210_PC_DEFAULT_DISABLED] = 0,
+       K210_PC_DEFAULT(IN),
+       [K210_PC_DEFAULT_IN_TIE] = K210_PC_MODE_IN,
+       K210_PC_DEFAULT(OUT),
+       K210_PC_DEFAULT(I2C),
+       K210_PC_DEFAULT(SCCB),
+       K210_PC_DEFAULT(SPI),
+       K210_PC_DEFAULT(GPIO),
+       [K210_PC_DEFAULT_INT13] = K210_PC_MODE_IN | K210_PC_PU,
+};
+
+#undef DEFAULT
+
+/*
+ * Pin functions configuration information.
+ */
+struct k210_pcf_info {
+       char name[15];
+       u8 mode_id;
+};
+
+#define K210_FUNC(id, mode)                            \
+       [K210_PCF_##id] = {                             \
+               .name = #id,                            \
+               .mode_id = K210_PC_DEFAULT_##mode       \
+       }
+
+static const struct k210_pcf_info k210_pcf_infos[] = {
+       K210_FUNC(JTAG_TCLK,            IN),
+       K210_FUNC(JTAG_TDI,             IN),
+       K210_FUNC(JTAG_TMS,             IN),
+       K210_FUNC(JTAG_TDO,             OUT),
+       K210_FUNC(SPI0_D0,              SPI),
+       K210_FUNC(SPI0_D1,              SPI),
+       K210_FUNC(SPI0_D2,              SPI),
+       K210_FUNC(SPI0_D3,              SPI),
+       K210_FUNC(SPI0_D4,              SPI),
+       K210_FUNC(SPI0_D5,              SPI),
+       K210_FUNC(SPI0_D6,              SPI),
+       K210_FUNC(SPI0_D7,              SPI),
+       K210_FUNC(SPI0_SS0,             OUT),
+       K210_FUNC(SPI0_SS1,             OUT),
+       K210_FUNC(SPI0_SS2,             OUT),
+       K210_FUNC(SPI0_SS3,             OUT),
+       K210_FUNC(SPI0_ARB,             IN_TIE),
+       K210_FUNC(SPI0_SCLK,            OUT),
+       K210_FUNC(UARTHS_RX,            IN),
+       K210_FUNC(UARTHS_TX,            OUT),
+       K210_FUNC(RESV6,                IN),
+       K210_FUNC(RESV7,                IN),
+       K210_FUNC(CLK_SPI1,             OUT),
+       K210_FUNC(CLK_I2C1,             OUT),
+       K210_FUNC(GPIOHS0,              GPIO),
+       K210_FUNC(GPIOHS1,              GPIO),
+       K210_FUNC(GPIOHS2,              GPIO),
+       K210_FUNC(GPIOHS3,              GPIO),
+       K210_FUNC(GPIOHS4,              GPIO),
+       K210_FUNC(GPIOHS5,              GPIO),
+       K210_FUNC(GPIOHS6,              GPIO),
+       K210_FUNC(GPIOHS7,              GPIO),
+       K210_FUNC(GPIOHS8,              GPIO),
+       K210_FUNC(GPIOHS9,              GPIO),
+       K210_FUNC(GPIOHS10,             GPIO),
+       K210_FUNC(GPIOHS11,             GPIO),
+       K210_FUNC(GPIOHS12,             GPIO),
+       K210_FUNC(GPIOHS13,             GPIO),
+       K210_FUNC(GPIOHS14,             GPIO),
+       K210_FUNC(GPIOHS15,             GPIO),
+       K210_FUNC(GPIOHS16,             GPIO),
+       K210_FUNC(GPIOHS17,             GPIO),
+       K210_FUNC(GPIOHS18,             GPIO),
+       K210_FUNC(GPIOHS19,             GPIO),
+       K210_FUNC(GPIOHS20,             GPIO),
+       K210_FUNC(GPIOHS21,             GPIO),
+       K210_FUNC(GPIOHS22,             GPIO),
+       K210_FUNC(GPIOHS23,             GPIO),
+       K210_FUNC(GPIOHS24,             GPIO),
+       K210_FUNC(GPIOHS25,             GPIO),
+       K210_FUNC(GPIOHS26,             GPIO),
+       K210_FUNC(GPIOHS27,             GPIO),
+       K210_FUNC(GPIOHS28,             GPIO),
+       K210_FUNC(GPIOHS29,             GPIO),
+       K210_FUNC(GPIOHS30,             GPIO),
+       K210_FUNC(GPIOHS31,             GPIO),
+       K210_FUNC(GPIO0,                GPIO),
+       K210_FUNC(GPIO1,                GPIO),
+       K210_FUNC(GPIO2,                GPIO),
+       K210_FUNC(GPIO3,                GPIO),
+       K210_FUNC(GPIO4,                GPIO),
+       K210_FUNC(GPIO5,                GPIO),
+       K210_FUNC(GPIO6,                GPIO),
+       K210_FUNC(GPIO7,                GPIO),
+       K210_FUNC(UART1_RX,             IN),
+       K210_FUNC(UART1_TX,             OUT),
+       K210_FUNC(UART2_RX,             IN),
+       K210_FUNC(UART2_TX,             OUT),
+       K210_FUNC(UART3_RX,             IN),
+       K210_FUNC(UART3_TX,             OUT),
+       K210_FUNC(SPI1_D0,              SPI),
+       K210_FUNC(SPI1_D1,              SPI),
+       K210_FUNC(SPI1_D2,              SPI),
+       K210_FUNC(SPI1_D3,              SPI),
+       K210_FUNC(SPI1_D4,              SPI),
+       K210_FUNC(SPI1_D5,              SPI),
+       K210_FUNC(SPI1_D6,              SPI),
+       K210_FUNC(SPI1_D7,              SPI),
+       K210_FUNC(SPI1_SS0,             OUT),
+       K210_FUNC(SPI1_SS1,             OUT),
+       K210_FUNC(SPI1_SS2,             OUT),
+       K210_FUNC(SPI1_SS3,             OUT),
+       K210_FUNC(SPI1_ARB,             IN_TIE),
+       K210_FUNC(SPI1_SCLK,            OUT),
+       K210_FUNC(SPI2_D0,              SPI),
+       K210_FUNC(SPI2_SS,              IN),
+       K210_FUNC(SPI2_SCLK,            IN),
+       K210_FUNC(I2S0_MCLK,            OUT),
+       K210_FUNC(I2S0_SCLK,            OUT),
+       K210_FUNC(I2S0_WS,              OUT),
+       K210_FUNC(I2S0_IN_D0,           IN),
+       K210_FUNC(I2S0_IN_D1,           IN),
+       K210_FUNC(I2S0_IN_D2,           IN),
+       K210_FUNC(I2S0_IN_D3,           IN),
+       K210_FUNC(I2S0_OUT_D0,          OUT),
+       K210_FUNC(I2S0_OUT_D1,          OUT),
+       K210_FUNC(I2S0_OUT_D2,          OUT),
+       K210_FUNC(I2S0_OUT_D3,          OUT),
+       K210_FUNC(I2S1_MCLK,            OUT),
+       K210_FUNC(I2S1_SCLK,            OUT),
+       K210_FUNC(I2S1_WS,              OUT),
+       K210_FUNC(I2S1_IN_D0,           IN),
+       K210_FUNC(I2S1_IN_D1,           IN),
+       K210_FUNC(I2S1_IN_D2,           IN),
+       K210_FUNC(I2S1_IN_D3,           IN),
+       K210_FUNC(I2S1_OUT_D0,          OUT),
+       K210_FUNC(I2S1_OUT_D1,          OUT),
+       K210_FUNC(I2S1_OUT_D2,          OUT),
+       K210_FUNC(I2S1_OUT_D3,          OUT),
+       K210_FUNC(I2S2_MCLK,            OUT),
+       K210_FUNC(I2S2_SCLK,            OUT),
+       K210_FUNC(I2S2_WS,              OUT),
+       K210_FUNC(I2S2_IN_D0,           IN),
+       K210_FUNC(I2S2_IN_D1,           IN),
+       K210_FUNC(I2S2_IN_D2,           IN),
+       K210_FUNC(I2S2_IN_D3,           IN),
+       K210_FUNC(I2S2_OUT_D0,          OUT),
+       K210_FUNC(I2S2_OUT_D1,          OUT),
+       K210_FUNC(I2S2_OUT_D2,          OUT),
+       K210_FUNC(I2S2_OUT_D3,          OUT),
+       K210_FUNC(RESV0,                DISABLED),
+       K210_FUNC(RESV1,                DISABLED),
+       K210_FUNC(RESV2,                DISABLED),
+       K210_FUNC(RESV3,                DISABLED),
+       K210_FUNC(RESV4,                DISABLED),
+       K210_FUNC(RESV5,                DISABLED),
+       K210_FUNC(I2C0_SCLK,            I2C),
+       K210_FUNC(I2C0_SDA,             I2C),
+       K210_FUNC(I2C1_SCLK,            I2C),
+       K210_FUNC(I2C1_SDA,             I2C),
+       K210_FUNC(I2C2_SCLK,            I2C),
+       K210_FUNC(I2C2_SDA,             I2C),
+       K210_FUNC(DVP_XCLK,             OUT),
+       K210_FUNC(DVP_RST,              OUT),
+       K210_FUNC(DVP_PWDN,             OUT),
+       K210_FUNC(DVP_VSYNC,            IN),
+       K210_FUNC(DVP_HSYNC,            IN),
+       K210_FUNC(DVP_PCLK,             IN),
+       K210_FUNC(DVP_D0,               IN),
+       K210_FUNC(DVP_D1,               IN),
+       K210_FUNC(DVP_D2,               IN),
+       K210_FUNC(DVP_D3,               IN),
+       K210_FUNC(DVP_D4,               IN),
+       K210_FUNC(DVP_D5,               IN),
+       K210_FUNC(DVP_D6,               IN),
+       K210_FUNC(DVP_D7,               IN),
+       K210_FUNC(SCCB_SCLK,            SCCB),
+       K210_FUNC(SCCB_SDA,             SCCB),
+       K210_FUNC(UART1_CTS,            IN),
+       K210_FUNC(UART1_DSR,            IN),
+       K210_FUNC(UART1_DCD,            IN),
+       K210_FUNC(UART1_RI,             IN),
+       K210_FUNC(UART1_SIR_IN,         IN),
+       K210_FUNC(UART1_DTR,            OUT),
+       K210_FUNC(UART1_RTS,            OUT),
+       K210_FUNC(UART1_OUT2,           OUT),
+       K210_FUNC(UART1_OUT1,           OUT),
+       K210_FUNC(UART1_SIR_OUT,        OUT),
+       K210_FUNC(UART1_BAUD,           OUT),
+       K210_FUNC(UART1_RE,             OUT),
+       K210_FUNC(UART1_DE,             OUT),
+       K210_FUNC(UART1_RS485_EN,       OUT),
+       K210_FUNC(UART2_CTS,            IN),
+       K210_FUNC(UART2_DSR,            IN),
+       K210_FUNC(UART2_DCD,            IN),
+       K210_FUNC(UART2_RI,             IN),
+       K210_FUNC(UART2_SIR_IN,         IN),
+       K210_FUNC(UART2_DTR,            OUT),
+       K210_FUNC(UART2_RTS,            OUT),
+       K210_FUNC(UART2_OUT2,           OUT),
+       K210_FUNC(UART2_OUT1,           OUT),
+       K210_FUNC(UART2_SIR_OUT,        OUT),
+       K210_FUNC(UART2_BAUD,           OUT),
+       K210_FUNC(UART2_RE,             OUT),
+       K210_FUNC(UART2_DE,             OUT),
+       K210_FUNC(UART2_RS485_EN,       OUT),
+       K210_FUNC(UART3_CTS,            IN),
+       K210_FUNC(UART3_DSR,            IN),
+       K210_FUNC(UART3_DCD,            IN),
+       K210_FUNC(UART3_RI,             IN),
+       K210_FUNC(UART3_SIR_IN,         IN),
+       K210_FUNC(UART3_DTR,            OUT),
+       K210_FUNC(UART3_RTS,            OUT),
+       K210_FUNC(UART3_OUT2,           OUT),
+       K210_FUNC(UART3_OUT1,           OUT),
+       K210_FUNC(UART3_SIR_OUT,        OUT),
+       K210_FUNC(UART3_BAUD,           OUT),
+       K210_FUNC(UART3_RE,             OUT),
+       K210_FUNC(UART3_DE,             OUT),
+       K210_FUNC(UART3_RS485_EN,       OUT),
+       K210_FUNC(TIMER0_TOGGLE1,       OUT),
+       K210_FUNC(TIMER0_TOGGLE2,       OUT),
+       K210_FUNC(TIMER0_TOGGLE3,       OUT),
+       K210_FUNC(TIMER0_TOGGLE4,       OUT),
+       K210_FUNC(TIMER1_TOGGLE1,       OUT),
+       K210_FUNC(TIMER1_TOGGLE2,       OUT),
+       K210_FUNC(TIMER1_TOGGLE3,       OUT),
+       K210_FUNC(TIMER1_TOGGLE4,       OUT),
+       K210_FUNC(TIMER2_TOGGLE1,       OUT),
+       K210_FUNC(TIMER2_TOGGLE2,       OUT),
+       K210_FUNC(TIMER2_TOGGLE3,       OUT),
+       K210_FUNC(TIMER2_TOGGLE4,       OUT),
+       K210_FUNC(CLK_SPI2,             OUT),
+       K210_FUNC(CLK_I2C2,             OUT),
+       K210_FUNC(INTERNAL0,            OUT),
+       K210_FUNC(INTERNAL1,            OUT),
+       K210_FUNC(INTERNAL2,            OUT),
+       K210_FUNC(INTERNAL3,            OUT),
+       K210_FUNC(INTERNAL4,            OUT),
+       K210_FUNC(INTERNAL5,            OUT),
+       K210_FUNC(INTERNAL6,            OUT),
+       K210_FUNC(INTERNAL7,            OUT),
+       K210_FUNC(INTERNAL8,            OUT),
+       K210_FUNC(INTERNAL9,            IN),
+       K210_FUNC(INTERNAL10,           IN),
+       K210_FUNC(INTERNAL11,           IN),
+       K210_FUNC(INTERNAL12,           IN),
+       K210_FUNC(INTERNAL13,           INT13),
+       K210_FUNC(INTERNAL14,           I2C),
+       K210_FUNC(INTERNAL15,           IN),
+       K210_FUNC(INTERNAL16,           IN),
+       K210_FUNC(INTERNAL17,           IN),
+       K210_FUNC(CONSTANT,             DISABLED),
+       K210_FUNC(INTERNAL18,           IN),
+       K210_FUNC(DEBUG0,               OUT),
+       K210_FUNC(DEBUG1,               OUT),
+       K210_FUNC(DEBUG2,               OUT),
+       K210_FUNC(DEBUG3,               OUT),
+       K210_FUNC(DEBUG4,               OUT),
+       K210_FUNC(DEBUG5,               OUT),
+       K210_FUNC(DEBUG6,               OUT),
+       K210_FUNC(DEBUG7,               OUT),
+       K210_FUNC(DEBUG8,               OUT),
+       K210_FUNC(DEBUG9,               OUT),
+       K210_FUNC(DEBUG10,              OUT),
+       K210_FUNC(DEBUG11,              OUT),
+       K210_FUNC(DEBUG12,              OUT),
+       K210_FUNC(DEBUG13,              OUT),
+       K210_FUNC(DEBUG14,              OUT),
+       K210_FUNC(DEBUG15,              OUT),
+       K210_FUNC(DEBUG16,              OUT),
+       K210_FUNC(DEBUG17,              OUT),
+       K210_FUNC(DEBUG18,              OUT),
+       K210_FUNC(DEBUG19,              OUT),
+       K210_FUNC(DEBUG20,              OUT),
+       K210_FUNC(DEBUG21,              OUT),
+       K210_FUNC(DEBUG22,              OUT),
+       K210_FUNC(DEBUG23,              OUT),
+       K210_FUNC(DEBUG24,              OUT),
+       K210_FUNC(DEBUG25,              OUT),
+       K210_FUNC(DEBUG26,              OUT),
+       K210_FUNC(DEBUG27,              OUT),
+       K210_FUNC(DEBUG28,              OUT),
+       K210_FUNC(DEBUG29,              OUT),
+       K210_FUNC(DEBUG30,              OUT),
+       K210_FUNC(DEBUG31,              OUT),
+};
+
+#define PIN_CONFIG_OUTPUT_INVERT       (PIN_CONFIG_END + 1)
+#define PIN_CONFIG_INPUT_INVERT                (PIN_CONFIG_END + 2)
+
+static const struct pinconf_generic_params k210_pinconf_custom_params[] = {
+       { "output-polarity-invert", PIN_CONFIG_OUTPUT_INVERT, 1 },
+       { "input-polarity-invert",  PIN_CONFIG_INPUT_INVERT, 1 },
+};
+
+/*
+ * Max drive strength in uA.
+ */
+static const int k210_pinconf_drive_strength[] = {
+       [0] = 11200,
+       [1] = 16800,
+       [2] = 22300,
+       [3] = 27800,
+       [4] = 33300,
+       [5] = 38700,
+       [6] = 44100,
+       [7] = 49500,
+};
+
+static int k210_pinconf_get_drive(unsigned int max_strength_ua)
+{
+       int i;
+
+       for (i = K210_PC_DRIVE_MAX; i; i--) {
+               if (k210_pinconf_drive_strength[i] <= max_strength_ua)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static void k210_pinmux_set_pin_function(struct pinctrl_dev *pctldev,
+                                        u32 pin, u32 func)
+{
+       struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
+       const struct k210_pcf_info *info = &k210_pcf_infos[func];
+       u32 mode = k210_pinconf_mode_id_to_mode[info->mode_id];
+       u32 val = func | mode;
+
+       dev_dbg(pdata->dev, "set pin %u function %s (%u) -> 0x%08x\n",
+               pin, info->name, func, val);
+
+       writel(val, &pdata->fpioa->pins[pin]);
+}
+
+static int k210_pinconf_set_param(struct pinctrl_dev *pctldev,
+                                 unsigned int pin,
+                                 unsigned int param, unsigned int arg)
+{
+       struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
+       u32 val = readl(&pdata->fpioa->pins[pin]);
+       int drive;
+
+       dev_dbg(pdata->dev, "set pin %u param %u, arg 0x%x\n",
+               pin, param, arg);
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               val &= ~K210_PC_BIAS_MASK;
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               if (!arg)
+                       return -EINVAL;
+               val |= K210_PC_PD;
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               if (!arg)
+                       return -EINVAL;
+               val |= K210_PC_PD;
+               break;
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               arg *= 1000;
+               fallthrough;
+       case PIN_CONFIG_DRIVE_STRENGTH_UA:
+               drive = k210_pinconf_get_drive(arg);
+               if (drive < 0)
+                       return drive;
+               val &= ~K210_PC_DRIVE_MASK;
+               val |= FIELD_PREP(K210_PC_DRIVE_MASK, drive);
+               break;
+       case PIN_CONFIG_INPUT_ENABLE:
+               if (arg)
+                       val |= K210_PC_IE;
+               else
+                       val &= ~K210_PC_IE;
+               break;
+       case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+               if (arg)
+                       val |= K210_PC_ST;
+               else
+                       val &= ~K210_PC_ST;
+               break;
+       case PIN_CONFIG_OUTPUT:
+               k210_pinmux_set_pin_function(pctldev, pin, K210_PCF_CONSTANT);
+               val = readl(&pdata->fpioa->pins[pin]);
+               val |= K210_PC_MODE_OUT;
+               if (!arg)
+                       val |= K210_PC_DO_INV;
+               break;
+       case PIN_CONFIG_OUTPUT_ENABLE:
+               if (arg)
+                       val |= K210_PC_OE;
+               else
+                       val &= ~K210_PC_OE;
+               break;
+       case PIN_CONFIG_SLEW_RATE:
+               if (arg)
+                       val |= K210_PC_SL;
+               else
+                       val &= ~K210_PC_SL;
+               break;
+       case PIN_CONFIG_OUTPUT_INVERT:
+               if (arg)
+                       val |= K210_PC_DO_INV;
+               else
+                       val &= ~K210_PC_DO_INV;
+               break;
+       case PIN_CONFIG_INPUT_INVERT:
+               if (arg)
+                       val |= K210_PC_DI_INV;
+               else
+                       val &= ~K210_PC_DI_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       writel(val, &pdata->fpioa->pins[pin]);
+
+       return 0;
+}
+
+static int k210_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+                           unsigned long *configs, unsigned int num_configs)
+{
+       unsigned int param, arg;
+       int i, ret;
+
+       if (WARN_ON(pin >= K210_NPINS))
+               return -EINVAL;
+
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               arg = pinconf_to_config_argument(configs[i]);
+               ret = k210_pinconf_set_param(pctldev, pin, param, arg);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void k210_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+                                 struct seq_file *s, unsigned int pin)
+{
+       struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
+
+       seq_printf(s, "%#x", readl(&pdata->fpioa->pins[pin]));
+}
+
+static int k210_pinconf_group_set(struct pinctrl_dev *pctldev,
+                                 unsigned int selector, unsigned long *configs,
+                                 unsigned int num_configs)
+{
+       struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
+       unsigned int param, arg;
+       u32 bit;
+       int i;
+
+       /* Pins should be configured with pinmux, not groups*/
+       if (selector < K210_NPINS)
+               return -EINVAL;
+
+       /* Otherwise it's a power domain */
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               if (param != PIN_CONFIG_POWER_SOURCE)
+                       return -EINVAL;
+
+               arg = pinconf_to_config_argument(configs[i]);
+               bit = BIT(selector - K210_NPINS);
+               regmap_update_bits(pdata->sysctl_map,
+                                  pdata->power_offset,
+                                  bit, arg ? bit : 0);
+       }
+
+       return 0;
+}
+
+static void k210_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+                                       struct seq_file *s,
+                                       unsigned int selector)
+{
+       struct k210_fpioa_data *pdata = pinctrl_dev_get_drvdata(pctldev);
+       int ret;
+       u32 val;
+
+       if (selector < K210_NPINS)
+               return k210_pinconf_dbg_show(pctldev, s, selector);
+
+       ret = regmap_read(pdata->sysctl_map, pdata->power_offset, &val);
+       if (ret) {
+               dev_err(pdata->dev, "Failed to read power reg\n");
+               return;
+       }
+
+       seq_printf(s, "%s: %s V", k210_group_names[selector],
+                  val & BIT(selector - K210_NPINS) ? "1.8" : "3.3");
+}
+
+static const struct pinconf_ops k210_pinconf_ops = {
+       .is_generic = true,
+       .pin_config_set = k210_pinconf_set,
+       .pin_config_group_set = k210_pinconf_group_set,
+       .pin_config_dbg_show = k210_pinconf_dbg_show,
+       .pin_config_group_dbg_show = k210_pinconf_group_dbg_show,
+};
+
+static int k210_pinmux_get_function_count(struct pinctrl_dev *pctldev)
+{
+       return ARRAY_SIZE(k210_pcf_infos);
+}
+
+static const char *k210_pinmux_get_function_name(struct pinctrl_dev *pctldev,
+                                                unsigned int selector)
+{
+       return k210_pcf_infos[selector].name;
+}
+
+static int k210_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
+                                          unsigned int selector,
+                                          const char * const **groups,
+                                          unsigned int * const num_groups)
+{
+       /* Any function can be mapped to any pin */
+       *groups = k210_group_names;
+       *num_groups = K210_NPINS;
+
+       return 0;
+}
+
+static int k210_pinmux_set_mux(struct pinctrl_dev *pctldev,
+                              unsigned int function,
+                              unsigned int group)
+{
+       /* Can't mux power domains */
+       if (group >= K210_NPINS)
+               return -EINVAL;
+
+       k210_pinmux_set_pin_function(pctldev, group, function);
+
+       return 0;
+}
+
+static const struct pinmux_ops k210_pinmux_ops = {
+       .get_functions_count = k210_pinmux_get_function_count,
+       .get_function_name = k210_pinmux_get_function_name,
+       .get_function_groups = k210_pinmux_get_function_groups,
+       .set_mux = k210_pinmux_set_mux,
+       .strict = true,
+};
+
+static int k210_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       return K210_NGROUPS;
+}
+
+static const char *k210_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+                                              unsigned int group)
+{
+       return k210_group_names[group];
+}
+
+static int k210_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+                                      unsigned int group,
+                                      const unsigned int **pins,
+                                      unsigned int *npins)
+{
+       if (group >= K210_NPINS) {
+               *pins = NULL;
+               *npins = 0;
+               return 0;
+       }
+
+       *pins = &k210_pins[group].number;
+       *npins = 1;
+
+       return 0;
+}
+
+static void k210_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
+                                     struct seq_file *s, unsigned int offset)
+{
+       seq_printf(s, "%s", dev_name(pctldev->dev));
+}
+
+static int k210_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+                                         struct device_node *np,
+                                         struct pinctrl_map **map,
+                                         unsigned int *reserved_maps,
+                                         unsigned int *num_maps)
+{
+       struct property *prop;
+       const __be32 *p;
+       int ret, pinmux_groups;
+       u32 pinmux_group;
+       unsigned long *configs = NULL;
+       unsigned int num_configs = 0;
+       unsigned int reserve = 0;
+
+       ret = of_property_count_strings(np, "groups");
+       if (!ret)
+               return pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+                                               reserved_maps, num_maps,
+                                               PIN_MAP_TYPE_CONFIGS_GROUP);
+
+       pinmux_groups = of_property_count_u32_elems(np, "pinmux");
+       if (pinmux_groups <= 0) {
+               /* Ignore this node */
+               return 0;
+       }
+
+       ret = pinconf_generic_parse_dt_config(np, pctldev, &configs,
+                                             &num_configs);
+       if (ret < 0) {
+               dev_err(pctldev->dev, "%pOF: could not parse node property\n",
+                       np);
+               return ret;
+       }
+
+       reserve = pinmux_groups * (1 + num_configs);
+       ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
+                                       reserve);
+       if (ret < 0)
+               goto exit;
+
+       of_property_for_each_u32(np, "pinmux", prop, p, pinmux_group) {
+               const char *group_name, *func_name;
+               u32 pin = FIELD_GET(K210_PG_PIN, pinmux_group);
+               u32 func = FIELD_GET(K210_PG_FUNC, pinmux_group);
+
+               if (pin >= K210_NPINS) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+
+               group_name = k210_group_names[pin];
+               func_name = k210_pcf_infos[func].name;
+
+               dev_dbg(pctldev->dev, "Pinmux %s: pin %u func %s\n",
+                       np->name, pin, func_name);
+
+               ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps,
+                                               num_maps, group_name,
+                                               func_name);
+               if (ret < 0) {
+                       dev_err(pctldev->dev, "%pOF add mux map failed %d\n",
+                               np, ret);
+                       goto exit;
+               }
+
+               if (num_configs) {
+                       ret = pinctrl_utils_add_map_configs(pctldev, map,
+                                       reserved_maps, num_maps, group_name,
+                                       configs, num_configs,
+                                       PIN_MAP_TYPE_CONFIGS_PIN);
+                       if (ret < 0) {
+                               dev_err(pctldev->dev,
+                                       "%pOF add configs map failed %d\n",
+                                       np, ret);
+                               goto exit;
+                       }
+               }
+       }
+
+       ret = 0;
+
+exit:
+       kfree(configs);
+       return ret;
+}
+
+static int k210_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+                                      struct device_node *np_config,
+                                      struct pinctrl_map **map,
+                                      unsigned int *num_maps)
+{
+       unsigned int reserved_maps;
+       struct device_node *np;
+       int ret;
+
+       reserved_maps = 0;
+       *map = NULL;
+       *num_maps = 0;
+
+       ret = k210_pinctrl_dt_subnode_to_map(pctldev, np_config, map,
+                                            &reserved_maps, num_maps);
+       if (ret < 0)
+               goto err;
+
+       for_each_available_child_of_node(np_config, np) {
+               ret = k210_pinctrl_dt_subnode_to_map(pctldev, np, map,
+                                                    &reserved_maps, num_maps);
+               if (ret < 0)
+                       goto err;
+       }
+       return 0;
+
+err:
+       pinctrl_utils_free_map(pctldev, *map, *num_maps);
+       return ret;
+}
+
+
+static const struct pinctrl_ops k210_pinctrl_ops = {
+       .get_groups_count = k210_pinctrl_get_groups_count,
+       .get_group_name = k210_pinctrl_get_group_name,
+       .get_group_pins = k210_pinctrl_get_group_pins,
+       .pin_dbg_show = k210_pinctrl_pin_dbg_show,
+       .dt_node_to_map = k210_pinctrl_dt_node_to_map,
+       .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static struct pinctrl_desc k210_pinctrl_desc = {
+       .name = "k210-pinctrl",
+       .pins = k210_pins,
+       .npins = K210_NPINS,
+       .pctlops = &k210_pinctrl_ops,
+       .pmxops = &k210_pinmux_ops,
+       .confops = &k210_pinconf_ops,
+       .custom_params = k210_pinconf_custom_params,
+       .num_custom_params = ARRAY_SIZE(k210_pinconf_custom_params),
+};
+
+static void k210_fpioa_init_ties(struct k210_fpioa_data *pdata)
+{
+       struct k210_fpioa __iomem *fpioa = pdata->fpioa;
+       u32 val;
+       int i, j;
+
+       dev_dbg(pdata->dev, "Init pin ties\n");
+
+       /* Init pin functions input ties */
+       for (i = 0; i < ARRAY_SIZE(fpioa->tie_en); i++) {
+               val = 0;
+               for (j = 0; j < 32; j++) {
+                       if (k210_pcf_infos[i * 32 + j].mode_id ==
+                           K210_PC_DEFAULT_IN_TIE) {
+                               dev_dbg(pdata->dev,
+                                       "tie_en function %d (%s)\n",
+                                       i * 32 + j,
+                                       k210_pcf_infos[i * 32 + j].name);
+                               val |= BIT(j);
+                       }
+               }
+
+               /* Set value before enable */
+               writel(val, &fpioa->tie_val[i]);
+               writel(val, &fpioa->tie_en[i]);
+       }
+}
+
+static int k210_fpioa_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct k210_fpioa_data *pdata;
+       int ret;
+
+       dev_info(dev, "K210 FPIOA pin controller\n");
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->dev = dev;
+       platform_set_drvdata(pdev, pdata);
+
+       pdata->fpioa = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(pdata->fpioa))
+               return PTR_ERR(pdata->fpioa);
+
+       pdata->clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(pdata->clk))
+               return PTR_ERR(pdata->clk);
+
+       ret = clk_prepare_enable(pdata->clk);
+       if (ret)
+               return ret;
+
+       pdata->pclk = devm_clk_get_optional(dev, "pclk");
+       if (!IS_ERR(pdata->pclk))
+               clk_prepare_enable(pdata->pclk);
+
+       pdata->sysctl_map =
+               syscon_regmap_lookup_by_phandle_args(np,
+                                               "canaan,k210-sysctl-power",
+                                               1, &pdata->power_offset);
+       if (IS_ERR(pdata->sysctl_map))
+               return PTR_ERR(pdata->sysctl_map);
+
+       k210_fpioa_init_ties(pdata);
+
+       pdata->pctl = pinctrl_register(&k210_pinctrl_desc, dev, (void *)pdata);
+       if (IS_ERR(pdata->pctl))
+               return PTR_ERR(pdata->pctl);
+
+       return 0;
+}
+
+static const struct of_device_id k210_fpioa_dt_ids[] = {
+       { .compatible = "canaan,k210-fpioa" },
+       { /* sentinel */ },
+};
+
+static struct platform_driver k210_fpioa_driver = {
+       .probe  = k210_fpioa_probe,
+       .driver = {
+               .name           = "k210-fpioa",
+               .of_match_table = k210_fpioa_dt_ids,
+       },
+};
+builtin_platform_driver(k210_fpioa_driver);
index 8ac5627564f08accf45e4cffdf2ce45652218cf0..4171c6f763858a034b2c35256953e14f237e944b 100644 (file)
@@ -89,6 +89,16 @@ config RESET_INTEL_GW
          Say Y to control the reset signals provided by reset controller.
          Otherwise, say N.
 
+config RESET_K210
+       bool "Reset controller driver for Canaan Kendryte K210 SoC"
+       depends on (SOC_CANAAN || COMPILE_TEST) && OF
+       select MFD_SYSCON
+       default SOC_CANAAN
+       help
+         Support for the Canaan Kendryte K210 RISC-V SoC reset controller.
+         Say Y if you want to control reset signals provided by this
+         controller.
+
 config RESET_LANTIQ
        bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
        default SOC_TYPE_XWAY
index 1054123fd1876fa810ebf8205d744a4da928010c..65a118a91b2734e1aaed8b84246f8fcd1dfaa884 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
 obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
 obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
 obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
+obj-$(CONFIG_RESET_K210) += reset-k210.o
 obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
 obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
diff --git a/drivers/reset/reset-k210.c b/drivers/reset/reset-k210.c
new file mode 100644 (file)
index 0000000..1b6e035
--- /dev/null
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <soc/canaan/k210-sysctl.h>
+
+#include <dt-bindings/reset/k210-rst.h>
+
+#define K210_RST_MASK  0x27FFFFFF
+
+struct k210_rst {
+       struct regmap *map;
+       struct reset_controller_dev rcdev;
+};
+
+static inline struct k210_rst *
+to_k210_rst(struct reset_controller_dev *rcdev)
+{
+       return container_of(rcdev, struct k210_rst, rcdev);
+}
+
+static inline int k210_rst_assert(struct reset_controller_dev *rcdev,
+                                 unsigned long id)
+{
+       struct k210_rst *ksr = to_k210_rst(rcdev);
+
+       return regmap_update_bits(ksr->map, K210_SYSCTL_PERI_RESET, BIT(id), 1);
+}
+
+static inline int k210_rst_deassert(struct reset_controller_dev *rcdev,
+                                   unsigned long id)
+{
+       struct k210_rst *ksr = to_k210_rst(rcdev);
+
+       return regmap_update_bits(ksr->map, K210_SYSCTL_PERI_RESET, BIT(id), 0);
+}
+
+static int k210_rst_reset(struct reset_controller_dev *rcdev,
+                         unsigned long id)
+{
+       int ret;
+
+       ret = k210_rst_assert(rcdev, id);
+       if (ret == 0) {
+               udelay(10);
+               ret = k210_rst_deassert(rcdev, id);
+       }
+
+       return ret;
+}
+
+static int k210_rst_status(struct reset_controller_dev *rcdev,
+                          unsigned long id)
+{
+       struct k210_rst *ksr = to_k210_rst(rcdev);
+       u32 reg, bit = BIT(id);
+       int ret;
+
+       ret = regmap_read(ksr->map, K210_SYSCTL_PERI_RESET, &reg);
+       if (ret)
+               return ret;
+
+       return reg & bit;
+}
+
+static int k210_rst_xlate(struct reset_controller_dev *rcdev,
+                         const struct of_phandle_args *reset_spec)
+{
+       unsigned long id = reset_spec->args[0];
+
+       if (!(BIT(id) & K210_RST_MASK))
+               return -EINVAL;
+
+       return id;
+}
+
+static const struct reset_control_ops k210_rst_ops = {
+       .assert         = k210_rst_assert,
+       .deassert       = k210_rst_deassert,
+       .reset          = k210_rst_reset,
+       .status         = k210_rst_status,
+};
+
+static int k210_rst_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *parent_np = of_get_parent(dev->of_node);
+       struct k210_rst *ksr;
+
+       dev_info(dev, "K210 reset controller\n");
+
+       ksr = devm_kzalloc(dev, sizeof(*ksr), GFP_KERNEL);
+       if (!ksr)
+               return -ENOMEM;
+
+       ksr->map = syscon_node_to_regmap(parent_np);
+       of_node_put(parent_np);
+       if (IS_ERR(ksr->map))
+               return PTR_ERR(ksr->map);
+
+       ksr->rcdev.owner = THIS_MODULE;
+       ksr->rcdev.dev = dev;
+       ksr->rcdev.of_node = dev->of_node;
+       ksr->rcdev.ops = &k210_rst_ops;
+       ksr->rcdev.nr_resets = fls(K210_RST_MASK);
+       ksr->rcdev.of_reset_n_cells = 1;
+       ksr->rcdev.of_xlate = k210_rst_xlate;
+
+       return devm_reset_controller_register(dev, &ksr->rcdev);
+}
+
+static const struct of_device_id k210_rst_dt_ids[] = {
+       { .compatible = "canaan,k210-rst" },
+       { /* sentinel */ },
+};
+
+static struct platform_driver k210_rst_driver = {
+       .probe  = k210_rst_probe,
+       .driver = {
+               .name           = "k210-rst",
+               .of_match_table = k210_rst_dt_ids,
+       },
+};
+builtin_platform_driver(k210_rst_driver);
index f357c6c659d2c59def4a9964740b9e99ca02fa33..e8a30c4c5aec5a4e5623a4e5dd155704c4d21c59 100644 (file)
@@ -6,6 +6,7 @@ source "drivers/soc/amlogic/Kconfig"
 source "drivers/soc/aspeed/Kconfig"
 source "drivers/soc/atmel/Kconfig"
 source "drivers/soc/bcm/Kconfig"
+source "drivers/soc/canaan/Kconfig"
 source "drivers/soc/fsl/Kconfig"
 source "drivers/soc/imx/Kconfig"
 source "drivers/soc/ixp4xx/Kconfig"
@@ -22,6 +23,5 @@ source "drivers/soc/ti/Kconfig"
 source "drivers/soc/ux500/Kconfig"
 source "drivers/soc/versatile/Kconfig"
 source "drivers/soc/xilinx/Kconfig"
-source "drivers/soc/kendryte/Kconfig"
 
 endmenu
index 9bceb12b291d3ebb0dad0113c016aea936655b95..f678e4d9e58505a384bf2bb68851f81226912c45 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_ACTIONS)      += actions/
 obj-y                          += aspeed/
 obj-$(CONFIG_ARCH_AT91)                += atmel/
 obj-y                          += bcm/
+obj-$(CONFIG_SOC_CANAAN)       += canaan/
 obj-$(CONFIG_ARCH_DOVE)                += dove/
 obj-$(CONFIG_MACH_DOVE)                += dove/
 obj-y                          += fsl/
@@ -28,4 +29,3 @@ obj-y                         += ti/
 obj-$(CONFIG_ARCH_U8500)       += ux500/
 obj-$(CONFIG_PLAT_VERSATILE)   += versatile/
 obj-y                          += xilinx/
-obj-$(CONFIG_SOC_KENDRYTE)     += kendryte/
diff --git a/drivers/soc/canaan/Kconfig b/drivers/soc/canaan/Kconfig
new file mode 100644 (file)
index 0000000..8179b69
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config SOC_K210_SYSCTL
+       bool "Canaan Kendryte K210 SoC system controller"
+       depends on RISCV && SOC_CANAAN && OF
+       default SOC_CANAAN
+        select PM
+        select SIMPLE_PM_BUS
+        select SYSCON
+        select MFD_SYSCON
+       help
+         Canaan Kendryte K210 SoC system controller driver.
diff --git a/drivers/soc/canaan/Makefile b/drivers/soc/canaan/Makefile
new file mode 100644 (file)
index 0000000..570280a
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SOC_K210_SYSCTL)  += k210-sysctl.o
diff --git a/drivers/soc/canaan/k210-sysctl.c b/drivers/soc/canaan/k210-sysctl.c
new file mode 100644 (file)
index 0000000..27a346c
--- /dev/null
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 Christoph Hellwig.
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ */
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <asm/soc.h>
+
+#include <soc/canaan/k210-sysctl.h>
+
+static int k210_sysctl_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct clk *pclk;
+       int ret;
+
+       dev_info(dev, "K210 system controller\n");
+
+       /* Get power bus clock */
+       pclk = devm_clk_get(dev, NULL);
+       if (IS_ERR(pclk))
+               return dev_err_probe(dev, PTR_ERR(pclk),
+                                    "Get bus clock failed\n");
+
+       ret = clk_prepare_enable(pclk);
+       if (ret) {
+               dev_err(dev, "Enable bus clock failed\n");
+               return ret;
+       }
+
+       /* Populate children */
+       ret = devm_of_platform_populate(dev);
+       if (ret)
+               dev_err(dev, "Populate platform failed %d\n", ret);
+
+       return ret;
+}
+
+static const struct of_device_id k210_sysctl_of_match[] = {
+       { .compatible = "canaan,k210-sysctl", },
+       { /* sentinel */ },
+};
+
+static struct platform_driver k210_sysctl_driver = {
+       .driver = {
+               .name           = "k210-sysctl",
+               .of_match_table = k210_sysctl_of_match,
+       },
+       .probe                  = k210_sysctl_probe,
+};
+builtin_platform_driver(k210_sysctl_driver);
+
+/*
+ * System controller registers base address and size.
+ */
+#define K210_SYSCTL_BASE_ADDR  0x50440000ULL
+#define K210_SYSCTL_BASE_SIZE  0x1000
+
+/*
+ * 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 *sysctl_base;
+
+       sysctl_base = ioremap(K210_SYSCTL_BASE_ADDR, K210_SYSCTL_BASE_SIZE);
+       if (!sysctl_base)
+               panic("k210-sysctl: ioremap failed");
+
+       k210_clk_early_init(sysctl_base);
+
+       iounmap(sysctl_base);
+}
+SOC_EARLY_INIT_DECLARE(k210_soc, "canaan,kendryte-k210", k210_soc_early_init);
diff --git a/drivers/soc/kendryte/Kconfig b/drivers/soc/kendryte/Kconfig
deleted file mode 100644 (file)
index 49785b1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# 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
deleted file mode 100644 (file)
index 002d9ce..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# 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
deleted file mode 100644 (file)
index 7070192..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-// 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);
-
-#ifdef CONFIG_SOC_KENDRYTE_K210_DTB_BUILTIN
-/*
- * Generic entry for the default k210.dtb embedded DTB for boards with:
- *   - Vendor ID: 0x4B5
- *   - Arch ID: 0xE59889E6A5A04149 (= "Canaan AI" in UTF-8 encoded Chinese)
- *   - Impl ID:        0x4D41495832303030 (= "MAIX2000")
- * These values are reported by the SiPEED MAXDUINO, SiPEED MAIX GO and
- * SiPEED Dan dock boards.
- */
-SOC_BUILTIN_DTB_DECLARE(k210, 0x4B5, 0xE59889E6A5A04149, 0x4D41495832303030);
-#endif
index 44d7e1951da3bcae0e117b6368b36ad75a007986..59640a1d0b28a1a9606a6ddc6dc702df82bc97b5 100644 (file)
 #define SIFIVE_L2_DIRECCFIX_HIGH 0x104
 #define SIFIVE_L2_DIRECCFIX_COUNT 0x108
 
+#define SIFIVE_L2_DIRECCFAIL_LOW 0x120
+#define SIFIVE_L2_DIRECCFAIL_HIGH 0x124
+#define SIFIVE_L2_DIRECCFAIL_COUNT 0x128
+
 #define SIFIVE_L2_DATECCFIX_LOW 0x140
 #define SIFIVE_L2_DATECCFIX_HIGH 0x144
 #define SIFIVE_L2_DATECCFIX_COUNT 0x148
@@ -29,7 +33,7 @@
 #define SIFIVE_L2_WAYENABLE 0x08
 #define SIFIVE_L2_ECCINJECTERR 0x40
 
-#define SIFIVE_L2_MAX_ECCINTR 3
+#define SIFIVE_L2_MAX_ECCINTR 4
 
 static void __iomem *l2_base;
 static int g_irq[SIFIVE_L2_MAX_ECCINTR];
@@ -39,6 +43,7 @@ enum {
        DIR_CORR = 0,
        DATA_CORR,
        DATA_UNCORR,
+       DIR_UNCORR,
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -93,6 +98,7 @@ static void l2_config_read(void)
 
 static const struct of_device_id sifive_l2_ids[] = {
        { .compatible = "sifive,fu540-c000-ccache" },
+       { .compatible = "sifive,fu740-c000-ccache" },
        { /* end of table */ },
 };
 
@@ -155,6 +161,15 @@ static irqreturn_t l2_int_handler(int irq, void *device)
                atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_CE,
                                           "DirECCFix");
        }
+       if (irq == g_irq[DIR_UNCORR]) {
+               add_h = readl(l2_base + SIFIVE_L2_DIRECCFAIL_HIGH);
+               add_l = readl(l2_base + SIFIVE_L2_DIRECCFAIL_LOW);
+               /* Reading this register clears the DirFail interrupt sig */
+               readl(l2_base + SIFIVE_L2_DIRECCFAIL_COUNT);
+               atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_UE,
+                                          "DirECCFail");
+               panic("L2CACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l);
+       }
        if (irq == g_irq[DATA_CORR]) {
                add_h = readl(l2_base + SIFIVE_L2_DATECCFIX_HIGH);
                add_l = readl(l2_base + SIFIVE_L2_DATECCFIX_LOW);
@@ -181,7 +196,7 @@ static int __init sifive_l2_init(void)
 {
        struct device_node *np;
        struct resource res;
-       int i, rc;
+       int i, rc, intr_num;
 
        np = of_find_matching_node(NULL, sifive_l2_ids);
        if (!np)
@@ -194,7 +209,13 @@ static int __init sifive_l2_init(void)
        if (!l2_base)
                return -ENOMEM;
 
-       for (i = 0; i < SIFIVE_L2_MAX_ECCINTR; i++) {
+       intr_num = of_property_count_u32_elems(np, "interrupts");
+       if (!intr_num) {
+               pr_err("L2CACHE: no interrupts property\n");
+               return -ENODEV;
+       }
+
+       for (i = 0; i < intr_num; i++) {
                g_irq[i] = irq_of_parse_and_map(np, i);
                rc = request_irq(g_irq[i], l2_int_handler, 0, "l2_ecc", NULL);
                if (rc) {
diff --git a/include/asm-generic/numa.h b/include/asm-generic/numa.h
new file mode 100644 (file)
index 0000000..1a3ad6d
--- /dev/null
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_GENERIC_NUMA_H
+#define __ASM_GENERIC_NUMA_H
+
+#ifdef CONFIG_NUMA
+
+#define NR_NODE_MEMBLKS                (MAX_NUMNODES * 2)
+
+int __node_distance(int from, int to);
+#define node_distance(a, b) __node_distance(a, b)
+
+extern nodemask_t numa_nodes_parsed __initdata;
+
+extern bool numa_off;
+
+/* Mappings between node number and cpus on that node. */
+extern cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
+void numa_clear_node(unsigned int cpu);
+
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+const struct cpumask *cpumask_of_node(int node);
+#else
+/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
+static inline const struct cpumask *cpumask_of_node(int node)
+{
+       if (node == NUMA_NO_NODE)
+               return cpu_all_mask;
+
+       return node_to_cpumask_map[node];
+}
+#endif
+
+void __init arch_numa_init(void);
+int __init numa_add_memblk(int nodeid, u64 start, u64 end);
+void __init numa_set_distance(int from, int to, int distance);
+void __init numa_free_distance(void);
+void __init early_map_cpu_to_node(unsigned int cpu, int nid);
+void numa_store_cpu_info(unsigned int cpu);
+void numa_add_cpu(unsigned int cpu);
+void numa_remove_cpu(unsigned int cpu);
+
+#else  /* CONFIG_NUMA */
+
+static inline void numa_store_cpu_info(unsigned int cpu) { }
+static inline void numa_add_cpu(unsigned int cpu) { }
+static inline void numa_remove_cpu(unsigned int cpu) { }
+static inline void arch_numa_init(void) { }
+static inline void early_map_cpu_to_node(unsigned int cpu, int nid) { }
+
+#endif /* CONFIG_NUMA */
+
+#endif /* __ASM_GENERIC_NUMA_H */
index a48176ad3c23a8469e7ea94fec0856250494445b..b2de702cbf75f54e2f8f8d3b3b8d459672ed51ef 100644 (file)
@@ -9,7 +9,6 @@
 /*
  * Kendryte K210 SoC clock identifiers (arbitrary values).
  */
-#define K210_CLK_ACLK  0
 #define K210_CLK_CPU   0
 #define K210_CLK_SRAM0 1
 #define K210_CLK_SRAM1 2
diff --git a/include/dt-bindings/pinctrl/k210-fpioa.h b/include/dt-bindings/pinctrl/k210-fpioa.h
new file mode 100644 (file)
index 0000000..314285e
--- /dev/null
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#ifndef PINCTRL_K210_FPIOA_H
+#define PINCTRL_K210_FPIOA_H
+
+/*
+ * Full list of FPIOA functions from
+ * kendryte-standalone-sdk/lib/drivers/include/fpioa.h
+ */
+#define K210_PCF_MASK          GENMASK(7, 0)
+#define K210_PCF_JTAG_TCLK     0   /* JTAG Test Clock */
+#define K210_PCF_JTAG_TDI      1   /* JTAG Test Data In */
+#define K210_PCF_JTAG_TMS      2   /* JTAG Test Mode Select */
+#define K210_PCF_JTAG_TDO      3   /* JTAG Test Data Out */
+#define K210_PCF_SPI0_D0       4   /* SPI0 Data 0 */
+#define K210_PCF_SPI0_D1       5   /* SPI0 Data 1 */
+#define K210_PCF_SPI0_D2       6   /* SPI0 Data 2 */
+#define K210_PCF_SPI0_D3       7   /* SPI0 Data 3 */
+#define K210_PCF_SPI0_D4       8   /* SPI0 Data 4 */
+#define K210_PCF_SPI0_D5       9   /* SPI0 Data 5 */
+#define K210_PCF_SPI0_D6       10  /* SPI0 Data 6 */
+#define K210_PCF_SPI0_D7       11  /* SPI0 Data 7 */
+#define K210_PCF_SPI0_SS0      12  /* SPI0 Chip Select 0 */
+#define K210_PCF_SPI0_SS1      13  /* SPI0 Chip Select 1 */
+#define K210_PCF_SPI0_SS2      14  /* SPI0 Chip Select 2 */
+#define K210_PCF_SPI0_SS3      15  /* SPI0 Chip Select 3 */
+#define K210_PCF_SPI0_ARB      16  /* SPI0 Arbitration */
+#define K210_PCF_SPI0_SCLK     17  /* SPI0 Serial Clock */
+#define K210_PCF_UARTHS_RX     18  /* UART High speed Receiver */
+#define K210_PCF_UARTHS_TX     19  /* UART High speed Transmitter */
+#define K210_PCF_RESV6         20  /* Reserved function */
+#define K210_PCF_RESV7         21  /* Reserved function */
+#define K210_PCF_CLK_SPI1      22  /* Clock SPI1 */
+#define K210_PCF_CLK_I2C1      23  /* Clock I2C1 */
+#define K210_PCF_GPIOHS0       24  /* GPIO High speed 0 */
+#define K210_PCF_GPIOHS1       25  /* GPIO High speed 1 */
+#define K210_PCF_GPIOHS2       26  /* GPIO High speed 2 */
+#define K210_PCF_GPIOHS3       27  /* GPIO High speed 3 */
+#define K210_PCF_GPIOHS4       28  /* GPIO High speed 4 */
+#define K210_PCF_GPIOHS5       29  /* GPIO High speed 5 */
+#define K210_PCF_GPIOHS6       30  /* GPIO High speed 6 */
+#define K210_PCF_GPIOHS7       31  /* GPIO High speed 7 */
+#define K210_PCF_GPIOHS8       32  /* GPIO High speed 8 */
+#define K210_PCF_GPIOHS9       33  /* GPIO High speed 9 */
+#define K210_PCF_GPIOHS10      34  /* GPIO High speed 10 */
+#define K210_PCF_GPIOHS11      35  /* GPIO High speed 11 */
+#define K210_PCF_GPIOHS12      36  /* GPIO High speed 12 */
+#define K210_PCF_GPIOHS13      37  /* GPIO High speed 13 */
+#define K210_PCF_GPIOHS14      38  /* GPIO High speed 14 */
+#define K210_PCF_GPIOHS15      39  /* GPIO High speed 15 */
+#define K210_PCF_GPIOHS16      40  /* GPIO High speed 16 */
+#define K210_PCF_GPIOHS17      41  /* GPIO High speed 17 */
+#define K210_PCF_GPIOHS18      42  /* GPIO High speed 18 */
+#define K210_PCF_GPIOHS19      43  /* GPIO High speed 19 */
+#define K210_PCF_GPIOHS20      44  /* GPIO High speed 20 */
+#define K210_PCF_GPIOHS21      45  /* GPIO High speed 21 */
+#define K210_PCF_GPIOHS22      46  /* GPIO High speed 22 */
+#define K210_PCF_GPIOHS23      47  /* GPIO High speed 23 */
+#define K210_PCF_GPIOHS24      48  /* GPIO High speed 24 */
+#define K210_PCF_GPIOHS25      49  /* GPIO High speed 25 */
+#define K210_PCF_GPIOHS26      50  /* GPIO High speed 26 */
+#define K210_PCF_GPIOHS27      51  /* GPIO High speed 27 */
+#define K210_PCF_GPIOHS28      52  /* GPIO High speed 28 */
+#define K210_PCF_GPIOHS29      53  /* GPIO High speed 29 */
+#define K210_PCF_GPIOHS30      54  /* GPIO High speed 30 */
+#define K210_PCF_GPIOHS31      55  /* GPIO High speed 31 */
+#define K210_PCF_GPIO0         56  /* GPIO pin 0 */
+#define K210_PCF_GPIO1         57  /* GPIO pin 1 */
+#define K210_PCF_GPIO2         58  /* GPIO pin 2 */
+#define K210_PCF_GPIO3         59  /* GPIO pin 3 */
+#define K210_PCF_GPIO4         60  /* GPIO pin 4 */
+#define K210_PCF_GPIO5         61  /* GPIO pin 5 */
+#define K210_PCF_GPIO6         62  /* GPIO pin 6 */
+#define K210_PCF_GPIO7         63  /* GPIO pin 7 */
+#define K210_PCF_UART1_RX      64  /* UART1 Receiver */
+#define K210_PCF_UART1_TX      65  /* UART1 Transmitter */
+#define K210_PCF_UART2_RX      66  /* UART2 Receiver */
+#define K210_PCF_UART2_TX      67  /* UART2 Transmitter */
+#define K210_PCF_UART3_RX      68  /* UART3 Receiver */
+#define K210_PCF_UART3_TX      69  /* UART3 Transmitter */
+#define K210_PCF_SPI1_D0       70  /* SPI1 Data 0 */
+#define K210_PCF_SPI1_D1       71  /* SPI1 Data 1 */
+#define K210_PCF_SPI1_D2       72  /* SPI1 Data 2 */
+#define K210_PCF_SPI1_D3       73  /* SPI1 Data 3 */
+#define K210_PCF_SPI1_D4       74  /* SPI1 Data 4 */
+#define K210_PCF_SPI1_D5       75  /* SPI1 Data 5 */
+#define K210_PCF_SPI1_D6       76  /* SPI1 Data 6 */
+#define K210_PCF_SPI1_D7       77  /* SPI1 Data 7 */
+#define K210_PCF_SPI1_SS0      78  /* SPI1 Chip Select 0 */
+#define K210_PCF_SPI1_SS1      79  /* SPI1 Chip Select 1 */
+#define K210_PCF_SPI1_SS2      80  /* SPI1 Chip Select 2 */
+#define K210_PCF_SPI1_SS3      81  /* SPI1 Chip Select 3 */
+#define K210_PCF_SPI1_ARB      82  /* SPI1 Arbitration */
+#define K210_PCF_SPI1_SCLK     83  /* SPI1 Serial Clock */
+#define K210_PCF_SPI2_D0       84  /* SPI2 Data 0 */
+#define K210_PCF_SPI2_SS       85  /* SPI2 Select */
+#define K210_PCF_SPI2_SCLK     86  /* SPI2 Serial Clock */
+#define K210_PCF_I2S0_MCLK     87  /* I2S0 Master Clock */
+#define K210_PCF_I2S0_SCLK     88  /* I2S0 Serial Clock(BCLK) */
+#define K210_PCF_I2S0_WS       89  /* I2S0 Word Select(LRCLK) */
+#define K210_PCF_I2S0_IN_D0    90  /* I2S0 Serial Data Input 0 */
+#define K210_PCF_I2S0_IN_D1    91  /* I2S0 Serial Data Input 1 */
+#define K210_PCF_I2S0_IN_D2    92  /* I2S0 Serial Data Input 2 */
+#define K210_PCF_I2S0_IN_D3    93  /* I2S0 Serial Data Input 3 */
+#define K210_PCF_I2S0_OUT_D0   94  /* I2S0 Serial Data Output 0 */
+#define K210_PCF_I2S0_OUT_D1   95  /* I2S0 Serial Data Output 1 */
+#define K210_PCF_I2S0_OUT_D2   96  /* I2S0 Serial Data Output 2 */
+#define K210_PCF_I2S0_OUT_D3   97  /* I2S0 Serial Data Output 3 */
+#define K210_PCF_I2S1_MCLK     98  /* I2S1 Master Clock */
+#define K210_PCF_I2S1_SCLK     99  /* I2S1 Serial Clock(BCLK) */
+#define K210_PCF_I2S1_WS       100 /* I2S1 Word Select(LRCLK) */
+#define K210_PCF_I2S1_IN_D0    101 /* I2S1 Serial Data Input 0 */
+#define K210_PCF_I2S1_IN_D1    102 /* I2S1 Serial Data Input 1 */
+#define K210_PCF_I2S1_IN_D2    103 /* I2S1 Serial Data Input 2 */
+#define K210_PCF_I2S1_IN_D3    104 /* I2S1 Serial Data Input 3 */
+#define K210_PCF_I2S1_OUT_D0   105 /* I2S1 Serial Data Output 0 */
+#define K210_PCF_I2S1_OUT_D1   106 /* I2S1 Serial Data Output 1 */
+#define K210_PCF_I2S1_OUT_D2   107 /* I2S1 Serial Data Output 2 */
+#define K210_PCF_I2S1_OUT_D3   108 /* I2S1 Serial Data Output 3 */
+#define K210_PCF_I2S2_MCLK     109 /* I2S2 Master Clock */
+#define K210_PCF_I2S2_SCLK     110 /* I2S2 Serial Clock(BCLK) */
+#define K210_PCF_I2S2_WS       111 /* I2S2 Word Select(LRCLK) */
+#define K210_PCF_I2S2_IN_D0    112 /* I2S2 Serial Data Input 0 */
+#define K210_PCF_I2S2_IN_D1    113 /* I2S2 Serial Data Input 1 */
+#define K210_PCF_I2S2_IN_D2    114 /* I2S2 Serial Data Input 2 */
+#define K210_PCF_I2S2_IN_D3    115 /* I2S2 Serial Data Input 3 */
+#define K210_PCF_I2S2_OUT_D0   116 /* I2S2 Serial Data Output 0 */
+#define K210_PCF_I2S2_OUT_D1   117 /* I2S2 Serial Data Output 1 */
+#define K210_PCF_I2S2_OUT_D2   118 /* I2S2 Serial Data Output 2 */
+#define K210_PCF_I2S2_OUT_D3   119 /* I2S2 Serial Data Output 3 */
+#define K210_PCF_RESV0         120 /* Reserved function */
+#define K210_PCF_RESV1         121 /* Reserved function */
+#define K210_PCF_RESV2         122 /* Reserved function */
+#define K210_PCF_RESV3         123 /* Reserved function */
+#define K210_PCF_RESV4         124 /* Reserved function */
+#define K210_PCF_RESV5         125 /* Reserved function */
+#define K210_PCF_I2C0_SCLK     126 /* I2C0 Serial Clock */
+#define K210_PCF_I2C0_SDA      127 /* I2C0 Serial Data */
+#define K210_PCF_I2C1_SCLK     128 /* I2C1 Serial Clock */
+#define K210_PCF_I2C1_SDA      129 /* I2C1 Serial Data */
+#define K210_PCF_I2C2_SCLK     130 /* I2C2 Serial Clock */
+#define K210_PCF_I2C2_SDA      131 /* I2C2 Serial Data */
+#define K210_PCF_DVP_XCLK      132 /* DVP System Clock */
+#define K210_PCF_DVP_RST       133 /* DVP System Reset */
+#define K210_PCF_DVP_PWDN      134 /* DVP Power Down Mode */
+#define K210_PCF_DVP_VSYNC     135 /* DVP Vertical Sync */
+#define K210_PCF_DVP_HSYNC     136 /* DVP Horizontal Sync */
+#define K210_PCF_DVP_PCLK      137 /* Pixel Clock */
+#define K210_PCF_DVP_D0                138 /* Data Bit 0 */
+#define K210_PCF_DVP_D1                139 /* Data Bit 1 */
+#define K210_PCF_DVP_D2                140 /* Data Bit 2 */
+#define K210_PCF_DVP_D3                141 /* Data Bit 3 */
+#define K210_PCF_DVP_D4                142 /* Data Bit 4 */
+#define K210_PCF_DVP_D5                143 /* Data Bit 5 */
+#define K210_PCF_DVP_D6                144 /* Data Bit 6 */
+#define K210_PCF_DVP_D7                145 /* Data Bit 7 */
+#define K210_PCF_SCCB_SCLK     146 /* Serial Camera Control Bus Clock */
+#define K210_PCF_SCCB_SDA      147 /* Serial Camera Control Bus Data */
+#define K210_PCF_UART1_CTS     148 /* UART1 Clear To Send */
+#define K210_PCF_UART1_DSR     149 /* UART1 Data Set Ready */
+#define K210_PCF_UART1_DCD     150 /* UART1 Data Carrier Detect */
+#define K210_PCF_UART1_RI      151 /* UART1 Ring Indicator */
+#define K210_PCF_UART1_SIR_IN  152 /* UART1 Serial Infrared Input */
+#define K210_PCF_UART1_DTR     153 /* UART1 Data Terminal Ready */
+#define K210_PCF_UART1_RTS     154 /* UART1 Request To Send */
+#define K210_PCF_UART1_OUT2    155 /* UART1 User-designated Output 2 */
+#define K210_PCF_UART1_OUT1    156 /* UART1 User-designated Output 1 */
+#define K210_PCF_UART1_SIR_OUT 157 /* UART1 Serial Infrared Output */
+#define K210_PCF_UART1_BAUD    158 /* UART1 Transmit Clock Output */
+#define K210_PCF_UART1_RE      159 /* UART1 Receiver Output Enable */
+#define K210_PCF_UART1_DE      160 /* UART1 Driver Output Enable */
+#define K210_PCF_UART1_RS485_EN        161 /* UART1 RS485 Enable */
+#define K210_PCF_UART2_CTS     162 /* UART2 Clear To Send */
+#define K210_PCF_UART2_DSR     163 /* UART2 Data Set Ready */
+#define K210_PCF_UART2_DCD     164 /* UART2 Data Carrier Detect */
+#define K210_PCF_UART2_RI      165 /* UART2 Ring Indicator */
+#define K210_PCF_UART2_SIR_IN  166 /* UART2 Serial Infrared Input */
+#define K210_PCF_UART2_DTR     167 /* UART2 Data Terminal Ready */
+#define K210_PCF_UART2_RTS     168 /* UART2 Request To Send */
+#define K210_PCF_UART2_OUT2    169 /* UART2 User-designated Output 2 */
+#define K210_PCF_UART2_OUT1    170 /* UART2 User-designated Output 1 */
+#define K210_PCF_UART2_SIR_OUT 171 /* UART2 Serial Infrared Output */
+#define K210_PCF_UART2_BAUD    172 /* UART2 Transmit Clock Output */
+#define K210_PCF_UART2_RE      173 /* UART2 Receiver Output Enable */
+#define K210_PCF_UART2_DE      174 /* UART2 Driver Output Enable */
+#define K210_PCF_UART2_RS485_EN        175 /* UART2 RS485 Enable */
+#define K210_PCF_UART3_CTS     176 /* UART3 Clear To Send */
+#define K210_PCF_UART3_DSR     177 /* UART3 Data Set Ready */
+#define K210_PCF_UART3_DCD     178 /* UART3 Data Carrier Detect */
+#define K210_PCF_UART3_RI      179 /* UART3 Ring Indicator */
+#define K210_PCF_UART3_SIR_IN  180 /* UART3 Serial Infrared Input */
+#define K210_PCF_UART3_DTR     181 /* UART3 Data Terminal Ready */
+#define K210_PCF_UART3_RTS     182 /* UART3 Request To Send */
+#define K210_PCF_UART3_OUT2    183 /* UART3 User-designated Output 2 */
+#define K210_PCF_UART3_OUT1    184 /* UART3 User-designated Output 1 */
+#define K210_PCF_UART3_SIR_OUT 185 /* UART3 Serial Infrared Output */
+#define K210_PCF_UART3_BAUD    186 /* UART3 Transmit Clock Output */
+#define K210_PCF_UART3_RE      187 /* UART3 Receiver Output Enable */
+#define K210_PCF_UART3_DE      188 /* UART3 Driver Output Enable */
+#define K210_PCF_UART3_RS485_EN        189 /* UART3 RS485 Enable */
+#define K210_PCF_TIMER0_TOGGLE1        190 /* TIMER0 Toggle Output 1 */
+#define K210_PCF_TIMER0_TOGGLE2        191 /* TIMER0 Toggle Output 2 */
+#define K210_PCF_TIMER0_TOGGLE3        192 /* TIMER0 Toggle Output 3 */
+#define K210_PCF_TIMER0_TOGGLE4        193 /* TIMER0 Toggle Output 4 */
+#define K210_PCF_TIMER1_TOGGLE1        194 /* TIMER1 Toggle Output 1 */
+#define K210_PCF_TIMER1_TOGGLE2        195 /* TIMER1 Toggle Output 2 */
+#define K210_PCF_TIMER1_TOGGLE3        196 /* TIMER1 Toggle Output 3 */
+#define K210_PCF_TIMER1_TOGGLE4        197 /* TIMER1 Toggle Output 4 */
+#define K210_PCF_TIMER2_TOGGLE1        198 /* TIMER2 Toggle Output 1 */
+#define K210_PCF_TIMER2_TOGGLE2        199 /* TIMER2 Toggle Output 2 */
+#define K210_PCF_TIMER2_TOGGLE3        200 /* TIMER2 Toggle Output 3 */
+#define K210_PCF_TIMER2_TOGGLE4        201 /* TIMER2 Toggle Output 4 */
+#define K210_PCF_CLK_SPI2      202 /* Clock SPI2 */
+#define K210_PCF_CLK_I2C2      203 /* Clock I2C2 */
+#define K210_PCF_INTERNAL0     204 /* Internal function signal 0 */
+#define K210_PCF_INTERNAL1     205 /* Internal function signal 1 */
+#define K210_PCF_INTERNAL2     206 /* Internal function signal 2 */
+#define K210_PCF_INTERNAL3     207 /* Internal function signal 3 */
+#define K210_PCF_INTERNAL4     208 /* Internal function signal 4 */
+#define K210_PCF_INTERNAL5     209 /* Internal function signal 5 */
+#define K210_PCF_INTERNAL6     210 /* Internal function signal 6 */
+#define K210_PCF_INTERNAL7     211 /* Internal function signal 7 */
+#define K210_PCF_INTERNAL8     212 /* Internal function signal 8 */
+#define K210_PCF_INTERNAL9     213 /* Internal function signal 9 */
+#define K210_PCF_INTERNAL10    214 /* Internal function signal 10 */
+#define K210_PCF_INTERNAL11    215 /* Internal function signal 11 */
+#define K210_PCF_INTERNAL12    216 /* Internal function signal 12 */
+#define K210_PCF_INTERNAL13    217 /* Internal function signal 13 */
+#define K210_PCF_INTERNAL14    218 /* Internal function signal 14 */
+#define K210_PCF_INTERNAL15    219 /* Internal function signal 15 */
+#define K210_PCF_INTERNAL16    220 /* Internal function signal 16 */
+#define K210_PCF_INTERNAL17    221 /* Internal function signal 17 */
+#define K210_PCF_CONSTANT      222 /* Constant function */
+#define K210_PCF_INTERNAL18    223 /* Internal function signal 18 */
+#define K210_PCF_DEBUG0                224 /* Debug function 0 */
+#define K210_PCF_DEBUG1                225 /* Debug function 1 */
+#define K210_PCF_DEBUG2                226 /* Debug function 2 */
+#define K210_PCF_DEBUG3                227 /* Debug function 3 */
+#define K210_PCF_DEBUG4                228 /* Debug function 4 */
+#define K210_PCF_DEBUG5                229 /* Debug function 5 */
+#define K210_PCF_DEBUG6                230 /* Debug function 6 */
+#define K210_PCF_DEBUG7                231 /* Debug function 7 */
+#define K210_PCF_DEBUG8                232 /* Debug function 8 */
+#define K210_PCF_DEBUG9                233 /* Debug function 9 */
+#define K210_PCF_DEBUG10       234 /* Debug function 10 */
+#define K210_PCF_DEBUG11       235 /* Debug function 11 */
+#define K210_PCF_DEBUG12       236 /* Debug function 12 */
+#define K210_PCF_DEBUG13       237 /* Debug function 13 */
+#define K210_PCF_DEBUG14       238 /* Debug function 14 */
+#define K210_PCF_DEBUG15       239 /* Debug function 15 */
+#define K210_PCF_DEBUG16       240 /* Debug function 16 */
+#define K210_PCF_DEBUG17       241 /* Debug function 17 */
+#define K210_PCF_DEBUG18       242 /* Debug function 18 */
+#define K210_PCF_DEBUG19       243 /* Debug function 19 */
+#define K210_PCF_DEBUG20       244 /* Debug function 20 */
+#define K210_PCF_DEBUG21       245 /* Debug function 21 */
+#define K210_PCF_DEBUG22       246 /* Debug function 22 */
+#define K210_PCF_DEBUG23       247 /* Debug function 23 */
+#define K210_PCF_DEBUG24       248 /* Debug function 24 */
+#define K210_PCF_DEBUG25       249 /* Debug function 25 */
+#define K210_PCF_DEBUG26       250 /* Debug function 26 */
+#define K210_PCF_DEBUG27       251 /* Debug function 27 */
+#define K210_PCF_DEBUG28       252 /* Debug function 28 */
+#define K210_PCF_DEBUG29       253 /* Debug function 29 */
+#define K210_PCF_DEBUG30       254 /* Debug function 30 */
+#define K210_PCF_DEBUG31       255 /* Debug function 31 */
+
+#define K210_FPIOA(pin, func)          (((pin) << 16) | (func))
+
+#define K210_PC_POWER_3V3      0
+#define K210_PC_POWER_1V8      1
+
+#endif /* PINCTRL_K210_FPIOA_H */
diff --git a/include/dt-bindings/reset/k210-rst.h b/include/dt-bindings/reset/k210-rst.h
new file mode 100644 (file)
index 0000000..883c1ae
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#ifndef RESET_K210_SYSCTL_H
+#define RESET_K210_SYSCTL_H
+
+/*
+ * Kendryte K210 SoC system controller K210_SYSCTL_SOFT_RESET register bits.
+ * Taken from Kendryte SDK (kendryte-standalone-sdk).
+ */
+#define K210_RST_ROM   0
+#define K210_RST_DMA   1
+#define K210_RST_AI    2
+#define K210_RST_DVP   3
+#define K210_RST_FFT   4
+#define K210_RST_GPIO  5
+#define K210_RST_SPI0  6
+#define K210_RST_SPI1  7
+#define K210_RST_SPI2  8
+#define K210_RST_SPI3  9
+#define K210_RST_I2S0  10
+#define K210_RST_I2S1  11
+#define K210_RST_I2S2  12
+#define K210_RST_I2C0  13
+#define K210_RST_I2C1  14
+#define K210_RST_I2C2  15
+#define K210_RST_UART1 16
+#define K210_RST_UART2 17
+#define K210_RST_UART3 18
+#define K210_RST_AES   19
+#define K210_RST_FPIOA 20
+#define K210_RST_TIMER0        21
+#define K210_RST_TIMER1        22
+#define K210_RST_TIMER2        23
+#define K210_RST_WDT0  24
+#define K210_RST_WDT1  25
+#define K210_RST_SHA   26
+#define K210_RST_RTC   29
+
+#endif /* RESET_K210_SYSCTL_H */
index 8db6f8c8030b688f79178b5425d2dc2854a01eb4..85c15717af34f5ab600038b7f3189e2be5430136 100644 (file)
@@ -1,5 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
+#ifndef __LINUX_INITRD_H
+#define __LINUX_INITRD_H
+
 #define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */
 
 /* starting block # of image */
@@ -15,6 +18,12 @@ extern int initrd_below_start_ok;
 extern unsigned long initrd_start, initrd_end;
 extern void free_initrd_mem(unsigned long, unsigned long);
 
+#ifdef CONFIG_BLK_DEV_INITRD
+extern void __init reserve_initrd_mem(void);
+#else
+static inline void __init reserve_initrd_mem(void) {}
+#endif
+
 extern phys_addr_t phys_initrd_start;
 extern unsigned long phys_initrd_size;
 
@@ -24,3 +33,5 @@ extern char __initramfs_start[];
 extern unsigned long __initramfs_size;
 
 void console_on_rootfs(void);
+
+#endif /* __LINUX_INITRD_H */
diff --git a/include/soc/canaan/k210-sysctl.h b/include/soc/canaan/k210-sysctl.h
new file mode 100644 (file)
index 0000000..0c2b2c2
--- /dev/null
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#ifndef K210_SYSCTL_H
+#define K210_SYSCTL_H
+
+/*
+ * Kendryte K210 SoC system controller registers offsets.
+ * Taken from Kendryte SDK (kendryte-standalone-sdk).
+ */
+#define K210_SYSCTL_GIT_ID     0x00 /* Git short commit id */
+#define K210_SYSCTL_UART_BAUD  0x04 /* Default UARTHS baud rate */
+#define K210_SYSCTL_PLL0       0x08 /* PLL0 controller */
+#define K210_SYSCTL_PLL1       0x0C /* PLL1 controller */
+#define K210_SYSCTL_PLL2       0x10 /* PLL2 controller */
+#define K210_SYSCTL_PLL_LOCK   0x18 /* PLL lock tester */
+#define K210_SYSCTL_ROM_ERROR  0x1C /* AXI ROM detector */
+#define K210_SYSCTL_SEL0       0x20 /* Clock select controller 0 */
+#define K210_SYSCTL_SEL1       0x24 /* Clock select controller 1 */
+#define K210_SYSCTL_EN_CENT    0x28 /* Central clock enable */
+#define K210_SYSCTL_EN_PERI    0x2C /* Peripheral clock enable */
+#define K210_SYSCTL_SOFT_RESET 0x30 /* Soft reset ctrl */
+#define K210_SYSCTL_PERI_RESET 0x34 /* Peripheral reset controller */
+#define K210_SYSCTL_THR0       0x38 /* Clock threshold controller 0 */
+#define K210_SYSCTL_THR1       0x3C /* Clock threshold controller 1 */
+#define K210_SYSCTL_THR2       0x40 /* Clock threshold controller 2 */
+#define K210_SYSCTL_THR3       0x44 /* Clock threshold controller 3 */
+#define K210_SYSCTL_THR4       0x48 /* Clock threshold controller 4 */
+#define K210_SYSCTL_THR5       0x4C /* Clock threshold controller 5 */
+#define K210_SYSCTL_THR6       0x50 /* Clock threshold controller 6 */
+#define K210_SYSCTL_MISC       0x54 /* Miscellaneous controller */
+#define K210_SYSCTL_PERI       0x58 /* Peripheral controller */
+#define K210_SYSCTL_SPI_SLEEP  0x5C /* SPI sleep controller */
+#define K210_SYSCTL_RESET_STAT 0x60 /* Reset source status */
+#define K210_SYSCTL_DMA_SEL0   0x64 /* DMA handshake selector 0 */
+#define K210_SYSCTL_DMA_SEL1   0x68 /* DMA handshake selector 1 */
+#define K210_SYSCTL_POWER_SEL  0x6C /* IO Power Mode Select controller */
+
+void k210_clk_early_init(void __iomem *regs);
+
+#endif
index 5fa84711127a851da8b1b02c92314dc666b30913..d677e8e717f1c3499a8d63163cb8c1a0c37b033a 100644 (file)
@@ -546,6 +546,51 @@ extern unsigned long __initramfs_size;
 #include <linux/initrd.h>
 #include <linux/kexec.h>
 
+void __init reserve_initrd_mem(void)
+{
+       phys_addr_t start;
+       unsigned long size;
+
+       /* Ignore the virtul address computed during device tree parsing */
+       initrd_start = initrd_end = 0;
+
+       if (!phys_initrd_size)
+               return;
+       /*
+        * Round the memory region to page boundaries as per free_initrd_mem()
+        * This allows us to detect whether the pages overlapping the initrd
+        * are in use, but more importantly, reserves the entire set of pages
+        * as we don't want these pages allocated for other purposes.
+        */
+       start = round_down(phys_initrd_start, PAGE_SIZE);
+       size = phys_initrd_size + (phys_initrd_start - start);
+       size = round_up(size, PAGE_SIZE);
+
+       if (!memblock_is_region_memory(start, size)) {
+               pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region",
+                      (u64)start, size);
+               goto disable;
+       }
+
+       if (memblock_is_region_reserved(start, size)) {
+               pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region\n",
+                      (u64)start, size);
+               goto disable;
+       }
+
+       memblock_reserve(start, size);
+       /* Now convert initrd to virtual addresses */
+       initrd_start = (unsigned long)__va(phys_initrd_start);
+       initrd_end = initrd_start + phys_initrd_size;
+       initrd_below_start_ok = 1;
+
+       return;
+disable:
+       pr_cont(" - disabling initrd\n");
+       initrd_start = 0;
+       initrd_end = 0;
+}
+
 void __weak __init free_initrd_mem(unsigned long start, unsigned long end)
 {
 #ifdef CONFIG_ARCH_KEEP_MEMBLOCK