Commit 8b83369d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'riscv-for-linus-5.12-mw0' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux

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
  ...
parents 8f47d753 d7fbcf40
......@@ -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:
......
......@@ -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:
......
# 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>;
};
};
# 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)>;
};
};
......@@ -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.
......
# 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>;
};
# 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
...
......@@ -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:
......
......@@ -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:
......
......@@ -17,12 +17,19 @@ properties:
$nodename:
const: '/'
compatible:
items:
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
...
......@@ -20,6 +20,7 @@ properties:
- enum:
- sifive,fu540-c000-uart
- sifive,fu740-c000-uart
- canaan,k210-uarths
- const: sifive,uart0
description:
......
......@@ -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:
......
......@@ -24,6 +24,9 @@ properties:
interrupts:
maxItems: 1
resets:
maxItems: 1
clocks:
minItems: 1
items:
......
......@@ -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)
......
......@@ -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
......
......@@ -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 */
......@@ -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;
}
......@@ -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
......
......@@ -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.
*/
......
......@@ -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"
......
......@@ -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
......@@ -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
......
# 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))
# 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
// 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;
};
};
// 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>;
};
};
};
};
// 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";
};
// 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;
};
};
// 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;
};
};
// 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;
};
};
// 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;
};
};
# 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))
// 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";
};
// 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>;
};
};
};
# 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
// 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";
};
};
};
// 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";
};
# 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
......
# 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
......@@ -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);
......
......@@ -41,10 +41,16 @@
#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 */
......
......@@ -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)))
......
......@@ -11,4 +11,44 @@
#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 */
......@@ -12,6 +12,8 @@
typedef struct {
#ifndef CONFIG_MMU
unsigned long end_brk;
#else
atomic_long_t id;
#endif
void *vdso;
#ifdef CONFIG_SMP
......
......@@ -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 */
/* 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 */
/* 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 */
......@@ -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)
......
......@@ -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 */
......@@ -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
......
/* 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 */
......@@ -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 { \
......
......@@ -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 */
......@@ -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 */
......@@ -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
......
......@@ -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
......@@ -24,6 +24,7 @@ static __always_inline void boot_init_stack_canary(void)
canary &= CANARY_MASK;
current->stack_canary = canary;
if (!IS_ENABLED(CONFIG_STACKPROTECTOR_PER_TASK))
__stack_chk_guard = current->stack_canary;
}
#endif /* _ASM_RISCV_STACKPROTECTOR_H */
......@@ -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 */
......@@ -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 | \
......
/* 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 */
......@@ -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
......
......@@ -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);
......
......@@ -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 */
......
......@@ -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
......
......@@ -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
......
......@@ -13,224 +13,186 @@
.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
......
......@@ -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)
{
......
# 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)
// 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;
}
/* 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 */
// 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;
}
// 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;
}
/* 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)
// 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;
}
/* 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 */
// 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);
}
......@@ -18,13 +18,14 @@
#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)
......
......@@ -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);
......
......@@ -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,11 +374,11 @@ 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,
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I,
hart_mask, 0, 0, 0, 0);
}
EXPORT_SYMBOL(sbi_remote_fence_i);
......@@ -390,13 +390,13 @@ 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,
return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
hart_mask, start, size, 0, 0);
}
EXPORT_SYMBOL(sbi_remote_sfence_vma);
......@@ -410,14 +410,14 @@ 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,
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;
}
......@@ -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,8 +259,8 @@ 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();
if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
}
......@@ -53,10 +53,16 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
/* Unwind stack frame */
frame = (struct stackframe *)fp - 1;
sp = fp;
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));
}
}
}
#else /* !CONFIG_FRAME_POINTER */
......@@ -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)
......
......@@ -12,10 +12,12 @@
#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
......
......@@ -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
......
......@@ -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
// 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);
......@@ -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
......
......@@ -2,13 +2,273 @@
/*
* 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);
}
......@@ -13,14 +13,30 @@
#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 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
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 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
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 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
mmap_read_unlock(mm);
if (unlikely(fault & VM_FAULT_ERROR)) {
tsk->thread.bad_cause = cause;
mm_fault_error(regs, addr, fault);
return;
}
......
......@@ -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
*/
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
......
......@@ -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)
{
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));
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);
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)));
set_pmd(pmd, pfn_pmd(PFN_DOWN(__pa(base_pte)), 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)));
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 vaddr = (unsigned long)start & PAGE_MASK;
unsigned long vend = PAGE_ALIGN((unsigned long)end);
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;
}
......@@ -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
......@@ -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/
......
......@@ -13,7 +13,6 @@
#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;
......@@ -356,11 +355,12 @@ static int __init numa_register_nodes(void)
/* 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 %#010Lx-%#010Lx]\n",
mblk_nid, mblk->base,
mblk->base + mblk->size - 1);
pr_warn("Warning: invalid memblk node %d [mem %pap-%pap]\n",
mblk_nid, &start, &end);
return -EINVAL;
}
}
......@@ -428,14 +428,14 @@ static int __init numa_init(int (*init_func)(void))
static int __init dummy_numa_init(void)
{
phys_addr_t start = memblock_start_of_DRAM();
phys_addr_t end = memblock_end_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 %#018Lx-%#018Lx]\n", start, end - 1);
pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end);
ret = numa_add_memblk(0, start, end);
ret = numa_add_memblk(0, start, end + 1);
if (ret) {
pr_err("NUMA init failed\n");
return ret;
......@@ -445,16 +445,36 @@ static int __init dummy_numa_init(void)
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
/**
* arm64_numa_init() - Initialize NUMA
* 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 arm64_numa_init(void)
void __init arch_numa_init(void)
{
if (!numa_off) {
if (!acpi_disabled && !numa_init(arm64_acpi_numa_init))
if (!acpi_disabled && !numa_init(arch_acpi_numa_init))
return;
if (acpi_disabled && !numa_init(of_numa_init))
return;
......
......@@ -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"
......
......@@ -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
......
// 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);
}
......@@ -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"
......
......@@ -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/
......
// 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);
......@@ -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
......
......@@ -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
......
// 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);
......@@ -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
......@@ -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/
# 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.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_K210_SYSCTL) += k210-sysctl.o
obj-$(CONFIG_SOC_K210_SYSCTL) += k210-sysctl.o
// 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);
# 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
// 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
......@@ -17,6 +17,10 @@
#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) {
......
/* 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 */
......@@ -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
......
/* 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 */
/* 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 */
/* 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 */
/* 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
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment