summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2021-05-17 16:23:50 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2021-05-17 16:23:50 +0200
commitc5e0e697de769d0e78a00b1cb47fe864fade9974 (patch)
tree1a9177fe4fb08156bee02cdd2df0dbed61f2a12a
parentec8f2fe6b1bc9e69a725863a61a6b84ab7a91989 (diff)
parentb383d7739c355fee9f81986e6e8d030185373ca5 (diff)
downloadbarebox-c5e0e697de769d0e78a00b1cb47fe864fade9974.tar.gz
barebox-c5e0e697de769d0e78a00b1cb47fe864fade9974.tar.xz
Merge branch 'for-next/riscv'
-rw-r--r--arch/arm/lib64/armlinux.c74
-rw-r--r--arch/riscv/Kconfig31
-rw-r--r--arch/riscv/Kconfig.socs43
-rw-r--r--arch/riscv/Makefile14
-rw-r--r--arch/riscv/boards/Makefile1
-rw-r--r--arch/riscv/boards/erizo/lowlevel.c2
-rw-r--r--arch/riscv/boards/hifive/Makefile3
-rw-r--r--arch/riscv/boards/hifive/lowlevel.c25
-rw-r--r--arch/riscv/boot/board-dt-2nd.c27
-rw-r--r--arch/riscv/configs/erizo_generic_defconfig1
-rw-r--r--arch/riscv/configs/sifive_defconfig128
-rw-r--r--arch/riscv/configs/virt32_defconfig2
-rw-r--r--arch/riscv/configs/virt64_defconfig2
-rw-r--r--arch/riscv/dts/Makefile2
-rw-r--r--arch/riscv/dts/hifive-unleashed-a00.dts3
-rw-r--r--arch/riscv/dts/hifive-unmatched-a00.dts3
-rw-r--r--arch/riscv/include/asm/barebox-riscv-head.h2
-rw-r--r--arch/riscv/include/asm/debug_ll.h (renamed from arch/riscv/mach-erizo/include/mach/debug_ll.h)36
-rw-r--r--arch/riscv/include/asm/debug_ll_ns16550.h51
-rw-r--r--arch/riscv/include/asm/ns16550.h48
-rw-r--r--arch/riscv/include/asm/sbi.h32
-rw-r--r--arch/riscv/lib/Makefile3
-rw-r--r--arch/riscv/lib/bootm.c51
-rw-r--r--arch/riscv/lib/cpuinfo.c45
-rw-r--r--arch/riscv/lib/sbi.c62
-rw-r--r--arch/riscv/mach-erizo/Kconfig11
-rw-r--r--arch/riscv/mach-erizo/Makefile3
-rw-r--r--arch/riscv/mach-virt/Makefile3
-rw-r--r--arch/riscv/mach-virt/include/mach/debug_ll.h25
-rw-r--r--commands/Kconfig7
-rw-r--r--common/Kconfig13
-rw-r--r--common/Makefile2
-rw-r--r--common/booti.c66
-rw-r--r--drivers/clk/Kconfig2
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/analogbits/Makefile3
-rw-r--r--drivers/clk/analogbits/wrpll-cln28hpc.c367
-rw-r--r--drivers/clk/sifive/Kconfig18
-rw-r--r--drivers/clk/sifive/Makefile2
-rw-r--r--drivers/clk/sifive/fu540-prci.c87
-rw-r--r--drivers/clk/sifive/fu540-prci.h16
-rw-r--r--drivers/clk/sifive/fu740-prci.c121
-rw-r--r--drivers/clk/sifive/fu740-prci.h21
-rw-r--r--drivers/clk/sifive/sifive-prci.c581
-rw-r--r--drivers/clk/sifive/sifive-prci.h298
-rw-r--r--drivers/gpio/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-generic.c445
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c6
-rw-r--r--drivers/gpio/gpio-sifive.c87
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_sifive.c171
-rw-r--r--images/Makefile.riscv5
-rw-r--r--include/bootm.h2
-rw-r--r--include/debug_ll.h4
-rw-r--r--include/io.h2
-rw-r--r--include/linux/basic_mmio_gpio.h14
-rw-r--r--include/linux/clk/analogbits-wrpll-cln28hpc.h79
-rw-r--r--include/pbl.h9
-rw-r--r--pbl/fdt.c35
-rw-r--r--pbl/string.c12
62 files changed, 2886 insertions, 342 deletions
diff --git a/arch/arm/lib64/armlinux.c b/arch/arm/lib64/armlinux.c
index a5f122edcd..0ba4d30b8e 100644
--- a/arch/arm/lib64/armlinux.c
+++ b/arch/arm/lib64/armlinux.c
@@ -1,90 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: 2018 Sascha Hauer <s.hauer@pengutronix.de>
-#include <boot.h>
#include <common.h>
-#include <environment.h>
-#include <image.h>
-#include <fs.h>
-#include <xfuncs.h>
-#include <malloc.h>
-#include <fcntl.h>
-#include <errno.h>
#include <memory.h>
-#include <of.h>
#include <init.h>
#include <bootm.h>
-#include <linux/list.h>
-#include <asm/byteorder.h>
-#include <asm/setup.h>
-#include <asm/barebox-arm.h>
-#include <asm/armlinux.h>
-#include <asm/system.h>
static int do_bootm_linux(struct image_data *data)
{
- const void *kernel_header =
- data->os_fit ? data->fit_kernel : data->os_header;
void (*fn)(unsigned long dtb, unsigned long x1, unsigned long x2,
unsigned long x3);
- resource_size_t start, end;
- unsigned long text_offset, image_size, devicetree, kernel;
- unsigned long image_end;
- int ret;
- void *fdt;
-
- text_offset = le64_to_cpup(kernel_header + 8);
- image_size = le64_to_cpup(kernel_header+ 16);
-
- ret = memory_bank_first_find_space(&start, &end);
- if (ret)
- goto out;
-
- kernel = ALIGN(start, SZ_2M) + text_offset;
-
- ret = bootm_load_os(data, kernel);
- if (ret)
- goto out;
-
- image_end = PAGE_ALIGN(kernel + image_size);
-
- if (bootm_has_initrd(data)) {
- ret = bootm_load_initrd(data, image_end);
- if (ret)
- return ret;
-
- image_end += resource_size(data->initrd_res);
- image_end = PAGE_ALIGN(image_end);
- }
+ phys_addr_t devicetree;
- devicetree = image_end;
-
- fdt = bootm_get_devicetree(data);
- if (IS_ERR(fdt)) {
- ret = PTR_ERR(fdt);
- goto out;
- }
-
- ret = bootm_load_devicetree(data, fdt, devicetree);
-
- free(fdt);
-
- if (ret)
- goto out;
-
- printf("Loaded kernel to 0x%08lx, devicetree at 0x%08lx\n",
- kernel, devicetree);
+ fn = booti_load_image(data, &devicetree);
+ if (IS_ERR(fn))
+ return PTR_ERR(fn);
shutdown_barebox();
- fn = (void *)kernel;
-
fn(devicetree, 0, 0, 0);
- ret = -EINVAL;
-
-out:
- return ret;
+ return -EINVAL;
}
static struct image_handler aarch64_linux_handler = {
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index c0583f3153..a4aa799acf 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -19,31 +19,6 @@ config ARCH_TEXT_BASE
hex
default 0x0
-menu "Machine selection"
-
-choice
- prompt "System type"
- default MACH_ERIZO
-
-config MACH_ERIZO
- bool "erizo family"
- select ARCH_RV32I
- select HAS_DEBUG_LL
- select HAS_NMON
- select USE_COMPRESSED_DTB
- select RISCV_M_MODE
- select RISCV_TIMER
-
-config MACH_VIRT
- bool "virt family"
- select BOARD_RISCV_GENERIC_DT
- select CLINT_TIMER
- help
- Generates an image tht can be be booted by QEMU. The image is called
- barebox-dt-2nd.img
-
-endchoice
-
choice
prompt "Base ISA"
default ARCH_RV32I
@@ -63,6 +38,8 @@ config ARCH_RV64I
endchoice
+source "arch/riscv/Kconfig.socs"
+
config CPU_SUPPORTS_32BIT_KERNEL
bool
@@ -82,8 +59,6 @@ config 64BIT
select ARCH_DMA_ADDR_T_64BIT
select PHYS_ADDR_T_64BIT
-source "arch/riscv/mach-erizo/Kconfig"
-
config BOARD_RISCV_GENERIC_DT
select BOARD_GENERIC_DT
bool "Build generic RISC-V device tree 2nd stage image"
@@ -93,8 +68,6 @@ config BOARD_RISCV_GENERIC_DT
in a1 like the Kernel does, so it could be used anywhere where a Kernel
image could be used. The image will be called images/barebox-dt-2nd.img
-endmenu
-
menu "RISC-V specific settings"
config RISCV_OPTIMZED_STRING_FUNCTIONS
diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
new file mode 100644
index 0000000000..c6875738d0
--- /dev/null
+++ b/arch/riscv/Kconfig.socs
@@ -0,0 +1,43 @@
+menu "SoC selection"
+
+config SOC_ERIZO
+ bool "Erizo SoC"
+ depends on ARCH_RV32I
+ select HAS_ASM_DEBUG_LL
+ select HAS_NMON
+ select USE_COMPRESSED_DTB
+ select RISCV_M_MODE
+ select RISCV_TIMER
+
+config BOARD_ERIZO_GENERIC
+ depends on SOC_ERIZO
+ def_bool y
+
+config SOC_VIRT
+ bool "QEMU Virt Machine"
+ select BOARD_RISCV_GENERIC_DT
+ select CLINT_TIMER
+ help
+ Generates an image tht can be be booted by QEMU. The image is called
+ barebox-dt-2nd.img
+
+config SOC_SIFIVE
+ bool "SiFive SoCs"
+ select CLK_SIFIVE
+ select CLK_SIFIVE_PRCI
+ select RISCV_TIMER
+ select HAS_MACB
+ select HAS_ASM_DEBUG_LL
+ help
+ This enables support for SiFive SoC platform hardware.
+
+if SOC_SIFIVE
+
+config BOARD_HIFIVE
+ bool "HiFive"
+ depends on ARCH_RV64I
+ select USE_COMPRESSED_DTB
+
+endif
+
+endmenu
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index aba4526bba..0b1278936d 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -19,9 +19,6 @@ LDFLAGS_pbl += $(riscv-ldflags-y)
cflags-y += $(riscv-cflags-y)
LDFLAGS_barebox += -nostdlib
-machine-$(CONFIG_MACH_ERIZO) := erizo
-machine-$(CONFIG_MACH_VIRT) := virt
-
LDFLAGS_barebox += $(riscv-ldflags-y)
ifndef CONFIG_MODULES
@@ -32,21 +29,10 @@ endif
KBUILD_BINARY := barebox.bin
-machdirs := $(patsubst %,arch/riscv/mach-%/,$(machine-y))
-
-KBUILD_CPPFLAGS += $(patsubst %,-I$(srctree)/%include,$(machdirs))
-
archprepare: maketools
PHONY += maketools
-ifneq ($(machine-y),)
-MACH := arch/riscv/mach-$(machine-y)/
-else
-MACH :=
-endif
-
-common-y += $(MACH)
common-y += arch/riscv/boards/
common-y += arch/riscv/cpu/
common-y += arch/riscv/lib/
diff --git a/arch/riscv/boards/Makefile b/arch/riscv/boards/Makefile
index 2ce9af41e0..99f22f32b4 100644
--- a/arch/riscv/boards/Makefile
+++ b/arch/riscv/boards/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_BOARD_ERIZO_GENERIC) += erizo/
+obj-$(CONFIG_BOARD_HIFIVE) += hifive/
diff --git a/arch/riscv/boards/erizo/lowlevel.c b/arch/riscv/boards/erizo/lowlevel.c
index d9edb530b7..6acf15931c 100644
--- a/arch/riscv/boards/erizo/lowlevel.c
+++ b/arch/riscv/boards/erizo/lowlevel.c
@@ -8,7 +8,7 @@ ENTRY_FUNCTION(start_erizo_generic, a0, a1, a2)
{
extern char __dtb_z_erizo_generic_start[];
- debug_ll_ns16550_init();
+ debug_ll_init();
putc_ll('>');
/* On POR, we are running from read-only memory here. */
diff --git a/arch/riscv/boards/hifive/Makefile b/arch/riscv/boards/hifive/Makefile
new file mode 100644
index 0000000000..3d217ffe0b
--- /dev/null
+++ b/arch/riscv/boards/hifive/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+pbl-y += lowlevel.o
diff --git a/arch/riscv/boards/hifive/lowlevel.c b/arch/riscv/boards/hifive/lowlevel.c
new file mode 100644
index 0000000000..1de13cac16
--- /dev/null
+++ b/arch/riscv/boards/hifive/lowlevel.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <asm/barebox-riscv.h>
+#include <debug_ll.h>
+
+ENTRY_FUNCTION(start_hifive_unmatched, a0, a1, a2)
+{
+ extern char __dtb_z_hifive_unmatched_a00_start[];
+
+ putc_ll('>');
+
+ barebox_riscv_entry(0x80000000, SZ_128M,
+ __dtb_z_hifive_unmatched_a00_start + get_runtime_offset());
+}
+
+ENTRY_FUNCTION(start_hifive_unleashed, a0, a1, a2)
+{
+ extern char __dtb_z_hifive_unleashed_a00_start[];
+
+ putc_ll('>');
+
+ barebox_riscv_entry(0x80000000, SZ_128M,
+ __dtb_z_hifive_unleashed_a00_start + get_runtime_offset());
+}
diff --git a/arch/riscv/boot/board-dt-2nd.c b/arch/riscv/boot/board-dt-2nd.c
index be28ea23cd..e9810f8add 100644
--- a/arch/riscv/boot/board-dt-2nd.c
+++ b/arch/riscv/boot/board-dt-2nd.c
@@ -3,7 +3,7 @@
#include <common.h>
#include <asm/sections.h>
#include <linux/sizes.h>
-#include <debug_ll.h>
+#include <asm/ns16550.h>
#include <pbl.h>
#include <fdt.h>
@@ -22,10 +22,29 @@
#include <asm/barebox-riscv.h>
+static void virt_ns16550_putc(void *base, int ch)
+{
+ early_ns16550_putc(ch, base, 0, readb, writeb);
+}
+
+static void virt_ns16550_init(void)
+{
+ void __iomem *base = IOMEM(0x10000000);
+
+ early_ns16550_init(base, 3686400 / CONFIG_BAUDRATE, 0, writeb);
+ pbl_set_putc(virt_ns16550_putc, base);
+}
+
+static const struct fdt_device_id console_ids[] = {
+ { .compatible = "riscv-virtio", .data = virt_ns16550_init },
+ { /* sentinel */ }
+};
+
ENTRY_FUNCTION(start_dt_2nd, a0, _fdt, a2)
{
unsigned long membase, memsize, endmem, endfdt, uncompressed_len;
struct fdt_header *fdt = (void *)_fdt;
+ void (*pbl_uart_init)(void);
if (!fdt)
hang();
@@ -33,6 +52,12 @@ ENTRY_FUNCTION(start_dt_2nd, a0, _fdt, a2)
relocate_to_current_adr();
setup_c();
+ pbl_uart_init = fdt_device_get_match_data(fdt, "/", console_ids);
+ if (pbl_uart_init) {
+ pbl_uart_init();
+ putchar('>');
+ }
+
fdt_find_mem(fdt, &membase, &memsize);
endmem = membase + memsize;
endfdt = _fdt + be32_to_cpu(fdt->totalsize);
diff --git a/arch/riscv/configs/erizo_generic_defconfig b/arch/riscv/configs/erizo_generic_defconfig
index 839b652ab5..247a179130 100644
--- a/arch/riscv/configs/erizo_generic_defconfig
+++ b/arch/riscv/configs/erizo_generic_defconfig
@@ -1,3 +1,4 @@
+CONFIG_SOC_ERIZO=y
# CONFIG_GLOBALVAR is not set
CONFIG_STACK_SIZE=0x20000
CONFIG_MALLOC_SIZE=0x100000
diff --git a/arch/riscv/configs/sifive_defconfig b/arch/riscv/configs/sifive_defconfig
new file mode 100644
index 0000000000..59cfebf194
--- /dev/null
+++ b/arch/riscv/configs/sifive_defconfig
@@ -0,0 +1,128 @@
+CONFIG_ARCH_RV64I=y
+CONFIG_SOC_SIFIVE=y
+CONFIG_BOARD_HIFIVE=y
+CONFIG_BOARD_RISCV_GENERIC_DT=y
+CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS=y
+CONFIG_STACK_SIZE=0x20000
+CONFIG_MALLOC_SIZE=0x0
+CONFIG_MALLOC_TLSF=y
+CONFIG_KALLSYMS=y
+CONFIG_RELOCATABLE=y
+CONFIG_PANIC_HANG=y
+CONFIG_HUSH_FANCY_PROMPT=y
+CONFIG_CMDLINE_EDITING=y
+CONFIG_AUTO_COMPLETE=y
+CONFIG_MENU=y
+CONFIG_IMD_TARGET=y
+CONFIG_CONSOLE_ALLOW_COLOR=y
+CONFIG_PBL_CONSOLE=y
+CONFIG_PARTITION_DISK_EFI=y
+CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
+CONFIG_BAREBOXENV_TARGET=y
+CONFIG_BAREBOXCRC32_TARGET=y
+CONFIG_STATE=y
+CONFIG_STATE_CRYPTO=y
+CONFIG_BOOTCHOOSER=y
+CONFIG_RESET_SOURCE=y
+CONFIG_MACHINE_ID=y
+CONFIG_CMD_DMESG=y
+CONFIG_LONGHELP=y
+CONFIG_CMD_IOMEM=y
+CONFIG_CMD_IMD=y
+CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_POLLER=y
+CONFIG_CMD_SLICE=y
+CONFIG_CMD_GO=y
+CONFIG_CMD_LOADY=y
+CONFIG_CMD_RESET=y
+CONFIG_CMD_BOOTCHOOSER=y
+CONFIG_CMD_EXPORT=y
+CONFIG_CMD_PRINTENV=y
+CONFIG_CMD_MAGICVAR=y
+CONFIG_CMD_MAGICVAR_HELP=y
+CONFIG_CMD_SAVEENV=y
+CONFIG_CMD_CMP=y
+CONFIG_CMD_FILETYPE=y
+CONFIG_CMD_LN=y
+CONFIG_CMD_MD5SUM=y
+CONFIG_CMD_SHA1SUM=y
+CONFIG_CMD_SHA256SUM=y
+CONFIG_CMD_MSLEEP=y
+CONFIG_CMD_SLEEP=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MIITOOL=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_EDIT=y
+CONFIG_CMD_SPLASH=y
+CONFIG_CMD_FBTEST=y
+CONFIG_CMD_READLINE=y
+CONFIG_CMD_TIMEOUT=y
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_MM=y
+CONFIG_CMD_CLK=y
+CONFIG_CMD_DETECT=y
+CONFIG_CMD_FLASH=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_POWEROFF=y
+CONFIG_CMD_SPI=y
+CONFIG_CMD_2048=y
+CONFIG_CMD_BAREBOX_UPDATE=y
+CONFIG_CMD_OF_DIFF=y
+CONFIG_CMD_OF_NODE=y
+CONFIG_CMD_OF_PROPERTY=y
+CONFIG_CMD_OF_DISPLAY_TIMINGS=y
+CONFIG_CMD_OF_FIXUP_STATUS=y
+CONFIG_CMD_OF_OVERLAY=y
+CONFIG_CMD_OFTREE=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_DHRYSTONE=y
+CONFIG_NET=y
+CONFIG_NET_NFS=y
+CONFIG_NET_FASTBOOT=y
+CONFIG_DRIVER_SERIAL_NS16550=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_SERIAL_SIFIVE=y
+CONFIG_DRIVER_NET_MACB=y
+CONFIG_DRIVER_SPI_GPIO=y
+CONFIG_I2C=y
+CONFIG_I2C_GPIO=y
+CONFIG_MTD=y
+# CONFIG_MTD_OOB_DEVICE is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_M25P80=y
+CONFIG_DRIVER_CFI=y
+CONFIG_DRIVER_CFI_BANK_WIDTH_8=y
+CONFIG_DISK=y
+CONFIG_DISK_WRITE=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIDEO=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_DRIVER_VIDEO_SIMPLEFB_CLIENT=y
+CONFIG_CLOCKSOURCE_DUMMY_RATE=60000
+CONFIG_EEPROM_AT24=y
+CONFIG_HWRNG=y
+CONFIG_HW_RANDOM_VIRTIO=y
+CONFIG_GPIO_SIFIVE=y
+# CONFIG_PINCTRL is not set
+CONFIG_SYSCON_REBOOT_MODE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_POWER_RESET_SYSCON_POWEROFF=y
+CONFIG_POWER_RESET_GPIO_RESTART=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_FS_EXT4=y
+CONFIG_FS_TFTP=y
+CONFIG_FS_NFS=y
+CONFIG_FS_FAT=y
+CONFIG_FS_FAT_WRITE=y
+CONFIG_FS_FAT_LFN=y
+CONFIG_FS_UIMAGEFS=y
+CONFIG_FS_PSTORE=y
+CONFIG_FS_SQUASHFS=y
+CONFIG_ZLIB=y
+CONFIG_BZLIB=y
+CONFIG_LZ4_DECOMPRESS=y
+CONFIG_ZSTD_DECOMPRESS=y
+CONFIG_XZ_DECOMPRESS=y
+CONFIG_BASE64=y
+CONFIG_DIGEST_CRC32_GENERIC=y
diff --git a/arch/riscv/configs/virt32_defconfig b/arch/riscv/configs/virt32_defconfig
index 83e3ca1bad..218fee57b7 100644
--- a/arch/riscv/configs/virt32_defconfig
+++ b/arch/riscv/configs/virt32_defconfig
@@ -1,4 +1,4 @@
-CONFIG_MACH_VIRT=y
+CONFIG_SOC_VIRT=y
CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS=y
CONFIG_STACK_SIZE=0x20000
CONFIG_MALLOC_SIZE=0x0
diff --git a/arch/riscv/configs/virt64_defconfig b/arch/riscv/configs/virt64_defconfig
index 17ce16637d..04a4f1e2f4 100644
--- a/arch/riscv/configs/virt64_defconfig
+++ b/arch/riscv/configs/virt64_defconfig
@@ -1,4 +1,4 @@
-CONFIG_MACH_VIRT=y
+CONFIG_SOC_VIRT=y
CONFIG_ARCH_RV64I=y
CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS=y
CONFIG_STACK_SIZE=0x20000
diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile
index 4041c34e0c..17fdc9445b 100644
--- a/arch/riscv/dts/Makefile
+++ b/arch/riscv/dts/Makefile
@@ -5,5 +5,7 @@
obj- += dummy.o
pbl-$(CONFIG_BOARD_ERIZO_GENERIC) += erizo-generic.dtb.o
+pbl-$(CONFIG_BOARD_HIFIVE) += hifive-unmatched-a00.dtb.o \
+ hifive-unleashed-a00.dtb.o
clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts
diff --git a/arch/riscv/dts/hifive-unleashed-a00.dts b/arch/riscv/dts/hifive-unleashed-a00.dts
new file mode 100644
index 0000000000..65694bfd24
--- /dev/null
+++ b/arch/riscv/dts/hifive-unleashed-a00.dts
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0 OR X11 */
+
+#include <riscv/sifive/hifive-unleashed-a00.dts>
diff --git a/arch/riscv/dts/hifive-unmatched-a00.dts b/arch/riscv/dts/hifive-unmatched-a00.dts
new file mode 100644
index 0000000000..b8793e9105
--- /dev/null
+++ b/arch/riscv/dts/hifive-unmatched-a00.dts
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0 OR X11 */
+
+#include <riscv/sifive/hifive-unmatched-a00.dts>
diff --git a/arch/riscv/include/asm/barebox-riscv-head.h b/arch/riscv/include/asm/barebox-riscv-head.h
index f681ec8bce..a4c33472cd 100644
--- a/arch/riscv/include/asm/barebox-riscv-head.h
+++ b/arch/riscv/include/asm/barebox-riscv-head.h
@@ -30,7 +30,7 @@
#ifndef __barebox_riscv_head
#define __barebox_riscv_head() \
- __barebox_riscv_header("nop", 0x55555555FFFFFFFF, 0x0, "barebox", "RSCV")
+ __barebox_riscv_header("nop", 0x0, 0x0, "barebox", "RSCV")
#endif
#endif /* __ASM_RISCV_HEAD_H */
diff --git a/arch/riscv/mach-erizo/include/mach/debug_ll.h b/arch/riscv/include/asm/debug_ll.h
index a20acfcdfb..6904460af9 100644
--- a/arch/riscv/mach-erizo/include/mach/debug_ll.h
+++ b/arch/riscv/include/asm/debug_ll.h
@@ -1,18 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2017 Antony Pavlov <antonynpavlov@gmail.com>
- *
- * This file is part of barebox.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
-#ifndef __MACH_ERIZO_DEBUG_LL__
-#define __MACH_ERIZO_DEBUG_LL__
+#ifndef __ASM_DEBUG_LL__
+#define __ASM_DEBUG_LL__
/** @file
* This File contains declaration for early output support
@@ -20,6 +12,8 @@
#include <linux/kconfig.h>
+#ifdef CONFIG_DEBUG_ERIZO
+
#define DEBUG_LL_UART_ADDR 0x90000000
#define DEBUG_LL_UART_SHIFT 2
#define DEBUG_LL_UART_IOSIZE32
@@ -30,4 +24,24 @@
#include <asm/debug_ll_ns16550.h>
-#endif /* __MACH_ERIZO_DEBUG_LL__ */
+#elif defined CONFIG_DEBUG_SIFIVE
+
+#include <io.h>
+
+static inline void PUTC_LL(char ch)
+{
+ void __iomem *uart0 = IOMEM(0x10010000);
+
+ while (readl(uart0) & 0x80000000)
+ ;
+
+ writel(ch, uart0);
+}
+
+#endif
+
+#ifndef debug_ll_init
+#define debug_ll_init() (void)0
+#endif
+
+#endif /* __ASM_DEBUG_LL__ */
diff --git a/arch/riscv/include/asm/debug_ll_ns16550.h b/arch/riscv/include/asm/debug_ll_ns16550.h
index 7d6d12df74..e208ef4fb1 100644
--- a/arch/riscv/include/asm/debug_ll_ns16550.h
+++ b/arch/riscv/include/asm/debug_ll_ns16550.h
@@ -35,19 +35,6 @@
#endif /* CONFIG_DEBUG_LL */
-#define UART_THR (0x0 << DEBUG_LL_UART_SHIFT)
-#define UART_RBR (0x0 << DEBUG_LL_UART_SHIFT)
-#define UART_DLL (0x0 << DEBUG_LL_UART_SHIFT)
-#define UART_DLM (0x1 << DEBUG_LL_UART_SHIFT)
-#define UART_LCR (0x3 << DEBUG_LL_UART_SHIFT)
-#define UART_LSR (0x5 << DEBUG_LL_UART_SHIFT)
-
-#define UART_LCR_W 0x07 /* Set UART to 8,N,2 & DLAB = 0 */
-#define UART_LCR_DLAB 0x87 /* Set UART to 8,N,2 & DLAB = 1 */
-
-#define UART_LSR_DR 0x01 /* UART received data present */
-#define UART_LSR_THRE 0x20 /* Xmit holding register empty */
-
#if defined(DEBUG_LL_UART_IOSIZE32)
#define UART_REG_L lw
#define UART_REG_S sw
@@ -62,31 +49,29 @@
#error "Please define DEBUG_LL_UART_IOSIZE{8,32}"
#endif
+#include <asm/ns16550.h>
+
#ifndef __ASSEMBLY__
/*
* C macros
*/
-#include <asm/io.h>
-
static inline void PUTC_LL(char ch)
{
#ifdef CONFIG_DEBUG_LL
- while (!(__uart_read((u8 *)DEBUG_LL_UART_ADDR + UART_LSR) & UART_LSR_THRE))
- ;
- __uart_write(ch, (u8 *)DEBUG_LL_UART_ADDR + UART_THR);
-#endif /* CONFIG_DEBUG_LL */
+ early_ns16550_putc(ch, DEBUG_LL_UART_ADDR, DEBUG_LL_UART_SHIFT,
+ __uart_read, __uart_write);
+#endif
}
static inline void debug_ll_ns16550_init(void)
{
#ifdef CONFIG_DEBUG_LL
- __uart_write(UART_LCR_DLAB, (u8 *)DEBUG_LL_UART_ADDR + UART_LCR);
- __uart_write(DEBUG_LL_UART_DIVISOR & 0xff, (u8 *)DEBUG_LL_UART_ADDR + UART_DLL);
- __uart_write((DEBUG_LL_UART_DIVISOR >> 8) & 0xff, (u8 *)DEBUG_LL_UART_ADDR + UART_DLM);
- __uart_write(UART_LCR_W, (u8 *)DEBUG_LL_UART_ADDR + UART_LCR);
-#endif /* CONFIG_DEBUG_LL */
+ early_ns16550_init(DEBUG_LL_UART_ADDR, DEBUG_LL_UART_DIVISOR,
+ DEBUG_LL_UART_SHIFT, __uart_write);
+#endif
}
+
#else /* __ASSEMBLY__ */
/*
* Macros for use in assembly language code
@@ -97,15 +82,15 @@ static inline void debug_ll_ns16550_init(void)
li t0, DEBUG_LL_UART_ADDR
li t1, UART_LCR_DLAB /* DLAB on */
- UART_REG_S t1, UART_LCR(t0) /* Write it out */
+ UART_REG_S t1, UART_LCR(DEBUG_LL_UART_SHIFT)(t0) /* Write it out */
li t1, \divisor
- UART_REG_S t1, UART_DLL(t0) /* write low order byte */
+ UART_REG_S t1, UART_DLL(DEBUG_LL_UART_SHIFT)(t0) /* write low order byte */
srl t1, t1, 8
- UART_REG_S t1, UART_DLM(t0) /* write high order byte */
+ UART_REG_S t1, UART_DLM(DEBUG_LL_UART_SHIFT)(t0) /* write high order byte */
li t1, UART_LCR_W /* DLAB off */
- UART_REG_S t1, UART_LCR(t0) /* Write it out */
+ UART_REG_S t1, UART_LCR(DEBUG_LL_UART_SHIFT)(t0) /* Write it out */
#endif /* CONFIG_DEBUG_LL */
.endm
@@ -118,11 +103,11 @@ static inline void debug_ll_ns16550_init(void)
li t0, DEBUG_LL_UART_ADDR
201:
- UART_REG_L t1, UART_LSR(t0) /* get line status */
+ UART_REG_L t1, UART_LSR(DEBUG_LL_UART_SHIFT)(t0) /* get line status */
andi t1, t1, UART_LSR_THRE /* check for transmitter empty */
beqz t1, 201b /* try again */
- UART_REG_S a0, UART_THR(t0) /* write the character */
+ UART_REG_S a0, UART_THR(DEBUG_LL_UART_SHIFT)(t0) /* write the character */
#endif /* CONFIG_DEBUG_LL */
.endm
@@ -158,7 +143,7 @@ static inline void debug_ll_ns16550_init(void)
li t0, DEBUG_LL_UART_ADDR
/* get line status and check for data present */
- UART_REG_L s0, UART_LSR(t0)
+ UART_REG_L s0, UART_LSR(DEBUG_LL_UART_SHIFT)(t0)
andi s0, s0, UART_LSR_DR
#endif /* CONFIG_DEBUG_LL */
@@ -177,10 +162,12 @@ static inline void debug_ll_ns16550_init(void)
beqz s0, 204b
/* read a character */
- UART_REG_L s0, UART_RBR(t0)
+ UART_REG_L s0, UART_RBR(DEBUG_LL_UART_SHIFT)(t0)
#endif /* CONFIG_DEBUG_LL */
.endm
#endif /* __ASSEMBLY__ */
+#define debug_ll_init debug_ll_ns16550_init
+
#endif /* __INCLUDE_RISCV_ASM_DEBUG_LL_NS16550_H__ */
diff --git a/arch/riscv/include/asm/ns16550.h b/arch/riscv/include/asm/ns16550.h
new file mode 100644
index 0000000000..7f56692b77
--- /dev/null
+++ b/arch/riscv/include/asm/ns16550.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016, 2017 Antony Pavlov <antonynpavlov@gmail.com>
+ */
+
+/** @file
+ * This file contains declaration for early output support
+ */
+#ifndef __ASM_NS16550_H__
+#define __ASM_NS16550_H__
+
+#include <linux/kconfig.h>
+
+#define UART_THR(shift) (0x0 << shift)
+#define UART_RBR(shift) (0x0 << shift)
+#define UART_DLL(shift) (0x0 << shift)
+#define UART_DLM(shift) (0x1 << shift)
+#define UART_LCR(shift) (0x3 << shift)
+#define UART_LSR(shift) (0x5 << shift)
+
+#define UART_LCR_W 0x07 /* Set UART to 8,N,2 & DLAB = 0 */
+#define UART_LCR_DLAB 0x87 /* Set UART to 8,N,2 & DLAB = 1 */
+
+#define UART_LSR_DR 0x01 /* UART received data present */
+#define UART_LSR_THRE 0x20 /* Xmit holding register empty */
+
+#ifndef __ASSEMBLY__
+
+#include <asm/io.h>
+
+#define early_ns16550_putc(ch, base, shift, readx, writex) \
+ do { \
+ while (!(readx((u8 *)base + UART_LSR(shift)) & UART_LSR_THRE)) \
+ ; \
+ writex(ch, (u8 *)base + UART_THR(shift)); \
+ } while (0)
+
+#define early_ns16550_init(base, divisor, shift, writex) \
+ do { \
+ writex(UART_LCR_DLAB, (u8 *)base + UART_LCR(shift)); \
+ writex(divisor & 0xff, (u8 *)base + UART_DLL(shift)); \
+ writex((divisor >> 8) & 0xff, (u8 *)base + UART_DLM(shift)); \
+ writex(UART_LCR_W, (u8 *)base + UART_LCR(shift)); \
+ } while (0)
+
+#endif
+
+#endif
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 99895d9c3b..ab1fc9a128 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -89,11 +89,32 @@ struct sbiret {
long value;
};
-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,
- unsigned long arg5);
+static inline struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5)
+{
+ struct sbiret ret;
+
+ register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
+ register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
+ register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
+ register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
+ register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
+ register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
+ register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
+ register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
+ asm volatile ("ecall"
+ : "+r" (a0), "+r" (a1)
+ : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
+ : "memory");
+ ret.error = a0;
+ ret.value = a1;
+
+ return ret;
+}
+
+long __sbi_base_ecall(int fid);
void sbi_console_putchar(int ch);
int sbi_console_getchar(void);
@@ -148,6 +169,5 @@ static inline unsigned long sbi_minor_version(void)
int sbi_err_map_linux_errno(int err);
#else /* CONFIG_RISCV_SBI */
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 */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index a4eaa1005d..a399de7399 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -6,3 +6,6 @@ obj-y += dtb.o
obj-pbl-y += sections.o setupc.o reloc.o sections.o runtime-offset.o
obj-$(CONFIG_HAS_ARCH_SJLJ) += setjmp.o longjmp.o
obj-$(CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS) += memcpy.o memset.o memmove.o
+obj-$(CONFIG_RISCV_SBI) += sbi.o
+obj-$(CONFIG_CMD_RISCV_CPUINFO) += cpuinfo.o
+obj-$(CONFIG_BOOTM) += bootm.o
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
new file mode 100644
index 0000000000..b3e41de4a8
--- /dev/null
+++ b/arch/riscv/lib/bootm.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2018 Sascha Hauer <s.hauer@pengutronix.de>
+
+#include <common.h>
+#include <bootm.h>
+
+static int do_bootm_linux(struct image_data *data)
+{
+ void (*fn)(unsigned long a0, unsigned long dtb, unsigned long a2);
+ phys_addr_t devicetree;
+
+ fn = booti_load_image(data, &devicetree);
+ if (IS_ERR(fn))
+ return PTR_ERR(fn);
+
+ shutdown_barebox();
+
+ fn(0, devicetree, 0);
+
+ return -EINVAL;
+}
+
+static struct image_handler riscv_linux_handler = {
+ .name = "RISC-V Linux image",
+ .bootm = do_bootm_linux,
+ .filetype = filetype_riscv_linux_image,
+};
+
+static struct image_handler riscv_fit_handler = {
+ .name = "FIT image",
+ .bootm = do_bootm_linux,
+ .filetype = filetype_oftree,
+};
+
+static struct image_handler riscv_barebox_handler = {
+ .name = "RISC-V barebox image",
+ .bootm = do_bootm_linux,
+ .filetype = filetype_riscv_barebox_image,
+};
+
+static int riscv_register_image_handler(void)
+{
+ register_image_handler(&riscv_linux_handler);
+ register_image_handler(&riscv_barebox_handler);
+
+ if (IS_ENABLED(CONFIG_FITIMAGE))
+ register_image_handler(&riscv_fit_handler);
+
+ return 0;
+}
+late_initcall(riscv_register_image_handler);
diff --git a/arch/riscv/lib/cpuinfo.c b/arch/riscv/lib/cpuinfo.c
new file mode 100644
index 0000000000..21b99a990a
--- /dev/null
+++ b/arch/riscv/lib/cpuinfo.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <common.h>
+#include <command.h>
+#include <asm/sbi.h>
+
+static const char *implementations[] = {
+ [0] = "\"Berkeley Boot Loader (BBL)\" ",
+ [1] = "\"OpenSBI\" ",
+ [2] = "\"Xvisor\" ",
+ [3] = "\"KVM\" ",
+ [4] = "\"RustSBI\" ",
+ [5] = "\"Diosix\" ",
+};
+
+static int do_cpuinfo(int argc, char *argv[])
+{
+ const char *implementation = "";
+ unsigned long impid;
+
+ printf("SBI specification v%lu.%lu detected\n",
+ sbi_major_version(), sbi_minor_version());
+
+ if (sbi_spec_is_0_1())
+ return 0;
+
+ impid = __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_ID);
+ if (impid < ARRAY_SIZE(implementations))
+ implementation = implementations[impid];
+
+ printf("SBI implementation ID=0x%lx %sVersion=0x%lx\n",
+ impid, implementation, __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION));
+
+ printf("SBI Machine VENDORID=0x%lx ARCHID=0x%lx MIMPID=0x%lx\n",
+ __sbi_base_ecall(SBI_EXT_BASE_GET_MVENDORID),
+ __sbi_base_ecall(SBI_EXT_BASE_GET_MARCHID),
+ __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID));
+
+ return 0;
+}
+
+BAREBOX_CMD_START(cpuinfo)
+ .cmd = do_cpuinfo,
+BAREBOX_CMD_DESC("show CPU information")
+BAREBOX_CMD_GROUP(CMD_GRP_INFO)
+ BAREBOX_CMD_END
diff --git a/arch/riscv/lib/sbi.c b/arch/riscv/lib/sbi.c
new file mode 100644
index 0000000000..973c9d9d0f
--- /dev/null
+++ b/arch/riscv/lib/sbi.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SBI initialilization and all extension implementation.
+ *
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+
+#include <asm/sbi.h>
+#include <linux/export.h>
+#include <errno.h>
+#include <init.h>
+
+/* default SBI version is 0.1 */
+unsigned long sbi_spec_version = SBI_SPEC_VERSION_DEFAULT;
+EXPORT_SYMBOL(sbi_spec_version);
+
+int sbi_err_map_linux_errno(int err)
+{
+ switch (err) {
+ case SBI_SUCCESS:
+ return 0;
+ case SBI_ERR_DENIED:
+ return -EPERM;
+ case SBI_ERR_INVALID_PARAM:
+ return -EINVAL;
+ case SBI_ERR_INVALID_ADDRESS:
+ return -EFAULT;
+ case SBI_ERR_NOT_SUPPORTED:
+ case SBI_ERR_FAILURE:
+ default:
+ return -ENOTSUPP;
+ };
+}
+EXPORT_SYMBOL(sbi_err_map_linux_errno);
+
+long __sbi_base_ecall(int fid)
+{
+ struct sbiret ret;
+
+ ret = sbi_ecall(SBI_EXT_BASE, fid, 0, 0, 0, 0, 0, 0);
+ if (!ret.error)
+ return ret.value;
+ else
+ return sbi_err_map_linux_errno(ret.error);
+}
+
+static inline long sbi_get_spec_version(void)
+{
+ return __sbi_base_ecall(SBI_EXT_BASE_GET_SPEC_VERSION);
+}
+
+static int sbi_init(void)
+{
+ int ret;
+
+ ret = sbi_get_spec_version();
+ if (ret > 0)
+ sbi_spec_version = ret;
+ return 0;
+
+}
+core_initcall(sbi_init);
diff --git a/arch/riscv/mach-erizo/Kconfig b/arch/riscv/mach-erizo/Kconfig
deleted file mode 100644
index 2400b4437b..0000000000
--- a/arch/riscv/mach-erizo/Kconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-if MACH_ERIZO
-
-choice
- prompt "Board type"
-
-config BOARD_ERIZO_GENERIC
- bool "erizo generic board"
-
-endchoice
-
-endif
diff --git a/arch/riscv/mach-erizo/Makefile b/arch/riscv/mach-erizo/Makefile
deleted file mode 100644
index d9c51e74c3..0000000000
--- a/arch/riscv/mach-erizo/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-# just to build a built-in.o. Otherwise compilation fails when no o-files is
-# created.
-obj- += dummy.o
diff --git a/arch/riscv/mach-virt/Makefile b/arch/riscv/mach-virt/Makefile
deleted file mode 100644
index d9c51e74c3..0000000000
--- a/arch/riscv/mach-virt/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-# just to build a built-in.o. Otherwise compilation fails when no o-files is
-# created.
-obj- += dummy.o
diff --git a/arch/riscv/mach-virt/include/mach/debug_ll.h b/arch/riscv/mach-virt/include/mach/debug_ll.h
deleted file mode 100644
index 056b7a330b..0000000000
--- a/arch/riscv/mach-virt/include/mach/debug_ll.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2017 Antony Pavlov <antonynpavlov@gmail.com>
- */
-
-#ifndef __MACH_VIRT_DEBUG_LL__
-#define __MACH_VIRT_DEBUG_LL__
-
-/** @file
- * This File contains declaration for early output support
- */
-
-#include <linux/kconfig.h>
-
-#define DEBUG_LL_UART_ADDR 0x10000000
-#define DEBUG_LL_UART_SHIFT 0
-#define DEBUG_LL_UART_IOSIZE8
-
-#define DEBUG_LL_UART_CLK 0x00384000
-#define DEBUG_LL_UART_BPS CONFIG_BAUDRATE
-#define DEBUG_LL_UART_DIVISOR (DEBUG_LL_UART_CLK / DEBUG_LL_UART_BPS)
-
-#include <asm/debug_ll_ns16550.h>
-
-#endif /* __MACH_VIRT_DEBUG_LL__ */
diff --git a/commands/Kconfig b/commands/Kconfig
index b3937fdd8d..5ae3cb3dd1 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -44,6 +44,13 @@ config CMD_ARM_CPUINFO
D-cache: 8192 bytes (linelen = 8)
Control register: M C W P D L I V RR DT IT U XP
+config CMD_RISCV_CPUINFO
+ bool "cpuinfo command"
+ default y
+ depends on RISCV_SBI
+ help
+ Show SBI info about RISC-V CPU
+
config CMD_DEVINFO
tristate
default y
diff --git a/common/Kconfig b/common/Kconfig
index 939cfab4f7..758acb751d 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1363,6 +1363,15 @@ config DEBUG_RPI3_MINI_UART
help
Say Y here if you want low-level debugging support on
RaspberryPi 3 board mini UART.
+
+config DEBUG_ERIZO
+ bool "Erizo ns16550 port"
+ depends on SOC_ERIZO
+
+config DEBUG_SIFIVE
+ bool "SiFive serial0 port"
+ depends on SOC_SIFIVE
+
endchoice
config DEBUG_IMX_UART_PORT
@@ -1483,6 +1492,10 @@ endmenu
config HAS_DEBUG_LL
bool
+config HAS_ASM_DEBUG_LL
+ bool
+ select HAS_DEBUG_LL
+
config DDR_SPD
bool
select CRC_ITU_T
diff --git a/common/Makefile b/common/Makefile
index c0b45d263e..2c679090a0 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -20,7 +20,7 @@ obj-$(CONFIG_BAREBOX_UPDATE) += bbu.o
obj-$(CONFIG_BINFMT) += binfmt.o
obj-$(CONFIG_BLOCK) += block.o
obj-$(CONFIG_BLSPEC) += blspec.o
-obj-$(CONFIG_BOOTM) += bootm.o
+obj-$(CONFIG_BOOTM) += bootm.o booti.o
obj-$(CONFIG_CMD_LOADS) += s_record.o
obj-$(CONFIG_CMD_MEMTEST) += memtest.o
obj-$(CONFIG_COMMAND_SUPPORT) += command.o
diff --git a/common/booti.c b/common/booti.c
new file mode 100644
index 0000000000..a2d63d8c31
--- /dev/null
+++ b/common/booti.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2018 Sascha Hauer <s.hauer@pengutronix.de>
+
+#include <common.h>
+#include <memory.h>
+#include <bootm.h>
+#include <linux/sizes.h>
+
+void *booti_load_image(struct image_data *data, phys_addr_t *oftree)
+{
+ const void *kernel_header =
+ data->os_fit ? data->fit_kernel : data->os_header;
+ resource_size_t start, end;
+ unsigned long text_offset, image_size, devicetree, kernel;
+ unsigned long image_end;
+ int ret;
+ void *fdt;
+
+ text_offset = le64_to_cpup(kernel_header + 8);
+ image_size = le64_to_cpup(kernel_header + 16);
+
+ ret = memory_bank_first_find_space(&start, &end);
+ if (ret)
+ return ERR_PTR(ret);
+
+ kernel = ALIGN(start, SZ_2M) + text_offset;
+
+ ret = bootm_load_os(data, kernel);
+ if (ret)
+ return ERR_PTR(ret);
+
+ image_end = PAGE_ALIGN(kernel + image_size);
+
+ if (oftree) {
+ if (bootm_has_initrd(data)) {
+ ret = bootm_load_initrd(data, image_end);
+ if (ret)
+ return ERR_PTR(ret);
+
+ image_end += resource_size(data->initrd_res);
+ image_end = PAGE_ALIGN(image_end);
+ }
+
+ devicetree = image_end;
+
+ fdt = bootm_get_devicetree(data);
+ if (IS_ERR(fdt))
+ return fdt;
+
+ ret = bootm_load_devicetree(data, fdt, devicetree);
+
+ free(fdt);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ *oftree = devicetree;
+ }
+
+ printf("Loaded kernel to 0x%08lx", kernel);
+ if (oftree)
+ printf(", devicetree at 0x%08lx", devicetree);
+ printf("\n");
+
+ return (void *)kernel;
+}
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c05e065651..d8649c3f9b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -19,3 +19,5 @@ config CLK_SOCFPGA
bool
select COMMON_CLK_OF_PROVIDER
default y if ARCH_SOCFPGA && OFDEVICE
+
+source "drivers/clk/sifive/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 04c797e7e0..b0be8d1bd8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -21,3 +21,5 @@ obj-$(CONFIG_ARCH_STM32MP) += clk-stm32mp1.o
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
obj-$(CONFIG_MACH_MIPS_LOONGSON)+= loongson/
obj-$(CONFIG_ARCH_LAYERSCAPE) += clk-qoric.o
+obj-y += analogbits/
+obj-$(CONFIG_CLK_SIFIVE) += sifive/
diff --git a/drivers/clk/analogbits/Makefile b/drivers/clk/analogbits/Makefile
new file mode 100644
index 0000000000..c893283ea5
--- /dev/null
+++ b/drivers/clk/analogbits/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += wrpll-cln28hpc.o
diff --git a/drivers/clk/analogbits/wrpll-cln28hpc.c b/drivers/clk/analogbits/wrpll-cln28hpc.c
new file mode 100644
index 0000000000..640af533d6
--- /dev/null
+++ b/drivers/clk/analogbits/wrpll-cln28hpc.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+ * Wesley Terpstra
+ * Paul Walmsley
+ *
+ * This library supports configuration parsing and reprogramming of
+ * the CLN28HPC variant of the Analog Bits Wide Range PLL. The
+ * intention is for this library to be reusable for any device that
+ * integrates this PLL; thus the register structure and programming
+ * details are expected to be provided by a separate IP block driver.
+ *
+ * The bulk of this code is primarily useful for clock configurations
+ * that must operate at arbitrary rates, as opposed to clock configurations
+ * that are restricted by software or manufacturer guidance to a small,
+ * pre-determined set of performance points.
+ *
+ * References:
+ * - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01
+ * - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset"
+ * https://static.dev.sifive.com/FU540-C000-v1.0.pdf
+ */
+
+#include <linux/kernel.h>
+#include <stdio.h>
+#include <printk.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/clk/analogbits-wrpll-cln28hpc.h>
+
+/* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */
+#define MIN_INPUT_FREQ 7000000
+
+/* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */
+#define MAX_INPUT_FREQ 600000000
+
+/* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */
+#define MIN_POST_DIVR_FREQ 7000000
+
+/* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */
+#define MAX_POST_DIVR_FREQ 200000000
+
+/* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */
+#define MIN_VCO_FREQ 2400000000UL
+
+/* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */
+#define MAX_VCO_FREQ 4800000000ULL
+
+/* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */
+#define MAX_DIVQ_DIVISOR 64
+
+/* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */
+#define MAX_DIVR_DIVISOR 64
+
+/* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */
+#define MAX_LOCK_US 70
+
+/*
+ * ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding
+ * algorithm
+ */
+#define ROUND_SHIFT 20
+
+/*
+ * Private functions
+ */
+
+/**
+ * __wrpll_calc_filter_range() - determine PLL loop filter bandwidth
+ * @post_divr_freq: input clock rate after the R divider
+ *
+ * Select the value to be presented to the PLL RANGE input signals, based
+ * on the input clock frequency after the post-R-divider @post_divr_freq.
+ * This code follows the recommendations in the PLL datasheet for filter
+ * range selection.
+ *
+ * Return: The RANGE value to be presented to the PLL configuration inputs,
+ * or a negative return code upon error.
+ */
+static int __wrpll_calc_filter_range(unsigned long post_divr_freq)
+{
+ if (post_divr_freq < MIN_POST_DIVR_FREQ ||
+ post_divr_freq > MAX_POST_DIVR_FREQ) {
+ WARN(1, "%s: post-divider reference freq out of range: %lu",
+ __func__, post_divr_freq);
+ return -ERANGE;
+ }
+
+ switch (post_divr_freq) {
+ case 0 ... 10999999:
+ return 1;
+ case 11000000 ... 17999999:
+ return 2;
+ case 18000000 ... 29999999:
+ return 3;
+ case 30000000 ... 49999999:
+ return 4;
+ case 50000000 ... 79999999:
+ return 5;
+ case 80000000 ... 129999999:
+ return 6;
+ }
+
+ return 7;
+}
+
+/**
+ * __wrpll_calc_fbdiv() - return feedback fixed divide value
+ * @c: ptr to a struct wrpll_cfg record to read from
+ *
+ * The internal feedback path includes a fixed by-two divider; the
+ * external feedback path does not. Return the appropriate divider
+ * value (2 or 1) depending on whether internal or external feedback
+ * is enabled. This code doesn't test for invalid configurations
+ * (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies
+ * on the caller to do so.
+ *
+ * Context: Any context. Caller must protect the memory pointed to by
+ * @c from simultaneous modification.
+ *
+ * Return: 2 if internal feedback is enabled or 1 if external feedback
+ * is enabled.
+ */
+static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c)
+{
+ return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1;
+}
+
+/**
+ * __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate
+ * @target_rate: target PLL output clock rate
+ * @vco_rate: pointer to a u64 to store the computed VCO rate into
+ *
+ * Determine a reasonable value for the PLL Q post-divider, based on the
+ * target output rate @target_rate for the PLL. Along with returning the
+ * computed Q divider value as the return value, this function stores the
+ * desired target VCO rate into the variable pointed to by @vco_rate.
+ *
+ * Context: Any context. Caller must protect the memory pointed to by
+ * @vco_rate from simultaneous access or modification.
+ *
+ * Return: a positive integer DIVQ value to be programmed into the hardware
+ * upon success, or 0 upon error (since 0 is an invalid DIVQ value)
+ */
+static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate)
+{
+ u64 s;
+ u8 divq = 0;
+
+ if (!vco_rate) {
+ WARN_ON(1);
+ goto wcd_out;
+ }
+
+ s = div_u64(MAX_VCO_FREQ, target_rate);
+ if (s <= 1) {
+ divq = 1;
+ *vco_rate = MAX_VCO_FREQ;
+ } else if (s > MAX_DIVQ_DIVISOR) {
+ divq = ilog2(MAX_DIVQ_DIVISOR);
+ *vco_rate = MIN_VCO_FREQ;
+ } else {
+ divq = ilog2(s);
+ *vco_rate = (u64)target_rate << divq;
+ }
+
+wcd_out:
+ return divq;
+}
+
+/**
+ * __wrpll_update_parent_rate() - update PLL data when parent rate changes
+ * @c: ptr to a struct wrpll_cfg record to write PLL data to
+ * @parent_rate: PLL input refclk rate (pre-R-divider)
+ *
+ * Pre-compute some data used by the PLL configuration algorithm when
+ * the PLL's reference clock rate changes. The intention is to avoid
+ * computation when the parent rate remains constant - expected to be
+ * the common case.
+ *
+ * Returns: 0 upon success or -ERANGE if the reference clock rate is
+ * out of range.
+ */
+static int __wrpll_update_parent_rate(struct wrpll_cfg *c,
+ unsigned long parent_rate)
+{
+ u8 max_r_for_parent;
+
+ if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ)
+ return -ERANGE;
+
+ c->parent_rate = parent_rate;
+ max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ);
+ c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent);
+
+ c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ);
+
+ return 0;
+}
+
+/**
+ * wrpll_configure() - compute PLL configuration for a target rate
+ * @c: ptr to a struct wrpll_cfg record to write into
+ * @target_rate: target PLL output clock rate (post-Q-divider)
+ * @parent_rate: PLL input refclk rate (pre-R-divider)
+ *
+ * Compute the appropriate PLL signal configuration values and store
+ * in PLL context @c. PLL reprogramming is not glitchless, so the
+ * caller should switch any downstream logic to a different clock
+ * source or clock-gate it before presenting these values to the PLL
+ * configuration signals.
+ *
+ * The caller must pass this function a pre-initialized struct
+ * wrpll_cfg record: either initialized to zero (with the
+ * exception of the .name and .flags fields) or read from the PLL.
+ *
+ * Context: Any context. Caller must protect the memory pointed to by @c
+ * from simultaneous access or modification.
+ *
+ * Return: 0 upon success; anything else upon failure.
+ */
+int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
+ unsigned long parent_rate)
+{
+ unsigned long ratio;
+ u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre;
+ u32 best_f, f, post_divr_freq;
+ u8 fbdiv, divq, best_r, r;
+ int range;
+
+ if (c->flags == 0) {
+ WARN(1, "%s called with uninitialized PLL config", __func__);
+ return -EINVAL;
+ }
+
+ /* Initialize rounding data if it hasn't been initialized already */
+ if (parent_rate != c->parent_rate) {
+ if (__wrpll_update_parent_rate(c, parent_rate)) {
+ pr_err("%s: PLL input rate is out of range\n",
+ __func__);
+ return -ERANGE;
+ }
+ }
+
+ c->flags &= ~WRPLL_FLAGS_RESET_MASK;
+
+ /* Put the PLL into bypass if the user requests the parent clock rate */
+ if (target_rate == parent_rate) {
+ c->flags |= WRPLL_FLAGS_BYPASS_MASK;
+ return 0;
+ }
+
+ c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;
+
+ /* Calculate the Q shift and target VCO rate */
+ divq = __wrpll_calc_divq(target_rate, &target_vco_rate);
+ if (!divq)
+ return -1;
+ c->divq = divq;
+
+ /* Precalculate the pre-Q divider target ratio */
+ ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);
+
+ fbdiv = __wrpll_calc_fbdiv(c);
+ best_r = 0;
+ best_f = 0;
+ best_delta = MAX_VCO_FREQ;
+
+ /*
+ * Consider all values for R which land within
+ * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R
+ */
+ for (r = c->init_r; r <= c->max_r; ++r) {
+ f_pre_div = ratio * r;
+ f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;
+ f >>= (fbdiv - 1);
+
+ post_divr_freq = div_u64(parent_rate, r);
+ vco_pre = fbdiv * post_divr_freq;
+ vco = vco_pre * f;
+
+ /* Ensure rounding didn't take us out of range */
+ if (vco > target_vco_rate) {
+ --f;
+ vco = vco_pre * f;
+ } else if (vco < MIN_VCO_FREQ) {
+ ++f;
+ vco = vco_pre * f;
+ }
+
+ delta = abs(target_rate - vco);
+ if (delta < best_delta) {
+ best_delta = delta;
+ best_r = r;
+ best_f = f;
+ }
+ }
+
+ c->divr = best_r - 1;
+ c->divf = best_f - 1;
+
+ post_divr_freq = div_u64(parent_rate, best_r);
+
+ /* Pick the best PLL jitter filter */
+ range = __wrpll_calc_filter_range(post_divr_freq);
+ if (range < 0)
+ return range;
+ c->range = range;
+
+ return 0;
+}
+
+/**
+ * wrpll_calc_output_rate() - calculate the PLL's target output rate
+ * @c: ptr to a struct wrpll_cfg record to read from
+ * @parent_rate: PLL refclk rate
+ *
+ * Given a pointer to the PLL's current input configuration @c and the
+ * PLL's input reference clock rate @parent_rate (before the R
+ * pre-divider), calculate the PLL's output clock rate (after the Q
+ * post-divider).
+ *
+ * Context: Any context. Caller must protect the memory pointed to by @c
+ * from simultaneous modification.
+ *
+ * Return: the PLL's output clock rate, in Hz. The return value from
+ * this function is intended to be convenient to pass directly
+ * to the Linux clock framework; thus there is no explicit
+ * error return value.
+ */
+unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
+ unsigned long parent_rate)
+{
+ u8 fbdiv;
+ u64 n;
+
+ if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) {
+ WARN(1, "external feedback mode not yet supported");
+ return ULONG_MAX;
+ }
+
+ fbdiv = __wrpll_calc_fbdiv(c);
+ n = parent_rate * fbdiv * (c->divf + 1);
+ n = div_u64(n, c->divr + 1);
+ n >>= c->divq;
+
+ return n;
+}
+
+/**
+ * wrpll_calc_max_lock_us() - return the time for the PLL to lock
+ * @c: ptr to a struct wrpll_cfg record to read from
+ *
+ * Return the minimum amount of time (in microseconds) that the caller
+ * must wait after reprogramming the PLL to ensure that it is locked
+ * to the input frequency and stable. This is likely to depend on the DIVR
+ * value; this is under discussion with the manufacturer.
+ *
+ * Return: the minimum amount of time the caller must wait for the PLL
+ * to lock (in microseconds)
+ */
+unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)
+{
+ return MAX_LOCK_US;
+}
diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
new file mode 100644
index 0000000000..6b3f4bc600
--- /dev/null
+++ b/drivers/clk/sifive/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig CLK_SIFIVE
+ bool "SiFive SoC driver support"
+ depends on RISCV || COMPILE_TEST
+ help
+ SoC drivers for SiFive Linux-capable SoCs.
+
+if CLK_SIFIVE
+
+config CLK_SIFIVE_PRCI
+ bool "PRCI driver for SiFive SoCs"
+ help
+ Supports the Power Reset Clock interface (PRCI) IP block found in
+ FU540/FU740 SoCs. If this kernel is meant to run on a SiFive FU540/
+ FU740 SoCs, enable this driver.
+
+endif
diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
new file mode 100644
index 0000000000..7b06fc04e6
--- /dev/null
+++ b/drivers/clk/sifive/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_CLK_SIFIVE_PRCI) += sifive-prci.o fu540-prci.o fu740-prci.o
diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c
new file mode 100644
index 0000000000..e6379dfd6a
--- /dev/null
+++ b/drivers/clk/sifive/fu540-prci.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+ * Copyright (C) 2018-2019 Wesley Terpstra
+ * Copyright (C) 2018-2019 Paul Walmsley
+ * Copyright (C) 2020 Zong Li
+ *
+ * The FU540 PRCI implements clock and reset control for the SiFive
+ * FU540-C000 chip. This driver assumes that it has sole control
+ * over all PRCI resources.
+ *
+ * This driver is based on the PRCI driver written by Wesley Terpstra:
+ * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60
+ *
+ * References:
+ * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset"
+ */
+
+#include <dt-bindings/clock/sifive-fu540-prci.h>
+
+#include "fu540-prci.h"
+#include "sifive-prci.h"
+
+/* PRCI integration data for each WRPLL instance */
+
+static struct __prci_wrpll_data __prci_corepll_data = {
+ .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_COREPLLCFG1_OFFSET,
+ .enable_bypass = sifive_prci_coreclksel_use_hfclk,
+ .disable_bypass = sifive_prci_coreclksel_use_corepll,
+};
+
+static struct __prci_wrpll_data __prci_ddrpll_data = {
+ .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_DDRPLLCFG1_OFFSET,
+};
+
+static struct __prci_wrpll_data __prci_gemgxlpll_data = {
+ .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_GEMGXLPLLCFG1_OFFSET,
+};
+
+/* Linux clock framework integration */
+
+static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
+ .set_rate = sifive_prci_wrpll_set_rate,
+ .round_rate = sifive_prci_wrpll_round_rate,
+ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+ .enable = sifive_prci_clock_enable,
+ .disable = sifive_prci_clock_disable,
+ .is_enabled = sifive_clk_is_enabled,
+};
+
+static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
+ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+};
+
+static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = {
+ .recalc_rate = sifive_prci_tlclksel_recalc_rate,
+};
+
+/* List of clock controls provided by the PRCI */
+struct __prci_clock __prci_init_clocks_fu540[] = {
+ [PRCI_CLK_COREPLL] = {
+ .name = "corepll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu540_prci_wrpll_clk_ops,
+ .pwd = &__prci_corepll_data,
+ },
+ [PRCI_CLK_DDRPLL] = {
+ .name = "ddrpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu540_prci_wrpll_ro_clk_ops,
+ .pwd = &__prci_ddrpll_data,
+ },
+ [PRCI_CLK_GEMGXLPLL] = {
+ .name = "gemgxlpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu540_prci_wrpll_clk_ops,
+ .pwd = &__prci_gemgxlpll_data,
+ },
+ [PRCI_CLK_TLCLK] = {
+ .name = "tlclk",
+ .parent_name = "corepll",
+ .ops = &sifive_fu540_prci_tlclksel_clk_ops,
+ },
+};
diff --git a/drivers/clk/sifive/fu540-prci.h b/drivers/clk/sifive/fu540-prci.h
new file mode 100644
index 0000000000..c220677dc0
--- /dev/null
+++ b/drivers/clk/sifive/fu540-prci.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 SiFive, Inc.
+ * Zong Li
+ */
+
+#ifndef __SIFIVE_CLK_FU540_PRCI_H
+#define __SIFIVE_CLK_FU540_PRCI_H
+
+#include "sifive-prci.h"
+
+#define NUM_CLOCK_FU540 4
+
+extern struct __prci_clock __prci_init_clocks_fu540[NUM_CLOCK_FU540];
+
+#endif /* __SIFIVE_CLK_FU540_PRCI_H */
diff --git a/drivers/clk/sifive/fu740-prci.c b/drivers/clk/sifive/fu740-prci.c
new file mode 100644
index 0000000000..14df75f7f6
--- /dev/null
+++ b/drivers/clk/sifive/fu740-prci.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 SiFive, Inc.
+ * Copyright (C) 2020 Zong Li
+ */
+
+#include <dt-bindings/clock/sifive-fu740-prci.h>
+
+#include "fu540-prci.h"
+#include "sifive-prci.h"
+
+/* PRCI integration data for each WRPLL instance */
+
+static struct __prci_wrpll_data __prci_corepll_data = {
+ .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_COREPLLCFG1_OFFSET,
+ .enable_bypass = sifive_prci_coreclksel_use_hfclk,
+ .disable_bypass = sifive_prci_coreclksel_use_final_corepll,
+};
+
+static struct __prci_wrpll_data __prci_ddrpll_data = {
+ .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_DDRPLLCFG1_OFFSET,
+};
+
+static struct __prci_wrpll_data __prci_gemgxlpll_data = {
+ .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_GEMGXLPLLCFG1_OFFSET,
+};
+
+static struct __prci_wrpll_data __prci_dvfscorepll_data = {
+ .cfg0_offs = PRCI_DVFSCOREPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_DVFSCOREPLLCFG1_OFFSET,
+ .enable_bypass = sifive_prci_corepllsel_use_corepll,
+ .disable_bypass = sifive_prci_corepllsel_use_dvfscorepll,
+};
+
+static struct __prci_wrpll_data __prci_hfpclkpll_data = {
+ .cfg0_offs = PRCI_HFPCLKPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_HFPCLKPLLCFG1_OFFSET,
+ .enable_bypass = sifive_prci_hfpclkpllsel_use_hfclk,
+ .disable_bypass = sifive_prci_hfpclkpllsel_use_hfpclkpll,
+};
+
+static struct __prci_wrpll_data __prci_cltxpll_data = {
+ .cfg0_offs = PRCI_CLTXPLLCFG0_OFFSET,
+ .cfg1_offs = PRCI_CLTXPLLCFG1_OFFSET,
+};
+
+/* Linux clock framework integration */
+
+static const struct clk_ops sifive_fu740_prci_wrpll_clk_ops = {
+ .set_rate = sifive_prci_wrpll_set_rate,
+ .round_rate = sifive_prci_wrpll_round_rate,
+ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+ .enable = sifive_prci_clock_enable,
+ .disable = sifive_prci_clock_disable,
+ .is_enabled = sifive_clk_is_enabled,
+};
+
+static const struct clk_ops sifive_fu740_prci_wrpll_ro_clk_ops = {
+ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+};
+
+static const struct clk_ops sifive_fu740_prci_tlclksel_clk_ops = {
+ .recalc_rate = sifive_prci_tlclksel_recalc_rate,
+};
+
+static const struct clk_ops sifive_fu740_prci_hfpclkplldiv_clk_ops = {
+ .recalc_rate = sifive_prci_hfpclkplldiv_recalc_rate,
+};
+
+/* List of clock controls provided by the PRCI */
+struct __prci_clock __prci_init_clocks_fu740[] = {
+ [PRCI_CLK_COREPLL] = {
+ .name = "corepll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_clk_ops,
+ .pwd = &__prci_corepll_data,
+ },
+ [PRCI_CLK_DDRPLL] = {
+ .name = "ddrpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_ro_clk_ops,
+ .pwd = &__prci_ddrpll_data,
+ },
+ [PRCI_CLK_GEMGXLPLL] = {
+ .name = "gemgxlpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_clk_ops,
+ .pwd = &__prci_gemgxlpll_data,
+ },
+ [PRCI_CLK_DVFSCOREPLL] = {
+ .name = "dvfscorepll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_clk_ops,
+ .pwd = &__prci_dvfscorepll_data,
+ },
+ [PRCI_CLK_HFPCLKPLL] = {
+ .name = "hfpclkpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_clk_ops,
+ .pwd = &__prci_hfpclkpll_data,
+ },
+ [PRCI_CLK_CLTXPLL] = {
+ .name = "cltxpll",
+ .parent_name = "hfclk",
+ .ops = &sifive_fu740_prci_wrpll_clk_ops,
+ .pwd = &__prci_cltxpll_data,
+ },
+ [PRCI_CLK_TLCLK] = {
+ .name = "tlclk",
+ .parent_name = "corepll",
+ .ops = &sifive_fu740_prci_tlclksel_clk_ops,
+ },
+ [PRCI_CLK_PCLK] = {
+ .name = "pclk",
+ .parent_name = "hfpclkpll",
+ .ops = &sifive_fu740_prci_hfpclkplldiv_clk_ops,
+ },
+};
diff --git a/drivers/clk/sifive/fu740-prci.h b/drivers/clk/sifive/fu740-prci.h
new file mode 100644
index 0000000000..13ef971f77
--- /dev/null
+++ b/drivers/clk/sifive/fu740-prci.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 SiFive, Inc.
+ * Zong Li
+ */
+
+#ifndef __SIFIVE_CLK_FU740_PRCI_H
+#define __SIFIVE_CLK_FU740_PRCI_H
+
+#include "sifive-prci.h"
+
+#define NUM_CLOCK_FU740 8
+
+extern struct __prci_clock __prci_init_clocks_fu740[NUM_CLOCK_FU740];
+
+static const struct prci_clk_desc prci_clk_fu740 = {
+ .clks = __prci_init_clocks_fu740,
+ .num_clks = ARRAY_SIZE(__prci_init_clocks_fu740),
+};
+
+#endif /* __SIFIVE_CLK_FU740_PRCI_H */
diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
new file mode 100644
index 0000000000..b452bbf8cc
--- /dev/null
+++ b/drivers/clk/sifive/sifive-prci.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 SiFive, Inc.
+ * Copyright (C) 2020 Zong Li
+ */
+
+#include <common.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/overflow.h>
+#include <printk.h>
+#include <clock.h>
+#include <io.h>
+#include <of.h>
+#include <driver.h>
+#include <init.h>
+#include "sifive-prci.h"
+#include "fu540-prci.h"
+#include "fu740-prci.h"
+
+static const struct prci_clk_desc prci_clk_fu540 = {
+ .clks = __prci_init_clocks_fu540,
+ .num_clks = ARRAY_SIZE(__prci_init_clocks_fu540),
+};
+
+/*
+ * Private functions
+ */
+
+/**
+ * __prci_readl() - read from a PRCI register
+ * @pd: PRCI context
+ * @offs: register offset to read from (in bytes, from PRCI base address)
+ *
+ * Read the register located at offset @offs from the base virtual
+ * address of the PRCI register target described by @pd, and return
+ * the value to the caller.
+ *
+ * Context: Any context.
+ *
+ * Return: the contents of the register described by @pd and @offs.
+ */
+static u32 __prci_readl(struct __prci_data *pd, u32 offs)
+{
+ return readl(pd->va + offs);
+}
+
+static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
+{
+ writel(v, pd->va + offs);
+}
+
+/* WRPLL-related private functions */
+
+/**
+ * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
+ * @c: ptr to a struct wrpll_cfg record to write config into
+ * @r: value read from the PRCI PLL configuration register
+ *
+ * Given a value @r read from an FU740 PRCI PLL configuration register,
+ * split it into fields and populate it into the WRPLL configuration record
+ * pointed to by @c.
+ *
+ * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
+ * have the same register layout.
+ *
+ * Context: Any context.
+ */
+static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
+{
+ u32 v;
+
+ v = r & PRCI_COREPLLCFG0_DIVR_MASK;
+ v >>= PRCI_COREPLLCFG0_DIVR_SHIFT;
+ c->divr = v;
+
+ v = r & PRCI_COREPLLCFG0_DIVF_MASK;
+ v >>= PRCI_COREPLLCFG0_DIVF_SHIFT;
+ c->divf = v;
+
+ v = r & PRCI_COREPLLCFG0_DIVQ_MASK;
+ v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT;
+ c->divq = v;
+
+ v = r & PRCI_COREPLLCFG0_RANGE_MASK;
+ v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
+ c->range = v;
+
+ c->flags &=
+ (WRPLL_FLAGS_INT_FEEDBACK_MASK | WRPLL_FLAGS_EXT_FEEDBACK_MASK);
+
+ /* external feedback mode not supported */
+ c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
+}
+
+/**
+ * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
+ * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
+ *
+ * Using a set of WRPLL configuration values pointed to by @c,
+ * assemble a PRCI PLL configuration register value, and return it to
+ * the caller.
+ *
+ * Context: Any context. Caller must ensure that the contents of the
+ * record pointed to by @c do not change during the execution
+ * of this function.
+ *
+ * Returns: a value suitable for writing into a PRCI PLL configuration
+ * register
+ */
+static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
+{
+ u32 r = 0;
+
+ r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT;
+ r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT;
+ r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT;
+ r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT;
+
+ /* external feedback mode not supported */
+ r |= PRCI_COREPLLCFG0_FSE_MASK;
+
+ return r;
+}
+
+/**
+ * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
+ * @pd: PRCI context
+ * @pwd: PRCI WRPLL metadata
+ *
+ * Read the current configuration of the PLL identified by @pwd from
+ * the PRCI identified by @pd, and store it into the local configuration
+ * cache in @pwd.
+ *
+ * Context: Any context. Caller must prevent the records pointed to by
+ * @pd and @pwd from changing during execution.
+ */
+static void __prci_wrpll_read_cfg0(struct __prci_data *pd,
+ struct __prci_wrpll_data *pwd)
+{
+ __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
+}
+
+/**
+ * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
+ * @pd: PRCI context
+ * @pwd: PRCI WRPLL metadata
+ * @c: WRPLL configuration record to write
+ *
+ * Write the WRPLL configuration described by @c into the WRPLL
+ * configuration register identified by @pwd in the PRCI instance
+ * described by @c. Make a cached copy of the WRPLL's current
+ * configuration so it can be used by other code.
+ *
+ * Context: Any context. Caller must prevent the records pointed to by
+ * @pd and @pwd from changing during execution.
+ */
+static void __prci_wrpll_write_cfg0(struct __prci_data *pd,
+ struct __prci_wrpll_data *pwd,
+ struct wrpll_cfg *c)
+{
+ __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
+
+ memcpy(&pwd->c, c, sizeof(*c));
+}
+
+/**
+ * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration
+ * into the PRCI
+ * @pd: PRCI context
+ * @pwd: PRCI WRPLL metadata
+ * @enable: Clock enable or disable value
+ */
+static void __prci_wrpll_write_cfg1(struct __prci_data *pd,
+ struct __prci_wrpll_data *pwd,
+ u32 enable)
+{
+ __prci_writel(enable, pwd->cfg1_offs, pd);
+}
+
+/*
+ * Linux clock framework integration
+ *
+ * See the Linux clock framework documentation for more information on
+ * these functions.
+ */
+
+unsigned long sifive_prci_wrpll_recalc_rate(struct clk *hw,
+ unsigned long parent_rate)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+
+ return wrpll_calc_output_rate(&pwd->c, parent_rate);
+}
+
+long sifive_prci_wrpll_round_rate(struct clk *hw,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+ struct wrpll_cfg c;
+
+ memcpy(&c, &pwd->c, sizeof(c));
+
+ wrpll_configure_for_rate(&c, rate, *parent_rate);
+
+ return wrpll_calc_output_rate(&c, *parent_rate);
+}
+
+int sifive_prci_wrpll_set_rate(struct clk *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+ struct __prci_data *pd = pc->pd;
+ int r;
+
+ r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate);
+ if (r)
+ return r;
+
+ if (pwd->enable_bypass)
+ pwd->enable_bypass(pd);
+
+ __prci_wrpll_write_cfg0(pd, pwd, &pwd->c);
+
+ udelay(wrpll_calc_max_lock_us(&pwd->c));
+
+ return 0;
+}
+
+int sifive_clk_is_enabled(struct clk *hw)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+ struct __prci_data *pd = pc->pd;
+ u32 r;
+
+ r = __prci_readl(pd, pwd->cfg1_offs);
+
+ if (r & PRCI_COREPLLCFG1_CKE_MASK)
+ return 1;
+ else
+ return 0;
+}
+
+int sifive_prci_clock_enable(struct clk *hw)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+ struct __prci_data *pd = pc->pd;
+
+ if (sifive_clk_is_enabled(hw))
+ return 0;
+
+ __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK);
+
+ if (pwd->disable_bypass)
+ pwd->disable_bypass(pd);
+
+ return 0;
+}
+
+void sifive_prci_clock_disable(struct clk *hw)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_wrpll_data *pwd = pc->pwd;
+ struct __prci_data *pd = pc->pd;
+ u32 r;
+
+ if (pwd->enable_bypass)
+ pwd->enable_bypass(pd);
+
+ r = __prci_readl(pd, pwd->cfg1_offs);
+ r &= ~PRCI_COREPLLCFG1_CKE_MASK;
+
+ __prci_wrpll_write_cfg1(pd, pwd, r);
+}
+
+/* TLCLKSEL clock integration */
+
+unsigned long sifive_prci_tlclksel_recalc_rate(struct clk *hw,
+ unsigned long parent_rate)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_data *pd = pc->pd;
+ u32 v;
+ u8 div;
+
+ v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET);
+ v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK;
+ div = v ? 1 : 2;
+
+ return div_u64(parent_rate, div);
+}
+
+/* HFPCLK clock integration */
+
+unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk *hw,
+ unsigned long parent_rate)
+{
+ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
+ struct __prci_data *pd = pc->pd;
+ u32 div = __prci_readl(pd, PRCI_HFPCLKPLLDIV_OFFSET);
+
+ return div_u64(parent_rate, div + 2);
+}
+
+/*
+ * Core clock mux control
+ */
+
+/**
+ * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
+ * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
+ *
+ * Switch the CORECLK mux to the HFCLK input source; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_CORECLKSEL_OFFSET register.
+ */
+void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
+ r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
+ __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output
+ * COREPLL
+ * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
+ *
+ * Switch the CORECLK mux to the COREPLL output clock; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_CORECLKSEL_OFFSET register.
+ */
+void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
+ r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
+ __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output
+ * FINAL_COREPLL
+ * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
+ *
+ * Switch the CORECLK mux to the final COREPLL output clock; return once
+ * complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_CORECLKSEL_OFFSET register.
+ */
+void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
+ r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
+ __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to
+ * output DVFS_COREPLL
+ * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
+ *
+ * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_COREPLLSEL_OFFSET register.
+ */
+void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
+ r |= PRCI_COREPLLSEL_COREPLLSEL_MASK;
+ __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to
+ * output COREPLL
+ * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg
+ *
+ * Switch the COREPLL mux to the COREPLL output clock; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_COREPLLSEL_OFFSET register.
+ */
+void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET);
+ r &= ~PRCI_COREPLLSEL_COREPLLSEL_MASK;
+ __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to
+ * output HFCLK
+ * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
+ *
+ * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_HFPCLKPLLSEL_OFFSET register.
+ */
+void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
+ r |= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
+ __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
+}
+
+/**
+ * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to
+ * output HFPCLKPLL
+ * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg
+ *
+ * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete.
+ *
+ * Context: Any context. Caller must prevent concurrent changes to the
+ * PRCI_HFPCLKPLLSEL_OFFSET register.
+ */
+void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd)
+{
+ u32 r;
+
+ r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET);
+ r &= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK;
+ __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd);
+
+ r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
+}
+
+/**
+ * __prci_register_clocks() - register clock controls in the PRCI
+ * @dev: Linux struct device
+ * @pd: The pointer for PRCI per-device instance data
+ * @desc: The pointer for the information of clocks of each SoCs
+ *
+ * Register the list of clock controls described in __prci_init_clocks[] with
+ * the Linux clock framework.
+ *
+ * Return: 0 upon success or a negative error code upon failure.
+ */
+static int __prci_register_clocks(struct device_d *dev, struct __prci_data *pd,
+ const struct prci_clk_desc *desc)
+{
+ struct __prci_clock *pic;
+ int parent_count, i, r;
+
+ parent_count = of_clk_get_parent_count(dev->device_node);
+ if (parent_count != EXPECTED_CLK_PARENT_COUNT) {
+ dev_err(dev, "expected only two parent clocks, found %d\n",
+ parent_count);
+ return -EINVAL;
+ }
+
+ /* Register PLLs */
+ for (i = 0; i < desc->num_clks; ++i) {
+ pic = &(desc->clks[i]);
+
+ pic->hw.name = pic->name;
+ pic->hw.parent_names = &pic->parent_name;
+ pic->hw.num_parents = 1;
+ pic->hw.ops = pic->ops;
+
+ pic->pd = pd;
+
+ if (pic->pwd)
+ __prci_wrpll_read_cfg0(pd, pic->pwd);
+
+ r = clk_register(&pic->hw);
+ if (r) {
+ dev_warn(dev, "Failed to register clock %s: %d\n",
+ pic->hw.name, r);
+ return r;
+ }
+
+ r = clk_register_clkdev(&pic->hw, pic->name, dev_name(dev));
+ if (r) {
+ dev_warn(dev, "Failed to register clkdev for %s: %d\n",
+ pic->hw.name, r);
+ return r;
+ }
+
+ pd->hw_clks.clks[i] = &pic->hw;
+ }
+
+ pd->hw_clks.clk_num = i;
+
+ r = of_clk_add_provider(dev->device_node, of_clk_src_onecell_get,
+ &pd->hw_clks);
+ if (r) {
+ dev_err(dev, "could not add hw_provider: %d\n", r);
+ return r;
+ }
+
+ return 0;
+}
+
+/**
+ * sifive_prci_init() - initialize prci data and check parent count
+ * @dev: platform device pointer for the prci
+ *
+ * Return: 0 upon success or a negative error code upon failure.
+ */
+static int sifive_prci_probe(struct device_d *dev)
+{
+ struct resource *res;
+ struct __prci_data *pd;
+ const struct prci_clk_desc *desc;
+ int r;
+
+ desc = device_get_match_data(dev);
+
+ pd = malloc(sizeof(*pd));
+ if (!pd)
+ return -ENOMEM;
+
+ pd->hw_clks.clk_num = desc->num_clks;
+ pd->hw_clks.clks = calloc(pd->hw_clks.clk_num, sizeof(*pd->hw_clks.clks));
+ if (!pd->hw_clks.clks)
+ return -ENOMEM;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ pd->va = IOMEM(res->start);
+
+ r = __prci_register_clocks(dev, pd, desc);
+ if (r) {
+ dev_err(dev, "could not register clocks: %d\n", r);
+ return r;
+ }
+
+ dev_dbg(dev, "SiFive PRCI probed\n");
+
+ return 0;
+}
+
+static const struct of_device_id sifive_prci_of_match[] = {
+ {.compatible = "sifive,fu540-c000-prci", .data = &prci_clk_fu540},
+ {.compatible = "sifive,fu740-c000-prci", .data = &prci_clk_fu740},
+ {}
+};
+
+static struct driver_d sifive_prci_driver = {
+ .name = "sifive-clk-prci",
+ .of_compatible = sifive_prci_of_match,
+ .probe = sifive_prci_probe,
+};
+core_platform_driver(sifive_prci_driver);
diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
new file mode 100644
index 0000000000..d851553818
--- /dev/null
+++ b/drivers/clk/sifive/sifive-prci.h
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+ * Wesley Terpstra
+ * Paul Walmsley
+ * Zong Li
+ */
+
+#ifndef __SIFIVE_CLK_SIFIVE_PRCI_H
+#define __SIFIVE_CLK_SIFIVE_PRCI_H
+
+#include <linux/clk/analogbits-wrpll-cln28hpc.h>
+#include <linux/clk.h>
+
+/*
+ * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects:
+ * hfclk and rtcclk
+ */
+#define EXPECTED_CLK_PARENT_COUNT 2
+
+/*
+ * Register offsets and bitmasks
+ */
+
+/* COREPLLCFG0 */
+#define PRCI_COREPLLCFG0_OFFSET 0x4
+#define PRCI_COREPLLCFG0_DIVR_SHIFT 0
+#define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT)
+#define PRCI_COREPLLCFG0_DIVF_SHIFT 6
+#define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT)
+#define PRCI_COREPLLCFG0_DIVQ_SHIFT 15
+#define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT)
+#define PRCI_COREPLLCFG0_RANGE_SHIFT 18
+#define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT)
+#define PRCI_COREPLLCFG0_BYPASS_SHIFT 24
+#define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT)
+#define PRCI_COREPLLCFG0_FSE_SHIFT 25
+#define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT)
+#define PRCI_COREPLLCFG0_LOCK_SHIFT 31
+#define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
+
+/* COREPLLCFG1 */
+#define PRCI_COREPLLCFG1_OFFSET 0x8
+#define PRCI_COREPLLCFG1_CKE_SHIFT 31
+#define PRCI_COREPLLCFG1_CKE_MASK (0x1 << PRCI_COREPLLCFG1_CKE_SHIFT)
+
+/* DDRPLLCFG0 */
+#define PRCI_DDRPLLCFG0_OFFSET 0xc
+#define PRCI_DDRPLLCFG0_DIVR_SHIFT 0
+#define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT)
+#define PRCI_DDRPLLCFG0_DIVF_SHIFT 6
+#define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT)
+#define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15
+#define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT)
+#define PRCI_DDRPLLCFG0_RANGE_SHIFT 18
+#define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT)
+#define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24
+#define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT)
+#define PRCI_DDRPLLCFG0_FSE_SHIFT 25
+#define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT)
+#define PRCI_DDRPLLCFG0_LOCK_SHIFT 31
+#define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT)
+
+/* DDRPLLCFG1 */
+#define PRCI_DDRPLLCFG1_OFFSET 0x10
+#define PRCI_DDRPLLCFG1_CKE_SHIFT 31
+#define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
+
+/* GEMGXLPLLCFG0 */
+#define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c
+#define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0
+#define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6
+#define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15
+#define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18
+#define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24
+#define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25
+#define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT)
+#define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31
+#define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT)
+
+/* GEMGXLPLLCFG1 */
+#define PRCI_GEMGXLPLLCFG1_OFFSET 0x20
+#define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 31
+#define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
+
+/* CORECLKSEL */
+#define PRCI_CORECLKSEL_OFFSET 0x24
+#define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0
+#define PRCI_CORECLKSEL_CORECLKSEL_MASK \
+ (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT)
+
+/* DEVICESRESETREG */
+#define PRCI_DEVICESRESETREG_OFFSET 0x28
+#define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0
+#define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT)
+#define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1
+#define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT)
+#define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2
+#define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT)
+#define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3
+#define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT)
+#define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5
+#define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT)
+#define PRCI_DEVICESRESETREG_CHIPLINK_RST_N_SHIFT 6
+#define PRCI_DEVICESRESETREG_CHIPLINK_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_CHIPLINK_RST_N_SHIFT)
+
+/* CLKMUXSTATUSREG */
+#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c
+#define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1
+#define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK \
+ (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT)
+
+/* CLTXPLLCFG0 */
+#define PRCI_CLTXPLLCFG0_OFFSET 0x30
+#define PRCI_CLTXPLLCFG0_DIVR_SHIFT 0
+#define PRCI_CLTXPLLCFG0_DIVR_MASK (0x3f << PRCI_CLTXPLLCFG0_DIVR_SHIFT)
+#define PRCI_CLTXPLLCFG0_DIVF_SHIFT 6
+#define PRCI_CLTXPLLCFG0_DIVF_MASK (0x1ff << PRCI_CLTXPLLCFG0_DIVF_SHIFT)
+#define PRCI_CLTXPLLCFG0_DIVQ_SHIFT 15
+#define PRCI_CLTXPLLCFG0_DIVQ_MASK (0x7 << PRCI_CLTXPLLCFG0_DIVQ_SHIFT)
+#define PRCI_CLTXPLLCFG0_RANGE_SHIFT 18
+#define PRCI_CLTXPLLCFG0_RANGE_MASK (0x7 << PRCI_CLTXPLLCFG0_RANGE_SHIFT)
+#define PRCI_CLTXPLLCFG0_BYPASS_SHIFT 24
+#define PRCI_CLTXPLLCFG0_BYPASS_MASK (0x1 << PRCI_CLTXPLLCFG0_BYPASS_SHIFT)
+#define PRCI_CLTXPLLCFG0_FSE_SHIFT 25
+#define PRCI_CLTXPLLCFG0_FSE_MASK (0x1 << PRCI_CLTXPLLCFG0_FSE_SHIFT)
+#define PRCI_CLTXPLLCFG0_LOCK_SHIFT 31
+#define PRCI_CLTXPLLCFG0_LOCK_MASK (0x1 << PRCI_CLTXPLLCFG0_LOCK_SHIFT)
+
+/* CLTXPLLCFG1 */
+#define PRCI_CLTXPLLCFG1_OFFSET 0x34
+#define PRCI_CLTXPLLCFG1_CKE_SHIFT 31
+#define PRCI_CLTXPLLCFG1_CKE_MASK (0x1 << PRCI_CLTXPLLCFG1_CKE_SHIFT)
+
+/* DVFSCOREPLLCFG0 */
+#define PRCI_DVFSCOREPLLCFG0_OFFSET 0x38
+
+/* DVFSCOREPLLCFG1 */
+#define PRCI_DVFSCOREPLLCFG1_OFFSET 0x3c
+#define PRCI_DVFSCOREPLLCFG1_CKE_SHIFT 31
+#define PRCI_DVFSCOREPLLCFG1_CKE_MASK (0x1 << PRCI_DVFSCOREPLLCFG1_CKE_SHIFT)
+
+/* COREPLLSEL */
+#define PRCI_COREPLLSEL_OFFSET 0x40
+#define PRCI_COREPLLSEL_COREPLLSEL_SHIFT 0
+#define PRCI_COREPLLSEL_COREPLLSEL_MASK \
+ (0x1 << PRCI_COREPLLSEL_COREPLLSEL_SHIFT)
+
+/* HFPCLKPLLCFG0 */
+#define PRCI_HFPCLKPLLCFG0_OFFSET 0x50
+#define PRCI_HFPCLKPLL_CFG0_DIVR_SHIFT 0
+#define PRCI_HFPCLKPLL_CFG0_DIVR_MASK \
+ (0x3f << PRCI_HFPCLKPLLCFG0_DIVR_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_DIVF_SHIFT 6
+#define PRCI_HFPCLKPLL_CFG0_DIVF_MASK \
+ (0x1ff << PRCI_HFPCLKPLLCFG0_DIVF_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_DIVQ_SHIFT 15
+#define PRCI_HFPCLKPLL_CFG0_DIVQ_MASK \
+ (0x7 << PRCI_HFPCLKPLLCFG0_DIVQ_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_RANGE_SHIFT 18
+#define PRCI_HFPCLKPLL_CFG0_RANGE_MASK \
+ (0x7 << PRCI_HFPCLKPLLCFG0_RANGE_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_BYPASS_SHIFT 24
+#define PRCI_HFPCLKPLL_CFG0_BYPASS_MASK \
+ (0x1 << PRCI_HFPCLKPLLCFG0_BYPASS_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_FSE_SHIFT 25
+#define PRCI_HFPCLKPLL_CFG0_FSE_MASK \
+ (0x1 << PRCI_HFPCLKPLLCFG0_FSE_SHIFT)
+#define PRCI_HFPCLKPLL_CFG0_LOCK_SHIFT 31
+#define PRCI_HFPCLKPLL_CFG0_LOCK_MASK \
+ (0x1 << PRCI_HFPCLKPLLCFG0_LOCK_SHIFT)
+
+/* HFPCLKPLLCFG1 */
+#define PRCI_HFPCLKPLLCFG1_OFFSET 0x54
+#define PRCI_HFPCLKPLLCFG1_CKE_SHIFT 31
+#define PRCI_HFPCLKPLLCFG1_CKE_MASK \
+ (0x1 << PRCI_HFPCLKPLLCFG1_CKE_SHIFT)
+
+/* HFPCLKPLLSEL */
+#define PRCI_HFPCLKPLLSEL_OFFSET 0x58
+#define PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_SHIFT 0
+#define PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK \
+ (0x1 << PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_SHIFT)
+
+/* HFPCLKPLLDIV */
+#define PRCI_HFPCLKPLLDIV_OFFSET 0x5c
+
+/* PRCIPLL */
+#define PRCI_PRCIPLL_OFFSET 0xe0
+
+/* PROCMONCFG */
+#define PRCI_PROCMONCFG_OFFSET 0xf0
+
+/*
+ * Private structures
+ */
+
+/**
+ * struct __prci_data - per-device-instance data
+ * @va: base virtual address of the PRCI IP block
+ * @hw_clks: encapsulates struct clk_hw records
+ *
+ * PRCI per-device instance data
+ */
+struct __prci_data {
+ void __iomem *va;
+ struct clk_onecell_data hw_clks;
+};
+
+/**
+ * struct __prci_wrpll_data - WRPLL configuration and integration data
+ * @c: WRPLL current configuration record
+ * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
+ * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
+ * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
+ * @cfg1_offs: WRPLL CFG1 register offset (in bytes) from the PRCI base address
+ *
+ * @enable_bypass and @disable_bypass are used for WRPLL instances
+ * that contain a separate external glitchless clock mux downstream
+ * from the PLL. The WRPLL internal bypass mux is not glitchless.
+ */
+struct __prci_wrpll_data {
+ struct wrpll_cfg c;
+ void (*enable_bypass)(struct __prci_data *pd);
+ void (*disable_bypass)(struct __prci_data *pd);
+ u8 cfg0_offs;
+ u8 cfg1_offs;
+};
+
+/**
+ * struct __prci_clock - describes a clock device managed by PRCI
+ * @name: user-readable clock name string - should match the manual
+ * @parent_name: parent name for this clock
+ * @ops: struct clk_ops for the Linux clock framework to use for control
+ * @hw: Linux-private clock data
+ * @pwd: WRPLL-specific data, associated with this clock (if not NULL)
+ * @pd: PRCI-specific data associated with this clock (if not NULL)
+ *
+ * PRCI clock data. Used by the PRCI driver to register PRCI-provided
+ * clocks to the Linux clock infrastructure.
+ */
+struct __prci_clock {
+ const char *name;
+ const char *parent_name;
+ const struct clk_ops *ops;
+ struct clk hw;
+ struct __prci_wrpll_data *pwd;
+ struct __prci_data *pd;
+};
+
+#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
+
+/*
+ * struct prci_clk_desc - describes the information of clocks of each SoCs
+ * @clks: point to a array of __prci_clock
+ * @num_clks: the number of element of clks
+ */
+struct prci_clk_desc {
+ struct __prci_clock *clks;
+ size_t num_clks;
+};
+
+/* Core clock mux control */
+void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd);
+void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd);
+void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd);
+void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd);
+void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd);
+void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd);
+void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd);
+
+/* Linux clock framework integration */
+long sifive_prci_wrpll_round_rate(struct clk *hw, unsigned long rate,
+ unsigned long *parent_rate);
+int sifive_prci_wrpll_set_rate(struct clk *hw, unsigned long rate,
+ unsigned long parent_rate);
+int sifive_clk_is_enabled(struct clk *hw);
+int sifive_prci_clock_enable(struct clk *hw);
+void sifive_prci_clock_disable(struct clk *hw);
+unsigned long sifive_prci_wrpll_recalc_rate(struct clk *hw,
+ unsigned long parent_rate);
+unsigned long sifive_prci_tlclksel_recalc_rate(struct clk *hw,
+ unsigned long parent_rate);
+unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk *hw,
+ unsigned long parent_rate);
+
+#endif /* __SIFIVE_CLK_SIFIVE_PRCI_H */
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 261b6e6662..a8ee9e58b8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -164,6 +164,13 @@ config GPIO_SX150X
Say Y here to build support for the Semtec Sx150x I2C GPIO
expander chip.
+config GPIO_SIFIVE
+ bool "SiFive GPIO support"
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ help
+ Say yes here to support the GPIO device on SiFive SoCs.
+
config GPIO_LIBFTDI1
bool "libftdi1 driver"
depends on SANDBOX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 77dcf58f64..25e12105d8 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_GPIO_DESIGNWARE) += gpio-dw.o
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
+obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 8259b799f9..713085267a 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -7,63 +7,117 @@
*/
#include <init.h>
-#include <malloc.h>
#include <linux/err.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <errno.h>
#include <linux/log2.h>
-#include <linux/err.h>
+#include <linux/ioport.h>
+#include <io.h>
#include <linux/basic_mmio_gpio.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <driver.h>
+#include <of.h>
+#include <of_device.h>
-static void bgpio_write8(void __iomem *reg, unsigned int data)
+static void bgpio_write8(void __iomem *reg, unsigned long data)
{
writeb(data, reg);
}
-static unsigned int bgpio_read8(void __iomem *reg)
+static unsigned long bgpio_read8(void __iomem *reg)
{
return readb(reg);
}
-static void bgpio_write16(void __iomem *reg, unsigned int data)
+static void bgpio_write16(void __iomem *reg, unsigned long data)
{
writew(data, reg);
}
-static unsigned int bgpio_read16(void __iomem *reg)
+static unsigned long bgpio_read16(void __iomem *reg)
{
return readw(reg);
}
-static void bgpio_write32(void __iomem *reg, unsigned int data)
+static void bgpio_write32(void __iomem *reg, unsigned long data)
{
writel(data, reg);
}
-static unsigned int bgpio_read32(void __iomem *reg)
+static unsigned long bgpio_read32(void __iomem *reg)
{
return readl(reg);
}
-static unsigned int bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin)
+#if BITS_PER_LONG >= 64
+static void bgpio_write64(void __iomem *reg, unsigned long data)
+{
+ writeq(data, reg);
+}
+
+static unsigned long bgpio_read64(void __iomem *reg)
+{
+ return readq(reg);
+}
+#endif /* BITS_PER_LONG >= 64 */
+
+static void bgpio_write16be(void __iomem *reg, unsigned long data)
+{
+ iowrite16be(data, reg);
+}
+
+static unsigned long bgpio_read16be(void __iomem *reg)
+{
+ return ioread16be(reg);
+}
+
+static void bgpio_write32be(void __iomem *reg, unsigned long data)
{
- return 1 << pin;
+ iowrite32be(data, reg);
}
-static unsigned int bgpio_pin2mask_be(struct bgpio_chip *bgc, unsigned int pin)
+static unsigned long bgpio_read32be(void __iomem *reg)
{
- return 1 << (bgc->bits - 1 - pin);
+ return ioread32be(reg);
+}
+
+static unsigned long bgpio_line2mask(struct bgpio_chip *bgc, unsigned int line)
+{
+ if (bgc->be_bits)
+ return BIT(bgc->bits - 1 - line);
+ return BIT(line);
+}
+
+static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ unsigned long pinmask = bgpio_line2mask(bgc, gpio);
+ bool dir = !!(bgc->dir & pinmask);
+
+ if (dir)
+ return !!(bgc->read_reg(bgc->reg_set) & pinmask);
+ else
+ return !!(bgc->read_reg(bgc->reg_dat) & pinmask);
}
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ return !!(bgc->read_reg(bgc->reg_dat) & bgpio_line2mask(bgc, gpio));
+}
- return !!(bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio));
+static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)
+{
}
static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- unsigned int mask = bgc->pin2mask(bgc, gpio);
+ unsigned long mask = bgpio_line2mask(bgc, gpio);
if (val)
bgc->data |= mask;
@@ -77,7 +131,7 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- unsigned int mask = bgc->pin2mask(bgc, gpio);
+ unsigned long mask = bgpio_line2mask(bgc, gpio);
if (val)
bgc->write_reg(bgc->reg_set, mask);
@@ -88,7 +142,7 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- unsigned int mask = bgc->pin2mask(bgc, gpio);
+ unsigned long mask = bgpio_line2mask(bgc, gpio);
if (val)
bgc->data |= mask;
@@ -103,6 +157,12 @@ static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
return 0;
}
+static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ return -EINVAL;
+}
+
static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
int val)
{
@@ -115,69 +175,115 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- bgc->dir &= ~bgc->pin2mask(bgc, gpio);
- bgc->write_reg(bgc->reg_dir, bgc->dir);
+ bgc->dir &= ~bgpio_line2mask(bgc, gpio);
+
+ if (bgc->reg_dir_in)
+ bgc->write_reg(bgc->reg_dir_in, ~bgc->dir);
+ if (bgc->reg_dir_out)
+ bgc->write_reg(bgc->reg_dir_out, bgc->dir);
return 0;
}
-static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
- gc->ops->set(gc, gpio, val);
+ /* Return 0 if output, 1 if input */
+ if (bgc->dir_unreadable) {
+ if (bgc->dir & bgpio_line2mask(bgc, gpio))
+ return GPIOF_DIR_OUT;
+ return GPIOF_DIR_IN;
+ }
- bgc->dir |= bgc->pin2mask(bgc, gpio);
- bgc->write_reg(bgc->reg_dir, bgc->dir);
+ if (bgc->reg_dir_out) {
+ if (bgc->read_reg(bgc->reg_dir_out) & bgpio_line2mask(bgc, gpio))
+ return GPIOF_DIR_OUT;
+ return GPIOF_DIR_IN;
+ }
- return 0;
+ if (bgc->reg_dir_in)
+ if (!(bgc->read_reg(bgc->reg_dir_in) & bgpio_line2mask(bgc, gpio)))
+ return GPIOF_DIR_OUT;
+
+ return GPIOF_DIR_IN;
}
-static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio)
+static void bgpio_dir_out(struct bgpio_chip *bgc, unsigned int gpio, int val)
{
- struct bgpio_chip *bgc = to_bgpio_chip(gc);
-
- bgc->dir |= bgc->pin2mask(bgc, gpio);
- bgc->write_reg(bgc->reg_dir, bgc->dir);
+ bgc->dir |= bgpio_line2mask(bgc, gpio);
- return 0;
+ if (bgc->reg_dir_in)
+ bgc->write_reg(bgc->reg_dir_in, ~bgc->dir);
+ if (bgc->reg_dir_out)
+ bgc->write_reg(bgc->reg_dir_out, bgc->dir);
}
-static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val)
+static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ bgpio_dir_out(bgc, gpio, val);
gc->ops->set(gc, gpio, val);
+ return 0;
+}
- bgc->dir &= ~bgc->pin2mask(bgc, gpio);
- bgc->write_reg(bgc->reg_dir, bgc->dir);
+static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+{
+ struct bgpio_chip *bgc = to_bgpio_chip(gc);
+ gc->ops->set(gc, gpio, val);
+ bgpio_dir_out(bgc, gpio, val);
return 0;
}
-static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc,
- bool be)
+static int bgpio_setup_accessors(struct device_d *dev,
+ struct bgpio_chip *bgc,
+ bool byte_be)
{
+
switch (bgc->bits) {
case 8:
bgc->read_reg = bgpio_read8;
bgc->write_reg = bgpio_write8;
break;
case 16:
- bgc->read_reg = bgpio_read16;
- bgc->write_reg = bgpio_write16;
+ if (byte_be) {
+ bgc->read_reg = bgpio_read16be;
+ bgc->write_reg = bgpio_write16be;
+ } else {
+ bgc->read_reg = bgpio_read16;
+ bgc->write_reg = bgpio_write16;
+ }
break;
case 32:
- bgc->read_reg = bgpio_read32;
- bgc->write_reg = bgpio_write32;
+ if (byte_be) {
+ bgc->read_reg = bgpio_read32be;
+ bgc->write_reg = bgpio_write32be;
+ } else {
+ bgc->read_reg = bgpio_read32;
+ bgc->write_reg = bgpio_write32;
+ }
break;
+#if BITS_PER_LONG >= 64
+ case 64:
+ if (byte_be) {
+ dev_err(dev,
+ "64 bit big endian byte order unsupported\n");
+ return -EINVAL;
+ } else {
+ bgc->read_reg = bgpio_read64;
+ bgc->write_reg = bgpio_write64;
+ }
+ break;
+#endif /* BITS_PER_LONG >= 64 */
default:
- dev_err(dev, "Unsupported data width %u bits\n", bgc->bits);
+ dev_err(dev, "unsupported data width %u bits\n", bgc->bits);
return -EINVAL;
}
- bgc->pin2mask = be ? bgpio_pin2mask_be : bgpio_pin2mask;
-
return 0;
}
@@ -188,7 +294,7 @@ static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc,
* - single input/output register resource (named "dat").
* - set/clear pair (named "set" and "clr").
* - single output register resource and single input resource ("set" and
- * dat").
+ * dat").
*
* For the single output register, this drives a 1 by setting a bit and a zero
* by clearing a bit. For the set clr pair, this drives a 1 by setting a bit
@@ -206,123 +312,211 @@ static int bgpio_setup_accessors(struct device_d *dev, struct bgpio_chip *bgc,
static int bgpio_setup_io(struct bgpio_chip *bgc,
void __iomem *dat,
void __iomem *set,
- void __iomem *clr)
+ void __iomem *clr,
+ unsigned long flags)
{
- if (!dat)
- return -EINVAL;
+ struct gpio_ops *ops = bgc->gc.ops;
bgc->reg_dat = dat;
+ if (!bgc->reg_dat)
+ return -EINVAL;
if (set && clr) {
bgc->reg_set = set;
bgc->reg_clr = clr;
- bgc->gc.ops->set = bgpio_set_with_clear;
+ ops->set = bgpio_set_with_clear;
} else if (set && !clr) {
bgc->reg_set = set;
- bgc->gc.ops->set = bgpio_set_set;
- } else
- bgc->gc.ops->set = bgpio_set;
+ ops->set = bgpio_set_set;
+ } else if (flags & BGPIOF_NO_OUTPUT) {
+ ops->set = bgpio_set_none;
+ } else {
+ ops->set = bgpio_set;
+ }
- bgc->gc.ops->get = bgpio_get;
+ if (!(flags & BGPIOF_UNREADABLE_REG_SET) && (flags & BGPIOF_READ_OUTPUT_REG_SET))
+ ops->get = bgpio_get_set;
+ else
+ ops->get = bgpio_get;
return 0;
}
static int bgpio_setup_direction(struct bgpio_chip *bgc,
void __iomem *dirout,
- void __iomem *dirin)
+ void __iomem *dirin,
+ unsigned long flags)
{
- if (dirout && dirin)
- return -EINVAL;
-
- if (dirout) {
- bgc->reg_dir = dirout;
- bgc->gc.ops->direction_output = bgpio_dir_out;
- bgc->gc.ops->direction_input = bgpio_dir_in;
- } else if (dirin) {
- bgc->reg_dir = dirin;
- bgc->gc.ops->direction_output = bgpio_dir_out_inv;
- bgc->gc.ops->direction_input = bgpio_dir_in_inv;
+ struct gpio_ops *ops = bgc->gc.ops;
+
+ if (dirout || dirin) {
+ bgc->reg_dir_out = dirout;
+ bgc->reg_dir_in = dirin;
+ if (flags & BGPIOF_NO_SET_ON_INPUT)
+ ops->direction_output = bgpio_dir_out_dir_first;
+ else
+ ops->direction_output = bgpio_dir_out_val_first;
+ ops->direction_input = bgpio_dir_in;
+ ops->get_direction = bgpio_get_dir;
} else {
- bgc->gc.ops->direction_output = bgpio_simple_dir_out;
- bgc->gc.ops->direction_input = bgpio_simple_dir_in;
+ if (flags & BGPIOF_NO_OUTPUT)
+ ops->direction_output = bgpio_dir_out_err;
+ else
+ ops->direction_output = bgpio_simple_dir_out;
+ ops->direction_input = bgpio_simple_dir_in;
}
return 0;
}
+static int bgpio_request(struct gpio_chip *chip, unsigned gpio_pin)
+{
+ if (gpio_pin < chip->ngpio)
+ return 0;
+
+ return -EINVAL;
+}
+
+/**
+ * bgpio_init() - Initialize generic GPIO accessor functions
+ * @bgc: the GPIO chip to set up
+ * @dev: the parent device of the new GPIO chip (compulsory)
+ * @sz: the size (width) of the MMIO registers in bytes, typically 1, 2 or 4
+ * @dat: MMIO address for the register to READ the value of the GPIO lines, it
+ * is expected that a 1 in the corresponding bit in this register means the
+ * line is asserted
+ * @set: MMIO address for the register to SET the value of the GPIO lines, it is
+ * expected that we write the line with 1 in this register to drive the GPIO line
+ * high.
+ * @clr: MMIO address for the register to CLEAR the value of the GPIO lines, it is
+ * expected that we write the line with 1 in this register to drive the GPIO line
+ * low. It is allowed to leave this address as NULL, in that case the SET register
+ * will be assumed to also clear the GPIO lines, by actively writing the line
+ * with 0.
+ * @dirout: MMIO address for the register to set the line as OUTPUT. It is assumed
+ * that setting a line to 1 in this register will turn that line into an
+ * output line. Conversely, setting the line to 0 will turn that line into
+ * an input.
+ * @dirin: MMIO address for the register to set this line as INPUT. It is assumed
+ * that setting a line to 1 in this register will turn that line into an
+ * input line. Conversely, setting the line to 0 will turn that line into
+ * an output.
+ * @flags: Different flags that will affect the behaviour of the device, such as
+ * endianness etc.
+ */
int bgpio_init(struct bgpio_chip *bgc, struct device_d *dev,
unsigned int sz, void __iomem *dat, void __iomem *set,
void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
unsigned long flags)
{
+ struct gpio_ops *ops = &bgc->ops;
int ret;
- if ((sz > 4) || !is_power_of_2(sz))
+ if (!is_power_of_2(sz))
return -EINVAL;
bgc->bits = sz * 8;
- bgc->gc.ngpio = bgc->bits;
+ if (bgc->bits > BITS_PER_LONG)
+ return -EINVAL;
+
bgc->gc.base = -1;
+ bgc->gc.ngpio = bgc->bits;
bgc->gc.dev = dev;
- bgc->gc.ops = &bgc->ops;
+ bgc->gc.ops = ops;
+ ops->request = bgpio_request;
+ bgc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN);
- ret = bgpio_setup_io(bgc, dat, set, clr);
+ ret = bgpio_setup_io(bgc, dat, set, clr, flags);
if (ret)
return ret;
- ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN);
+ ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return ret;
- ret = bgpio_setup_direction(bgc, dirout, dirin);
+ ret = bgpio_setup_direction(bgc, dirout, dirin, flags);
if (ret)
return ret;
bgc->data = bgc->read_reg(bgc->reg_dat);
-
- if (bgc->gc.ops->set == bgpio_set_set && !(flags &
- BGPIOF_UNREADABLE_REG_SET))
+ if (ops->set == bgpio_set_set &&
+ !(flags & BGPIOF_UNREADABLE_REG_SET))
bgc->data = bgc->read_reg(bgc->reg_set);
- if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR))
- bgc->dir = bgc->read_reg(bgc->reg_dir);
+ if (flags & BGPIOF_UNREADABLE_REG_DIR)
+ bgc->dir_unreadable = true;
+
+ /*
+ * Inspect hardware to find initial direction setting.
+ */
+ if ((bgc->reg_dir_out || bgc->reg_dir_in) &&
+ !(flags & BGPIOF_UNREADABLE_REG_DIR)) {
+ if (bgc->reg_dir_out)
+ bgc->dir = bgc->read_reg(bgc->reg_dir_out);
+ else if (bgc->reg_dir_in)
+ bgc->dir = ~bgc->read_reg(bgc->reg_dir_in);
+ /*
+ * If we have two direction registers, synchronise
+ * input setting to output setting, the library
+ * can not handle a line being input and output at
+ * the same time.
+ */
+ if (bgc->reg_dir_out && bgc->reg_dir_in)
+ bgc->write_reg(bgc->reg_dir_in, ~bgc->dir);
+ }
return ret;
}
+EXPORT_SYMBOL_GPL(bgpio_init);
void bgpio_remove(struct bgpio_chip *bgc)
{
gpiochip_remove(&bgc->gc);
free(bgc);
}
+EXPORT_SYMBOL_GPL(bgpio_remove);
#ifdef CONFIG_GPIO_GENERIC_PLATFORM
-static void __iomem *bgpio_map(struct device_d *dev, const char *name,
- resource_size_t sane_sz, int *err)
+static void __iomem *bgpio_map(struct device_d *dev,
+ const char *name,
+ resource_size_t sane_sz)
{
struct resource *r;
- struct resource *ret;
+ resource_size_t sz;
- *err = 0;
-
- r = dev_get_resource_by_name(dev, IORESOURCE_MEM, name);
+ r = dev_request_mem_resource_by_name(dev, name);
if (IS_ERR(r))
return NULL;
- if (resource_size(r) != sane_sz) {
- *err = -EINVAL;
- return NULL;
- }
+ sz = resource_size(r);
+ if (sz != sane_sz)
+ return IOMEM_ERR_PTR(-EINVAL);
+
+ return IOMEM(r->start);
+}
+
+static const struct of_device_id bgpio_of_match[];
+
+static struct bgpio_pdata *bgpio_parse_dt(struct device_d *dev,
+ unsigned long *flags)
+{
+ struct bgpio_pdata *pdata;
- ret = request_iomem_region(dev_name(dev), r->start, r->end);
- if (IS_ERR(ret)) {
- *err = PTR_ERR(ret);
+ if (!of_match_device(bgpio_of_match, dev))
return NULL;
- }
- return IOMEM(ret->start);
+ pdata = xzalloc(sizeof(struct bgpio_pdata));
+
+ pdata->base = -1;
+
+ if (of_device_is_big_endian(dev->device_node))
+ *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER;
+
+ if (of_property_read_bool(dev->device_node, "no-output"))
+ *flags |= BGPIOF_NO_OUTPUT;
+
+ return pdata;
}
static int bgpio_dev_probe(struct device_d *dev)
@@ -337,35 +531,37 @@ static int bgpio_dev_probe(struct device_d *dev)
unsigned long flags = 0;
int err;
struct bgpio_chip *bgc;
- struct bgpio_pdata *pdata = dev->platform_data;
+ struct bgpio_pdata *pdata;
- r = dev_get_resource_by_name(dev, IORESOURCE_MEM, "dat");
- if (IS_ERR(r))
- return PTR_ERR(r);
+ pdata = bgpio_parse_dt(dev, &flags);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
- sz = resource_size(r);
+ r = dev_request_mem_resource_by_name(dev, "dat");
+ if (!r)
+ return -EINVAL;
- dat = bgpio_map(dev, "dat", sz, &err);
- if (!dat)
- return err ? err : -EINVAL;
+ sz = resource_size(r);
- set = bgpio_map(dev, "set", sz, &err);
- if (err)
- return err;
+ dat = bgpio_map(dev, "dat", sz);
+ if (IS_ERR(dat))
+ return PTR_ERR(dat);
- clr = bgpio_map(dev, "clr", sz, &err);
- if (err)
- return err;
+ set = bgpio_map(dev, "set", sz);
+ if (IS_ERR(set))
+ return PTR_ERR(set);
- dirout = bgpio_map(dev, "dirout", sz, &err);
- if (err)
- return err;
+ clr = bgpio_map(dev, "clr", sz);
+ if (IS_ERR(clr))
+ return PTR_ERR(clr);
- dirin = bgpio_map(dev, "dirin", sz, &err);
- if (err)
- return err;
+ dirout = bgpio_map(dev, "dirout", sz);
+ if (IS_ERR(dirout))
+ return PTR_ERR(dirout);
- dev_get_drvdata(dev, (const void **)&flags);
+ dirin = bgpio_map(dev, "dirin", sz);
+ if (IS_ERR(dirin))
+ return PTR_ERR(dirin);
bgc = xzalloc(sizeof(struct bgpio_chip));
@@ -373,11 +569,11 @@ static int bgpio_dev_probe(struct device_d *dev)
if (err)
return err;
- if (pdata) {
- bgc->gc.base = pdata->base;
- if (pdata->ngpio > 0)
- bgc->gc.ngpio = pdata->ngpio;
- }
+ bgc->gc.base = pdata->base;
+ bgc->gc.dev = dev;
+ bgc->gc.ops = &bgc->ops;
+ if (pdata->ngpio > 0)
+ bgc->gc.ngpio = pdata->ngpio;
dev->priv = bgc;
@@ -391,21 +587,15 @@ static void bgpio_dev_remove(struct device_d *dev)
bgpio_remove(bgc);
}
-static struct platform_device_id bgpio_id_table[] = {
+static const struct of_device_id bgpio_of_match[] = {
{
- .name = "basic-mmio-gpio",
- .driver_data = 0,
+ .compatible = "wd,mbl-gpio",
},
{
- .name = "basic-mmio-gpio-be",
- .driver_data = BGPIOF_BIG_ENDIAN,
+ .compatible = "brcm,bcm6345-gpio"
},
- { }
-};
-
-static struct of_device_id __maybe_unused bgpio_of_match[] = {
{
- .compatible = "wd,mbl-gpio",
+ .compatible = "ni,169445-nand-gpio"
}, {
/* sentinel */
}
@@ -413,8 +603,7 @@ static struct of_device_id __maybe_unused bgpio_of_match[] = {
static struct driver_d bgpio_driver = {
.name = "basic-mmio-gpio",
- .id_table = bgpio_id_table,
- .of_compatible = DRV_OF_COMPAT(bgpio_of_match),
+ .of_compatible = bgpio_of_match,
.probe = bgpio_dev_probe,
.remove = bgpio_dev_remove,
};
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 1ef459684d..d48a8aa7fb 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -65,7 +65,8 @@ static int mpc8xxx_probe(struct device_d *dev)
ret = bgpio_init(bgc, dev, 4,
mpc8xxx_gc->regs + GPIO_DAT,
NULL, NULL,
- mpc8xxx_gc->regs + GPIO_DIR, NULL, 0);
+ mpc8xxx_gc->regs + GPIO_DIR, NULL,
+ BGPIOF_BIG_ENDIAN);
if (ret)
goto err;
dev_dbg(dev, "GPIO registers are LITTLE endian\n");
@@ -74,7 +75,8 @@ static int mpc8xxx_probe(struct device_d *dev)
mpc8xxx_gc->regs + GPIO_DAT,
NULL, NULL,
mpc8xxx_gc->regs + GPIO_DIR, NULL,
- BGPIOF_BIG_ENDIAN);
+ BGPIOF_BIG_ENDIAN
+ | BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
goto err;
dev_dbg(dev, "GPIO registers are BIG endian\n");
diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
new file mode 100644
index 0000000000..63f2c097e4
--- /dev/null
+++ b/drivers/gpio/gpio-sifive.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 SiFive
+ */
+
+#include <linux/basic_mmio_gpio.h>
+#include <printk.h>
+#include <driver.h>
+#include <errno.h>
+
+#define SIFIVE_GPIO_INPUT_VAL 0x00
+#define SIFIVE_GPIO_INPUT_EN 0x04
+#define SIFIVE_GPIO_OUTPUT_EN 0x08
+#define SIFIVE_GPIO_OUTPUT_VAL 0x0C
+#define SIFIVE_GPIO_RISE_IE 0x18
+#define SIFIVE_GPIO_FALL_IE 0x20
+#define SIFIVE_GPIO_HIGH_IE 0x28
+#define SIFIVE_GPIO_LOW_IE 0x30
+
+#define SIFIVE_GPIO_MAX 32
+
+static int __of_irq_count(struct device_node *np)
+{
+ unsigned npins = 0;
+
+ of_get_property(np, "interrupts", &npins);
+
+ return npins / sizeof(__be32);
+}
+
+static int sifive_gpio_probe(struct device_d *dev)
+{
+ struct bgpio_chip *bgc;
+ struct resource *res;
+ void __iomem *base;
+ int ret, ngpio;
+
+ bgc = xzalloc(sizeof(*bgc));
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ dev_err(dev, "failed to request device memory\n");
+ return PTR_ERR(res);
+ }
+ base = IOMEM(res->start);
+
+ ngpio = __of_irq_count(dev->device_node);
+ if (ngpio > SIFIVE_GPIO_MAX) {
+ dev_err(dev, "Too many GPIO interrupts (max=%d)\n",
+ SIFIVE_GPIO_MAX);
+ return -ENXIO;
+ }
+
+ ret = bgpio_init(bgc, dev, 4,
+ base + SIFIVE_GPIO_INPUT_VAL,
+ base + SIFIVE_GPIO_OUTPUT_VAL,
+ NULL,
+ base + SIFIVE_GPIO_OUTPUT_EN,
+ base + SIFIVE_GPIO_INPUT_EN,
+ 0);
+ if (ret) {
+ dev_err(dev, "unable to init generic GPIO\n");
+ return ret;
+ }
+
+ /* Disable all GPIO interrupts */
+ writel(0, base + SIFIVE_GPIO_RISE_IE);
+ writel(0, base + SIFIVE_GPIO_FALL_IE);
+ writel(0, base + SIFIVE_GPIO_HIGH_IE);
+ writel(0, base + SIFIVE_GPIO_LOW_IE);
+
+ bgc->gc.ngpio = ngpio;
+ return gpiochip_add(&bgc->gc);
+}
+
+static const struct of_device_id sifive_gpio_match[] = {
+ { .compatible = "sifive,gpio0" },
+ { .compatible = "sifive,fu540-c000-gpio" },
+ { },
+};
+
+static struct driver_d sifive_gpio_driver = {
+ .name = "sifive_gpio",
+ .of_compatible = sifive_gpio_match,
+ .probe = sifive_gpio_probe,
+};
+postcore_platform_driver(sifive_gpio_driver);
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index db924efa02..b9750d1774 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -164,4 +164,13 @@ config VIRTIO_CONSOLE
Also serves as a general-purpose serial device for data
transfer between the guest and host.
+
+config SERIAL_SIFIVE
+ tristate "SiFive UART support"
+ depends on OFDEVICE
+ help
+ Select this option if you are building barebox for a device that
+ contains a SiFive UART IP block. This type of UART is present on
+ SiFive FU540 SoCs, among others.
+
endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 7ff41cd5c7..5120b17376 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_DRIVER_SERIAL_EFI_STDIO) += efi-stdio.o
obj-$(CONFIG_DRIVER_SERIAL_DIGIC) += serial_digic.o
obj-$(CONFIG_DRIVER_SERIAL_LPUART) += serial_lpuart.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
+obj-$(CONFIG_SERIAL_SIFIVE) += serial_sifive.o
diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c
new file mode 100644
index 0000000000..45f7c2bc9a
--- /dev/null
+++ b/drivers/serial/serial_sifive.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Anup Patel <anup@brainfault.org>
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <malloc.h>
+#include <io.h>
+#include <of.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#define UART_TXFIFO_FULL 0x80000000
+#define UART_RXFIFO_EMPTY 0x80000000
+#define UART_RXFIFO_DATA 0x000000ff
+#define UART_TXCTRL_TXEN 0x1
+#define UART_RXCTRL_RXEN 0x1
+
+/* IP register */
+#define UART_IP_RXWM 0x2
+
+struct sifive_serial_regs {
+ u32 txfifo;
+ u32 rxfifo;
+ u32 txctrl;
+ u32 rxctrl;
+ u32 ie;
+ u32 ip;
+ u32 div;
+};
+
+struct sifive_serial_priv {
+ unsigned long freq;
+ struct sifive_serial_regs __iomem *regs;
+ struct console_device cdev;
+};
+
+#define to_priv(cdev) container_of(cdev, struct sifive_serial_priv, cdev)
+
+/**
+ * Find minimum divisor divides in_freq to max_target_hz;
+ * Based on uart driver n SiFive FSBL.
+ *
+ * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1
+ * The nearest integer solution requires rounding up as to not exceed
+ * max_target_hz.
+ * div = ceil(f_in / f_baud) - 1
+ * = floor((f_in - 1 + f_baud) / f_baud) - 1
+ * This should not overflow as long as (f_in - 1 + f_baud) does not exceed
+ * 2^32 - 1, which is unlikely since we represent frequencies in kHz.
+ */
+static inline unsigned int uart_min_clk_divisor(unsigned long in_freq,
+ unsigned long max_target_hz)
+{
+ unsigned long quotient =
+ (in_freq + max_target_hz - 1) / (max_target_hz);
+ /* Avoid underflow */
+ if (quotient == 0)
+ return 0;
+ else
+ return quotient - 1;
+}
+
+static void sifive_serial_init(struct sifive_serial_regs __iomem *regs)
+{
+ writel(UART_TXCTRL_TXEN, &regs->txctrl);
+ writel(UART_RXCTRL_RXEN, &regs->rxctrl);
+ writel(0, &regs->ie);
+}
+
+static int sifive_serial_setbrg(struct console_device *cdev, int baudrate)
+{
+ struct sifive_serial_priv *priv = to_priv(cdev);
+
+ writel((uart_min_clk_divisor(priv->freq, baudrate)), &priv->regs->div);
+
+ return 0;
+}
+
+static int sifive_serial_getc(struct console_device *cdev)
+{
+ struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs;
+ u32 ch;
+
+ do {
+ ch = readl(&regs->rxfifo);
+ } while (ch & UART_RXFIFO_EMPTY);
+
+ return ch & UART_RXFIFO_DATA;
+}
+
+static void sifive_serial_putc(struct console_device *cdev, const char ch)
+{
+ struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs;
+
+ // TODO: how to check for !empty to utilize fifo?
+ while (readl(&regs->txfifo) & UART_TXFIFO_FULL)
+ ;
+
+ writel(ch, &regs->txfifo);
+}
+
+static int sifive_serial_tstc(struct console_device *cdev)
+{
+ struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs;
+
+ return readl(&regs->ip) & UART_IP_RXWM;
+}
+
+static void sifive_serial_flush(struct console_device *cdev)
+{
+ struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs;
+
+ while (readl(&regs->txfifo) & UART_TXFIFO_FULL)
+ ;
+}
+
+static int sifive_serial_probe(struct device_d *dev)
+{
+ struct sifive_serial_priv *priv;
+ struct resource *iores;
+ struct clk *clk;
+ u32 freq;
+ int ret;
+
+ clk = clk_get(dev, NULL);
+ if (!IS_ERR(clk)) {
+ freq = clk_get_rate(clk);
+ } else {
+ dev_dbg(dev, "failed to get clock. Fallback to device tree.\n");
+
+ ret = of_property_read_u32(dev->device_node, "clock-frequency", &freq);
+ if (ret) {
+ dev_warn(dev, "unknown clock frequency\n");
+ return ret;
+ }
+ }
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->freq = freq;
+ priv->regs = IOMEM(iores->start);
+
+ priv->cdev.dev = dev;
+ priv->cdev.putc = sifive_serial_putc;
+ priv->cdev.getc = sifive_serial_getc;
+ priv->cdev.tstc = sifive_serial_tstc;
+ priv->cdev.flush = sifive_serial_flush;
+ priv->cdev.setbrg = sifive_serial_setbrg,
+
+ sifive_serial_init(priv->regs);
+
+ return console_register(&priv->cdev);
+}
+
+static __maybe_unused struct of_device_id sifive_serial_dt_ids[] = {
+ { .compatible = "sifive,uart0" },
+ { /* sentinel */ }
+};
+
+static struct driver_d serial_sifive_driver = {
+ .name = "serial_sifive",
+ .probe = sifive_serial_probe,
+ .of_compatible = sifive_serial_dt_ids,
+};
+console_platform_driver(serial_sifive_driver);
diff --git a/images/Makefile.riscv b/images/Makefile.riscv
index 463c6ce440..c44c683431 100644
--- a/images/Makefile.riscv
+++ b/images/Makefile.riscv
@@ -10,3 +10,8 @@ $(obj)/%.nmon: $(obj)/%.img FORCE
pblb-$(CONFIG_BOARD_ERIZO_GENERIC) += start_erizo_generic
FILE_barebox-erizo-generic.img = start_erizo_generic.pblb
image-$(CONFIG_BOARD_ERIZO_GENERIC) += barebox-erizo-generic.img barebox-erizo-generic.nmon
+
+pblb-$(CONFIG_BOARD_HIFIVE) += start_hifive_unmatched start_hifive_unleashed
+FILE_barebox-hifive-unmatched.img = start_hifive_unmatched.pblb
+FILE_barebox-hifive-unleashed.img = start_hifive_unleashed.pblb
+image-$(CONFIG_BOARD_HIFIVE) += barebox-hifive-unmatched.img barebox-hifive-unleashed.img
diff --git a/include/bootm.h b/include/bootm.h
index 51e9b3d71a..655c5152d9 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -148,4 +148,6 @@ enum bootm_verify bootm_get_verify_mode(void);
#define UIMAGE_SOME_ADDRESS (UIMAGE_INVALID_ADDRESS - 1)
+void *booti_load_image(struct image_data *data, phys_addr_t *oftree);
+
#endif /* __BOOTM_H */
diff --git a/include/debug_ll.h b/include/debug_ll.h
index 5bd1afe6ac..735033b314 100644
--- a/include/debug_ll.h
+++ b/include/debug_ll.h
@@ -12,6 +12,9 @@
#define __INCLUDE_DEBUG_LL_H__
#ifdef CONFIG_HAS_DEBUG_LL
+#ifdef CONFIG_HAS_ASM_DEBUG_LL
+#include <asm/debug_ll.h>
+#else
/*
* mach/debug_ll.h should implement PUTC_LL. This can be a macro or a static
* inline function. Note that several SoCs expect the UART to be initialized
@@ -21,6 +24,7 @@
*/
#include <mach/debug_ll.h>
#endif
+#endif
#if defined (CONFIG_DEBUG_LL)
diff --git a/include/io.h b/include/io.h
index 9130020722..79d8b56c4e 100644
--- a/include/io.h
+++ b/include/io.h
@@ -4,4 +4,6 @@
#include <asm/io.h>
+#define IOMEM_ERR_PTR(err) (__force void __iomem *)ERR_PTR(err)
+
#endif /* __IO_H */
diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h
index e927194b51..34e2f470fb 100644
--- a/include/linux/basic_mmio_gpio.h
+++ b/include/linux/basic_mmio_gpio.h
@@ -27,13 +27,17 @@ struct bgpio_chip {
struct gpio_chip gc;
struct gpio_ops ops;
- unsigned int (*read_reg)(void __iomem *reg);
- void (*write_reg)(void __iomem *reg, unsigned int data);
+ unsigned long (*read_reg)(void __iomem *reg);
+ void (*write_reg)(void __iomem *reg, unsigned long data);
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
- void __iomem *reg_dir;
+ void __iomem *reg_dir_out;
+ void __iomem *reg_dir_in;
+
+ bool dir_unreadable;
+ bool be_bits;
/* Number of bits (GPIOs): <register width> * 8. */
int bits;
@@ -65,5 +69,9 @@ void bgpio_remove(struct bgpio_chip *bgc);
#define BGPIOF_BIG_ENDIAN BIT(0)
#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */
#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */
+#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3)
+#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
+#define BGPIOF_NO_OUTPUT BIT(5) /* only input */
+#define BGPIOF_NO_SET_ON_INPUT BIT(6)
#endif /* __BASIC_MMIO_GPIO_H */
diff --git a/include/linux/clk/analogbits-wrpll-cln28hpc.h b/include/linux/clk/analogbits-wrpll-cln28hpc.h
new file mode 100644
index 0000000000..03279097e1
--- /dev/null
+++ b/include/linux/clk/analogbits-wrpll-cln28hpc.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+ * Wesley Terpstra
+ * Paul Walmsley
+ */
+
+#ifndef __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H
+#define __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H
+
+#include <linux/types.h>
+
+/* DIVQ_VALUES: number of valid DIVQ values */
+#define DIVQ_VALUES 6
+
+/*
+ * Bit definitions for struct wrpll_cfg.flags
+ *
+ * WRPLL_FLAGS_BYPASS_FLAG: if set, the PLL is either in bypass, or should be
+ * programmed to enter bypass
+ * WRPLL_FLAGS_RESET_FLAG: if set, the PLL is in reset
+ * WRPLL_FLAGS_INT_FEEDBACK_FLAG: if set, the PLL is configured for internal
+ * feedback mode
+ * WRPLL_FLAGS_EXT_FEEDBACK_FLAG: if set, the PLL is configured for external
+ * feedback mode (not yet supported by this driver)
+ */
+#define WRPLL_FLAGS_BYPASS_SHIFT 0
+#define WRPLL_FLAGS_BYPASS_MASK BIT(WRPLL_FLAGS_BYPASS_SHIFT)
+#define WRPLL_FLAGS_RESET_SHIFT 1
+#define WRPLL_FLAGS_RESET_MASK BIT(WRPLL_FLAGS_RESET_SHIFT)
+#define WRPLL_FLAGS_INT_FEEDBACK_SHIFT 2
+#define WRPLL_FLAGS_INT_FEEDBACK_MASK BIT(WRPLL_FLAGS_INT_FEEDBACK_SHIFT)
+#define WRPLL_FLAGS_EXT_FEEDBACK_SHIFT 3
+#define WRPLL_FLAGS_EXT_FEEDBACK_MASK BIT(WRPLL_FLAGS_EXT_FEEDBACK_SHIFT)
+
+/**
+ * struct wrpll_cfg - WRPLL configuration values
+ * @divr: reference divider value (6 bits), as presented to the PLL signals
+ * @divf: feedback divider value (9 bits), as presented to the PLL signals
+ * @divq: output divider value (3 bits), as presented to the PLL signals
+ * @flags: PLL configuration flags. See above for more information
+ * @range: PLL loop filter range. See below for more information
+ * @output_rate_cache: cached output rates, swept across DIVQ
+ * @parent_rate: PLL refclk rate for which values are valid
+ * @max_r: maximum possible R divider value, given @parent_rate
+ * @init_r: initial R divider value to start the search from
+ *
+ * @divr, @divq, @divq, @range represent what the PLL expects to see
+ * on its input signals. Thus @divr and @divf are the actual divisors
+ * minus one. @divq is a power-of-two divider; for example, 1 =
+ * divide-by-2 and 6 = divide-by-64. 0 is an invalid @divq value.
+ *
+ * When initially passing a struct wrpll_cfg record, the
+ * record should be zero-initialized with the exception of the @flags
+ * field. The only flag bits that need to be set are either
+ * WRPLL_FLAGS_INT_FEEDBACK or WRPLL_FLAGS_EXT_FEEDBACK.
+ */
+struct wrpll_cfg {
+ u8 divr;
+ u8 divq;
+ u8 range;
+ u8 flags;
+ u16 divf;
+/* private: */
+ u32 output_rate_cache[DIVQ_VALUES];
+ unsigned long parent_rate;
+ u8 max_r;
+ u8 init_r;
+};
+
+int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
+ unsigned long parent_rate);
+
+unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c);
+
+unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
+ unsigned long parent_rate);
+
+#endif /* __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H */
diff --git a/include/pbl.h b/include/pbl.h
index 194d5e7508..f58daec735 100644
--- a/include/pbl.h
+++ b/include/pbl.h
@@ -34,4 +34,13 @@ ssize_t pbl_fat_load(struct pbl_bio *, const char *filename, void *dest, size_t
void fdt_find_mem(const void *fdt, unsigned long *membase, unsigned long *memsize);
+struct fdt_device_id {
+ const char *compatible;
+ const void *data;
+};
+
+const void *
+fdt_device_get_match_data(const void *fdt, const char *nodepath,
+ const struct fdt_device_id ids[]);
+
#endif /* __PBL_H__ */
diff --git a/pbl/fdt.c b/pbl/fdt.c
index b4a40a514b..18ddb9f48a 100644
--- a/pbl/fdt.c
+++ b/pbl/fdt.c
@@ -68,3 +68,38 @@ err:
pr_err("No memory, cannot continue\n");
while (1);
}
+
+const void *fdt_device_get_match_data(const void *fdt, const char *nodepath,
+ const struct fdt_device_id ids[])
+{
+ int node, length;
+ const char *list, *end;
+ const struct fdt_device_id *id;
+
+ node = fdt_path_offset(fdt, nodepath);
+ if (node < 0)
+ return NULL;
+
+ list = fdt_getprop(fdt, node, "compatible", &length);
+ if (!list)
+ return NULL;
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return NULL;
+
+ for (id = ids; id->compatible; id++) {
+ if (!strcasecmp(list, id->compatible))
+ return id->data;
+ }
+
+ list += length;
+ }
+
+ return NULL;
+}
diff --git a/pbl/string.c b/pbl/string.c
index e6c0997ebc..e96eb99fc2 100644
--- a/pbl/string.c
+++ b/pbl/string.c
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/compiler.h>
+#include <linux/ctype.h>
void *memcpy(void *__dest, __const void *__src, size_t __n)
{
@@ -98,6 +99,17 @@ int strcmp(const char *cs, const char *ct)
return res;
}
+int strcasecmp(const char *s1, const char *s2)
+{
+ int c1, c2;
+
+ do {
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ } while (c1 == c2 && c1 != 0);
+ return c1 - c2;
+}
+
void *memchr(const void *s, int c, size_t count)
{
const unsigned char *p = s;