--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=riscv64
+BOARD:=hifiveu
+BOARDNAME:=SiFive RISC-V boards
+FEATURES:=ext4
+KERNELNAME:=Image dtbs
+MAINTAINER:=Zoltan HERPAI <wigyori@uid0.hu>
+
+KERNEL_PATCHVER:=5.10
+
+include $(INCLUDE_DIR)/target.mk
+
+define Target/Description
+ Build firmware images for the HiFive boards
+endef
+
+$(eval $(call BuildTarget))
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+
+. /lib/functions/uci-defaults.sh
+
+board_config_update
+
+case "$(board_name)" in
+sifive,hifive-unleashed-a00)
+ ucidef_set_led_netdev "lan" "LAN" "green:d3" "eth0"
+ ;;
+sifive,hifive-unmatched-a00)
+ ucidef_set_led_netdev "lan" "LAN" "green:d2" "eth0"
+ ;;
+esac
+
+board_config_flush
+
+exit 0
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+
+. /lib/functions/uci-defaults.sh
+
+board_config_update
+
+case "$(board_name)" in
+*)
+ ucidef_set_interface_lan 'eth0'
+ ;;
+esac
+
+board_config_flush
+
+exit 0
--- /dev/null
+::sysinit:/etc/init.d/rcS S boot
+::shutdown:/etc/init.d/rcS K shutdown
+tts/0::askfirst:/usr/libexec/login.sh
+ttyS0::askfirst:/usr/libexec/login.sh
+ttySIF0::askfirst:/usr/libexec/login.sh
+tty1::askfirst:/usr/libexec/login.sh
--- /dev/null
+CONFIG_64BIT=y
+CONFIG_ARCH_CLOCKSOURCE_INIT=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_ARCH_MMAP_RND_BITS=18
+CONFIG_ARCH_MMAP_RND_BITS_MAX=24
+CONFIG_ARCH_MMAP_RND_BITS_MIN=18
+CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
+CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
+# CONFIG_ARCH_RV32I is not set
+CONFIG_ARCH_RV64I=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ASN1=y
+CONFIG_ASSOCIATIVE_ARRAY=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_ASYMMETRIC_TPM_KEY_SUBTYPE=y
+CONFIG_ATA=y
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_MQ_PCI=y
+CONFIG_BLK_MQ_VIRTIO=y
+CONFIG_BLK_SCSI_REQUEST=y
+CONFIG_CAVIUM_PTP=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC=y
+CONFIG_CLK_SIFIVE=y
+CONFIG_CLK_SIFIVE_FU540_PRCI=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_CLZ_TAB=y
+CONFIG_CMODEL_MEDANY=y
+# CONFIG_CMODEL_MEDLOW is not set
+CONFIG_COMMON_CLK=y
+# CONFIG_COMPAT_32BIT_TIME is not set
+CONFIG_COMPAT_BRK=y
+CONFIG_COREDUMP=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_CPU_ISOLATION=y
+CONFIG_CPU_RMAP=y
+CONFIG_CRC16=y
+# CONFIG_CRC32_SARWATE is not set
+CONFIG_CRC32_SLICEBY8=y
+CONFIG_CRC7=y
+CONFIG_CRC_ITU_T=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DRBG=y
+CONFIG_CRYPTO_DRBG_HMAC=y
+CONFIG_CRYPTO_DRBG_MENU=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_GF128MUL=y
+CONFIG_CRYPTO_HASH_INFO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_JITTERENTROPY=y
+CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
+CONFIG_CRYPTO_LIB_SHA256=y
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_RNG_DEFAULT=y
+CONFIG_CRYPTO_RSA=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_DA9063_WATCHDOG=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DEVMEM=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DNOTIFY=y
+CONFIG_DTC=y
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_LEGACY_SYSFS=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_EDAC_SIFIVE=y
+CONFIG_ELF_CORE=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_ERRATA_SIFIVE_CIP_453=y
+CONFIG_ERRATA_SIFIVE_CIP_1200=y
+CONFIG_EXT4_FS=y
+CONFIG_FAILOVER=y
+CONFIG_FHANDLE=y
+CONFIG_FIXED_PHY=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_FPU=y
+CONFIG_FRAME_POINTER=y
+CONFIG_FRAME_WARN=2048
+CONFIG_FS_IOMAP=y
+CONFIG_FS_MBCACHE=y
+CONFIG_FW_LOADER_PAGED_BUF=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_ARCH_TOPOLOGY=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CSUM=y
+CONFIG_GENERIC_EARLY_IOREMAP=y
+CONFIG_GENERIC_GETTIMEOFDAY=y
+CONFIG_GENERIC_IOREMAP=y
+CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GLOB=y
+CONFIG_GOLDFISH=y
+# CONFIG_GOLDFISH_AUDIO is not set
+# CONFIG_GOLDFISH_PIPE is not set
+# CONFIG_GOLDFISH_TTY is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SIFIVE=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HID=y
+CONFIG_HID_GENERIC=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_HOTPLUG_PCI_SHPC=y
+CONFIG_HVC_DRIVER=y
+CONFIG_HVC_RISCV_SBI=y
+CONFIG_HZ=250
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+CONFIG_HZ_PERIODIC=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_OCORES=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_INPUT=y
+# CONFIG_INPUT_DA9063_ONKEY is not set
+CONFIG_IO_URING=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_WORK=y
+CONFIG_JBD2=y
+CONFIG_KALLSYMS=y
+CONFIG_KEYS=y
+# CONFIG_KEYBOARD_GOLDFISH_EVENTS is not set
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGER_DISK=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+CONFIG_LIBFDT=y
+CONFIG_LLD_VERSION=0
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_LOCK_DEBUGGING_SUPPORT=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_MACB=y
+# CONFIG_MACB_PCI is not set
+CONFIG_MACB_USE_HWSTAMP=y
+CONFIG_MANDATORY_FILE_LOCKING=y
+CONFIG_MAXPHYSMEM_128GB=y
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MDIO_DEVRES=y
+CONFIG_MEMFD_CREATE=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MFD_DA9063=y
+CONFIG_MICROSEMI_PHY=y
+CONFIG_MIGRATION=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+# CONFIG_MMC_GOLDFISH is not set
+CONFIG_MMC_SPI=y
+CONFIG_MMIOWB=y
+CONFIG_MODULES_USE_ELF_RELA=y
+CONFIG_MODULE_SECTIONS=y
+CONFIG_MPILIB=y
+CONFIG_MQ_IOSCHED_DEADLINE=y
+CONFIG_MQ_IOSCHED_KYBER=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NAMESPACES=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NET_FAILOVER=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_NS=y
+CONFIG_NET_PTP_CLASSIFY=y
+CONFIG_NLS=y
+CONFIG_NR_CPUS=8
+CONFIG_NVMEM=y
+CONFIG_NVMEM_SYSFS=y
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_KOBJ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_NET=y
+CONFIG_OID_REGISTRY=y
+CONFIG_PADATA=y
+CONFIG_PAGE_OFFSET=0xffffffe000000000
+CONFIG_PANIC_TIMEOUT=0
+CONFIG_PA_BITS=56
+CONFIG_PCI=y
+CONFIG_PCI_DEBUG=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_ECAM=y
+CONFIG_PCI_HOST_COMMON=y
+CONFIG_PCI_HOST_GENERIC=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_MSI_IRQ_DOMAIN=y
+CONFIG_PCI_SW_SWITCHTEC=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEAER_INJECT=m
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEASPM=y
+CONFIG_PCIEASPM_DEFAULT=y
+# CONFIG_PCIEASPM_POWERSAVE is not set
+# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
+# CONFIG_PCIEASPM_PERFORMANCE is not set
+CONFIG_PCIE_PME=y
+CONFIG_PCIE_DPC=y
+CONFIG_PCIE_PTM=y
+# CONFIG_PCIE_BW is not set
+CONFIG_PCIE_EDR=y
+CONFIG_PCIE_FU740=y
+CONFIG_PCIE_XILINX=y
+CONFIG_PGTABLE_LEVELS=3
+CONFIG_PHYLIB=y
+CONFIG_PHYLINK=y
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_PID_NS=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_RESTART=y
+CONFIG_POWER_RESET_GPIO=y
+CONFIG_POWER_RESET_GPIO_RESTART=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_POWER_RESET_SYSCON_POWEROFF=y
+CONFIG_PPS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_PWM=y
+CONFIG_PWM_SIFIVE=y
+CONFIG_PWM_SYSFS=y
+CONFIG_R8169=y
+CONFIG_RATIONAL=y
+CONFIG_RCU_TRACE=y
+CONFIG_RD_GZIP=y
+CONFIG_REALTEK_PHY=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RISCV=y
+CONFIG_RISCV_ERRATA_ALTERNATIVE=y
+CONFIG_RISCV_INTC=y
+CONFIG_RISCV_ISA_C=y
+CONFIG_RISCV_SBI=y
+CONFIG_RISCV_SBI_V01=y
+CONFIG_RISCV_TIMER=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DA9063=y
+CONFIG_RTC_DRV_GOLDFISH=y
+CONFIG_RTC_I2C_AND_SPI=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_SCHED_DEBUG=y
+CONFIG_SCSI=y
+CONFIG_SENSORS_DA9063=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
+CONFIG_SERIAL_MCTRL_GPIO=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_SIFIVE=y
+CONFIG_SERIAL_SIFIVE_CONSOLE=y
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SG_POOL=y
+CONFIG_SIFIVE_L2=y
+CONFIG_SIFIVE_PLIC=y
+CONFIG_SLUB_DEBUG=y
+CONFIG_SMP=y
+CONFIG_SOC_SIFIVE=y
+CONFIG_SOC_VIRT=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_MEM=y
+CONFIG_SPI_SIFIVE=y
+CONFIG_SRCU=y
+CONFIG_STACKTRACE=y
+CONFIG_SWIOTLB=y
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_SYS_SUPPORTS_HUGETLBFS=y
+CONFIG_THREAD_INFO_IN_TASK=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_OF=y
+CONFIG_TIMER_PROBE=y
+# CONFIG_TPM_KEY_PARSER is not set
+CONFIG_TRACE_CLOCK=y
+CONFIG_TREE_RCU=y
+CONFIG_TREE_SRCU=y
+CONFIG_TUNE_GENERIC=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_USB=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_HCD_PLATFORM is not set
+CONFIG_USB_EHCI_PCI=y
+CONFIG_USB_HID=y
+CONFIG_USB_NET_DRIVERS=y
+CONFIG_USB_PCI=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SUPPORT=y
+# CONFIG_USB_UHCI_HCD is not set
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_PCI=y
+# CONFIG_USB_XHCI_PLATFORM is not set
+# CONFIG_USER_NS is not set
+CONFIG_UTS_NS=y
+CONFIG_VA_BITS=39
+CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_NET=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_XPS=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA32=y
--- /dev/null
+config HIFIVEU_SD_BOOT_PARTSIZE
+ int "Boot (SD Card) filesystem partition size (in MB)"
+ depends on TARGET_hifiveu
+ default 32
+
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+FAT32_BLOCK_SIZE=1024
+FAT32_BLOCKS=$(shell echo $$(($(CONFIG_HIFIVEU_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE))))
+
+KERNEL_LOADADDR:=0x80200000
+
+define Build/riscv-sdcard
+ rm -f $@.boot #$(KDIR_TMP)/$(IMG_PREFIX)-$(PROFILE)-boot.img
+ mkfs.fat $@.boot -C $(FAT32_BLOCKS)
+
+ mcopy -i $@.boot $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-boot.scr ::boot.scr
+ mcopy -i $@.boot $(DTS_DIR)/$(DEVICE_DTS).dtb ::dtb
+ mcopy -i $@.boot $(IMAGE_KERNEL) ::uImage
+
+ ./gen_hifiveu_sdcard_img.sh \
+ $@ \
+ $@.boot \
+ $(IMAGE_ROOTFS) \
+ $(CONFIG_HIFIVEU_SD_BOOT_PARTSIZE) \
+ $(CONFIG_TARGET_ROOTFS_PARTSIZE) \
+ $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-u-boot.itb \
+ $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)-u-boot.itb-spl
+endef
+
+define Device/Default
+ PROFILES := Default
+ KERNEL_NAME := Image
+ KERNEL := kernel-bin | uImage none -a $(KERNEL_LOADADDR)
+ IMAGES := sdcard.img.gz
+ IMAGE/sdcard.img.gz := riscv-sdcard | append-metadata | gzip
+endef
+
+define Device/FitImageGzip
+ KERNEL_SUFFIX := -fit-uImage.itb
+ KERNEL = kernel-bin | gzip | fit gzip $$(DTS_DIR)/$$(DEVICE_DTS).dtb
+ KERNEL_NAME := Image
+endef
+
+define Device/FitImage
+ KERNEL_SUFFIX := -fit-uImage.itb
+ KERNEL = kernel-bin | fit none $$(DTS_DIR)/$$(DEVICE_DTS).dtb
+ KERNEL_NAME := Image
+endef
+
+define Device/sifive_unleashed
+ $(call Device/FitImage)
+ DEVICE_VENDOR := SiFive
+ DEVICE_MODEL := Unleashed (FU540)
+ DEVICE_DTS := sifive/hifive-unleashed-a00
+ UBOOT := sifive_unleashed
+endef
+TARGET_DEVICES += sifive_unleashed
+
+define Device/sifive_unmatched
+ $(call Device/FitImage)
+ DEVICE_VENDOR := SiFive
+ DEVICE_MODEL := Unmatched (FU740)
+ DEVICE_DTS := sifive/hifive-unmatched-a00
+ DEVICE_PACKAGES += kmod-eeprom-at24 kmod-hwmon-lm90
+ UBOOT := sifive_unmatched
+endef
+TARGET_DEVICES += sifive_unmatched
+
+$(eval $(call BuildImage))
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+
+set -ex
+[ $# -eq 7 ] || {
+ echo "SYNTAX: $0 <file> <bbl/sbl image> <rootfs image> <bbl/sbl size> <rootfs size> <u-boot ITB image> <u-boot SPL>"
+ exit 1
+}
+
+SPL_UUID="5B193300-FC78-40CD-8002-E86C45580B47"
+UBOOT_UUID="2E54B353-1271-4842-806F-E436D6AF6985"
+BOOT_UUID="EF00"
+LINUX_UUID="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
+
+#BBL_SIZE=4
+OUTPUT="$1"
+BOOTFS="$2"
+ROOTFS="$3"
+BOOTFSSIZE="$4"
+ROOTFSSIZE="$5"
+UBOOT="$6"
+UBOOT_SPL="$7"
+
+FULLSIZE="$(($BOOTFSSIZE+$ROOTFSSIZE+6))"
+#echo "Full size is: ${FULLSIZE}M"
+
+BOOTFSOFFSET="$(($BOOTFSSIZE * 1048576 / 512 + 10274))"
+BOOTFSEND="$(( ($BBL_SIZE+$BOOTFSSIZE) * 1048576 / 512 + 10274 ))"
+echo "Bootfs is: $BOOTFSOFFSET - $BOOTFSEND"
+
+ROOTFSOFFSET="$((($BOOTFSSIZE) * 1048576 / 512 + 10274 + 2 ))"
+ROOTFSEND="$((($BOOTFSSIZE+$ROOTFSSIZE) * 1048576 / 512 + 10274 + 2))"
+echo "Rootfs is: $ROOTFSOFFSET - $ROOTFSEND"
+
+dd if=/dev/zero of=$OUTPUT bs=1M count=$FULLSIZE
+
+sgdisk --clear \
+ --set-alignment=2 \
+ --new=1:34:2081 --change-name=1:loader1 --typecode=1:${SPL_UUID} \
+ --new=2:2082:10273 --change-name=2:loader2 --typecode=2:${UBOOT_UUID} \
+ --new=3:10274:${BOOTFSEND} --change-name=3:boot --typecode=3:${BOOT_UUID} \
+ --new=4:${ROOTFSOFFSET}:${ROOTFSEND} --change-name=4:rootfs --typecode=4:${LINUX_UUID} $OUTPUT
+
+dd bs=512 if="$UBOOT_SPL" of="$OUTPUT" seek=34 conv=notrunc
+dd bs=512 if="$UBOOT" of="$OUTPUT" seek=2082 conv=notrunc
+dd bs=512 if="$BOOTFS" of="$OUTPUT" seek=10274 conv=notrunc
+dd bs=512 if="$ROOTFS" of="$OUTPUT" seek=${ROOTFSOFFSET} conv=notrunc
--- /dev/null
+From fe033b899ca3461c0439d6cd775476ae7595e327 Mon Sep 17 00:00:00 2001
+From: Zong Li <zong.li@sifive.com>
+Date: Wed, 9 Dec 2020 17:49:12 +0800
+Subject: [PATCH 01/29] clk: sifive: Extract prci core to common base
+
+Extract common core of prci driver to an independent file, it could
+allow other chips to reuse it. Separate SoCs-dependent code 'fu540'
+from prci core, then we can easily add 'fu740' later.
+
+Almost these changes are code movement. The different is adding the
+private data for each SoC use, so it needs to get match data in probe
+callback function, then use the data for initialization.
+
+Signed-off-by: Zong Li <zong.li@sifive.com>
+Reviewed-by: Pragnesh Patel <Pragnesh.patel@sifive.com>
+Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>
+---
+ drivers/clk/sifive/Makefile | 2 +-
+ drivers/clk/sifive/fu540-prci.c | 592 ++-------------------------------------
+ drivers/clk/sifive/fu540-prci.h | 21 ++
+ drivers/clk/sifive/sifive-prci.c | 395 ++++++++++++++++++++++++++
+ drivers/clk/sifive/sifive-prci.h | 201 +++++++++++++
+ 5 files changed, 639 insertions(+), 572 deletions(-)
+ create mode 100644 drivers/clk/sifive/fu540-prci.h
+ create mode 100644 drivers/clk/sifive/sifive-prci.c
+ create mode 100644 drivers/clk/sifive/sifive-prci.h
+
+diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
+index 0797f14..51b6ebc 100644
+--- a/drivers/clk/sifive/Makefile
++++ b/drivers/clk/sifive/Makefile
+@@ -1,2 +1,2 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += fu540-prci.o
++obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += sifive-prci.o fu540-prci.o
+diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c
+index a8901f9..e2353de 100644
+--- a/drivers/clk/sifive/fu540-prci.c
++++ b/drivers/clk/sifive/fu540-prci.c
+@@ -1,17 +1,9 @@
+ // SPDX-License-Identifier: GPL-2.0
+ /*
+ * Copyright (C) 2018-2019 SiFive, Inc.
+- * Wesley Terpstra
+- * Paul Walmsley
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License version 2 as
+- * published by the Free Software Foundation.
+- *
+- * 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.
++ * 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
+@@ -25,463 +17,43 @@
+ */
+
+ #include <dt-bindings/clock/sifive-fu540-prci.h>
+-#include <linux/clkdev.h>
+-#include <linux/clk-provider.h>
+-#include <linux/clk/analogbits-wrpll-cln28hpc.h>
+-#include <linux/delay.h>
+-#include <linux/err.h>
+-#include <linux/io.h>
+ #include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/of_clk.h>
+-#include <linux/platform_device.h>
+-#include <linux/slab.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)
+-
+-/* 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)
++#include "sifive-prci.h"
+
+-/* DDRPLLCFG1 */
+-#define PRCI_DDRPLLCFG1_OFFSET 0x10
+-# define PRCI_DDRPLLCFG1_CKE_SHIFT 24
+-# define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
++/* PRCI integration data for each WRPLL instance */
+
+-/* 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 24
+-# 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)
+-
+-/* 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)
+-
+-/*
+- * 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_hw_onecell_data hw_clks;
++static struct __prci_wrpll_data __prci_corepll_data = {
++ .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
++ .enable_bypass = sifive_prci_coreclksel_use_hfclk,
++ .disable_bypass = sifive_prci_coreclksel_use_corepll,
+ };
+
+-/**
+- * 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
+- *
+- * @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;
++static struct __prci_wrpll_data __prci_ddrpll_data = {
++ .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
+ };
+
+-/**
+- * 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 hw;
+- struct __prci_wrpll_data *pwd;
+- struct __prci_data *pd;
++static struct __prci_wrpll_data __prci_gemgxlpll_data = {
++ .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
+ };
+
+-#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
+-
+-/*
+- * 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_relaxed(pd->va + offs);
+-}
+-
+-static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
+-{
+- writel_relaxed(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 FU540 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_cfg() - 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_cfg(struct __prci_data *pd,
+- struct __prci_wrpll_data *pwd)
+-{
+- __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
+-}
+-
+-/**
+- * __prci_wrpll_write_cfg() - 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_cfg(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));
+-}
+-
+-/* Core clock mux control */
+-
+-/**
+- * __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.
+- */
+-static void __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 */
+-}
+-
+-/**
+- * __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 PLL output clock; return once complete.
+- *
+- * Context: Any context. Caller must prevent concurrent changes to the
+- * PRCI_CORECLKSEL_OFFSET register.
+- */
+-static void __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 */
+-}
+-
+-/*
+- * Linux clock framework integration
+- *
+- * See the Linux clock framework documentation for more information on
+- * these functions.
+- */
+-
+-static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *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);
+-}
+-
+-static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *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);
+-}
+-
+-static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *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_cfg(pd, pwd, &pwd->c);
+-
+- udelay(wrpll_calc_max_lock_us(&pwd->c));
+-
+- if (pwd->disable_bypass)
+- pwd->disable_bypass(pd);
+-
+- return 0;
+-}
++/* Linux clock framework integration */
+
+ static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
+- .set_rate = sifive_fu540_prci_wrpll_set_rate,
+- .round_rate = sifive_fu540_prci_wrpll_round_rate,
+- .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
++ .set_rate = sifive_prci_wrpll_set_rate,
++ .round_rate = sifive_prci_wrpll_round_rate,
++ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+ };
+
+ static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
+- .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
++ .recalc_rate = sifive_prci_wrpll_recalc_rate,
+ };
+
+-/* TLCLKSEL clock integration */
+-
+-static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *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);
+-}
+-
+ static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = {
+- .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate,
+-};
+-
+-/*
+- * PRCI integration data for each WRPLL instance
+- */
+-
+-static struct __prci_wrpll_data __prci_corepll_data = {
+- .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
+- .enable_bypass = __prci_coreclksel_use_hfclk,
+- .disable_bypass = __prci_coreclksel_use_corepll,
+-};
+-
+-static struct __prci_wrpll_data __prci_ddrpll_data = {
+- .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
++ .recalc_rate = sifive_prci_tlclksel_recalc_rate,
+ };
+
+-static struct __prci_wrpll_data __prci_gemgxlpll_data = {
+- .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
+-};
+-
+-/*
+- * List of clock controls provided by the PRCI
+- */
+-
+-static struct __prci_clock __prci_init_clocks[] = {
++/* List of clock controls provided by the PRCI */
++struct __prci_clock __prci_init_clocks_fu540[] = {
+ [PRCI_CLK_COREPLL] = {
+ .name = "corepll",
+ .parent_name = "hfclk",
+@@ -506,125 +78,3 @@ static struct __prci_clock __prci_init_clocks[] = {
+ .ops = &sifive_fu540_prci_tlclksel_clk_ops,
+ },
+ };
+-
+-/**
+- * __prci_register_clocks() - register clock controls in the PRCI with Linux
+- * @dev: Linux struct device *
+- *
+- * Register the list of clock controls described in __prci_init_plls[] with
+- * the Linux clock framework.
+- *
+- * Return: 0 upon success or a negative error code upon failure.
+- */
+-static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
+-{
+- struct clk_init_data init = { };
+- struct __prci_clock *pic;
+- int parent_count, i, r;
+-
+- parent_count = of_clk_get_parent_count(dev->of_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 < ARRAY_SIZE(__prci_init_clocks); ++i) {
+- pic = &__prci_init_clocks[i];
+-
+- init.name = pic->name;
+- init.parent_names = &pic->parent_name;
+- init.num_parents = 1;
+- init.ops = pic->ops;
+- pic->hw.init = &init;
+-
+- pic->pd = pd;
+-
+- if (pic->pwd)
+- __prci_wrpll_read_cfg(pd, pic->pwd);
+-
+- r = devm_clk_hw_register(dev, &pic->hw);
+- if (r) {
+- dev_warn(dev, "Failed to register clock %s: %d\n",
+- init.name, r);
+- return r;
+- }
+-
+- r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev));
+- if (r) {
+- dev_warn(dev, "Failed to register clkdev for %s: %d\n",
+- init.name, r);
+- return r;
+- }
+-
+- pd->hw_clks.hws[i] = &pic->hw;
+- }
+-
+- pd->hw_clks.num = i;
+-
+- r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+- &pd->hw_clks);
+- if (r) {
+- dev_err(dev, "could not add hw_provider: %d\n", r);
+- return r;
+- }
+-
+- return 0;
+-}
+-
+-/*
+- * Linux device model integration
+- *
+- * See the Linux device model documentation for more information about
+- * these functions.
+- */
+-static int sifive_fu540_prci_probe(struct platform_device *pdev)
+-{
+- struct device *dev = &pdev->dev;
+- struct resource *res;
+- struct __prci_data *pd;
+- int r;
+-
+- pd = devm_kzalloc(dev,
+- struct_size(pd, hw_clks.hws,
+- ARRAY_SIZE(__prci_init_clocks)),
+- GFP_KERNEL);
+- if (!pd)
+- return -ENOMEM;
+-
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- pd->va = devm_ioremap_resource(dev, res);
+- if (IS_ERR(pd->va))
+- return PTR_ERR(pd->va);
+-
+- r = __prci_register_clocks(dev, pd);
+- if (r) {
+- dev_err(dev, "could not register clocks: %d\n", r);
+- return r;
+- }
+-
+- dev_dbg(dev, "SiFive FU540 PRCI probed\n");
+-
+- return 0;
+-}
+-
+-static const struct of_device_id sifive_fu540_prci_of_match[] = {
+- { .compatible = "sifive,fu540-c000-prci", },
+- {}
+-};
+-MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match);
+-
+-static struct platform_driver sifive_fu540_prci_driver = {
+- .driver = {
+- .name = "sifive-fu540-prci",
+- .of_match_table = sifive_fu540_prci_of_match,
+- },
+- .probe = sifive_fu540_prci_probe,
+-};
+-
+-static int __init sifive_fu540_prci_init(void)
+-{
+- return platform_driver_register(&sifive_fu540_prci_driver);
+-}
+-core_initcall(sifive_fu540_prci_init);
+diff --git a/drivers/clk/sifive/fu540-prci.h b/drivers/clk/sifive/fu540-prci.h
+new file mode 100644
+index 00000000..c8271ef
+--- /dev/null
++++ b/drivers/clk/sifive/fu540-prci.h
+@@ -0,0 +1,21 @@
++/* 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];
++
++static const struct prci_clk_desc prci_clk_fu540 = {
++ .clks = __prci_init_clocks_fu540,
++ .num_clks = ARRAY_SIZE(__prci_init_clocks_fu540),
++};
++
++#endif /* __SIFIVE_CLK_FU540_PRCI_H */
+diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
+new file mode 100644
+index 00000000..70653d3
+--- /dev/null
++++ b/drivers/clk/sifive/sifive-prci.c
+@@ -0,0 +1,395 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 SiFive, Inc.
++ * Copyright (C) 2020 Zong Li
++ */
++
++#include <linux/clkdev.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/of_device.h>
++#include "sifive-prci.h"
++#include "fu540-prci.h"
++
++/*
++ * 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_relaxed(pd->va + offs);
++}
++
++static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
++{
++ writel_relaxed(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_cfg() - 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_cfg(struct __prci_data *pd,
++ struct __prci_wrpll_data *pwd)
++{
++ __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
++}
++
++/**
++ * __prci_wrpll_write_cfg() - 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_cfg(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));
++}
++
++/*
++ * 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 *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 *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 *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_cfg(pd, pwd, &pwd->c);
++
++ udelay(wrpll_calc_max_lock_us(&pwd->c));
++
++ if (pwd->disable_bypass)
++ pwd->disable_bypass(pd);
++
++ return 0;
++}
++
++/* TLCLKSEL clock integration */
++
++unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *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);
++}
++
++/*
++ * 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 */
++}
++
++/**
++ * __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 *dev, struct __prci_data *pd,
++ const struct prci_clk_desc *desc)
++{
++ struct clk_init_data init = { };
++ struct __prci_clock *pic;
++ int parent_count, i, r;
++
++ parent_count = of_clk_get_parent_count(dev->of_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]);
++
++ init.name = pic->name;
++ init.parent_names = &pic->parent_name;
++ init.num_parents = 1;
++ init.ops = pic->ops;
++ pic->hw.init = &init;
++
++ pic->pd = pd;
++
++ if (pic->pwd)
++ __prci_wrpll_read_cfg(pd, pic->pwd);
++
++ r = devm_clk_hw_register(dev, &pic->hw);
++ if (r) {
++ dev_warn(dev, "Failed to register clock %s: %d\n",
++ init.name, r);
++ return r;
++ }
++
++ r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev));
++ if (r) {
++ dev_warn(dev, "Failed to register clkdev for %s: %d\n",
++ init.name, r);
++ return r;
++ }
++
++ pd->hw_clks.hws[i] = &pic->hw;
++ }
++
++ pd->hw_clks.num = i;
++
++ r = devm_of_clk_add_hw_provider(dev, of_clk_hw_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
++ * @pdev: platform device pointer for the prci
++ *
++ * Return: 0 upon success or a negative error code upon failure.
++ */
++static int sifive_prci_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++ struct __prci_data *pd;
++ const struct prci_clk_desc *desc;
++ int r;
++
++ desc = of_device_get_match_data(&pdev->dev);
++
++ pd = devm_kzalloc(dev, struct_size(pd, hw_clks.hws, desc->num_clks), GFP_KERNEL);
++ if (!pd)
++ return -ENOMEM;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pd->va = devm_ioremap_resource(dev, res);
++ if (IS_ERR(pd->va))
++ return PTR_ERR(pd->va);
++
++ 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},
++ {}
++};
++
++static struct platform_driver sifive_prci_driver = {
++ .driver = {
++ .name = "sifive-clk-prci",
++ .of_match_table = sifive_prci_of_match,
++ },
++ .probe = sifive_prci_probe,
++};
++
++static int __init sifive_prci_init(void)
++{
++ return platform_driver_register(&sifive_prci_driver);
++}
++core_initcall(sifive_prci_init);
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+new file mode 100644
+index 00000000..280df63
+--- /dev/null
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -0,0 +1,201 @@
++/* 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-provider.h>
++#include <linux/platform_device.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)
++
++/* 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 24
++#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 24
++#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)
++
++/*
++ * 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_hw_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
++ *
++ * @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;
++};
++
++/**
++ * 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 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);
++
++/* Linux clock framework integration */
++long sifive_prci_wrpll_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate);
++int sifive_prci_wrpll_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate);
++unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate);
++unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate);
++
++#endif /* __SIFIVE_CLK_SIFIVE_PRCI_H */
+--
+2.7.4
+
--- /dev/null
+From df86ca5b9416ebeacb5ecee63118170c7ea20035 Mon Sep 17 00:00:00 2001
+From: Zong Li <zong.li@sifive.com>
+Date: Wed, 9 Dec 2020 17:49:13 +0800
+Subject: [PATCH 02/29] clk: sifive: Use common name for prci configuration
+
+Use generic name CLK_SIFIVE_PRCI instead of CLK_SIFIVE_FU540_PRCI. This
+patch is prepared for fu740 support.
+
+Signed-off-by: Zong Li <zong.li@sifive.com>
+Reviewed-by: Palmer Dabbelt <palmerdabbelt@google.com>
+Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>
+Reviewed-by: Pragnesh Patel <Pragnesh.patel@sifive.com>
+---
+ arch/riscv/Kconfig.socs | 2 +-
+ drivers/clk/sifive/Kconfig | 6 +++---
+ drivers/clk/sifive/Makefile | 2 +-
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
+index 8a55f61..3284d5c 100644
+--- a/arch/riscv/Kconfig.socs
++++ b/arch/riscv/Kconfig.socs
+@@ -5,7 +5,7 @@ config SOC_SIFIVE
+ select SERIAL_SIFIVE if TTY
+ select SERIAL_SIFIVE_CONSOLE if TTY
+ select CLK_SIFIVE
+- select CLK_SIFIVE_FU540_PRCI
++ select CLK_SIFIVE_PRCI
+ select SIFIVE_PLIC
+ help
+ This enables support for SiFive SoC platform hardware.
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+index f3b4eb9..ab48cf7 100644
+--- a/drivers/clk/sifive/Kconfig
++++ b/drivers/clk/sifive/Kconfig
+@@ -8,12 +8,12 @@ menuconfig CLK_SIFIVE
+
+ if CLK_SIFIVE
+
+-config CLK_SIFIVE_FU540_PRCI
+- bool "PRCI driver for SiFive FU540 SoCs"
++config CLK_SIFIVE_PRCI
++ bool "PRCI driver for SiFive SoCs"
+ select CLK_ANALOGBITS_WRPLL_CLN28HPC
+ help
+ Supports the Power Reset Clock interface (PRCI) IP block found in
+- FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC,
++ FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC,
+ enable this driver.
+
+ endif
+diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
+index 51b6ebc..3074cdb 100644
+--- a/drivers/clk/sifive/Makefile
++++ b/drivers/clk/sifive/Makefile
+@@ -1,2 +1,2 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += sifive-prci.o fu540-prci.o
++obj-$(CONFIG_CLK_SIFIVE_PRCI) += sifive-prci.o fu540-prci.o
+--
+2.7.4
+
--- /dev/null
+From c5d3ecfcbdcf2e2d24b636fb739501e1a3e85f14 Mon Sep 17 00:00:00 2001
+From: Zong Li <zong.li@sifive.com>
+Date: Wed, 9 Dec 2020 17:49:14 +0800
+Subject: [PATCH 03/29] clk: sifive: Add a driver for the SiFive FU740 PRCI IP
+ block
+
+Add driver code for the SiFive FU740 PRCI IP block. This IP block
+handles reset and clock control for the SiFive FU740 device and
+implements SoC-level clock tree controls and dividers.
+
+The link of unmatched as follow, and the U740-C000 manual would
+be present in the same page as soon.
+https://www.sifive.com/boards/hifive-unmatched
+
+This driver contains bug fixes and contributions from
+Henry Styles <hes@sifive.com>
+Erik Danie <erik.danie@sifive.com>
+Pragnesh Patel <pragnesh.patel@sifive.com>
+
+Signed-off-by: Zong Li <zong.li@sifive.com>
+Reviewed-by: Pragnesh Patel <Pragnesh.patel@sifive.com>
+Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Henry Styles <hes@sifive.com>
+Cc: Erik Danie <erik.danie@sifive.com>
+Cc: Pragnesh Patel <pragnesh.patel@sifive.com>
+---
+ drivers/clk/sifive/Kconfig | 4 +-
+ drivers/clk/sifive/Makefile | 2 +-
+ drivers/clk/sifive/fu740-prci.c | 111 ++++++++++++++++++++++++
+ drivers/clk/sifive/fu740-prci.h | 21 +++++
+ drivers/clk/sifive/sifive-prci.c | 120 ++++++++++++++++++++++++++
+ drivers/clk/sifive/sifive-prci.h | 88 +++++++++++++++++++
+ include/dt-bindings/clock/sifive-fu740-prci.h | 23 +++++
+ 7 files changed, 366 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/clk/sifive/fu740-prci.c
+ create mode 100644 drivers/clk/sifive/fu740-prci.h
+ create mode 100644 include/dt-bindings/clock/sifive-fu740-prci.h
+
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+index ab48cf7..1c14eb2 100644
+--- a/drivers/clk/sifive/Kconfig
++++ b/drivers/clk/sifive/Kconfig
+@@ -13,7 +13,7 @@ config CLK_SIFIVE_PRCI
+ select CLK_ANALOGBITS_WRPLL_CLN28HPC
+ help
+ Supports the Power Reset Clock interface (PRCI) IP block found in
+- FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC,
+- enable this driver.
++ 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
+index 3074cdb..7b06fc0 100644
+--- a/drivers/clk/sifive/Makefile
++++ b/drivers/clk/sifive/Makefile
+@@ -1,2 +1,2 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-$(CONFIG_CLK_SIFIVE_PRCI) += sifive-prci.o fu540-prci.o
++obj-$(CONFIG_CLK_SIFIVE_PRCI) += sifive-prci.o fu540-prci.o fu740-prci.o
+diff --git a/drivers/clk/sifive/fu740-prci.c b/drivers/clk/sifive/fu740-prci.c
+new file mode 100644
+index 00000000..41ddd44
+--- /dev/null
++++ b/drivers/clk/sifive/fu740-prci.c
+@@ -0,0 +1,111 @@
++// 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 <linux/module.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,
++ .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,
++};
++
++static struct __prci_wrpll_data __prci_gemgxlpll_data = {
++ .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
++};
++
++static struct __prci_wrpll_data __prci_dvfscorepll_data = {
++ .cfg0_offs = PRCI_DVFSCOREPLLCFG0_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,
++ .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,
++};
++
++/* 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,
++};
++
++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 00000000..13ef971f7
+--- /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
+index 70653d3..cc4b4c6 100644
+--- a/drivers/clk/sifive/sifive-prci.c
++++ b/drivers/clk/sifive/sifive-prci.c
+@@ -10,6 +10,7 @@
+ #include <linux/of_device.h>
+ #include "sifive-prci.h"
+ #include "fu540-prci.h"
++#include "fu740-prci.h"
+
+ /*
+ * Private functions
+@@ -225,6 +226,18 @@ unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
+ return div_u64(parent_rate, div);
+ }
+
++/* HFPCLK clock integration */
++
++unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *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
+ */
+@@ -271,6 +284,112 @@ void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd)
+ }
+
+ /**
++ * 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
+@@ -377,6 +496,7 @@ static int sifive_prci_probe(struct platform_device *pdev)
+
+ 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},
+ {}
+ };
+
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+index 280df63..7e509df 100644
+--- a/drivers/clk/sifive/sifive-prci.h
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -117,6 +117,87 @@
+ #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
+ */
+@@ -187,6 +268,11 @@ struct prci_clk_desc {
+ /* 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 *hw, unsigned long rate,
+@@ -197,5 +283,7 @@ unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate);
+ unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate);
++unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate);
+
+ #endif /* __SIFIVE_CLK_SIFIVE_PRCI_H */
+diff --git a/include/dt-bindings/clock/sifive-fu740-prci.h b/include/dt-bindings/clock/sifive-fu740-prci.h
+new file mode 100644
+index 00000000..cd7706e
+--- /dev/null
++++ b/include/dt-bindings/clock/sifive-fu740-prci.h
+@@ -0,0 +1,23 @@
++/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
++/*
++ * Copyright (C) 2019 SiFive, Inc.
++ * Wesley Terpstra
++ * Paul Walmsley
++ * Zong Li
++ */
++
++#ifndef __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H
++#define __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H
++
++/* Clock indexes for use by Device Tree data and the PRCI driver */
++
++#define PRCI_CLK_COREPLL 0
++#define PRCI_CLK_DDRPLL 1
++#define PRCI_CLK_GEMGXLPLL 2
++#define PRCI_CLK_DVFSCOREPLL 3
++#define PRCI_CLK_HFPCLKPLL 4
++#define PRCI_CLK_CLTXPLL 5
++#define PRCI_CLK_TLCLK 6
++#define PRCI_CLK_PCLK 7
++
++#endif /* __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H */
+--
+2.7.4
+
--- /dev/null
+From ff2bcef458e1fc95c6f3da7304df4119628f92c5 Mon Sep 17 00:00:00 2001
+From: Zong Li <zong.li@sifive.com>
+Date: Wed, 9 Dec 2020 17:49:15 +0800
+Subject: [PATCH 04/29] clk: sifive: Fix the wrong bit field shift
+
+The clk enable bit should be 31 instead of 24.
+
+Signed-off-by: Zong Li <zong.li@sifive.com>
+Reported-by: Pragnesh Patel <pragnesh.patel@sifive.com>
+---
+ drivers/clk/sifive/sifive-prci.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+index 7e509df..88493f3 100644
+--- a/drivers/clk/sifive/sifive-prci.h
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -59,7 +59,7 @@
+
+ /* DDRPLLCFG1 */
+ #define PRCI_DDRPLLCFG1_OFFSET 0x10
+-#define PRCI_DDRPLLCFG1_CKE_SHIFT 24
++#define PRCI_DDRPLLCFG1_CKE_SHIFT 31
+ #define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
+
+ /* GEMGXLPLLCFG0 */
+@@ -81,7 +81,7 @@
+
+ /* GEMGXLPLLCFG1 */
+ #define PRCI_GEMGXLPLLCFG1_OFFSET 0x20
+-#define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24
++#define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 31
+ #define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
+
+ /* CORECLKSEL */
+--
+2.7.4
+
--- /dev/null
+From 37338a04280c6add6e376ed03d094276fb2759ba Mon Sep 17 00:00:00 2001
+From: Pragnesh Patel <pragnesh.patel@sifive.com>
+Date: Wed, 9 Dec 2020 17:49:16 +0800
+Subject: [PATCH 05/29] clk: sifive: Add clock enable and disable ops
+
+Add new functions "sifive_prci_clock_enable(), sifive_prci_clock_disable()
+and sifive_clk_is_enabled()" to enable or disable the PRCI clock
+
+Signed-off-by: Pragnesh Patel <pragnesh.patel@sifive.com>
+Tested-by: Zong Li <zong.li@sifive.com>
+---
+ drivers/clk/sifive/fu540-prci.c | 6 ++++
+ drivers/clk/sifive/fu740-prci.c | 9 +++++
+ drivers/clk/sifive/sifive-prci.c | 77 +++++++++++++++++++++++++++++++++++-----
+ drivers/clk/sifive/sifive-prci.h | 10 ++++++
+ 4 files changed, 93 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c
+index e2353de..3b55883 100644
+--- a/drivers/clk/sifive/fu540-prci.c
++++ b/drivers/clk/sifive/fu540-prci.c
+@@ -24,16 +24,19 @@
+
+ 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 */
+@@ -42,6 +45,9 @@ 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 = {
+diff --git a/drivers/clk/sifive/fu740-prci.c b/drivers/clk/sifive/fu740-prci.c
+index 41ddd44..db83002 100644
+--- a/drivers/clk/sifive/fu740-prci.c
++++ b/drivers/clk/sifive/fu740-prci.c
+@@ -12,32 +12,38 @@
+
+ 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 */
+@@ -46,6 +52,9 @@ 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 = {
+diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
+index cc4b4c6..c78b042 100644
+--- a/drivers/clk/sifive/sifive-prci.c
++++ b/drivers/clk/sifive/sifive-prci.c
+@@ -113,7 +113,7 @@ static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
+ }
+
+ /**
+- * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI
++ * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
+ * @pd: PRCI context
+ * @pwd: PRCI WRPLL metadata
+ *
+@@ -124,14 +124,14 @@ static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
+ * Context: Any context. Caller must prevent the records pointed to by
+ * @pd and @pwd from changing during execution.
+ */
+-static void __prci_wrpll_read_cfg(struct __prci_data *pd,
+- struct __prci_wrpll_data *pwd)
++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_cfg() - write WRPLL configuration into the PRCI
++ * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
+ * @pd: PRCI context
+ * @pwd: PRCI WRPLL metadata
+ * @c: WRPLL configuration record to write
+@@ -144,15 +144,29 @@ static void __prci_wrpll_read_cfg(struct __prci_data *pd,
+ * Context: Any context. Caller must prevent the records pointed to by
+ * @pd and @pwd from changing during execution.
+ */
+-static void __prci_wrpll_write_cfg(struct __prci_data *pd,
+- struct __prci_wrpll_data *pwd,
+- struct wrpll_cfg *c)
++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
+ *
+@@ -199,16 +213,61 @@ int sifive_prci_wrpll_set_rate(struct clk_hw *hw,
+ if (pwd->enable_bypass)
+ pwd->enable_bypass(pd);
+
+- __prci_wrpll_write_cfg(pd, pwd, &pwd->c);
++ __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 *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 *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 *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 *hw,
+@@ -427,7 +486,7 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd,
+ pic->pd = pd;
+
+ if (pic->pwd)
+- __prci_wrpll_read_cfg(pd, pic->pwd);
++ __prci_wrpll_read_cfg0(pd, pic->pwd);
+
+ r = devm_clk_hw_register(dev, &pic->hw);
+ if (r) {
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+index 88493f3..dbdbd17 100644
+--- a/drivers/clk/sifive/sifive-prci.h
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -40,6 +40,11 @@
+ #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
+@@ -220,6 +225,7 @@ struct __prci_data {
+ * @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
+@@ -230,6 +236,7 @@ struct __prci_wrpll_data {
+ void (*enable_bypass)(struct __prci_data *pd);
+ void (*disable_bypass)(struct __prci_data *pd);
+ u8 cfg0_offs;
++ u8 cfg1_offs;
+ };
+
+ /**
+@@ -279,6 +286,9 @@ long sifive_prci_wrpll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate);
+ int sifive_prci_wrpll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
++int sifive_clk_is_enabled(struct clk_hw *hw);
++int sifive_prci_clock_enable(struct clk_hw *hw);
++void sifive_prci_clock_disable(struct clk_hw *hw);
+ unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate);
+ unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
+--
+2.7.4
+
--- /dev/null
+From 34d032292a2b6db16fc60e7c6706b1b508c2d932 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:33 +0530
+Subject: [PATCH 06/29] dt-bindings: riscv: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings in cpus.yaml to support the E71 and U74 CPU
+cores ("harts") that are present on FU740-C000 SoC.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Bin Meng <bin.meng@windriver.com>
+---
+ Documentation/devicetree/bindings/riscv/cpus.yaml | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml
+index c6925e0..eb6843f 100644
+--- a/Documentation/devicetree/bindings/riscv/cpus.yaml
++++ b/Documentation/devicetree/bindings/riscv/cpus.yaml
+@@ -28,11 +28,17 @@ properties:
+ - items:
+ - enum:
+ - sifive,rocket0
++ - sifive,bullet0
+ - sifive,e5
++ - sifive,e7
+ - sifive,e51
++ - sifive,e71
+ - sifive,u54-mc
++ - sifive,u74-mc
+ - sifive,u54
++ - sifive,u74
+ - sifive,u5
++ - sifive,u7
+ - const: riscv
+ - const: riscv # Simulator only
+ description:
+--
+2.7.4
+
--- /dev/null
+From 8a3d7aa89632d354932a621eb4fe22c560a406bd Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:34 +0530
+Subject: [PATCH 07/29] dt-bindings: spi: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings to the DT binding documents to support SiFive
+FU740-C000.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+---
+ Documentation/devicetree/bindings/spi/spi-sifive.yaml | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/spi/spi-sifive.yaml b/Documentation/devicetree/bindings/spi/spi-sifive.yaml
+index 56dcf1d..6e7e394 100644
+--- a/Documentation/devicetree/bindings/spi/spi-sifive.yaml
++++ b/Documentation/devicetree/bindings/spi/spi-sifive.yaml
+@@ -17,15 +17,17 @@ allOf:
+ properties:
+ compatible:
+ items:
+- - const: sifive,fu540-c000-spi
++ - enum:
++ - sifive,fu540-c000-spi
++ - sifive,fu740-c000-spi
+ - const: sifive,spi0
+
+ description:
+ Should be "sifive,<chip>-spi" and "sifive,spi<version>".
+ Supported compatible strings are -
+- "sifive,fu540-c000-spi" for the SiFive SPI v0 as integrated
+- onto the SiFive FU540 chip, and "sifive,spi0" for the SiFive
+- SPI v0 IP block with no chip integration tweaks.
++ "sifive,fu540-c000-spi" and "sifive,fu740-c000-spi" for the SiFive SPI v0
++ as integrated onto the SiFive FU540 and FU740 chip resp, and "sifive,spi0"
++ for the SiFive SPI v0 IP block with no chip integration tweaks.
+ Please refer to sifive-blocks-ip-versioning.txt for details
+
+ SPI RTL that corresponds to the IP block version numbers can be found here -
+--
+2.7.4
+
--- /dev/null
+From 789dc44312525e9669d0c6e01ccfc01bbdfb0e10 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:35 +0530
+Subject: [PATCH 08/29] dt-bindings: pwm: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings to the DT binding documents to support SiFive
+FU740-C000.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+---
+ Documentation/devicetree/bindings/pwm/pwm-sifive.yaml | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/pwm/pwm-sifive.yaml b/Documentation/devicetree/bindings/pwm/pwm-sifive.yaml
+index 5ac2527..84e6691 100644
+--- a/Documentation/devicetree/bindings/pwm/pwm-sifive.yaml
++++ b/Documentation/devicetree/bindings/pwm/pwm-sifive.yaml
+@@ -25,12 +25,15 @@ description:
+ properties:
+ compatible:
+ items:
+- - const: sifive,fu540-c000-pwm
++ - enum:
++ - sifive,fu540-c000-pwm
++ - sifive,fu740-c000-pwm
+ - const: sifive,pwm0
+ description:
+ Should be "sifive,<chip>-pwm" and "sifive,pwm<version>". Supported
+- compatible strings are "sifive,fu540-c000-pwm" for the SiFive PWM v0
+- as integrated onto the SiFive FU540 chip, and "sifive,pwm0" for the
++ compatible strings are "sifive,fu540-c000-pwm" and
++ "sifive,fu740-c000-pwm" for the SiFive PWM v0 as integrated onto the
++ SiFive FU540 and FU740 chip respectively, and "sifive,pwm0" for the
+ SiFive PWM v0 IP block with no chip integration tweaks.
+ Please refer to sifive-blocks-ip-versioning.txt for details.
+
+--
+2.7.4
+
--- /dev/null
+From 6bc4ee53eaffad7385babe568a7889b13752606f Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:36 +0530
+Subject: [PATCH 09/29] dt-bindings: serial: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings to the DT binding documents to support SiFive
+FU740-C000.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/serial/sifive-serial.yaml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/serial/sifive-serial.yaml b/Documentation/devicetree/bindings/serial/sifive-serial.yaml
+index 92283f6..3ac5c7f 100644
+--- a/Documentation/devicetree/bindings/serial/sifive-serial.yaml
++++ b/Documentation/devicetree/bindings/serial/sifive-serial.yaml
+@@ -17,7 +17,9 @@ allOf:
+ properties:
+ compatible:
+ items:
+- - const: sifive,fu540-c000-uart
++ - enum:
++ - sifive,fu540-c000-uart
++ - sifive,fu740-c000-uart
+ - const: sifive,uart0
+
+ description:
+--
+2.7.4
+
--- /dev/null
+From 9791e30869ff598c0faede92500d4d35c8bbba45 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:37 +0530
+Subject: [PATCH 10/29] dt-bindings: gpio: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings to the DT binding documents to support SiFive
+FU740-C000.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/gpio/sifive,gpio.yaml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml
+index a0efd8d..ab22056 100644
+--- a/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml
++++ b/Documentation/devicetree/bindings/gpio/sifive,gpio.yaml
+@@ -13,7 +13,9 @@ maintainers:
+ properties:
+ compatible:
+ items:
+- - const: sifive,fu540-c000-gpio
++ - enum:
++ - sifive,fu540-c000-gpio
++ - sifive,fu740-c000-gpio
+ - const: sifive,gpio0
+
+ reg:
+--
+2.7.4
+
--- /dev/null
+From 909e7d4601aa183dbdd3e8edb964e4c683a2c9e1 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:38 +0530
+Subject: [PATCH 11/29] dt-bindings: i2c: Update DT binding docs to support
+ SiFive FU740 SoC
+
+Add new compatible strings to the DT binding documents to support SiFive
+FU740-C000.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/i2c/i2c-ocores.txt | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/i2c/i2c-ocores.txt b/Documentation/devicetree/bindings/i2c/i2c-ocores.txt
+index 6b25a80..a37c945 100644
+--- a/Documentation/devicetree/bindings/i2c/i2c-ocores.txt
++++ b/Documentation/devicetree/bindings/i2c/i2c-ocores.txt
+@@ -5,8 +5,12 @@ Required properties:
+ "aeroflexgaisler,i2cmst"
+ "sifive,fu540-c000-i2c", "sifive,i2c0"
+ For Opencore based I2C IP block reimplemented in
+- FU540-C000 SoC. Please refer to sifive-blocks-ip-versioning.txt
+- for additional details.
++ FU540-C000 SoC.
++ "sifive,fu740-c000-i2c", "sifive,i2c0"
++ For Opencore based I2C IP block reimplemented in
++ FU740-C000 SoC.
++ Please refer to sifive-blocks-ip-versioning.txt for
++ additional details.
+ - reg : bus address start and address range size of device
+ - clocks : handle to the controller clock; see the note below.
+ Mutually exclusive with opencores,ip-clock-frequency
+--
+2.7.4
+
--- /dev/null
+From 7d2e730f1281b5530e55ebca1b0d9165e0298c00 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:39 +0530
+Subject: [PATCH 12/29] riscv: dts: add initial support for the SiFive
+ FU740-C000 SoC
+
+Add initial support for the SiFive FU540-C000 SoC. FU740-C000 is built
+around the SiFIve U7 Core Complex and a TileLink interconnect.
+
+This file is expected to grow as more device drivers are added to the
+kernel.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/fu740-c000.dtsi | 293 +++++++++++++++++++++++++++++
+ 1 file changed, 293 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+
+diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+new file mode 100644
+index 00000000..eeb4f8c3
+--- /dev/null
++++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+@@ -0,0 +1,293 @@
++// SPDX-License-Identifier: (GPL-2.0 OR MIT)
++/* Copyright (c) 2020 SiFive, Inc */
++
++/dts-v1/;
++
++#include <dt-bindings/clock/sifive-fu740-prci.h>
++
++/ {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ compatible = "sifive,fu740-c000", "sifive,fu740";
++
++ aliases {
++ serial0 = &uart0;
++ serial1 = &uart1;
++ ethernet0 = ð0;
++ };
++
++ chosen {
++ };
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ cpu0: cpu@0 {
++ compatible = "sifive,bullet0", "riscv";
++ device_type = "cpu";
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <16384>;
++ next-level-cache = <&ccache>;
++ reg = <0x0>;
++ riscv,isa = "rv64imac";
++ status = "disabled";
++ cpu0_intc: interrupt-controller {
++ #interrupt-cells = <1>;
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ };
++ };
++ cpu1: cpu@1 {
++ compatible = "sifive,bullet0", "riscv";
++ d-cache-block-size = <64>;
++ d-cache-sets = <64>;
++ d-cache-size = <32768>;
++ d-tlb-sets = <1>;
++ d-tlb-size = <40>;
++ device_type = "cpu";
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ i-tlb-sets = <1>;
++ i-tlb-size = <40>;
++ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
++ reg = <0x1>;
++ riscv,isa = "rv64imafdc";
++ tlb-split;
++ cpu1_intc: interrupt-controller {
++ #interrupt-cells = <1>;
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ };
++ };
++ cpu2: cpu@2 {
++ compatible = "sifive,bullet0", "riscv";
++ d-cache-block-size = <64>;
++ d-cache-sets = <64>;
++ d-cache-size = <32768>;
++ d-tlb-sets = <1>;
++ d-tlb-size = <40>;
++ device_type = "cpu";
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ i-tlb-sets = <1>;
++ i-tlb-size = <40>;
++ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
++ reg = <0x2>;
++ riscv,isa = "rv64imafdc";
++ tlb-split;
++ cpu2_intc: interrupt-controller {
++ #interrupt-cells = <1>;
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ };
++ };
++ cpu3: cpu@3 {
++ compatible = "sifive,bullet0", "riscv";
++ d-cache-block-size = <64>;
++ d-cache-sets = <64>;
++ d-cache-size = <32768>;
++ d-tlb-sets = <1>;
++ d-tlb-size = <40>;
++ device_type = "cpu";
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ i-tlb-sets = <1>;
++ i-tlb-size = <40>;
++ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
++ reg = <0x3>;
++ riscv,isa = "rv64imafdc";
++ tlb-split;
++ cpu3_intc: interrupt-controller {
++ #interrupt-cells = <1>;
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ };
++ };
++ cpu4: cpu@4 {
++ compatible = "sifive,bullet0", "riscv";
++ d-cache-block-size = <64>;
++ d-cache-sets = <64>;
++ d-cache-size = <32768>;
++ d-tlb-sets = <1>;
++ d-tlb-size = <40>;
++ device_type = "cpu";
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ i-tlb-sets = <1>;
++ i-tlb-size = <40>;
++ mmu-type = "riscv,sv39";
++ next-level-cache = <&ccache>;
++ reg = <0x4>;
++ riscv,isa = "rv64imafdc";
++ tlb-split;
++ cpu4_intc: interrupt-controller {
++ #interrupt-cells = <1>;
++ compatible = "riscv,cpu-intc";
++ interrupt-controller;
++ };
++ };
++ };
++ soc {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ compatible = "simple-bus";
++ ranges;
++ plic0: interrupt-controller@c000000 {
++ #interrupt-cells = <1>;
++ #address-cells = <0>;
++ compatible = "sifive,fu540-c000-plic", "sifive,plic-1.0.0";
++ reg = <0x0 0xc000000 0x0 0x4000000>;
++ riscv,ndev = <69>;
++ interrupt-controller;
++ interrupts-extended = <
++ &cpu0_intc 0xffffffff
++ &cpu1_intc 0xffffffff &cpu1_intc 9
++ &cpu2_intc 0xffffffff &cpu2_intc 9
++ &cpu3_intc 0xffffffff &cpu3_intc 9
++ &cpu4_intc 0xffffffff &cpu4_intc 9>;
++ };
++ prci: clock-controller@10000000 {
++ compatible = "sifive,fu740-c000-prci";
++ reg = <0x0 0x10000000 0x0 0x1000>;
++ clocks = <&hfclk>, <&rtcclk>;
++ #clock-cells = <1>;
++ };
++ uart0: serial@10010000 {
++ compatible = "sifive,fu740-c000-uart", "sifive,uart0";
++ reg = <0x0 0x10010000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <39>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ status = "disabled";
++ };
++ uart1: serial@10011000 {
++ compatible = "sifive,fu740-c000-uart", "sifive,uart0";
++ reg = <0x0 0x10011000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <40>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ status = "disabled";
++ };
++ i2c0: i2c@10030000 {
++ compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
++ reg = <0x0 0x10030000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <52>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ reg-shift = <2>;
++ reg-io-width = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ i2c1: i2c@10031000 {
++ compatible = "sifive,fu740-c000-i2c", "sifive,i2c0";
++ reg = <0x0 0x10031000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <53>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ reg-shift = <2>;
++ reg-io-width = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ qspi0: spi@10040000 {
++ compatible = "sifive,fu740-c000-spi", "sifive,spi0";
++ reg = <0x0 0x10040000 0x0 0x1000>,
++ <0x0 0x20000000 0x0 0x10000000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <41>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ qspi1: spi@10041000 {
++ compatible = "sifive,fu740-c000-spi", "sifive,spi0";
++ reg = <0x0 0x10041000 0x0 0x1000>,
++ <0x0 0x30000000 0x0 0x10000000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <42>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ spi0: spi@10050000 {
++ compatible = "sifive,fu740-c000-spi", "sifive,spi0";
++ reg = <0x0 0x10050000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <43>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ eth0: ethernet@10090000 {
++ compatible = "sifive,fu540-c000-gem";
++ interrupt-parent = <&plic0>;
++ interrupts = <55>;
++ reg = <0x0 0x10090000 0x0 0x2000>,
++ <0x0 0x100a0000 0x0 0x1000>;
++ local-mac-address = [00 00 00 00 00 00];
++ clock-names = "pclk", "hclk";
++ clocks = <&prci PRCI_CLK_GEMGXLPLL>,
++ <&prci PRCI_CLK_GEMGXLPLL>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++ pwm0: pwm@10020000 {
++ compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
++ reg = <0x0 0x10020000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <44>, <45>, <46>, <47>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ #pwm-cells = <3>;
++ status = "disabled";
++ };
++ pwm1: pwm@10021000 {
++ compatible = "sifive,fu740-c000-pwm", "sifive,pwm0";
++ reg = <0x0 0x10021000 0x0 0x1000>;
++ interrupt-parent = <&plic0>;
++ interrupts = <48>, <49>, <50>, <51>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ #pwm-cells = <3>;
++ status = "disabled";
++ };
++ ccache: cache-controller@2010000 {
++ compatible = "sifive,fu740-c000-ccache", "cache";
++ cache-block-size = <64>;
++ cache-level = <2>;
++ cache-sets = <2048>;
++ cache-size = <2097152>;
++ cache-unified;
++ interrupt-parent = <&plic0>;
++ interrupts = <19 20 21 22>;
++ reg = <0x0 0x2010000 0x0 0x1000>;
++ };
++ gpio: gpio@10060000 {
++ compatible = "sifive,fu740-c000-gpio", "sifive,gpio0";
++ interrupt-parent = <&plic0>;
++ interrupts = <23>, <24>, <25>, <26>, <27>, <28>, <29>,
++ <30>, <31>, <32>, <33>, <34>, <35>, <36>,
++ <37>, <38>;
++ reg = <0x0 0x10060000 0x0 0x1000>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ clocks = <&prci PRCI_CLK_PCLK>;
++ status = "disabled";
++ };
++ };
++};
+--
+2.7.4
+
--- /dev/null
+From 323c7d104a6fbe22100fdd7b62d53a4f4739affa Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:40 +0530
+Subject: [PATCH 13/29] dt-bindings: riscv: Update YAML doc to support SiFive
+ HiFive Unmatched board
+
+Add new compatible strings to the YAML DT binding document to support
+SiFive's HiFive Unmatched board
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Bin Meng <bin.meng@windriver.com>
+---
+ Documentation/devicetree/bindings/riscv/sifive.yaml | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/riscv/sifive.yaml b/Documentation/devicetree/bindings/riscv/sifive.yaml
+index 3a8647d..ee0a239 100644
+--- a/Documentation/devicetree/bindings/riscv/sifive.yaml
++++ b/Documentation/devicetree/bindings/riscv/sifive.yaml
+@@ -17,11 +17,18 @@ properties:
+ $nodename:
+ const: '/'
+ compatible:
+- items:
+- - enum:
+- - sifive,hifive-unleashed-a00
+- - const: sifive,fu540-c000
+- - const: sifive,fu540
++ oneOf:
++ - items:
++ - enum:
++ - sifive,hifive-unleashed-a00
++ - const: sifive,fu540-c000
++ - const: sifive,fu540
++
++ - items:
++ - enum:
++ - sifive,hifive-unmatched-a00
++ - const: sifive,fu740-c000
++ - const: sifive,fu740
+
+ additionalProperties: true
+
+--
+2.7.4
+
--- /dev/null
+From 721f85343651f07ab6ed8064680ad8bbadf76d3f Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Tue, 8 Dec 2020 10:25:41 +0530
+Subject: [PATCH 14/29] riscv: dts: add initial board data for the SiFive
+ HiFive Unmatched
+
+Add initial board data for the SiFive HiFive Unmatched A00.
+This patch is dependent on Zong's Patchset[0].
+
+[0]: https://lore.kernel.org/linux-riscv/20201130082330.77268-4-zong.li@sifive.com/T/#u
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+Reviewed-by: Bin Meng <bin.meng@windriver.com>
+---
+ arch/riscv/boot/dts/sifive/Makefile | 3 +-
+ .../riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 253 +++++++++++++++++++++
+ 2 files changed, 255 insertions(+), 1 deletion(-)
+ create mode 100644 arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+
+diff --git a/arch/riscv/boot/dts/sifive/Makefile b/arch/riscv/boot/dts/sifive/Makefile
+index 6d6189e..74c47fe 100644
+--- a/arch/riscv/boot/dts/sifive/Makefile
++++ b/arch/riscv/boot/dts/sifive/Makefile
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0
+-dtb-$(CONFIG_SOC_SIFIVE) += hifive-unleashed-a00.dtb
++dtb-$(CONFIG_SOC_SIFIVE) += hifive-unleashed-a00.dtb \
++ hifive-unmatched-a00.dtb
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+new file mode 100644
+index 00000000..b1c3c59
+--- /dev/null
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -0,0 +1,253 @@
++// SPDX-License-Identifier: (GPL-2.0 OR MIT)
++/* Copyright (c) 2020 SiFive, Inc */
++
++#include "fu740-c000.dtsi"
++#include <dt-bindings/interrupt-controller/irq.h>
++
++/* Clock frequency (in Hz) of the PCB crystal for rtcclk */
++#define RTCCLK_FREQ 1000000
++
++/ {
++ #address-cells = <2>;
++ #size-cells = <2>;
++ model = "SiFive HiFive Unmatched A00";
++ compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000",
++ "sifive,fu740";
++
++ chosen {
++ stdout-path = "serial0";
++ };
++
++ cpus {
++ timebase-frequency = <RTCCLK_FREQ>;
++ };
++
++ memory@80000000 {
++ device_type = "memory";
++ reg = <0x0 0x80000000 0x2 0x00000000>;
++ };
++
++ soc {
++ };
++
++ hfclk: hfclk {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <26000000>;
++ clock-output-names = "hfclk";
++ };
++
++ rtcclk: rtcclk {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <RTCCLK_FREQ>;
++ clock-output-names = "rtcclk";
++ };
++};
++
++&uart0 {
++ status = "okay";
++};
++
++&uart1 {
++ status = "okay";
++};
++
++&i2c0 {
++ status = "okay";
++
++ temperature-sensor@4c {
++ compatible = "ti,tmp451";
++ reg = <0x4c>;
++ interrupt-parent = <&gpio>;
++ interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
++ };
++
++ pmic@58 {
++ compatible = "dlg,da9063";
++ reg = <0x58>;
++ interrupt-parent = <&gpio>;
++ interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
++ interrupt-controller;
++
++ regulators {
++ vdd_bcore1: bcore1 {
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ regulator-min-microamp = <5000000>;
++ regulator-max-microamp = <5000000>;
++ regulator-always-on;
++ };
++
++ vdd_bcore2: bcore2 {
++ regulator-min-microvolt = <900000>;
++ regulator-max-microvolt = <900000>;
++ regulator-min-microamp = <5000000>;
++ regulator-max-microamp = <5000000>;
++ regulator-always-on;
++ };
++
++ vdd_bpro: bpro {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <2500000>;
++ regulator-max-microamp = <2500000>;
++ regulator-always-on;
++ };
++
++ vdd_bperi: bperi {
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1050000>;
++ regulator-min-microamp = <1500000>;
++ regulator-max-microamp = <1500000>;
++ regulator-always-on;
++ };
++
++ vdd_bmem: bmem {
++ regulator-min-microvolt = <1200000>;
++ regulator-max-microvolt = <1200000>;
++ regulator-min-microamp = <3000000>;
++ regulator-max-microamp = <3000000>;
++ regulator-always-on;
++ };
++
++ vdd_bio: bio {
++ regulator-min-microvolt = <1200000>;
++ regulator-max-microvolt = <1200000>;
++ regulator-min-microamp = <3000000>;
++ regulator-max-microamp = <3000000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo1: ldo1 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <100000>;
++ regulator-max-microamp = <100000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo2: ldo2 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo3: ldo3 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo4: ldo4 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo5: ldo5 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <100000>;
++ regulator-max-microamp = <100000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo6: ldo6 {
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo7: ldo7 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ldo8: ldo8 {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ regulator-always-on;
++ };
++
++ vdd_ld09: ldo9 {
++ regulator-min-microvolt = <1050000>;
++ regulator-max-microvolt = <1050000>;
++ regulator-min-microamp = <200000>;
++ regulator-max-microamp = <200000>;
++ };
++
++ vdd_ldo10: ldo10 {
++ regulator-min-microvolt = <1000000>;
++ regulator-max-microvolt = <1000000>;
++ regulator-min-microamp = <300000>;
++ regulator-max-microamp = <300000>;
++ };
++
++ vdd_ldo11: ldo11 {
++ regulator-min-microvolt = <2500000>;
++ regulator-max-microvolt = <2500000>;
++ regulator-min-microamp = <300000>;
++ regulator-max-microamp = <300000>;
++ regulator-always-on;
++ };
++ };
++ };
++};
++
++&qspi0 {
++ status = "okay";
++ flash@0 {
++ compatible = "issi,is25wp256", "jedec,spi-nor";
++ reg = <0>;
++ spi-max-frequency = <50000000>;
++ m25p,fast-read;
++ spi-tx-bus-width = <4>;
++ spi-rx-bus-width = <4>;
++ };
++};
++
++&spi0 {
++ status = "okay";
++ mmc@0 {
++ compatible = "mmc-spi-slot";
++ reg = <0>;
++ spi-max-frequency = <20000000>;
++ voltage-ranges = <3300 3300>;
++ disable-wp;
++ };
++};
++
++ð0 {
++ status = "okay";
++ phy-mode = "gmii";
++ phy-handle = <&phy0>;
++ phy0: ethernet-phy@0 {
++ reg = <0>;
++ };
++};
++
++&pwm0 {
++ status = "okay";
++};
++
++&pwm1 {
++ status = "okay";
++};
++
++&gpio {
++ status = "okay";
++};
+--
+2.7.4
+
--- /dev/null
+From f4a28f41637429a1784ab2bf5e16222ec91da575 Mon Sep 17 00:00:00 2001
+From: Yash Shah <yash.shah@sifive.com>
+Date: Thu, 10 Dec 2020 15:58:03 +0530
+Subject: [PATCH 16/29] RISC-V: sifive_l2_cache: Update L2 cache driver to
+ support SiFive FU740
+
+SiFive FU740 has 4 ECC interrupt sources as compared to 3 in FU540.
+Update the L2 cache controller driver to support this additional
+interrupt in case of FU740-C000 chip.
+
+Signed-off-by: Yash Shah <yash.shah@sifive.com>
+---
+ drivers/soc/sifive/sifive_l2_cache.c | 27 ++++++++++++++++++++++++---
+ 1 file changed, 24 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/soc/sifive/sifive_l2_cache.c b/drivers/soc/sifive/sifive_l2_cache.c
+index 44d7e1951..59640a1 100644
+--- a/drivers/soc/sifive/sifive_l2_cache.c
++++ b/drivers/soc/sifive/sifive_l2_cache.c
+@@ -17,6 +17,10 @@
+ #define SIFIVE_L2_DIRECCFIX_HIGH 0x104
+ #define SIFIVE_L2_DIRECCFIX_COUNT 0x108
+
++#define SIFIVE_L2_DIRECCFAIL_LOW 0x120
++#define SIFIVE_L2_DIRECCFAIL_HIGH 0x124
++#define SIFIVE_L2_DIRECCFAIL_COUNT 0x128
++
+ #define SIFIVE_L2_DATECCFIX_LOW 0x140
+ #define SIFIVE_L2_DATECCFIX_HIGH 0x144
+ #define SIFIVE_L2_DATECCFIX_COUNT 0x148
+@@ -29,7 +33,7 @@
+ #define SIFIVE_L2_WAYENABLE 0x08
+ #define SIFIVE_L2_ECCINJECTERR 0x40
+
+-#define SIFIVE_L2_MAX_ECCINTR 3
++#define SIFIVE_L2_MAX_ECCINTR 4
+
+ static void __iomem *l2_base;
+ static int g_irq[SIFIVE_L2_MAX_ECCINTR];
+@@ -39,6 +43,7 @@ enum {
+ DIR_CORR = 0,
+ DATA_CORR,
+ DATA_UNCORR,
++ DIR_UNCORR,
+ };
+
+ #ifdef CONFIG_DEBUG_FS
+@@ -93,6 +98,7 @@ static void l2_config_read(void)
+
+ static const struct of_device_id sifive_l2_ids[] = {
+ { .compatible = "sifive,fu540-c000-ccache" },
++ { .compatible = "sifive,fu740-c000-ccache" },
+ { /* end of table */ },
+ };
+
+@@ -155,6 +161,15 @@ static irqreturn_t l2_int_handler(int irq, void *device)
+ atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_CE,
+ "DirECCFix");
+ }
++ if (irq == g_irq[DIR_UNCORR]) {
++ add_h = readl(l2_base + SIFIVE_L2_DIRECCFAIL_HIGH);
++ add_l = readl(l2_base + SIFIVE_L2_DIRECCFAIL_LOW);
++ /* Reading this register clears the DirFail interrupt sig */
++ readl(l2_base + SIFIVE_L2_DIRECCFAIL_COUNT);
++ atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_UE,
++ "DirECCFail");
++ panic("L2CACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l);
++ }
+ if (irq == g_irq[DATA_CORR]) {
+ add_h = readl(l2_base + SIFIVE_L2_DATECCFIX_HIGH);
+ add_l = readl(l2_base + SIFIVE_L2_DATECCFIX_LOW);
+@@ -181,7 +196,7 @@ static int __init sifive_l2_init(void)
+ {
+ struct device_node *np;
+ struct resource res;
+- int i, rc;
++ int i, rc, intr_num;
+
+ np = of_find_matching_node(NULL, sifive_l2_ids);
+ if (!np)
+@@ -194,7 +209,13 @@ static int __init sifive_l2_init(void)
+ if (!l2_base)
+ return -ENOMEM;
+
+- for (i = 0; i < SIFIVE_L2_MAX_ECCINTR; i++) {
++ intr_num = of_property_count_u32_elems(np, "interrupts");
++ if (!intr_num) {
++ pr_err("L2CACHE: no interrupts property\n");
++ return -ENODEV;
++ }
++
++ for (i = 0; i < intr_num; i++) {
+ g_irq[i] = irq_of_parse_and_map(np, i);
+ rc = request_irq(g_irq[i], l2_int_handler, 0, "l2_ecc", NULL);
+ if (rc) {
+--
+2.7.4
+
--- /dev/null
+From e00f15b712b61f35fec747912dd5d3e9553d89a1 Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Fri, 13 Nov 2020 10:33:55 +0800
+Subject: [PATCH 17/29] gpio: sifive: To get gpio irq offset from device tree
+ data
+
+We can get hwirq number of the gpio by its irq_data->hwirq so that we don't
+need to add more macros for different platforms. This patch is tested in
+SiFive Unleashed board and SiFive Unmatched board.
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ drivers/gpio/gpio-sifive.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
+index d5eb9ca..63593c2 100644
+--- a/drivers/gpio/gpio-sifive.c
++++ b/drivers/gpio/gpio-sifive.c
+@@ -29,7 +29,6 @@
+ #define SIFIVE_GPIO_OUTPUT_XOR 0x40
+
+ #define SIFIVE_GPIO_MAX 32
+-#define SIFIVE_GPIO_IRQ_OFFSET 7
+
+ struct sifive_gpio {
+ void __iomem *base;
+@@ -37,7 +36,7 @@ struct sifive_gpio {
+ struct regmap *regs;
+ unsigned long irq_state;
+ unsigned int trigger[SIFIVE_GPIO_MAX];
+- unsigned int irq_parent[SIFIVE_GPIO_MAX];
++ unsigned int irq_number[SIFIVE_GPIO_MAX];
+ };
+
+ static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset)
+@@ -144,8 +143,12 @@ static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+ unsigned int *parent,
+ unsigned int *parent_type)
+ {
++ struct sifive_gpio *chip = gpiochip_get_data(gc);
++ struct irq_data *d = irq_get_irq_data(chip->irq_number[child]);
++
+ *parent_type = IRQ_TYPE_NONE;
+- *parent = child + SIFIVE_GPIO_IRQ_OFFSET;
++ *parent = irqd_to_hwirq(d);
++
+ return 0;
+ }
+
+@@ -165,7 +168,7 @@ static int sifive_gpio_probe(struct platform_device *pdev)
+ struct irq_domain *parent;
+ struct gpio_irq_chip *girq;
+ struct sifive_gpio *chip;
+- int ret, ngpio;
++ int ret, ngpio, i;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+@@ -200,6 +203,9 @@ static int sifive_gpio_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
++ for (i = 0; i < ngpio; i++)
++ chip->irq_number[i] = platform_get_irq(pdev, i);
++
+ ret = bgpio_init(&chip->gc, dev, 4,
+ chip->base + SIFIVE_GPIO_INPUT_VAL,
+ chip->base + SIFIVE_GPIO_OUTPUT_VAL,
+--
+2.7.4
+
--- /dev/null
+From 49e87f076a3d9d4b1ebadfe2b19250977be6d668 Mon Sep 17 00:00:00 2001
+From: Vincent Chen <vincent.chen@sifive.com>
+Date: Tue, 12 Jan 2021 14:43:50 +0800
+Subject: [PATCH 18/29] riscv: Add 3 SBI wrapper functions to get cpu
+ manufactory information
+
+Add 3 wrapper functions to get vendor id, architecture id and implement id
+from M-mode
+
+Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
+---
+ arch/riscv/include/asm/sbi.h | 3 +++
+ arch/riscv/kernel/sbi.c | 15 +++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index 653edb2..97eb78d 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -97,6 +97,9 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
+
+ void sbi_console_putchar(int ch);
+ int sbi_console_getchar(void);
++long sbi_get_vendorid(void);
++long sbi_get_archid(void);
++long sbi_get_impid(void);
+ void sbi_set_timer(uint64_t stime_value);
+ void sbi_shutdown(void);
+ void sbi_clear_ipi(void);
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 226ccce..b8f82c7 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -547,6 +547,21 @@ static inline long sbi_get_firmware_version(void)
+ return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION);
+ }
+
++long sbi_get_vendorid(void)
++{
++ return __sbi_base_ecall(SBI_EXT_BASE_GET_MVENDORID);
++}
++
++long sbi_get_archid(void)
++{
++ return __sbi_base_ecall(SBI_EXT_BASE_GET_MARCHID);
++}
++
++long sbi_get_impid(void)
++{
++ return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID);
++}
++
+ static void sbi_send_cpumask_ipi(const struct cpumask *target)
+ {
+ struct cpumask hartid_mask;
+--
+2.7.4
+
--- /dev/null
+From 34c4f71f957e4e15776493d094593970178ddc9e Mon Sep 17 00:00:00 2001
+From: Vincent Chen <vincent.chen@sifive.com>
+Date: Tue, 12 Jan 2021 15:15:23 +0800
+Subject: [PATCH 19/29] riscv: Get CPU manufactory information
+
+Issue 3 SBI calls to get the vendor ID, architecture ID and
+implementataion ID.
+
+Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
+---
+ arch/riscv/include/asm/csr.h | 3 +++
+ arch/riscv/include/asm/hwcap.h | 6 ++++++
+ arch/riscv/include/asm/processor.h | 2 ++
+ arch/riscv/kernel/cpufeature.c | 17 +++++++++++++++++
+ arch/riscv/kernel/setup.c | 2 ++
+ 5 files changed, 30 insertions(+)
+
+diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
+index cec462e..076beb9 100644
+--- a/arch/riscv/include/asm/csr.h
++++ b/arch/riscv/include/asm/csr.h
+@@ -109,6 +109,9 @@
+ #define CSR_MIP 0x344
+ #define CSR_PMPCFG0 0x3a0
+ #define CSR_PMPADDR0 0x3b0
++#define CSR_MVENDORID 0xf11
++#define CSR_MARCHID 0xf12
++#define CSR_MIMPID 0xf13
+ #define CSR_MHARTID 0xf14
+
+ #ifdef CONFIG_RISCV_M_MODE
+diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
+index 5ce5046..952fe76 100644
+--- a/arch/riscv/include/asm/hwcap.h
++++ b/arch/riscv/include/asm/hwcap.h
+@@ -44,6 +44,12 @@ bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, int bit);
+ #define riscv_isa_extension_available(isa_bitmap, ext) \
+ __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_##ext)
+
++struct cpu_manufactor_info_t {
++ unsigned long vendorid;
++ unsigned long archid;
++ unsigned long impid;
++};
++
+ #endif
+
+ #endif /* _ASM_RISCV_HWCAP_H */
+diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
+index bdddcd5..c9639b5 100644
+--- a/arch/riscv/include/asm/processor.h
++++ b/arch/riscv/include/asm/processor.h
+@@ -71,6 +71,8 @@ int riscv_of_parent_hartid(struct device_node *node);
+
+ extern void riscv_fill_hwcap(void);
+
++void riscv_fill_cpu_manufactor_info(void);
++
+ #endif /* __ASSEMBLY__ */
+
+ #endif /* _ASM_RISCV_PROCESSOR_H */
+diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
+index ac202f4..0315297 100644
+--- a/arch/riscv/kernel/cpufeature.c
++++ b/arch/riscv/kernel/cpufeature.c
+@@ -12,6 +12,8 @@
+ #include <asm/hwcap.h>
+ #include <asm/smp.h>
+ #include <asm/switch_to.h>
++#include <asm/sbi.h>
++#include <asm/csr.h>
+
+ unsigned long elf_hwcap __read_mostly;
+
+@@ -22,6 +24,8 @@ static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
+ bool has_fpu __read_mostly;
+ #endif
+
++struct cpu_manufactor_info_t cpu_manufactor_info;
++
+ /**
+ * riscv_isa_extension_base() - Get base extension word
+ *
+@@ -149,3 +153,16 @@ void riscv_fill_hwcap(void)
+ has_fpu = true;
+ #endif
+ }
++
++void riscv_fill_cpu_manufactor_info(void)
++{
++#ifndef CONFIG_RISCV_M_MODE
++ cpu_manufactor_info.vendorid = sbi_get_vendorid();
++ cpu_manufactor_info.archid = sbi_get_archid();
++ cpu_manufactor_info.impid = sbi_get_impid();
++#else
++ cpu_manufactor_info.vendorid = csr_read(CSR_MVENDORID);
++ cpu_manufactor_info.archid = csr_read(CSR_MARCHID);
++ cpu_manufactor_info.impid = csr_read(CSR_MIMPID);
++#endif
++}
+diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
+index 117f321..c7c1ae9 100644
+--- a/arch/riscv/kernel/setup.c
++++ b/arch/riscv/kernel/setup.c
+@@ -107,6 +107,8 @@ void __init setup_arch(char **cmdline_p)
+ #endif
+
+ riscv_fill_hwcap();
++
++ riscv_fill_cpu_manufactor_info();
+ }
+
+ static int __init topology_init(void)
+--
+2.7.4
+
--- /dev/null
+From a3e6ca97499c444fee45c7efafcdef311142952c Mon Sep 17 00:00:00 2001
+From: Vincent Chen <vincent.chen@sifive.com>
+Date: Tue, 19 Jan 2021 03:30:22 -0800
+Subject: [PATCH 20/29] riscv: Introduce alternative mechanism to apply errata
+ solution
+
+Introduce the "alternative" mechanism from ARM64 and x86 to apply the CPU
+vendors' errata solution at runtime. The main purpose of this patch is
+to provide a framework. Therefore, the implementation is quite basic for
+now so that some scenarios could not use this scheme such as patching code
+to a module, relocating the patching code and heterogeneous CPU topology.
+
+Users could use the two macros ALTINSN and ALTDATA to modify the existing
+instruction and data respectively. By specifying the parameters vendorid,
+archid and impid, the kernel can probably apply the patch codes based on
+the same information of the running CPU. To keep the flexibility, the user
+can pass the specific kernel configure to the alternative macro to enable
+or disable the errata solution at compile time.
+
+Rebased for v5.10.8 by David Abdurachmanov.
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/Kconfig | 7 ++
+ arch/riscv/Makefile | 1 +
+ arch/riscv/errata/Makefile | 1 +
+ arch/riscv/errata/alternative.c | 74 ++++++++++++++++
+ arch/riscv/include/asm/alternative-macros.h | 133 ++++++++++++++++++++++++++++
+ arch/riscv/include/asm/alternative.h | 43 +++++++++
+ arch/riscv/include/asm/asm.h | 1 +
+ arch/riscv/include/asm/sections.h | 3 +
+ arch/riscv/kernel/smpboot.c | 4 +
+ arch/riscv/kernel/vmlinux.lds.S | 21 +++++
+ 10 files changed, 288 insertions(+)
+ create mode 100644 arch/riscv/errata/Makefile
+ create mode 100644 arch/riscv/errata/alternative.c
+ create mode 100644 arch/riscv/include/asm/alternative-macros.h
+ create mode 100644 arch/riscv/include/asm/alternative.h
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 3474286..2717aa0 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -84,6 +84,7 @@ config RISCV
+ select PCI_MSI if PCI
+ select RISCV_INTC
+ select RISCV_TIMER if RISCV_SBI
++ select RISCV_ERRATA
+ select SPARSE_IRQ
+ select SYSCTL_EXCEPTION_TRACE
+ select THREAD_INFO_IN_TASK
+@@ -423,6 +424,12 @@ config BUILTIN_DTB
+ depends on RISCV_M_MODE
+ depends on OF
+
++config RISCV_ERRATA
++ bool "Runtime apply errata patch"
++ help
++ This option provides the support for applying the errata patch
++ at runtime.
++
+ menu "Power management options"
+
+ source "kernel/power/Kconfig"
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index 0289a97..cd23fb0 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -75,6 +75,7 @@ KBUILD_IMAGE := $(boot)/Image.gz
+ head-y := arch/riscv/kernel/head.o
+
+ core-y += arch/riscv/
++core-$(CONFIG_RISCV_ERRATA) += arch/riscv/errata/
+
+ libs-y += arch/riscv/lib/
+ libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
+diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile
+new file mode 100644
+index 00000000..43e6d54
+--- /dev/null
++++ b/arch/riscv/errata/Makefile
+@@ -0,0 +1 @@
++obj-y += alternative.o
+diff --git a/arch/riscv/errata/alternative.c b/arch/riscv/errata/alternative.c
+new file mode 100644
+index 00000000..0827c05
+--- /dev/null
++++ b/arch/riscv/errata/alternative.c
+@@ -0,0 +1,74 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * alternative runtime patching
++ * inspired by the ARM64 and x86 version
++ *
++ * Copyright (C) 2021 Sifive.
++ */
++
++#include <linux/init.h>
++#include <linux/cpu.h>
++#include <linux/uaccess.h>
++#include <asm/patch.h>
++#include <asm/alternative.h>
++#include <asm/sections.h>
++
++struct alt_region {
++ struct alt_entry *begin;
++ struct alt_entry *end;
++};
++
++static bool __init default_checkfunc(struct alt_entry *alt)
++{
++ return false;
++}
++
++static bool (*errata_checkfunc)(struct alt_entry *alt) = default_checkfunc;
++typedef int (*patch_func_t)(void *addr, const void *insn, size_t size);
++
++static void __apply_alternatives(void *alt_region, void *alt_patch_func)
++{
++ struct alt_entry *alt;
++ struct alt_region *region = alt_region;
++
++ for (alt = region->begin; alt < region->end; alt++) {
++ if (!errata_checkfunc(alt))
++ continue;
++ ((patch_func_t)alt_patch_func)(alt->old_ptr, alt->alt_ptr, alt->old_len);
++ }
++}
++
++static void __init init_alternatvie(void)
++{
++ struct errata_checkfunc_id *ptr;
++
++ for (ptr = (struct errata_checkfunc_id *)__alt_checkfunc_table;
++ ptr < (struct errata_checkfunc_id *)__alt_checkfunc_table_end;
++ ptr++) {
++ if (cpu_manufactor_info.vendorid == ptr->vendorid)
++ errata_checkfunc = ptr->func;
++ }
++}
++
++/*
++ * This is called very early in the boot process (directly after we run
++ * a feature detect on the boot CPU). No need to worry about other CPUs
++ * here.
++ */
++void __init apply_boot_alternatives(void)
++{
++ struct alt_region region;
++
++ init_alternatvie();
++ /* If called on non-boot cpu things could go wrong */
++ WARN_ON(smp_processor_id() != 0);
++
++ region.begin = (struct alt_entry *)__alt_insn;
++ region.end = (struct alt_entry *)__alt_insn_end;
++ __apply_alternatives(®ion, patch_text_nosync);
++
++ region.begin = (struct alt_entry *)__alt_data;
++ region.end = (struct alt_entry *)__alt_data_end;
++ __apply_alternatives(®ion, copy_to_kernel_nofault);
++}
++
+diff --git a/arch/riscv/include/asm/alternative-macros.h b/arch/riscv/include/asm/alternative-macros.h
+new file mode 100644
+index 00000000..dd06ded
+--- /dev/null
++++ b/arch/riscv/include/asm/alternative-macros.h
+@@ -0,0 +1,133 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __ASM_ALTERNATIVE_MACROS_H
++#define __ASM_ALTERNATIVE_MACROS_H
++
++#ifndef __ASSEMBLY__
++
++#include <asm/asm.h>
++#include <linux/stringify.h>
++
++#define ALT_ENTRY(oldptr, altptr, vendorid, archid, impid, oldlen, altlen) \
++ RISCV_PTR " " oldptr "\n" \
++ RISCV_PTR " " altptr "\n" \
++ REG_ASM " " vendorid "\n" \
++ REG_ASM " " archid "\ n" \
++ REG_ASM " " impid "\n" \
++ ".word " oldlen "\n" \
++ ".word " altlen "\n" \
++
++#define __ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, enable) \
++ ".if " __stringify(enable) " == 1\n" \
++ "886 :\n\t" \
++ oldinsn "\n" \
++ "887 :\n" \
++ ".pushsection .altinsn, \"a\"\n" \
++ ALT_ENTRY("886b", "888f", vendorid, archid, impid, "887b - 886b", "889f - 888f") \
++ ".popsection\n" \
++ ".subsection 1\n" \
++ "888 :\n\t" \
++ altinsn "\n" \
++ "889 :\n\t" \
++ ".previous\n" \
++ ".org . - (887b - 886b) + (889b - 888b)\n\t" \
++ ".org . - (889b - 888b) + (887b - 886b)\n\t" \
++ ".endif\n"
++
++#define _ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, CONFIG_k, ...) \
++ __ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, IS_ENABLED(CONFIG_k))
++
++#define __ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, enable) \
++ ".if " __stringify(enable) " == 1\n" \
++ ".pushsection .altdata, \"a\"\n" \
++ ALT_ENTRY(oldptr, altptr, vendorid, archid, impid, oldlen, altlen) \
++ ".popsection\n" \
++ ".previous\n" \
++ ".endif\n"
++
++#define _ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, CONFIG_k, ...) \
++ __ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, IS_ENABLED(CONFIG_k))
++
++#else
++.macro ALT_ENTRY oldptr altptr vendorid archid impid oldlen alt_len
++ RISCV_PTR \oldptr
++ RISCV_PTR \altptr
++ REG_ASM \vendorid
++ REG_ASM \archid
++ REG_ASM \impid
++ .word \oldlen
++ .word \alt_len
++.endm
++
++.macro __ALTINSN_CFG insn1 insn2 vendorid archid impid enable = 1
++ .if \enable
++886 :
++ \insn1
++887 :
++ .pushsection .altinsn, "a"
++ ALT_ENTRY 886b, 888f, \vendorid, \archid, \impid, 887b - 886b, 889f - 888f
++ .popsection
++ .subsection 1
++888 :
++ \insn2
++889 :
++ .previous
++ .org . - (889b - 888b) + (887b - 886b)
++ .org . - (887b - 886b) + (889b - 888b)
++ .endif
++.endm
++
++#define _ALTINSN_CFG(oldinsn, altinsn, vendorid, archid, impid, CONFIG_k, ...) \
++ __ALTINSN_CFG oldinsn, altinsn, vendorid, archid, impid, IS_ENABLED(CONFIG_k)
++
++.macro __ALTDATA_CFG oldptr altptr vendorid archid impid oldlen altlen enable = 1
++ .if \enable
++ .pushsection .altdata, "a"
++ ALT_ENTRY \oldptr \altptr \vendorid \archid \impid \oldlen \altlen
++ .popsection
++ .org . - \oldlen + \altlen
++ .org . - \altlen + \oldlen
++ .endif
++.endm
++
++#define _ALTDATA_CFG(oldptr, altptr, vendorid, archid, impid, oldlen, altlen, CONFIG_k, ...) \
++ __ALTDATA_CFG oldptr, altptr, vendorid, archid, impid, oldlen, altlen, IS_ENABLED(CONFIG_k)
++
++#endif
++
++/*
++ * Usage: asm(ALTINSN(oldinsn, altinsn, vendorid, archid, impid));
++ *
++ * Usage: asm(ALTERNATIVE(oldinsn, altinsn, vendorid, archid, impid, CONFIG_FOO));
++ *
++ * oldinsn: The old instruction which will be replaced.
++ * altinsn: The replacement instruction.
++ * vendorid: The CPU vendor ID.
++ * archid: The CPU architecture ID.
++ * impid: The CPU implement ID.
++ *
++ * N.B. If CONFIG_FOO is specified, but not selected, the whole block
++ * will be omitted, including oldinstr.
++ */
++#define ALTINSN(oldinsn, altinsn, ...) \
++ _ALTINSN_CFG(oldinsn, altinsn, __VA_ARGS__, 1)
++
++/*
++ * Usage: asm(ALTDATA(oldptr, altptr, vendorid, archid, impid, oldlen, altlen));
++ *
++ * Usage: asm(ALTERNATIVE(oldptr, altptr, feature, CONFIG_FOO));
++ *
++ * oldptr: The address of old data.
++ * altinsn: The address of replacement data.
++ * vendorid: The CPU vendor ID.
++ * archid: The CPU architecture ID.
++ * impid: The CPU implement ID.
++ * oldlen: The data length of old data.
++ * newlen: The data length of new data.
++ *
++ * N.B. If CONFIG_FOO is specified, but not selected, the whole block
++ * will be omitted.
++ */
++
++#define ALTDATA(oldptr, altptr, ...) \
++ _ALTDATA_CFG(oldptr, altptr, __VA_ARGS__, 1)
++#endif
+diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h
+new file mode 100644
+index 00000000..fc1d929
+--- /dev/null
++++ b/arch/riscv/include/asm/alternative.h
+@@ -0,0 +1,43 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2021 Sifive.
++ */
++
++#ifndef __ASM_ALTERNATIVE_H
++#define __ASM_ALTERNATIVE_H
++
++#include <asm/alternative-macros.h>
++#ifndef __ASSEMBLY__
++
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/stddef.h>
++#include <asm/hwcap.h>
++
++void __init apply_boot_alternatives(void);
++
++struct alt_entry {
++ void *old_ptr; /* address of original instruciton or data */
++ void *alt_ptr; /* address of replacement instruction or data */
++ unsigned long vendorid; /* cpu vendor id */
++ unsigned long archid; /* cpu architecture id */
++ unsigned long impid; /* cpu implement id */
++ unsigned int old_len; /* size of original instruction(s) or data(s) */
++ unsigned int alt_len; /* size of new instruction(s) or data(s) */
++};
++
++struct errata_checkfunc_id {
++ unsigned long vendorid;
++ bool (*func)(struct alt_entry *alt);
++};
++
++extern struct cpu_manufactor_info_t cpu_manufactor_info;
++
++#define REGISTER_ERRATA_CHECKFUNC(checkfunc, vendor_id) \
++ static const struct errata_checkfunc_id _errata_check_##vendor_id \
++ __used __section(".alt_checkfunc_table") \
++ __aligned(__alignof__(struct errata_checkfunc_id)) = \
++ { .vendorid = vendor_id, \
++ .func = checkfunc }
++#endif
++#endif
+diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
+index 9c992a8..618d7c5 100644
+--- a/arch/riscv/include/asm/asm.h
++++ b/arch/riscv/include/asm/asm.h
+@@ -23,6 +23,7 @@
+ #define REG_L __REG_SEL(ld, lw)
+ #define REG_S __REG_SEL(sd, sw)
+ #define REG_SC __REG_SEL(sc.d, sc.w)
++#define REG_ASM __REG_SEL(.dword, .word)
+ #define SZREG __REG_SEL(8, 4)
+ #define LGREG __REG_SEL(3, 2)
+
+diff --git a/arch/riscv/include/asm/sections.h b/arch/riscv/include/asm/sections.h
+index 3a9971b..2ee8e12 100644
+--- a/arch/riscv/include/asm/sections.h
++++ b/arch/riscv/include/asm/sections.h
+@@ -9,5 +9,8 @@
+
+ extern char _start[];
+ extern char _start_kernel[];
++extern char __alt_checkfunc_table[], __alt_checkfunc_table_end[];
++extern char __alt_data[], __alt_data_end[];
++extern char __alt_insn[], __alt_insn_end[];
+
+ #endif /* __ASM_SECTIONS_H */
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 96167d5..7177ee2 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -31,6 +31,7 @@
+ #include <asm/sections.h>
+ #include <asm/sbi.h>
+ #include <asm/smp.h>
++#include <asm/alternative.h>
+
+ #include "head.h"
+
+@@ -39,6 +40,9 @@ static DECLARE_COMPLETION(cpu_running);
+ void __init smp_prepare_boot_cpu(void)
+ {
+ init_cpu_topology();
++#ifdef CONFIG_RISCV_ERRATA
++ apply_boot_alternatives();
++#endif
+ }
+
+ void __init smp_prepare_cpus(unsigned int max_cpus)
+diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S
+index 3ffbd6c..9df933c 100644
+--- a/arch/riscv/kernel/vmlinux.lds.S
++++ b/arch/riscv/kernel/vmlinux.lds.S
+@@ -77,6 +77,27 @@ SECTIONS
+
+ INIT_DATA_SECTION(16)
+
++ . = ALIGN(8);
++ .alt_checkfunc_table : {
++ __alt_checkfunc_table = .;
++ *(.alt_checkfunc_table)
++ __alt_checkfunc_table_end = .;
++ }
++
++ . = ALIGN(8);
++ .altinsn : {
++ __alt_insn = .;
++ *(.altinsn)
++ __alt_insn_end = .;
++ }
++
++ . = ALIGN(8);
++ .altdata : {
++ __alt_data = .;
++ *(.altdata)
++ __alt_data_end = .;
++ }
++
+ /* Start of data section */
+ _sdata = .;
+ RO_DATA(SECTION_ALIGN)
+--
+2.7.4
+
--- /dev/null
+From fb63756f6b2e49160aaf85789f8ed88135eeea50 Mon Sep 17 00:00:00 2001
+From: Vincent Chen <vincent.chen@sifive.com>
+Date: Tue, 12 Jan 2021 15:29:29 +0800
+Subject: [PATCH 21/29] riscv: sifive: apply errata cip-453 patch
+
+Add sign extension to the $badaddr when the exception type is instruction
+page fault or instruciton access fault to workaround the errata cip-453.
+
+To avoid affecting the existing code sequence, this patch creates a
+trampoline to add sign extension to the $baddaddr, and then replaces
+the original exception handlers with this trampoline by ALTDATA. In this
+case, only the specific sifive CPU jumps to the do_page_fault through
+this trampoline. Other CPUs are not affected.
+
+Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
+---
+ arch/riscv/errata/Makefile | 1 +
+ arch/riscv/errata/sifive/Makefile | 2 +
+ arch/riscv/errata/sifive/altern_ops.c | 20 ++++++++++
+ arch/riscv/errata/sifive/errata.h | 6 +++
+ arch/riscv/errata/sifive/errata_cip_453.S | 64 +++++++++++++++++++++++++++++++
+ 5 files changed, 93 insertions(+)
+ create mode 100644 arch/riscv/errata/sifive/Makefile
+ create mode 100644 arch/riscv/errata/sifive/altern_ops.c
+ create mode 100644 arch/riscv/errata/sifive/errata.h
+ create mode 100644 arch/riscv/errata/sifive/errata_cip_453.S
+
+diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile
+index 43e6d54..be93ded 100644
+--- a/arch/riscv/errata/Makefile
++++ b/arch/riscv/errata/Makefile
+@@ -1 +1,2 @@
+ obj-y += alternative.o
++obj-$(CONFIG_SOC_SIFIVE) += sifive/
+diff --git a/arch/riscv/errata/sifive/Makefile b/arch/riscv/errata/sifive/Makefile
+new file mode 100644
+index 00000000..b7f4cd7
+--- /dev/null
++++ b/arch/riscv/errata/sifive/Makefile
+@@ -0,0 +1,2 @@
++obj-y += altern_ops.o
++obj-y += errata_cip_453.o
+diff --git a/arch/riscv/errata/sifive/altern_ops.c b/arch/riscv/errata/sifive/altern_ops.c
+new file mode 100644
+index 00000000..0dcec17
+--- /dev/null
++++ b/arch/riscv/errata/sifive/altern_ops.c
+@@ -0,0 +1,20 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2021 Sifive.
++ */
++
++#include <asm/alternative.h>
++#include <linux/kernel.h>
++
++#include "errata.h"
++
++static bool __init sifive_errata_check(struct alt_entry *alt)
++{
++ if (cpu_manufactor_info.vendorid == alt->vendorid &&
++ cpu_manufactor_info.archid == alt->archid &&
++ cpu_manufactor_info.impid == alt->impid)
++ return true;
++ return false;
++}
++
++REGISTER_ERRATA_CHECKFUNC(sifive_errata_check, SIFIVE_VENDOR_ID);
+diff --git a/arch/riscv/errata/sifive/errata.h b/arch/riscv/errata/sifive/errata.h
+new file mode 100644
+index 00000000..1f3be47
+--- /dev/null
++++ b/arch/riscv/errata/sifive/errata.h
+@@ -0,0 +1,6 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2021 SiFive
++ */
++
++#define SIFIVE_VENDOR_ID 0x489
+diff --git a/arch/riscv/errata/sifive/errata_cip_453.S b/arch/riscv/errata/sifive/errata_cip_453.S
+new file mode 100644
+index 00000000..c1ea974
+--- /dev/null
++++ b/arch/riscv/errata/sifive/errata_cip_453.S
+@@ -0,0 +1,64 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (C) 2021 SiFive
++ */
++
++#include <linux/linkage.h>
++#include <asm/asm.h>
++#include <asm/asm-offsets.h>
++#include <asm/alternative.h>
++#include "errata.h"
++
++#define INSN_PAGE_FAULT excp_vect_table + 12 << RISCV_LGPTR
++#define INSN_ACCESS_FAULT excp_vect_table + 1 << RISCV_LGPTR
++#define P_TRAMPOLINE sifive_ipage_fault_trampoline
++#define A_TRAMPOLINE sifive_iaccess_fault_trampoline
++#define MARCHID 0x8000000000000007
++#define MIMPID 0x20181004
++#define LEN 1 << RISCV_LGPTR
++
++.macro ADD_SIGN_EXT pt_reg badaddr tmp_reg
++ REG_L \badaddr, PT_BADADDR(\pt_reg)
++ li \tmp_reg,1
++ slli \tmp_reg,\tmp_reg,0x26
++ and \tmp_reg,\tmp_reg,\badaddr
++ beqz \tmp_reg, 1f
++ li \tmp_reg,-1
++ slli \tmp_reg,\tmp_reg,0x27
++ or \badaddr,\tmp_reg,\badaddr
++ REG_S \badaddr, PT_BADADDR(\pt_reg)
++1:
++.endm
++
++
++.globl sifive_ipage_fault_trampoline
++.type sifive_ipage_fault_trampoline, @object
++.size sifive_ipage_fault_trampoline, 8
++sifive_ipage_fault_trampoline:
++.dword do_page_fault_trampoline
++
++ENTRY(do_page_fault_trampoline)
++ ADD_SIGN_EXT a0, t0, t1
++ la t0, do_page_fault
++ jr t0
++END(do_page_fault_trampoline)
++
++.globl sifive_iaccess_fault_trampoline
++.type sifive_iaccess_fault_trampoline, @object
++.size sifive_iaccess_fault_trampoline, 8
++sifive_iaccess_fault_trampoline:
++.dword do_trap_insn_fault_trampoline
++
++ENTRY(do_trap_insn_fault_trampoline)
++ ADD_SIGN_EXT a0, t0, t1
++ la t0, do_trap_insn_fault
++ jr t0
++END(do_trap_insn_fault_trampoline)
++
++/*
++ * Replace the page fault exception handler with sifive_page_fault_trampoline
++ * function
++ */
++ALTDATA(INSN_PAGE_FAULT, P_TRAMPOLINE, SIFIVE_VENDOR_ID, MARCHID, MIMPID, LEN, LEN)
++ALTDATA(INSN_ACCESS_FAULT, A_TRAMPOLINE, SIFIVE_VENDOR_ID, MARCHID, MIMPID, LEN, LEN)
++
+--
+2.7.4
+
--- /dev/null
+From 8a0380c3eee96adf57daf2f54fec5089c4dee576 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Wed, 3 Feb 2021 07:00:15 -0800
+Subject: [PATCH 22/29] riscv: dts: fu740: fix cache-controller interrupts
+
+The order of interrupt numbers is incorrect.
+
+The order for FU740 is: DirError, DataError, DataFail, DirFail
+
+From SiFive FU740-C000 Manual:
+19 - L2 Cache DirError
+20 - L2 Cache DirFail
+21 - L2 Cache DataError
+22 - L2 Cache DataFail
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/fu740-c000.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+index eeb4f8c3..d0d206c 100644
+--- a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
++++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+@@ -272,7 +272,7 @@
+ cache-size = <2097152>;
+ cache-unified;
+ interrupt-parent = <&plic0>;
+- interrupts = <19 20 21 22>;
++ interrupts = <19 21 22 20>;
+ reg = <0x0 0x2010000 0x0 0x1000>;
+ };
+ gpio: gpio@10060000 {
+--
+2.7.4
+
--- /dev/null
+From 58231edb72ab61199226250483038159b2c56c65 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Wed, 17 Feb 2021 06:06:14 -0800
+Subject: [PATCH 23/29] riscv: sifive: fu740: cpu{1,2,3,4} set compatible to
+ sifive,u74-mc
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/fu740-c000.dtsi | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+index d0d206c..cd9cc02 100644
+--- a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
++++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+@@ -39,7 +39,7 @@
+ };
+ };
+ cpu1: cpu@1 {
+- compatible = "sifive,bullet0", "riscv";
++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
+ d-cache-block-size = <64>;
+ d-cache-sets = <64>;
+ d-cache-size = <32768>;
+@@ -63,7 +63,7 @@
+ };
+ };
+ cpu2: cpu@2 {
+- compatible = "sifive,bullet0", "riscv";
++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
+ d-cache-block-size = <64>;
+ d-cache-sets = <64>;
+ d-cache-size = <32768>;
+@@ -87,7 +87,7 @@
+ };
+ };
+ cpu3: cpu@3 {
+- compatible = "sifive,bullet0", "riscv";
++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
+ d-cache-block-size = <64>;
+ d-cache-sets = <64>;
+ d-cache-size = <32768>;
+@@ -111,7 +111,7 @@
+ };
+ };
+ cpu4: cpu@4 {
+- compatible = "sifive,bullet0", "riscv";
++ compatible = "sifive,u74-mc", "sifive,bullet0", "riscv";
+ d-cache-block-size = <64>;
+ d-cache-sets = <64>;
+ d-cache-size = <32768>;
+--
+2.7.4
+
--- /dev/null
+From 34222949288ef8b2666facee9ca9001c8cd93486 Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Wed, 3 Feb 2021 11:22:10 +0800
+Subject: [PATCH 24/29] riscv: dts: Add PCIe support for the SiFive FU740-C000
+ SoC
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/fu740-c000.dtsi | 35 ++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+index cd9cc02..727fd91 100644
+--- a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
++++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+@@ -159,6 +159,7 @@
+ reg = <0x0 0x10000000 0x0 0x1000>;
+ clocks = <&hfclk>, <&rtcclk>;
+ #clock-cells = <1>;
++ #reset-cells = <1>;
+ };
+ uart0: serial@10010000 {
+ compatible = "sifive,fu740-c000-uart", "sifive,uart0";
+@@ -289,5 +290,39 @@
+ clocks = <&prci PRCI_CLK_PCLK>;
+ status = "disabled";
+ };
++ pcie@e00000000 {
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #num-lanes = <8>;
++ #size-cells = <2>;
++ compatible = "sifive,fu740-pcie";
++ reg = <0xe 0x00000000 0x1 0x0
++ 0xd 0xf0000000 0x0 0x10000000
++ 0x0 0x100d0000 0x0 0x1000>;
++ reg-names = "dbi", "config", "mgmt";
++ device_type = "pci";
++ dma-coherent;
++ bus-range = <0x0 0xff>;
++ ranges = <0x81000000 0x0 0x60080000 0x0 0x60080000 0x0 0x10000 /* I/O */
++ 0x82000000 0x0 0x60090000 0x0 0x60090000 0x0 0xff70000 /* mem */
++ 0x82000000 0x0 0x70000000 0x0 0x70000000 0x0 0x1000000 /* mem */
++ 0xc3000000 0x20 0x00000000 0x20 0x00000000 0x20 0x00000000>; /* mem prefetchable */
++ num-lanes = <0x8>;
++ msi-parent = <&plic0>;
++ interrupts = <56 57 58 59 60 61 62 63 64>;
++ interrupt-names = "msi", "inta", "intb", "intc", "intd";
++ interrupt-parent = <&plic0>;
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0x0 0x0 0x0 0x1 &plic0 57>,
++ <0x0 0x0 0x0 0x2 &plic0 58>,
++ <0x0 0x0 0x0 0x3 &plic0 59>,
++ <0x0 0x0 0x0 0x4 &plic0 60>;
++ clock-names = "pcie_aux";
++ clocks = <&prci PRCI_CLK_PCIE_AUX>;
++ pwren-gpios = <&gpio 5 0>;
++ perstn-gpios = <&gpio 8 0>;
++ resets = <&prci 4>;
++ status = "okay";
++ };
+ };
+ };
+--
+2.7.4
+
--- /dev/null
+From 90591acb975baa3bedc7178e04bc92daaca3740f Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Wed, 3 Feb 2021 11:00:32 +0800
+Subject: [PATCH 25/29] clk: sifive: Add pcie_aux clock in prci driver for PCIe
+ driver
+
+We add pcie_aux clock in this patch so that pcie driver can use
+clk_prepare_enable() and clk_disable_unprepare() to enable and disable
+pcie_aux clock.
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ drivers/clk/sifive/fu740-prci.c | 11 +++++++
+ drivers/clk/sifive/fu740-prci.h | 2 +-
+ drivers/clk/sifive/sifive-prci.c | 41 +++++++++++++++++++++++++++
+ drivers/clk/sifive/sifive-prci.h | 9 ++++++
+ include/dt-bindings/clock/sifive-fu740-prci.h | 1 +
+ 5 files changed, 63 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/sifive/fu740-prci.c b/drivers/clk/sifive/fu740-prci.c
+index db83002..76cbfb1 100644
+--- a/drivers/clk/sifive/fu740-prci.c
++++ b/drivers/clk/sifive/fu740-prci.c
+@@ -69,6 +69,12 @@ static const struct clk_ops sifive_fu740_prci_hfpclkplldiv_clk_ops = {
+ .recalc_rate = sifive_prci_hfpclkplldiv_recalc_rate,
+ };
+
++static const struct clk_ops sifive_fu740_prci_pcie_aux_clk_ops = {
++ .enable = sifive_prci_pcie_aux_clock_enable,
++ .disable = sifive_prci_pcie_aux_clock_disable,
++ .is_enabled = sifive_prci_pcie_aux_clock_is_enabled,
++};
++
+ /* List of clock controls provided by the PRCI */
+ struct __prci_clock __prci_init_clocks_fu740[] = {
+ [PRCI_CLK_COREPLL] = {
+@@ -117,4 +123,9 @@ struct __prci_clock __prci_init_clocks_fu740[] = {
+ .parent_name = "hfpclkpll",
+ .ops = &sifive_fu740_prci_hfpclkplldiv_clk_ops,
+ },
++ [PRCI_CLK_PCIE_AUX] = {
++ .name = "pcie_aux",
++ .parent_name = "hfclk",
++ .ops = &sifive_fu740_prci_pcie_aux_clk_ops,
++ },
+ };
+diff --git a/drivers/clk/sifive/fu740-prci.h b/drivers/clk/sifive/fu740-prci.h
+index 13ef971f7..511a0bf 100644
+--- a/drivers/clk/sifive/fu740-prci.h
++++ b/drivers/clk/sifive/fu740-prci.h
+@@ -9,7 +9,7 @@
+
+ #include "sifive-prci.h"
+
+-#define NUM_CLOCK_FU740 8
++#define NUM_CLOCK_FU740 9
+
+ extern struct __prci_clock __prci_init_clocks_fu740[NUM_CLOCK_FU740];
+
+diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
+index c78b042..baf7313 100644
+--- a/drivers/clk/sifive/sifive-prci.c
++++ b/drivers/clk/sifive/sifive-prci.c
+@@ -448,6 +448,47 @@ void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd)
+ r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */
+ }
+
++/* PCIE AUX clock APIs for enable, disable. */
++int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw *hw)
++{
++ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
++ struct __prci_data *pd = pc->pd;
++ u32 r;
++
++ r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET);
++
++ if (r & PRCI_PCIE_AUX_EN_MASK)
++ return 1;
++ else
++ return 0;
++}
++
++int sifive_prci_pcie_aux_clock_enable(struct clk_hw *hw)
++{
++ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
++ struct __prci_data *pd = pc->pd;
++ u32 r;
++
++ if (sifive_prci_pcie_aux_clock_is_enabled(hw))
++ return 0;
++
++ __prci_writel(1, PRCI_PCIE_AUX_OFFSET, pd);
++ r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */
++
++ return 0;
++}
++
++void sifive_prci_pcie_aux_clock_disable(struct clk_hw *hw)
++{
++ struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
++ struct __prci_data *pd = pc->pd;
++ u32 r;
++
++ __prci_writel(0, PRCI_PCIE_AUX_OFFSET, pd);
++ r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */
++
++}
++
+ /**
+ * __prci_register_clocks() - register clock controls in the PRCI
+ * @dev: Linux struct device
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+index dbdbd17..022c67c 100644
+--- a/drivers/clk/sifive/sifive-prci.h
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -67,6 +67,11 @@
+ #define PRCI_DDRPLLCFG1_CKE_SHIFT 31
+ #define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
+
++/* PCIEAUX */
++#define PRCI_PCIE_AUX_OFFSET 0x14
++#define PRCI_PCIE_AUX_EN_SHIFT 0
++#define PRCI_PCIE_AUX_EN_MASK (0x1 << PRCI_PCIE_AUX_EN_SHIFT)
++
+ /* GEMGXLPLLCFG0 */
+ #define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c
+ #define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0
+@@ -296,4 +301,8 @@ unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
+ unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate);
+
++int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw *hw);
++int sifive_prci_pcie_aux_clock_enable(struct clk_hw *hw);
++void sifive_prci_pcie_aux_clock_disable(struct clk_hw *hw);
++
+ #endif /* __SIFIVE_CLK_SIFIVE_PRCI_H */
+diff --git a/include/dt-bindings/clock/sifive-fu740-prci.h b/include/dt-bindings/clock/sifive-fu740-prci.h
+index cd7706e..7899b7f 100644
+--- a/include/dt-bindings/clock/sifive-fu740-prci.h
++++ b/include/dt-bindings/clock/sifive-fu740-prci.h
+@@ -19,5 +19,6 @@
+ #define PRCI_CLK_CLTXPLL 5
+ #define PRCI_CLK_TLCLK 6
+ #define PRCI_CLK_PCLK 7
++#define PRCI_CLK_PCIE_AUX 8
+
+ #endif /* __DT_BINDINGS_CLOCK_SIFIVE_FU740_PRCI_H */
+--
+2.7.4
+
--- /dev/null
+From a9b30eacb76d59ea18f138e0ad2b66e8a50ef24d Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Wed, 3 Feb 2021 11:07:16 +0800
+Subject: [PATCH 26/29] clk: sifive: Use reset-simple in prci driver for PCIe
+ driver
+
+We use reset-simple in this patch so that pcie driver can use
+devm_reset_control_get() to get this reset data structure and use
+reset_control_deassert() to deassert pcie_power_up_rst_n.
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ drivers/clk/sifive/Kconfig | 2 ++
+ drivers/clk/sifive/sifive-prci.c | 14 ++++++++++++++
+ drivers/clk/sifive/sifive-prci.h | 4 ++++
+ drivers/reset/Kconfig | 3 ++-
+ 4 files changed, 22 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+index 1c14eb2..9132c3c 100644
+--- a/drivers/clk/sifive/Kconfig
++++ b/drivers/clk/sifive/Kconfig
+@@ -10,6 +10,8 @@ if CLK_SIFIVE
+
+ config CLK_SIFIVE_PRCI
+ bool "PRCI driver for SiFive SoCs"
++ select RESET_CONTROLLER
++ select RESET_SIMPLE
+ select CLK_ANALOGBITS_WRPLL_CLN28HPC
+ help
+ Supports the Power Reset Clock interface (PRCI) IP block found in
+diff --git a/drivers/clk/sifive/sifive-prci.c b/drivers/clk/sifive/sifive-prci.c
+index baf7313..925affc 100644
+--- a/drivers/clk/sifive/sifive-prci.c
++++ b/drivers/clk/sifive/sifive-prci.c
+@@ -583,7 +583,21 @@ static int sifive_prci_probe(struct platform_device *pdev)
+ if (IS_ERR(pd->va))
+ return PTR_ERR(pd->va);
+
++ pd->reset.rcdev.owner = THIS_MODULE;
++ pd->reset.rcdev.nr_resets = PRCI_RST_NR;
++ pd->reset.rcdev.ops = &reset_simple_ops;
++ pd->reset.rcdev.of_node = pdev->dev.of_node;
++ pd->reset.active_low = true;
++ pd->reset.membase = pd->va + PRCI_DEVICESRESETREG_OFFSET;
++ spin_lock_init(&pd->reset.lock);
++
++ r = devm_reset_controller_register(&pdev->dev, &pd->reset.rcdev);
++ if (r) {
++ dev_err(dev, "could not register reset controller: %d\n", r);
++ return r;
++ }
+ r = __prci_register_clocks(dev, pd, desc);
++
+ if (r) {
+ dev_err(dev, "could not register clocks: %d\n", r);
+ return r;
+diff --git a/drivers/clk/sifive/sifive-prci.h b/drivers/clk/sifive/sifive-prci.h
+index 022c67c..91658a8 100644
+--- a/drivers/clk/sifive/sifive-prci.h
++++ b/drivers/clk/sifive/sifive-prci.h
+@@ -11,6 +11,7 @@
+
+ #include <linux/clk/analogbits-wrpll-cln28hpc.h>
+ #include <linux/clk-provider.h>
++#include <linux/reset/reset-simple.h>
+ #include <linux/platform_device.h>
+
+ /*
+@@ -121,6 +122,8 @@
+ #define PRCI_DEVICESRESETREG_CHIPLINK_RST_N_MASK \
+ (0x1 << PRCI_DEVICESRESETREG_CHIPLINK_RST_N_SHIFT)
+
++#define PRCI_RST_NR 7
++
+ /* CLKMUXSTATUSREG */
+ #define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c
+ #define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1
+@@ -221,6 +224,7 @@
+ */
+ struct __prci_data {
+ void __iomem *va;
++ struct reset_simple_data reset;
+ struct clk_hw_onecell_data hw_clks;
+ };
+
+diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
+index 07d162b..124f4dc 100644
+--- a/drivers/reset/Kconfig
++++ b/drivers/reset/Kconfig
+@@ -165,7 +165,7 @@ config RESET_SCMI
+
+ config RESET_SIMPLE
+ bool "Simple Reset Controller Driver" if COMPILE_TEST
+- default ARCH_AGILEX || ARCH_ASPEED || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARC
++ default ARCH_AGILEX || ARCH_ASPEED || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARC || RISCV
+ help
+ This enables a simple reset controller driver for reset lines that
+ that can be asserted and deasserted by toggling bits in a contiguous,
+@@ -179,6 +179,7 @@ config RESET_SIMPLE
+ - RCC reset controller in STM32 MCUs
+ - Allwinner SoCs
+ - ZTE's zx2967 family
++ - SiFive FU740 SoCs
+
+ config RESET_STM32MP157
+ bool "STM32MP157 Reset Driver" if COMPILE_TEST
+--
+2.7.4
+
--- /dev/null
+From a57464df4cfa31061f665d2bac4222d625ec5ca9 Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Wed, 3 Feb 2021 11:11:57 +0800
+Subject: [PATCH 27/29] MAINTAINERS: Add maintainers for SiFive FU740 PCIe
+ driver
+
+Here add maintainer information for SiFive FU740 PCIe driver.
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ MAINTAINERS | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 281de21..5980c85 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -13389,6 +13389,14 @@ S: Maintained
+ F: Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+ F: drivers/pci/controller/dwc/*imx6*
+
++PCI DRIVER FOR FU740
++M: Paul Walmsley <paul.walmsley@sifive.com>
++M: Greentime Hu <greentime.hu@sifive.com>
++L: linux-pci@vger.kernel.org
++S: Maintained
++F: Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
++F: drivers/pci/controller/dwc/pcie-fu740.c
++
+ PCI DRIVER FOR INTEL VOLUME MANAGEMENT DEVICE (VMD)
+ M: Jonathan Derrick <jonathan.derrick@intel.com>
+ L: linux-pci@vger.kernel.org
+--
+2.7.4
+
--- /dev/null
+From 38fecc0dae7765bcd4de25863ef3156a18992040 Mon Sep 17 00:00:00 2001
+From: Greentime Hu <greentime.hu@sifive.com>
+Date: Wed, 3 Feb 2021 11:17:35 +0800
+Subject: [PATCH 28/29] dt-bindings: PCI: Add SiFive FU740 PCIe host controller
+
+Add PCIe host controller DT bindings of SiFive FU740.
+
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+---
+ .../devicetree/bindings/pci/sifive,fu740-pcie.yaml | 121 +++++++++++++++++++++
+ 1 file changed, 121 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
+
+diff --git a/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
+new file mode 100644
+index 00000000..bcd1d18
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/sifive,fu740-pcie.yaml
+@@ -0,0 +1,121 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pci/sifive,fu740-pcie.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: SiFive fu740 PCIe host controller
++
++description: |
++ SiFive fu740 PCIe host controller is based on the Synopsys DesignWare
++ PCI core. It shares common features with the PCIe DesignWare core and
++ inherits common properties defined in
++ Documentation/devicetree/bindings/pci/designware-pcie.txt.
++
++maintainers:
++ - Paul Walmsley <paul.walmsley@sifive.com>
++ - Greentime Hu <greentime.hu@sifive.com>
++
++allOf:
++ - $ref: /schemas/pci/pci-bus.yaml#
++
++properties:
++ compatible:
++ const: sifive,fu740-pcie
++
++ reg:
++ maxItems: 4
++
++ reg-names:
++ items:
++ - const: dbi
++ - const: config
++ - const: mgmt
++
++ device_type:
++ const: pci
++
++ dma-coherent:
++ description: Indicates that the PCIe IP block can ensure the coherency
++
++ bus-range:
++ description: Range of bus numbers associated with this controller.
++
++ num-lanes: true
++
++ msi-parent: true
++
++ interrupt-names:
++ items:
++ - const: msi
++ - const: inta
++ - const: intb
++ - const: intc
++ - const: intd
++
++ resets:
++ description: A phandle to the PCIe power up reset line
++
++ pwren-gpios:
++ description: Should specify the GPIO for controlling the PCI bus device power on
++
++ perstn-gpios:
++ description: Should specify the GPIO for controlling the PCI bus device reset
++
++required:
++ - compatible
++ - reg
++ - reg-names
++ - device_type
++ - dma-coherent
++ - bus-range
++ - ranges
++ - num-lanes
++ - msi-parent
++ - interrupts
++ - interrupt-names
++ - interrupt-parent
++ - interrupt-map-mask
++ - interrupt-map
++ - clock-names
++ - clocks
++ - resets
++ - pwren-gpios
++ - perstn-gpios
++
++additionalProperties: false
++
++examples:
++ - |
++ pcie@e00000000 {
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #size-cells = <2>;
++ compatible = "sifive,fu740-pcie";
++ reg = <0xe 0x00000000 0x1 0x0
++ 0xd 0xf0000000 0x0 0x10000000
++ 0x0 0x100d0000 0x0 0x1000>;
++ reg-names = "dbi", "config", "mgmt";
++ device_type = "pci";
++ dma-coherent;
++ bus-range = <0x0 0xff>;
++ ranges = <0x81000000 0x0 0x60080000 0x0 0x60080000 0x0 0x10000 /* I/O */
++ 0x82000000 0x0 0x60090000 0x0 0x60090000 0x0 0xff70000 /* mem */
++ 0x82000000 0x0 0x70000000 0x0 0x70000000 0x0 0x1000000 /* mem */
++ 0xc3000000 0x20 0x00000000 0x20 0x00000000 0x20 0x00000000>; /* mem prefetchable */
++ num-lanes = <0x8>;
++ msi-parent = <&plic0>;
++ interrupts = <56 57 58 59 60 61 62 63 64>;
++ interrupt-names = "msi", "inta", "intb", "intc", "intd";
++ interrupt-parent = <&plic0>;
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0x0 0x0 0x0 0x1 &plic0 57>,
++ <0x0 0x0 0x0 0x2 &plic0 58>,
++ <0x0 0x0 0x0 0x3 &plic0 59>,
++ <0x0 0x0 0x0 0x4 &plic0 60>;
++ clock-names = "pcie_aux";
++ clocks = <&prci PRCI_CLK_PCIE_AUX>;
++ resets = <&prci 4>;
++ pwren-gpios = <&gpio 5 0>;
++ perstn-gpios = <&gpio 8 0>;
++ };
+--
+2.7.4
+
--- /dev/null
+From 6da0f49824ae1fca4d0bf1ce433c870cf1b424ea Mon Sep 17 00:00:00 2001
+From: Paul Walmsley <paul.walmsley@sifive.com>
+Date: Wed, 3 Feb 2021 11:19:08 +0800
+Subject: [PATCH 29/29] PCI: designware: Add SiFive FU740 PCIe host controller
+ driver
+
+Add driver for the SiFive FU740 PCIe host controller.
+This controller is based on the DesignWare PCIe core.
+
+Co-developed-by: Henry Styles <hes@sifive.com>
+Signed-off-by: Henry Styles <hes@sifive.com>
+Co-developed-by: Erik Danie <erik.danie@sifive.com>
+Signed-off-by: Erik Danie <erik.danie@sifive.com>
+Co-developed-by: Greentime Hu <greentime.hu@sifive.com>
+Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
+Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
+---
+ drivers/pci/controller/dwc/Kconfig | 9 +
+ drivers/pci/controller/dwc/Makefile | 1 +
+ drivers/pci/controller/dwc/pcie-fu740.c | 552 ++++++++++++++++++++++++++++++++
+ 3 files changed, 562 insertions(+)
+ create mode 100644 drivers/pci/controller/dwc/pcie-fu740.c
+
+diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
+index bc04986..ee13834 100644
+--- a/drivers/pci/controller/dwc/Kconfig
++++ b/drivers/pci/controller/dwc/Kconfig
+@@ -312,4 +312,13 @@ config PCIE_AL
+ required only for DT-based platforms. ACPI platforms with the
+ Annapurna Labs PCIe controller don't need to enable this.
+
++config PCIE_FU740
++ bool "SiFive FU740 PCIe host controller"
++ depends on PCI_MSI_IRQ_DOMAIN
++ depends on SOC_SIFIVE || COMPILE_TEST
++ select PCIE_DW_HOST
++ help
++ Say Y here if you want PCIe controller support for the SiFive
++ FU740.
++
+ endmenu
+diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
+index a751553..625f6aa 100644
+--- a/drivers/pci/controller/dwc/Makefile
++++ b/drivers/pci/controller/dwc/Makefile
+@@ -5,6 +5,7 @@ obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
+ obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
+ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
++obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o
+ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
+diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c
+new file mode 100644
+index 00000000..afb0404
+--- /dev/null
++++ b/drivers/pci/controller/dwc/pcie-fu740.c
+@@ -0,0 +1,552 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * FU740 DesignWare PCIe Controller integration
++ * Copyright (C) 2019-2021 SiFive, Inc.
++ * Paul Walmsley
++ * Greentime Hu
++ *
++ * Based in part on the i.MX6 PCIe host controller shim which is:
++ *
++ * Copyright (C) 2013 Kosagi
++ * http://www.kosagi.com
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of_gpio.h>
++#include <linux/of_device.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
++#include <linux/resource.h>
++#include <linux/signal.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/reset.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_runtime.h>
++
++#include "pcie-designware.h"
++
++#define to_fu740_pcie(x) dev_get_drvdata((x)->dev)
++
++struct fu740_pcie {
++ struct dw_pcie *pci;
++ void __iomem *mgmt_base;
++ int perstn_gpio;
++ int pwren_gpio;
++ struct clk *pcie_aux;
++ struct reset_control *rst;
++};
++
++#define SIFIVE_DEVICESRESETREG 0x28
++
++#define PCIEX8MGMT_PERST_N 0x0
++#define PCIEX8MGMT_APP_LTSSM_ENABLE 0x10
++#define PCIEX8MGMT_APP_HOLD_PHY_RST 0x18
++#define PCIEX8MGMT_DEVICE_TYPE 0x708
++#define PCIEX8MGMT_PHY0_CR_PARA_ADDR 0x860
++#define PCIEX8MGMT_PHY0_CR_PARA_RD_EN 0x870
++#define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA 0x878
++#define PCIEX8MGMT_PHY0_CR_PARA_SEL 0x880
++#define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA 0x888
++#define PCIEX8MGMT_PHY0_CR_PARA_WR_EN 0x890
++#define PCIEX8MGMT_PHY0_CR_PARA_ACK 0x898
++#define PCIEX8MGMT_PHY1_CR_PARA_ADDR 0x8a0
++#define PCIEX8MGMT_PHY1_CR_PARA_RD_EN 0x8b0
++#define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA 0x8b8
++#define PCIEX8MGMT_PHY1_CR_PARA_SEL 0x8c0
++#define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA 0x8c8
++#define PCIEX8MGMT_PHY1_CR_PARA_WR_EN 0x8d0
++#define PCIEX8MGMT_PHY1_CR_PARA_ACK 0x8d8
++
++/* PCIe Root Complex registers (memory-mapped) */
++#define PCIE_RC_PF0_MSI_CAP 0x50
++#define PCI_MSI_CAP_ID_NEXT_CTRL_REG (PCIE_RC_PF0_MSI_CAP + 0x0)
++
++#define PCIE_DSP_PF0_PCIE_CAP_BASE 0x70
++#define PCIE_RC_LCR (PCIE_DSP_PF0_PCIE_CAP_BASE + 0xc)
++#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1
++#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2
++#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN3 0x3
++#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf
++
++#define PCIE_RC_LCSR (PCIE_DSP_PF0_PCIE_CAP_BASE + 0x10)
++
++/* PCIe Port Logic registers (memory-mapped) */
++#define PL_OFFSET 0x700
++#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
++#define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16)
++#define PCIE_PL_PFLR_FORCE_LINK (1 << 15)
++#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
++#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
++#define PCIE_PL_GEN2_CTRL_OFF (PL_OFFSET + 0x10c)
++#define PCIE_PL_DIRECTED_SPEED_CHANGE_OFF 0x20000
++
++#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
++#define PCIE_PHY_CTRL_DATA_LOC 0
++#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
++#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
++#define PCIE_PHY_CTRL_WR_LOC 18
++#define PCIE_PHY_CTRL_RD_LOC 19
++
++#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
++#define PCIE_PHY_STAT_ACK_LOC 16
++
++#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
++
++#define PCIE_PHY_MAX_RETRY_CNT 1000
++
++static void fu740_pcie_assert_perstn(struct fu740_pcie *afp)
++{
++ /* PERST_N GPIO */
++ if (gpio_is_valid(afp->perstn_gpio))
++ gpio_direction_output(afp->perstn_gpio, 0);
++
++ /* Controller PERST_N */
++ __raw_writel(0x0, afp->mgmt_base + PCIEX8MGMT_PERST_N);
++}
++
++static void fu740_pcie_deassert_perstn(struct fu740_pcie *afp)
++{
++ /* Controller PERST_N */
++ __raw_writel(0x1, afp->mgmt_base + PCIEX8MGMT_PERST_N);
++ /* PERST_N GPIO */
++ if (gpio_is_valid(afp->perstn_gpio))
++ gpio_direction_output(afp->perstn_gpio, 1);
++}
++
++static void fu740_pcie_power_on(struct fu740_pcie *afp)
++{
++ if (gpio_is_valid(afp->pwren_gpio)) {
++ gpio_direction_output(afp->pwren_gpio, 1);
++ mdelay(100);
++ }
++}
++
++static void fu740_pcie_drive_perstn(struct fu740_pcie *afp)
++{
++ fu740_pcie_assert_perstn(afp);
++ fu740_pcie_power_on(afp);
++ fu740_pcie_deassert_perstn(afp);
++}
++
++static void fu740_phyregreadwrite(const uint8_t phy, const uint8_t write,
++ const uint16_t addr,
++ const uint16_t wrdata, uint16_t *rddata,
++ struct fu740_pcie *afp)
++{
++ unsigned char ack = 0;
++ unsigned int cnt = 0;
++ struct device *dev = afp->pci->dev;
++
++ /* setup */
++ __raw_writel(addr,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_ADDR :
++ PCIEX8MGMT_PHY0_CR_PARA_ADDR));
++ if (write)
++ __raw_writel(wrdata,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_DATA :
++ PCIEX8MGMT_PHY0_CR_PARA_WR_DATA));
++ if (write)
++ __raw_writel(1,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
++ PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
++ else
++ __raw_writel(1,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
++ PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
++
++ /* wait for wait_idle */
++ do {
++ if (__raw_readl
++ (afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
++ PCIEX8MGMT_PHY0_CR_PARA_ACK))) {
++ ack = 1;
++ if (!write)
++ __raw_readl(afp->mgmt_base +
++ (phy ?
++ PCIEX8MGMT_PHY1_CR_PARA_RD_DATA :
++ PCIEX8MGMT_PHY0_CR_PARA_RD_DATA));
++ }
++ } while (!ack);
++
++ /* clear */
++ if (write)
++ __raw_writel(0,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_WR_EN :
++ PCIEX8MGMT_PHY0_CR_PARA_WR_EN));
++ else
++ __raw_writel(0,
++ afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_RD_EN :
++ PCIEX8MGMT_PHY0_CR_PARA_RD_EN));
++
++ /* wait for ~wait_idle */
++ while (__raw_readl
++ (afp->mgmt_base +
++ (phy ? PCIEX8MGMT_PHY1_CR_PARA_ACK :
++ PCIEX8MGMT_PHY0_CR_PARA_ACK))) {
++ cpu_relax();
++ cnt++;
++ if (cnt > PCIE_PHY_MAX_RETRY_CNT) {
++ dev_err(dev, "PCIE phy doesn't enter idle state.\n");
++ break;
++ }
++ }
++}
++
++static void fu740_pcie_init_phy(struct fu740_pcie *afp)
++{
++ int lane;
++
++ /* enable phy cr_para_sel interfaces */
++ __raw_writel(0x1, afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_SEL);
++ __raw_writel(0x1, afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_SEL);
++
++ /* wait 10 cr_para cycles */
++ msleep(1);
++
++ /* set PHY AC termination mode */
++ for (lane = 0; lane < 4; lane++) {
++ fu740_phyregreadwrite(0, 1,
++ 0x1008 + (0x100 * lane),
++ 0x0e21, NULL, afp);
++ fu740_phyregreadwrite(1, 1,
++ 0x1008 + (0x100 * lane),
++ 0x0e21, NULL, afp);
++ }
++
++}
++
++static int fu740_pcie_wait_for_link(struct fu740_pcie *afp)
++{
++ struct dw_pcie *pci = afp->pci;
++
++ /* check if the link is up or not */
++ if (!dw_pcie_wait_for_link(pci))
++ return 0;
++
++ return -ETIMEDOUT;
++}
++
++static void fu740_pcie_ltssm_enable(struct device *dev)
++{
++ struct fu740_pcie *afp = dev_get_drvdata(dev);
++
++ /* Enable LTSSM */
++ __raw_writel(0x1, afp->mgmt_base + PCIEX8MGMT_APP_LTSSM_ENABLE);
++}
++
++static int fu740_pcie_establish_link(struct fu740_pcie *afp)
++{
++ struct dw_pcie *pci = afp->pci;
++ struct device *dev = pci->dev;
++ u32 tmp;
++ int ret;
++
++ /*
++ * Force Gen1 operation when starting the link. In case the link is
++ * started in Gen2 mode, there is a possibility the devices on the
++ * bus will not be detected at all. This happens with PCIe switches.
++ */
++ dw_pcie_dbi_ro_wr_en(pci);
++ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
++ tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
++ tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
++ dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
++ dw_pcie_dbi_ro_wr_dis(pci);
++
++ /* Start LTSSM. */
++ fu740_pcie_ltssm_enable(dev);
++
++ ret = fu740_pcie_wait_for_link(afp);
++ if (ret)
++ goto err_reset_phy;
++
++ /* Now set it to operate in Gen3 */
++ dw_pcie_dbi_ro_wr_en(pci);
++ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
++ tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
++ tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN3;
++ dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
++ /* Enable DIRECTED SPEED CHANGE bit of GEN2_CTRL_OFF register */
++ tmp = dw_pcie_readl_dbi(pci, PCIE_PL_GEN2_CTRL_OFF);
++ tmp |= PCIE_PL_DIRECTED_SPEED_CHANGE_OFF;
++ dw_pcie_writel_dbi(pci, PCIE_PL_GEN2_CTRL_OFF, tmp);
++ dw_pcie_dbi_ro_wr_dis(pci);
++
++ ret = fu740_pcie_wait_for_link(afp);
++ if (ret)
++ goto err_reset_phy;
++
++ /*
++ * Reenable DIRECTED SPEED CHANGE.
++ *
++ * You need to set this bit after each speed change, but after
++ * reaching G1, setting it once doesn't seem to work (it reaches G3
++ * equalization states and then times out, falls back to G1). But
++ * If after that, you set it again, it then reaches G3 perfectly
++ * fine.
++ */
++ dw_pcie_dbi_ro_wr_en(pci);
++ tmp = dw_pcie_readl_dbi(pci, PCIE_PL_GEN2_CTRL_OFF);
++ tmp |= PCIE_PL_DIRECTED_SPEED_CHANGE_OFF;
++ dw_pcie_writel_dbi(pci, PCIE_PL_GEN2_CTRL_OFF, tmp);
++ dw_pcie_dbi_ro_wr_dis(pci);
++
++ ret = fu740_pcie_wait_for_link(afp);
++ if (ret)
++ goto err_reset_phy;
++
++ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR);
++ dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
++ return 0;
++
++ err_reset_phy:
++ dev_err(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
++ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
++ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
++ return ret;
++}
++
++static int fu740_pcie_host_init(struct pcie_port *pp)
++{
++ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
++ struct fu740_pcie *afp = to_fu740_pcie(pci);
++ struct device *dev = pci->dev;
++ int ret = 0;
++
++ /* power on reset */
++ fu740_pcie_drive_perstn(afp);
++
++ /* enable pcieauxclk */
++ ret = clk_prepare_enable(afp->pcie_aux);
++ if (ret)
++ dev_err(dev, "unable to enable pcie_aux clock\n");
++
++ /*
++ * assert hold_phy_rst (hold the controller LTSSM in reset after
++ * power_up_rst_n
++ * for register programming with cr_para)
++ */
++ __raw_writel(0x1, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
++
++ /* deassert power_up_rst_n */
++ ret = reset_control_deassert(afp->rst);
++ if (ret)
++ dev_err(dev, "unable to deassert pcie_power_up_rst_n\n");
++
++ fu740_pcie_init_phy(afp);
++
++ /* disable pcieauxclk */
++ clk_disable_unprepare(afp->pcie_aux);
++ /* clear hold_phy_rst */
++ __raw_writel(0x0, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST);
++ /* enable pcieauxclk */
++ ret = clk_prepare_enable(afp->pcie_aux);
++ /* set RC mode */
++ __raw_writel(0x4, afp->mgmt_base + PCIEX8MGMT_DEVICE_TYPE);
++
++ dw_pcie_setup_rc(pp);
++ fu740_pcie_establish_link(afp);
++
++ if (IS_ENABLED(CONFIG_PCI_MSI))
++ dw_pcie_msi_init(pp);
++
++ return 0;
++}
++
++static const struct dw_pcie_host_ops fu740_pcie_host_ops = {
++ .host_init = fu740_pcie_host_init,
++};
++
++static irqreturn_t fu740_pcie_msi_handler(int irq, void *arg)
++{
++ struct fu740_pcie *afp = arg;
++ struct dw_pcie *pci = afp->pci;
++ struct pcie_port *pp = &pci->pp;
++
++ return dw_handle_msi_irq(pp);
++}
++
++static int fu740_pcie_add_pcie_port(struct fu740_pcie *afp,
++ struct platform_device *pdev)
++{
++ struct dw_pcie *pci = afp->pci;
++ struct pcie_port *pp = &pci->pp;
++ struct device *dev = &pdev->dev;
++ int ret;
++
++ if (IS_ENABLED(CONFIG_PCI_MSI)) {
++ pp->msi_irq = platform_get_irq_byname(pdev, "msi");
++ if (pp->msi_irq <= 0) {
++ dev_err(dev, "failed to get MSI irq\n");
++ return -ENODEV;
++ }
++
++ ret = devm_request_irq(dev, pp->msi_irq,
++ fu740_pcie_msi_handler,
++ IRQF_SHARED | IRQF_NO_THREAD,
++ "fu740-pcie-msi", afp);
++ if (ret) {
++ dev_err(dev, "failed to request MSI irq\n");
++ return ret;
++ }
++ }
++
++ pp->ops = &fu740_pcie_host_ops;
++
++ ret = dw_pcie_host_init(pp);
++ if (ret) {
++ dev_err(dev, "failed to initialize host\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct dw_pcie_ops dw_pcie_ops = { };
++
++static const struct dev_pm_ops fu740_pcie_pm_ops = {
++ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(fu740_pcie_suspend_noirq,
++ fu740_pcie_resume_noirq)
++};
++
++static int fu740_pcie_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct dw_pcie *pci;
++ struct fu740_pcie *afp;
++ struct resource *dbi_res, *mgmt_res;
++ struct device_node *node = dev->of_node;
++ int ret;
++ u16 val;
++
++ afp = devm_kzalloc(dev, sizeof(*afp), GFP_KERNEL);
++ if (!afp)
++ return -ENOMEM;
++
++ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
++ if (!pci)
++ return -ENOMEM;
++
++ pci->dev = dev;
++ pci->ops = &dw_pcie_ops;
++
++ afp->pci = pci;
++
++ dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
++ if (!dbi_res) {
++ dev_warn(dev, "missing required dbi address range");
++ return -ENOENT;
++ }
++ pci->dbi_base = devm_ioremap_resource(dev, dbi_res);
++ if (IS_ERR(pci->dbi_base))
++ return PTR_ERR(pci->dbi_base);
++
++ mgmt_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mgmt");
++ if (!mgmt_res) {
++ dev_warn(dev, "missing required mgmt address range");
++ return -ENOENT;
++ }
++ afp->mgmt_base = devm_ioremap_resource(dev, mgmt_res);
++ if (IS_ERR(afp->mgmt_base))
++ return PTR_ERR(afp->mgmt_base);
++
++ /* Fetch GPIOs */
++ afp->perstn_gpio = of_get_named_gpio(node, "perstn-gpios", 0);
++ if (gpio_is_valid(afp->perstn_gpio)) {
++ ret = devm_gpio_request_one(dev, afp->perstn_gpio,
++ GPIOF_OUT_INIT_LOW, "perstn-gpios");
++ if (ret) {
++ dev_err(dev, "unable to get perstn gpio\n");
++ return ret;
++ }
++ } else if (afp->perstn_gpio == -EPROBE_DEFER) {
++ dev_err(dev, "perst-gpios EPROBE_DEFER\n");
++ return afp->perstn_gpio;
++ }
++
++ afp->pwren_gpio = of_get_named_gpio(node, "pwren-gpios", 0);
++ if (gpio_is_valid(afp->pwren_gpio)) {
++ ret = devm_gpio_request_one(dev, afp->pwren_gpio,
++ GPIOF_OUT_INIT_LOW, "pwren-gpios");
++ if (ret) {
++ dev_err(dev, "unable to get pwren gpio\n");
++ return ret;
++ }
++ } else if (afp->pwren_gpio == -EPROBE_DEFER) {
++ dev_err(dev, "pwren-gpios EPROBE_DEFER\n");
++ return afp->pwren_gpio;
++ }
++
++ /* Fetch clocks */
++ afp->pcie_aux = devm_clk_get(dev, "pcie_aux");
++ if (IS_ERR(afp->pcie_aux))
++ return dev_err_probe(dev, PTR_ERR(afp->pcie_aux),
++ "pcie_aux clock source missing or invalid\n");
++
++ /* Fetch reset */
++ afp->rst = devm_reset_control_get(dev, NULL);
++ if (IS_ERR(afp->rst))
++ return dev_err_probe(dev, PTR_ERR(afp->rst), "unable to get reset\n");
++
++ platform_set_drvdata(pdev, afp);
++
++ ret = fu740_pcie_add_pcie_port(afp, pdev);
++ if (ret < 0)
++ return ret;
++
++ if (pci_msi_enabled()) {
++ val = dw_pcie_readw_dbi(pci, PCI_MSI_CAP_ID_NEXT_CTRL_REG +
++ PCI_MSI_FLAGS);
++ val |= PCI_MSI_FLAGS_ENABLE;
++ dw_pcie_writew_dbi(pci, PCI_MSI_CAP_ID_NEXT_CTRL_REG +
++ PCI_MSI_FLAGS, val);
++ }
++
++ return 0;
++}
++
++static void fu740_pcie_shutdown(struct platform_device *pdev)
++{
++ struct fu740_pcie *afp = platform_get_drvdata(pdev);
++
++ /* bring down link, so bootloader gets clean state in case of reboot */
++ fu740_pcie_assert_perstn(afp);
++}
++
++static const struct of_device_id fu740_pcie_of_match[] = {
++ {.compatible = "sifive,fu740-pcie"},
++ {},
++};
++
++static struct platform_driver fu740_pcie_driver = {
++ .driver = {
++ .name = "fu740-pcie",
++ .of_match_table = fu740_pcie_of_match,
++ .suppress_bind_attrs = true,
++ .pm = &fu740_pcie_pm_ops,
++ },
++ .probe = fu740_pcie_probe,
++ .shutdown = fu740_pcie_shutdown,
++};
++
++static int __init fu740_pcie_init(void)
++{
++ return platform_driver_register(&fu740_pcie_driver);
++}
++
++device_initcall(fu740_pcie_init);
+--
+2.7.4
+
--- /dev/null
+From 63d7d0e6553ed2990fec51b3f449e50153461650 Mon Sep 17 00:00:00 2001
+From: Stanislaw Kardach <kda@semihalf.com>
+Date: Mon, 12 Apr 2021 13:10:12 +0200
+Subject: [PATCH 28/28] riscv: enable generic PCI resource mapping
+
+Enable the PCI resource mapping on RISC-V using the generic framework.
+This allows userspace applications to mmap PCI resources using
+/sys/devices/pci*/*/resource* interface.
+The mmap has been tested with Intel x520-DA2 NIC card on a HiFive
+Unmatched board (SiFive FU740 SoC).
+
+Signed-off-by: Stanislaw Kardach <kda@semihalf.com>
+---
+ arch/riscv/include/asm/pci.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/arch/riscv/include/asm/pci.h b/arch/riscv/include/asm/pci.h
+index 1c473a1..46e844f 100644
+--- a/arch/riscv/include/asm/pci.h
++++ b/arch/riscv/include/asm/pci.h
+@@ -18,6 +18,8 @@
+ /* RISC-V shim does not initialize PCI bus */
+ #define pcibios_assign_all_busses() 1
+
++#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
++
+ extern int isa_dma_bridge_buggy;
+
+ #ifdef CONFIG_PCI
+--
+2.7.4
+
--- /dev/null
+From 35f046065b988ea1b29df78728f5dbb6877aadc4 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Tue, 28 Jan 2020 02:55:56 -0800
+Subject: [PATCH 4/7] SiFive Unleashed CPUFreq
+
+Source: https://github.com/sifive/riscv-linux/commits/dev/paulw/cpufreq-dt-aloe-v5.3-rc4
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Upstream-Status: Not posted for a review
+---
+ arch/riscv/Kconfig | 8 +++++
+ arch/riscv/boot/dts/sifive/fu540-c000.dtsi | 5 ++++
+ .../riscv/boot/dts/sifive/hifive-unleashed-a00.dts | 34 ++++++++++++++++++++++
+ arch/riscv/configs/defconfig | 5 ++++
+ 4 files changed, 52 insertions(+)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index fa7dc03..73b1138 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -356,6 +356,14 @@ endchoice
+
+ endmenu
+
++menu "CPU Power Management"
++
++source "drivers/cpuidle/Kconfig"
++
++source "drivers/cpufreq/Kconfig"
++
++endmenu
++
+ menu "Power management options"
+
+ source "kernel/power/Kconfig"
+diff --git a/arch/riscv/boot/dts/sifive/fu540-c000.dtsi b/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
+index a2e3d54..a380bc7 100644
+--- a/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
++++ b/arch/riscv/boot/dts/sifive/fu540-c000.dtsi
+@@ -30,6 +30,7 @@
+ i-cache-size = <16384>;
+ reg = <0>;
+ riscv,isa = "rv64imac";
++ clocks = <&prci PRCI_CLK_COREPLL>;
+ status = "disabled";
+ cpu0_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+@@ -54,6 +55,7 @@
+ reg = <1>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
++ clocks = <&prci PRCI_CLK_COREPLL>;
+ next-level-cache = <&l2cache>;
+ cpu1_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+@@ -78,6 +80,7 @@
+ reg = <2>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
++ clocks = <&prci PRCI_CLK_COREPLL>;
+ next-level-cache = <&l2cache>;
+ cpu2_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+@@ -102,6 +105,7 @@
+ reg = <3>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
++ clocks = <&prci PRCI_CLK_COREPLL>;
+ next-level-cache = <&l2cache>;
+ cpu3_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+@@ -126,6 +130,7 @@
+ reg = <4>;
+ riscv,isa = "rv64imafdc";
+ tlb-split;
++ clocks = <&prci PRCI_CLK_COREPLL>;
+ next-level-cache = <&l2cache>;
+ cpu4_intc: interrupt-controller {
+ #interrupt-cells = <1>;
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+index 88cfcb9..e1724e3 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+@@ -41,6 +41,40 @@
+ clock-frequency = <RTCCLK_FREQ>;
+ clock-output-names = "rtcclk";
+ };
++
++ fu540_c000_opp_table: opp-table {
++ compatible = "operating-points-v2";
++ opp-shared;
++
++ opp-350000000 {
++ opp-hz = /bits/ 64 <350000000>;
++ };
++ opp-700000000 {
++ opp-hz = /bits/ 64 <700000000>;
++ };
++ opp-999999999 {
++ opp-hz = /bits/ 64 <999999999>;
++ };
++ opp-1400000000 {
++ opp-hz = /bits/ 64 <1400000000>;
++ };
++ };
++};
++
++&cpu0 {
++ operating-points-v2 = <&fu540_c000_opp_table>;
++};
++&cpu1 {
++ operating-points-v2 = <&fu540_c000_opp_table>;
++};
++&cpu2 {
++ operating-points-v2 = <&fu540_c000_opp_table>;
++};
++&cpu3 {
++ operating-points-v2 = <&fu540_c000_opp_table>;
++};
++&cpu4 {
++ operating-points-v2 = <&fu540_c000_opp_table>;
+ };
+
+ &uart0 {
+diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
+index e2ff95c..a2fb392 100644
+--
+2.7.4
+
--- /dev/null
+From ce24ba4d603a19690548b986d87ae2d9af26d015 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Thu, 25 Mar 2021 04:34:52 -0700
+Subject: [PATCH 23/28] riscv: sifive: unmatched: add D12 PWM LED
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index e026f60..8461b33 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -3,6 +3,7 @@
+
+ #include "fu740-c000.dtsi"
+ #include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/pwm/pwm.h>
+
+ /* Clock frequency (in Hz) of the PCB crystal for rtcclk */
+ #define RTCCLK_FREQ 1000000
+@@ -30,6 +31,17 @@
+ soc {
+ };
+
++ pwmleds {
++ compatible = "pwm-leds";
++ d12 {
++ label = "green:d12";
++ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++ };
++
+ hfclk: hfclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+--
+2.7.4
+
--- /dev/null
+From f19634daf24481664cdc89dc0b5abd9b622718f5 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Thu, 25 Mar 2021 04:37:20 -0700
+Subject: [PATCH 24/28] riscv: sifive: unmatched: add gpio-poweroff node
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 8461b33..9a7fa9b 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -2,6 +2,7 @@
+ /* Copyright (c) 2020 SiFive, Inc */
+
+ #include "fu740-c000.dtsi"
++#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/pwm/pwm.h>
+
+@@ -55,6 +56,11 @@
+ clock-frequency = <RTCCLK_FREQ>;
+ clock-output-names = "rtcclk";
+ };
++
++ gpio-poweroff {
++ compatible = "gpio-poweroff";
++ gpios = <&gpio 2 GPIO_ACTIVE_LOW>;
++ };
+ };
+
+ &uart0 {
+--
+2.7.4
+
--- /dev/null
+From 2d375478fd6b5eeea711d081502cc8fd1a22987d Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Fri, 2 Apr 2021 06:31:07 -0700
+Subject: [PATCH 25/28] riscv: sifive: unmatched: add D2 RGB LED
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ .../riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 26 +++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 9a7fa9b..235f78a 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -34,13 +34,37 @@
+
+ pwmleds {
+ compatible = "pwm-leds";
+- d12 {
++ green-d12 {
+ label = "green:d12";
+ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+ linux,default-trigger = "none";
+ };
++
++ green-d2 {
++ label = "green:d2";
++ pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++
++ red-d2 {
++ label = "red:d2";
++ pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++
++ blue-d2 {
++ label = "blue:d2";
++ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
+ };
+
+ hfclk: hfclk {
+--
+2.7.4
+
--- /dev/null
+From 45ed220373844f72e9ddb05ef0a8f2bd391e0980 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Tue, 6 Apr 2021 05:00:11 -0700
+Subject: [PATCH 26/28] riscv: sifive: unmatched: remove "A00" from model
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 235f78a..5fdd183 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -12,7 +12,7 @@
+ / {
+ #address-cells = <2>;
+ #size-cells = <2>;
+- model = "SiFive HiFive Unmatched A00";
++ model = "SiFive HiFive Unmatched";
+ compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000",
+ "sifive,fu740";
+
+--
+2.7.4
+
--- /dev/null
+From 665297adfa377a3682b261b23f2a6f3ec56daa8b Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Wed, 7 Apr 2021 06:08:33 -0700
+Subject: [PATCH 27/28] riscv: sifive: unmatched: define LEDs color
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 5fdd183..9be0564 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -4,6 +4,7 @@
+ #include "fu740-c000.dtsi"
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/leds/common.h>
+ #include <dt-bindings/pwm/pwm.h>
+
+ /* Clock frequency (in Hz) of the PCB crystal for rtcclk */
+@@ -36,6 +37,7 @@
+ compatible = "pwm-leds";
+ green-d12 {
+ label = "green:d12";
++ color = <LED_COLOR_ID_GREEN>;
+ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+@@ -44,6 +46,7 @@
+
+ green-d2 {
+ label = "green:d2";
++ color = <LED_COLOR_ID_GREEN>;
+ pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+@@ -52,6 +55,7 @@
+
+ red-d2 {
+ label = "red:d2";
++ color = <LED_COLOR_ID_RED>;
+ pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+@@ -60,6 +64,7 @@
+
+ blue-d2 {
+ label = "blue:d2";
++ color = <LED_COLOR_ID_BLUE>;
+ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+--
+2.7.4
+
--- /dev/null
+From c1c831af223931219b7bf9158b1306b500116167 Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <david.abdurachmanov@sifive.com>
+Date: Fri, 5 Jun 2020 07:02:10 +0000
+Subject: [PATCH] SiFive HiFive Unleashed: Add PWM LEDs (D1, D2, D3, D4)
+
+By default no functions are assigned to LEDs. It's up to user/distribution
+to provide udev rules to configure them.
+
+Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
+---
+ .../riscv/boot/dts/sifive/hifive-unleashed-a00.dts | 32 ++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+index 6dd6fa4..c8a47bf 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+@@ -3,6 +3,7 @@
+
+ #include "fu540-c000.dtsi"
+ #include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/pwm/pwm.h>
+
+ /* Clock frequency (in Hz) of the PCB crystal for rtcclk */
+ #define RTCCLK_FREQ 1000000
+@@ -27,6 +28,37 @@
+ };
+
+ soc {
++ pwmleds {
++ compatible = "pwm-leds";
++ d1 {
++ label = "green:d1";
++ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++ d2 {
++ label = "green:d2";
++ pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++ d3 {
++ label = "green:d3";
++ pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++ d4 {
++ label = "green:d4";
++ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
++ active-low = <1>;
++ max-brightness = <255>;
++ linux,default-trigger = "none";
++ };
++ };
+ };
+
+ hfclk: hfclk {
+--
+2.7.4
+
--- /dev/null
+diff -ruN a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts 2021-11-06 19:23:11.657526676 +0100
++++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts 2021-11-06 19:00:10.140770182 +0100
+@@ -14,6 +14,13 @@
+ model = "SiFive HiFive Unleashed A00";
+ compatible = "sifive,hifive-unleashed-a00", "sifive,fu540-c000";
+
++ aliases {
++ led-boot = &power_green;
++ led-failsafe = &power_green;
++ led-running = &power_green;
++ led-upgrade = &power_green;
++ };
++
+ chosen {
+ stdout-path = "serial0";
+ };
+@@ -30,7 +37,7 @@
+ soc {
+ pwmleds {
+ compatible = "pwm-leds";
+- d1 {
++ power_green: d1 {
+ label = "green:d1";
+ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+@@ -56,7 +63,7 @@
+ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
+ active-low = <1>;
+ max-brightness = <255>;
+- linux,default-trigger = "none";
++ linux,default-trigger = "mmc0";
+ };
+ };
+ };
+diff -ruN a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts 2021-11-06 19:23:21.949472627 +0100
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts 2021-11-06 19:21:08.002175934 +0100
+@@ -17,6 +17,13 @@
+ compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000",
+ "sifive,fu740";
+
++ aliases {
++ led-boot = &power_green;
++ led-failsafe = &power_green;
++ led-running = &power_green;
++ led-upgrade = &power_green;
++ };
++
+ chosen {
+ stdout-path = "serial0";
+ };
+@@ -35,7 +42,7 @@
+
+ pwmleds {
+ compatible = "pwm-leds";
+- green-d12 {
++ power_green: green-d12 {
+ label = "green:d12";
+ color = <LED_COLOR_ID_GREEN>;
+ pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
--- /dev/null
+From 89c9a963944ed10a890b627e2e63e96fbff3aa36 Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 19 Jun 2021 14:36:47 +0000
+Subject: [PATCH] riscv: dts: sifive unmatched: Expose the FU740 core supply
+ regulator.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 9be05644e3c02a..8728391b4a81ef 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -103,6 +103,11 @@
+ &i2c0 {
+ status = "okay";
+
++ tps544b20@1e {
++ compatible = "tps544b20";
++ reg = <0x1e>;
++ };
++
+ temperature-sensor@4c {
+ compatible = "ti,tmp451";
+ reg = <0x4c>;
--- /dev/null
+From c5faf98f30ef3e1a3f56ace78741ccef2e4856aa Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 19 Jun 2021 14:37:27 +0000
+Subject: [PATCH] riscv: dts: sifive unmatched: Link the tmp451 with its power
+ supply.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 8728391b4a81ef..a6f3556c82ba15 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -111,6 +111,7 @@
+ temperature-sensor@4c {
+ compatible = "ti,tmp451";
+ reg = <0x4c>;
++ vcc-supply = <&vdd_bpro>;
+ interrupt-parent = <&gpio>;
+ interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
+ };
--- /dev/null
+From 3eb8033e1f318477cf87e6e0ea837e75a87856e9 Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 19 Jun 2021 14:37:53 +0000
+Subject: [PATCH] riscv: dts: sifive unmatched: Expose the board ID eeprom.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index a6f3556c82ba15..4a7a7352b46dde 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -116,6 +116,16 @@
+ interrupts = <6 IRQ_TYPE_LEVEL_LOW>;
+ };
+
++ eeprom@54 {
++ compatible = "microchip,24c02", "atmel,24c02";
++ reg = <0x54>;
++ vcc-supply = <&vdd_bpro>;
++ label = "board-id";
++ pagesize = <16>;
++ read-only;
++ size = <256>;
++ };
++
+ pmic@58 {
+ compatible = "dlg,da9063";
+ reg = <0x58>;
--- /dev/null
+From eeaad1f6ab4fa5f854780587296c71dda9cda3b5 Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 19 Jun 2021 14:40:14 +0000
+Subject: [PATCH] riscv: dts: sifive unmatched: Expose the PMIC sub-functions.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 4a7a7352b46dde..66d3c50213443a 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -133,6 +133,18 @@
+ interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+
++ onkey {
++ compatible = "dlg,da9063-onkey";
++ };
++
++ rtc {
++ compatible = "dlg,da9063-rtc";
++ };
++
++ wdt {
++ compatible = "dlg,da9063-watchdog";
++ };
++
+ regulators {
+ vdd_bcore1: bcore1 {
+ regulator-min-microvolt = <1050000>;
--- /dev/null
+From 00a903b88b10f77b053e910f72bd9feb0d5c7c17 Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sat, 19 Jun 2021 14:36:12 +0000
+Subject: [PATCH] riscv: dts: sifive unmatched: Expose fan PWM.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+index 73b3f787773859..888a0aa3b7f475 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
+@@ -90,6 +90,18 @@
+ compatible = "gpio-poweroff";
+ gpios = <&gpio 2 GPIO_ACTIVE_LOW>;
+ };
++
++ fan0: asm2824-fan {
++ compatible = "pwm-fan";
++ pwms = <&pwm1 2 7812500 PWM_POLARITY_INVERTED>;
++ cooling-levels = <255 153 85 25 0>;
++ };
++
++ fan1: chassis-fan {
++ compatible = "pwm-fan";
++ pwms = <&pwm1 3 7812500 PWM_POLARITY_INVERTED>;
++ cooling-levels = <255 153 85 25 0>;
++ };
+ };
+
+ &uart0 {
--- /dev/null
+From 1690b165f7a5b4858eac60abf221666e9cdb90de Mon Sep 17 00:00:00 2001
+From: "Opensource [Steve Twiss]" <stwiss.opensource@diasemi.com>
+Date: Sun, 23 Mar 2014 20:37:30 +0000
+Subject: [PATCH] mfd: da9063: Add HWMON dependencies
+
+Dependencies required for DA9063 HWMON support.
+
+Signed-off-by: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
+
+Moved temperature offset reading to hwmon driver.
+
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ include/linux/mfd/da9063/registers.h | 34 ++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h
+index 6e0f66a2e7279f..297631ddda3994 100644
+--- a/include/linux/mfd/da9063/registers.h
++++ b/include/linux/mfd/da9063/registers.h
+@@ -512,6 +512,7 @@
+
+ /* DA9063_REG_GPIO_0_1 (addr=0x15) */
+ #define DA9063_GPIO0_PIN_MASK 0x03
++#define DA9063_GPIO0_PIN_MASK_SHIFT 0
+ #define DA9063_GPIO0_PIN_ADCIN1 0x00
+ #define DA9063_GPIO0_PIN_GPI 0x01
+ #define DA9063_GPIO0_PIN_GPO_OD 0x02
+@@ -523,6 +524,7 @@
+ #define DA9063_GPIO0_TYPE_GPO_VDD_IO2 0x04
+ #define DA9063_GPIO0_NO_WAKEUP 0x08
+ #define DA9063_GPIO1_PIN_MASK 0x30
++#define DA9063_GPIO1_PIN_MASK_SHIFT 4
+ #define DA9063_GPIO1_PIN_ADCIN2_COMP 0x00
+ #define DA9063_GPIO1_PIN_GPI 0x10
+ #define DA9063_GPIO1_PIN_GPO_OD 0x20
+@@ -536,6 +538,7 @@
+
+ /* DA9063_REG_GPIO_2_3 (addr=0x16) */
+ #define DA9063_GPIO2_PIN_MASK 0x03
++#define DA9063_GPIO2_PIN_MASK_SHIFT 0
+ #define DA9063_GPIO2_PIN_ADCIN3 0x00
+ #define DA9063_GPIO2_PIN_GPI 0x01
+ #define DA9063_GPIO2_PIN_GPO_PSS 0x02
+@@ -851,6 +854,7 @@
+ #define DA9063_VSYS_VAL_BASE 0x00
+
+ /* DA9063_REG_ADC_RES_L (addr=0x37) */
++#define DA9063_ADC_RES_L_SHIFT 6
+ #define DA9063_ADC_RES_L_BITS 2
+ #define DA9063_ADC_RES_L_MASK 0xC0
+
+@@ -1014,6 +1018,36 @@
+ #define DA9063_GPIO_DIM 0x80
+ #define DA9063_GPIO_PWM_MASK 0x7F
+
++/* DA9063_REG_ADC_CFG (addr=0xC9) */
++#define DA9063_REG_ADCIN1_CUR_MASK 0x03
++#define DA9063_REG_ADCIN1_CUR_SHIFT 0
++#define DA9063_ADCIN1_CUR_1UA 0x00
++#define DA9063_ADCIN1_CUR_2UA 0x01
++#define DA9063_ADCIN1_CUR_10UA 0x02
++#define DA9063_ADCIN1_CUR_40UA 0x03
++#define DA9063_REG_ADCIN2_CUR_MASK 0x0C
++#define DA9063_REG_ADCIN2_CUR_SHIFT 2
++#define DA9063_ADCIN2_CUR_1UA 0x00
++#define DA9063_ADCIN2_CUR_2UA 0x01
++#define DA9063_ADCIN2_CUR_10UA 0x02
++#define DA9063_ADCIN2_CUR_40UA 0x03
++#define DA9063_REG_ADCIN3_CUR_MASK 0x10
++#define DA9063_REG_ADCIN3_CUR_SHIFT 4
++#define DA9063_ADCIN3_CUR_10UA 0x00
++#define DA9063_ADCIN3_CUR_40UA 0x01
++#define DA9063_REG_ADCIN1_DEB_MASK 0x20
++#define DA9063_REG_ADCIN1_DEB_SHIFT 5
++#define DA9063_ADCIN1_DEB_OFF 0x00
++#define DA9063_ADCIN1_DEB_ON 0x01
++#define DA9063_REG_ADCIN2_DEB_MASK 0x40
++#define DA9063_REG_ADCIN2_DEB_SHIFT 6
++#define DA9063_ADCIN2_DEB_OFF 0x00
++#define DA9063_ADCIN2_DEB_ON 0x01
++#define DA9063_REG_ADCIN3_DEB_MASK 0x80
++#define DA9063_REG_ADCIN3_DEB_SHIFT 7
++#define DA9063_ADCIN3_DEB_OFF 0x00
++#define DA9063_ADCIN3_DEB_ON 0x01
++
+ /* DA9063_REG_CONFIG_H (addr=0x10D) */
+ #define DA9063_PWM_CLK_MASK 0x01
+ #define DA9063_PWM_CLK_PWM2MHZ 0x00
--- /dev/null
+From a829b924463d46792484ab6341b9c606cc7269a7 Mon Sep 17 00:00:00 2001
+From: Vincent Pelletier <plr.vincent@gmail.com>
+Date: Sun, 23 Mar 2014 20:37:30 +0000
+Subject: [PATCH] hwmon: da9063: HWMON driver
+
+Add the HWMON driver for DA9063
+
+Originally-from: Opensource [Steve Twiss] <stwiss.opensource@diasemi.com>
+Signed-off-by: Vincent Pelletier <plr.vincent@gmail.com>
+---
+ drivers/hwmon/Kconfig | 10 ++
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/da9063-hwmon.c | 260 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 271 insertions(+)
+ create mode 100644 drivers/hwmon/da9063-hwmon.c
+
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 87624902ea8090..17244cfaa855db 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -515,6 +515,16 @@ config SENSORS_DA9055
+ This driver can also be built as a module. If so, the module
+ will be called da9055-hwmon.
+
++config SENSORS_DA9063
++ tristate "Dialog Semiconductor DA9063"
++ depends on MFD_DA9063
++ help
++ If you say yes here you get support for the hardware
++ monitoring features of the DA9063 Power Management IC.
++
++ This driver can also be built as a module. If so, the module
++ will be called da9063-hwmon.
++
+ config SENSORS_I5K_AMB
+ tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
+ depends on PCI
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index 59e78bc212cf3c..6855711ed9ec0f 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -60,6 +60,7 @@ obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o
+ obj-$(CONFIG_SENSORS_CORSAIR_PSU) += corsair-psu.o
+ obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
+ obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
++obj-$(CONFIG_SENSORS_DA9063) += da9063-hwmon.o
+ obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o
+ obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
+ obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o
+diff --git a/drivers/hwmon/da9063-hwmon.c b/drivers/hwmon/da9063-hwmon.c
+new file mode 100644
+index 00000000000000..6367685536a179
+--- /dev/null
++++ b/drivers/hwmon/da9063-hwmon.c
+@@ -0,0 +1,260 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/* da9063-hwmon.c - Hardware monitor support for DA9063
++ * Copyright (C) 2014 Dialog Semiconductor Ltd.
++ * Copyright (C) 2021 Vincent Pelletier <plr.vincent@gmail.com>
++ */
++
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mfd/da9063/core.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++
++#define DA9063_ADC_RES (1 << (DA9063_ADC_RES_L_BITS + DA9063_ADC_RES_M_BITS))
++#define DA9063_ADC_MAX (DA9063_ADC_RES - 1)
++#define DA9063_2V5 2500
++#define DA9063_5V0 5000
++#define DA9063_5V5 5500
++#define DA9063_TJUNC_M -398
++#define DA9063_TJUNC_O 330000
++#define DA9063_VBBAT_M 2048
++
++enum da9063_adc {
++ DA9063_CHAN_VSYS = DA9063_ADC_MUX_VSYS,
++ DA9063_CHAN_ADCIN1 = DA9063_ADC_MUX_ADCIN1,
++ DA9063_CHAN_ADCIN2 = DA9063_ADC_MUX_ADCIN2,
++ DA9063_CHAN_ADCIN3 = DA9063_ADC_MUX_ADCIN3,
++ DA9063_CHAN_TJUNC = DA9063_ADC_MUX_T_SENSE,
++ DA9063_CHAN_VBBAT = DA9063_ADC_MUX_VBBAT,
++ DA9063_CHAN_LDO_G1 = DA9063_ADC_MUX_LDO_G1,
++ DA9063_CHAN_LDO_G2 = DA9063_ADC_MUX_LDO_G2,
++ DA9063_CHAN_LDO_G3 = DA9063_ADC_MUX_LDO_G3
++};
++
++struct da9063_hwmon {
++ struct da9063 *da9063;
++ struct mutex hwmon_mutex;
++ struct completion adc_ready;
++ signed char tjunc_offset;
++};
++
++static int da9063_adc_manual_read(struct da9063_hwmon *hwmon, int channel)
++{
++ int ret;
++ unsigned char val;
++ unsigned char data[2];
++ int adc_man;
++
++ mutex_lock(&hwmon->hwmon_mutex);
++
++ val = (channel & DA9063_ADC_MUX_MASK) | DA9063_ADC_MAN;
++ ret = regmap_update_bits(hwmon->da9063->regmap, DA9063_REG_ADC_MAN,
++ DA9063_ADC_MUX_MASK | DA9063_ADC_MAN, val);
++ if (ret < 0)
++ goto err_mread;
++
++ ret = wait_for_completion_timeout(&hwmon->adc_ready,
++ msecs_to_jiffies(100));
++ reinit_completion(&hwmon->adc_ready);
++ if (ret == 0)
++ dev_dbg(hwmon->da9063->dev,
++ "Timeout while waiting for ADC completion IRQ\n");
++
++ ret = regmap_read(hwmon->da9063->regmap, DA9063_REG_ADC_MAN, &adc_man);
++ if (ret < 0)
++ goto err_mread;
++
++ /* data value is not ready */
++ if (adc_man & DA9063_ADC_MAN) {
++ ret = -ETIMEDOUT;
++ goto err_mread;
++ }
++
++ ret = regmap_bulk_read(hwmon->da9063->regmap,
++ DA9063_REG_ADC_RES_L, data, 2);
++ if (ret < 0)
++ goto err_mread;
++
++ ret = (data[0] & DA9063_ADC_RES_L_MASK) >> DA9063_ADC_RES_L_SHIFT;
++ ret |= data[1] << DA9063_ADC_RES_L_BITS;
++err_mread:
++ mutex_unlock(&hwmon->hwmon_mutex);
++ return ret;
++}
++
++static irqreturn_t da9063_hwmon_irq_handler(int irq, void *irq_data)
++{
++ struct da9063_hwmon *hwmon = irq_data;
++
++ complete(&hwmon->adc_ready);
++ return IRQ_HANDLED;
++}
++
++static umode_t da9063_is_visible(const void *drvdata, enum
++ hwmon_sensor_types type, u32 attr, int channel)
++{
++ return 0444;
++}
++
++static const enum da9063_adc da9063_in_index[] = {
++ DA9063_CHAN_VSYS, DA9063_CHAN_VBBAT
++};
++
++static int da9063_read(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, long *val)
++{
++ struct da9063_hwmon *hwmon = dev_get_drvdata(dev);
++ enum da9063_adc adc_channel;
++ int tmp;
++
++ switch (type) {
++ case hwmon_in:
++ if (attr != hwmon_in_input)
++ return -EOPNOTSUPP;
++ adc_channel = da9063_in_index[channel];
++ break;
++ case hwmon_temp:
++ if (attr != hwmon_temp_input)
++ return -EOPNOTSUPP;
++ adc_channel = DA9063_CHAN_TJUNC;
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ tmp = da9063_adc_manual_read(hwmon, adc_channel);
++ if (tmp < 0)
++ return tmp;
++
++ switch (adc_channel) {
++ case DA9063_CHAN_VSYS:
++ *val = ((DA9063_5V5 - DA9063_2V5) * tmp) / DA9063_ADC_MAX +
++ DA9063_2V5;
++ break;
++ case DA9063_CHAN_TJUNC:
++ tmp -= hwmon->tjunc_offset;
++ *val = DA9063_TJUNC_M * tmp + DA9063_TJUNC_O;
++ break;
++ case DA9063_CHAN_VBBAT:
++ *val = (DA9063_5V0 * tmp) / DA9063_ADC_MAX;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static const char * const da9063_in_name[] = {
++ "VSYS", "VBBAT"
++};
++
++static int da9063_read_string(struct device *dev, enum hwmon_sensor_types type,
++ u32 attr, int channel, const char **str)
++{
++ switch (type) {
++ case hwmon_in:
++ if (attr != hwmon_in_label)
++ return -EOPNOTSUPP;
++ *str = da9063_in_name[channel];
++ break;
++ case hwmon_temp:
++ if (attr != hwmon_temp_label)
++ return -EOPNOTSUPP;
++ *str = "TJUNC";
++ break;
++ default:
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
++}
++
++static const struct hwmon_ops da9063_ops = {
++ .is_visible = da9063_is_visible,
++ .read = da9063_read,
++ .read_string = da9063_read_string,
++};
++
++static const struct hwmon_channel_info *da9063_channel_info[] = {
++ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
++ HWMON_CHANNEL_INFO(in,
++ HWMON_I_INPUT | HWMON_I_LABEL,
++ HWMON_I_INPUT | HWMON_I_LABEL),
++ HWMON_CHANNEL_INFO(temp,
++ HWMON_T_INPUT | HWMON_T_LABEL),
++ NULL
++};
++
++static const struct hwmon_chip_info da9063_chip_info = {
++ .ops = &da9063_ops,
++ .info = da9063_channel_info,
++};
++
++static int da9063_hwmon_probe(struct platform_device *pdev)
++{
++ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
++ struct da9063_hwmon *hwmon;
++ struct device *hwmon_dev;
++ unsigned int tmp;
++ int irq;
++ int ret;
++
++ hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9063_hwmon),
++ GFP_KERNEL);
++ if (!hwmon)
++ return -ENOMEM;
++
++ mutex_init(&hwmon->hwmon_mutex);
++ init_completion(&hwmon->adc_ready);
++ hwmon->da9063 = da9063;
++
++ irq = platform_get_irq_byname(pdev, DA9063_DRVNAME_HWMON);
++ if (irq < 0)
++ return irq;
++
++ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
++ da9063_hwmon_irq_handler,
++ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
++ "HWMON", hwmon);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed to request IRQ.\n");
++ return ret;
++ }
++
++ ret = regmap_read(da9063->regmap, DA9063_REG_T_OFFSET, &tmp);
++ if (ret < 0) {
++ tmp = 0;
++ dev_warn(&pdev->dev,
++ "Temperature trimming value cannot be read (defaulting to 0)\n");
++ }
++ hwmon->tjunc_offset = (signed char) tmp;
++
++ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "da9063",
++ hwmon,
++ &da9063_chip_info,
++ NULL);
++
++ return PTR_ERR_OR_ZERO(hwmon_dev);
++}
++
++static struct platform_driver da9063_hwmon_driver = {
++ .probe = da9063_hwmon_probe,
++ .driver = {
++ .name = DA9063_DRVNAME_HWMON,
++ },
++};
++module_platform_driver(da9063_hwmon_driver);
++
++MODULE_DESCRIPTION("Hardware monitor support device driver for Dialog DA9063");
++MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:" DA9063_DRVNAME_HWMON);
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+
+define Profile/Default
+ NAME:=Default Profile
+ PRIORITY := 1
+endef
+
+define Profile/Default/Description
+ Default package set compatible with most boards.
+endef
+$(eval $(call Profile,Default))