--- /dev/null
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=riscv64
+BOARD:=sunxid1
+BOARDNAME:=AllWinner D1-based boards
+FEATURES:=ext4
+KERNELNAME:=Image dtbs
+MAINTAINER:=Zoltan HERPAI <wigyori@uid0.hu>
+
+KERNEL_PATCHVER:=5.15
+
+include $(INCLUDE_DIR)/target.mk
+
+define Target/Description
+ Build firmware images for AllWinner D1-based boards
+endef
+
+$(eval $(call BuildTarget))
--- /dev/null
+#!/bin/sh
+#
+# Copyright (C) 2013-2015 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
+#!/bin/sh
+# Copyright (C) 2012-2015 OpenWrt.org
+
+riscv_debug() {
+ cat /proc/cpuinfo
+}
+
+boot_hook_add preinit_main riscv_debug
+
--- /dev/null
+CONFIG_64BIT=y
+CONFIG_AF_UNIX_OOB=y
+# CONFIG_AHCI_SUNXI is not set
+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_ARCH_STACKWALK=y
+CONFIG_ARCH_SUNXI=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ASN1=y
+CONFIG_ASSOCIATIVE_ARRAY=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_ATA=y
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_BINARY_PRINTF=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_MQ_PCI=y
+CONFIG_CAVIUM_PTP=y
+CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
+CONFIG_CLKSRC_MMIO=y
+# CONFIG_CLK_SIFIVE is not set
+CONFIG_CLK_SUNXI=y
+CONFIG_CLK_SUNXI_CLOCKS=y
+CONFIG_CLK_SUNXI_PRCM_SUN6I=y
+CONFIG_CLK_SUNXI_PRCM_SUN8I=y
+CONFIG_CLK_SUNXI_PRCM_SUN9I=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_DEV_ALLWINNER=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_HW=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_CRYPTO_SHA512=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DEVMEM=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_DIRECT_REMAP=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_REMAP=y
+CONFIG_DMA_SUN6I=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DNOTIFY=y
+CONFIG_DTC=y
+CONFIG_DWMAC_GENERIC=y
+CONFIG_DWMAC_SUN8I=y
+CONFIG_DWMAC_SUNXI=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_ELF_CORE=y
+CONFIG_EXT4_FS=y
+CONFIG_EXTCON=y
+# CONFIG_EXTCON_USBC_TUSB320 is not set
+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_FWNODE_MDIO=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_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CSUM=y
+CONFIG_GENERIC_EARLY_IOREMAP=y
+CONFIG_GENERIC_GETTIMEOFDAY=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IOREMAP=y
+CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_PINCTRL_GROUPS=y
+CONFIG_GENERIC_PINMUX_FUNCTIONS=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_GPIOLIB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_CDEV=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HID=y
+CONFIG_HID_GENERIC=y
+# CONFIG_HID_PLAYSTATION is not set
+# CONFIG_HID_SEMITEK is not set
+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_HID_OF is not set
+# CONFIG_I2C_HID_OF_GOODIX is not set
+CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_OCORES=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_INPUT=y
+# CONFIG_INPUT_DA7280_HAPTICS is not set
+# CONFIG_INPUT_IQS626A is not set
+CONFIG_IO_URING=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_JBD2=y
+CONFIG_KALLSYMS=y
+CONFIG_KEYS=y
+# CONFIG_KFENCE is not set
+CONFIG_LEDS_SUN50I_R329=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_LTO_NONE=y
+CONFIG_MACB=y
+# CONFIG_MACB_PCI is not set
+CONFIG_MACB_USE_HWSTAMP=y
+CONFIG_MAILBOX=y
+# CONFIG_MAILBOX_TEST is not set
+CONFIG_MAXPHYSMEM_128GB=y
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_BUS_MUX=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MDIO_DEVRES=y
+# CONFIG_MDIO_SUN4I is not set
+CONFIG_MEMFD_CREATE=y
+CONFIG_MFD_AXP20X=y
+CONFIG_MFD_AXP20X_I2C=y
+CONFIG_MFD_CORE=y
+# CONFIG_MFD_SUN4I_GPADC is not set
+CONFIG_MFD_SUN6I_PRCM=y
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGRATION=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_SUNXI=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_NAND_CORE=y
+CONFIG_MTD_NAND_ECC=y
+# CONFIG_MTD_NAND_ECC_SW_HAMMING is not set
+CONFIG_MTD_SPI_NAND=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
+# CONFIG_MUSB_PIO_ONLY is not set
+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_NET_SELFTESTS=y
+CONFIG_NET_SOCK_MSG=y
+CONFIG_NET_VENDOR_ALLWINNER=y
+CONFIG_NET_VENDOR_MICROSOFT=y
+CONFIG_NLS=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_NR_CPUS=8
+CONFIG_NVMEM=y
+CONFIG_NVMEM_SUNXI_SID=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_PAGE_POOL=y
+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_PCPU_DEV_REFCNT=y
+CONFIG_PCS_XPCS=y
+CONFIG_PGTABLE_LEVELS=3
+CONFIG_PHYLIB=y
+CONFIG_PHYLINK=y
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_PHYS_RAM_BASE=0x40000000
+CONFIG_PHYS_RAM_BASE_FIXED=y
+CONFIG_PHY_SUN4I_USB=y
+# CONFIG_PHY_SUN50I_USB3 is not set
+# CONFIG_PHY_SUN6I_MIPI_DPHY is not set
+# CONFIG_PHY_SUN9I_USB is not set
+CONFIG_PID_NS=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_SUN20I_D1=y
+# CONFIG_PINCTRL_SUN4I_A10 is not set
+# CONFIG_PINCTRL_SUN50I_A100 is not set
+# CONFIG_PINCTRL_SUN50I_A100_R is not set
+# CONFIG_PINCTRL_SUN50I_A64 is not set
+# CONFIG_PINCTRL_SUN50I_A64_R is not set
+# CONFIG_PINCTRL_SUN50I_H5 is not set
+# CONFIG_PINCTRL_SUN50I_H6 is not set
+# CONFIG_PINCTRL_SUN50I_H616 is not set
+# CONFIG_PINCTRL_SUN50I_H616_R is not set
+# CONFIG_PINCTRL_SUN50I_H6_R is not set
+# CONFIG_PINCTRL_SUN5I is not set
+# CONFIG_PINCTRL_SUN6I_A31 is not set
+# CONFIG_PINCTRL_SUN6I_A31_R is not set
+# CONFIG_PINCTRL_SUN8I_A23 is not set
+# CONFIG_PINCTRL_SUN8I_A23_R is not set
+# CONFIG_PINCTRL_SUN8I_A33 is not set
+# CONFIG_PINCTRL_SUN8I_A83T is not set
+# CONFIG_PINCTRL_SUN8I_A83T_R is not set
+# CONFIG_PINCTRL_SUN8I_H3 is not set
+# CONFIG_PINCTRL_SUN8I_H3_R is not set
+# CONFIG_PINCTRL_SUN8I_V3S is not set
+# CONFIG_PINCTRL_SUN9I_A80 is not set
+# CONFIG_PINCTRL_SUN9I_A80_R is not set
+CONFIG_PINCTRL_SUNXI=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
+CONFIG_POWER_RESET=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_POWER_RESET_SYSCON_POWEROFF=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PPS=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PTP_1588_CLOCK=y
+CONFIG_PTP_1588_CLOCK_OPTIONAL=y
+CONFIG_PWM=y
+# CONFIG_PWM_ATMEL_TCB is not set
+# CONFIG_PWM_DWC is not set
+# CONFIG_PWM_SIFIVE is not set
+# CONFIG_PWM_SUN4I is not set
+CONFIG_PWM_SUN8I_V536=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_I2C=y
+CONFIG_REGMAP_IRQ=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_AXP20X is not set
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_RESET_SIMPLE=y
+CONFIG_RESET_SUNXI=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RISCV=y
+# CONFIG_RISCV_ERRATA_ALTERNATIVE is not set
+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_GOLDFISH=y
+CONFIG_RTC_DRV_SUN6I=y
+CONFIG_RTC_I2C_AND_SPI=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_HOST=y
+CONFIG_SATA_PMP=y
+CONFIG_SATA_SIL24=y
+CONFIG_SCHED_DEBUG=y
+CONFIG_SCSI=y
+CONFIG_SCSI_COMMON=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_8250_DWLIB=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_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SG_POOL=y
+CONFIG_SIFIVE_PLIC=y
+CONFIG_SLUB_DEBUG=y
+CONFIG_SMP=y
+CONFIG_SOCK_RX_QUEUE_MAPPING=y
+# CONFIG_SOC_MICROCHIP_POLARFIRE is not set
+# CONFIG_SOC_SIFIVE is not set
+# CONFIG_SOC_VIRT is not set
+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_SUN4I is not set
+CONFIG_SPI_SUN6I=y
+CONFIG_SRCU=y
+CONFIG_STACKTRACE=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+# CONFIG_STMMAC_SELFTESTS is not set
+CONFIG_SUN20I_D1_CCU=y
+CONFIG_SUN20I_D1_R_CCU=y
+CONFIG_SUN20I_INTC=y
+# CONFIG_SUN4I_EMAC is not set
+CONFIG_SUN4I_TIMER=y
+CONFIG_SUN5I_HSTIMER=y
+CONFIG_SUN6I_MSGBOX=y
+CONFIG_SUN6I_RTC_CCU=y
+# CONFIG_SUN8I_A83T_CCU is not set
+CONFIG_SUN8I_DE2_CCU=y
+CONFIG_SUN8I_MSGBOX=y
+# CONFIG_SUN8I_R_CCU is not set
+CONFIG_SUNXI_CCU=y
+# CONFIG_SUNXI_RSB is not set
+CONFIG_SUNXI_SRAM=y
+CONFIG_SUNXI_WATCHDOG=y
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_SYSFS_SYSCALL=y
+CONFIG_THREAD_INFO_IN_TASK=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_OF=y
+CONFIG_TIMER_PROBE=y
+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=y
+CONFIG_USB_EHCI_PCI=y
+CONFIG_USB_HID=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_HOST=y
+CONFIG_USB_MUSB_SUNXI=y
+CONFIG_USB_NET_DRIVERS=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_PCI=y
+CONFIG_USB_PHY=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_VHOST_MENU is not set
+# CONFIG_VIRTIO_MENU is not set
+CONFIG_VMAP_STACK=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_XPS=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA32=y
--- /dev/null
+config SUNXID1_SD_BOOT_PARTSIZE
+ int "Boot (SD Card) filesystem partition size (in MB)"
+ depends on TARGET_sunxid1
+ default 32
+
--- /dev/null
+#
+# Copyright (C) 2021 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+FAT32_BLOCK_SIZE=1024
+FAT32_BLOCKS=$(shell echo $$(($(CONFIG_SUNXID1_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE))))
+
+KERNEL_LOADADDR:=0x40200000
+#KERNEL_LOADADDR:=0x40200000
+
+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_sunxi_sdcard_img.sh \
+ $@ \
+ $@.boot \
+ $(IMAGE_ROOTFS) \
+ $(CONFIG_SUNXID1_SD_BOOT_PARTSIZE) \
+ $(CONFIG_TARGET_ROOTFS_PARTSIZE) \
+ $(STAGING_DIR_IMAGE)/nezha-u-boot.toc1 \
+ $(STAGING_DIR_IMAGE)/boot0_sdcard_sun20iw1p1.bin
+endef
+
+define Device/Default
+ PROFILES := Default
+ KERNEL_NAME := Image
+# KERNEL := kernel-bin | uImage gzip -a $(KERNEL_LOADADDR)
+ KERNEL := kernel-bin | uImage
+ 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/nezha
+# $(call Device/FitImage)
+ DEVICE_VENDOR := Nezha
+ DEVICE_MODEL := D1
+ DEVICE_DTS := allwinner/sun20i-d1-nezha
+ UBOOT := nezha
+endef
+
+define Image/Build
+ $(call Image/Build/$(1),$(1))
+endef
+
+TARGET_DEVICES += nezha
+
+$(eval $(call BuildImage))
--- /dev/null
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2013 OpenWrt.org
+
+set -ex
+[ $# -eq 7 ] || {
+ echo "SYNTAX: $0 <file> <bootfs image> <rootfs image> <bootfs size> <rootfs size> <u-boot image> <d1 spl>"
+ exit 1
+}
+
+OUTPUT="$1"
+BOOTFS="$2"
+ROOTFS="$3"
+BOOTFSSIZE="$4"
+ROOTFSSIZE="$5"
+UBOOT="$6"
+UBOOT_SPL="$7"
+
+ROOTFSPTOFFSET=$(($BOOTFSSIZE + 20 + 1))
+
+head=4
+sect=63
+
+set $(ptgen -o $OUTPUT -h $head -s $sect -l 1024 -t c -p ${BOOTFSSIZE}M@20M -t 83 -p ${ROOTFSSIZE}M@${ROOTFSPTOFFSET}M)
+
+BOOTOFFSET="$(($1 / 512))"
+BOOTSIZE="$(($2 / 512))"
+ROOTFSOFFSET="$(($3 / 512))"
+ROOTFSSIZE="$(($4 / 512))"
+
+dd bs=512 if="$UBOOT_SPL" of="$OUTPUT" seek=256 conv=notrunc
+dd bs=512 if="$UBOOT" of="$OUTPUT" seek=32800 conv=notrunc
+dd bs=512 if="$BOOTFS" of="$OUTPUT" seek="$BOOTOFFSET" conv=notrunc
+dd bs=512 if="$ROOTFS" of="$OUTPUT" seek="$ROOTFSOFFSET" conv=notrunc
+
+
+
+#dd bs=512 if="$UBOOT_SPL" of="$OUTPUT" seek=256 conv=notrunc
+#dd bs=512 if="$UBOOT" of="$OUTPUT" seek=2082 conv=notrunc
+#16M + 34
+#dd bs=512 if="$BOOTFS" of="$OUTPUT" seek=32802 conv=notrunc
+#dd bs=512 if="$ROOTFS" of="$OUTPUT" seek=${ROOTFSOFFSET} conv=notrunc
--- /dev/null
+From 7599e32a45d303268886ba6b4a08c37e877e42a2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 1 Sep 2021 00:05:19 -0500
+Subject: [PATCH 001/124] clk: sunxi-ng: Unregister clocks/resets when
+ unbinding
+
+Currently, unbinding a CCU driver unmaps the device's MMIO region, while
+leaving its clocks/resets and their providers registered. This can cause
+a page fault later when some clock operation tries to perform MMIO. Fix
+this by separating the CCU initialization from the memory allocation,
+and then using a devres callback to unregister the clocks and resets.
+
+This also fixes a memory leak of the `struct ccu_reset`, and uses the
+correct owner (the specific platform driver) for the clocks and resets.
+
+Early OF clock providers are never unregistered, and limited error
+handling is possible, so they are mostly unchanged. The error reporting
+is made more consistent by moving the message inside of_sunxi_ccu_probe.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+Link: https://lore.kernel.org/r/20210901050526.45673-2-samuel@sholland.org
+---
+ drivers/clk/sunxi-ng/ccu-sun4i-a10.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-a100.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-h616.c | 4 +-
+ drivers/clk/sunxi-ng/ccu-sun5i.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun6i-a31.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-a23.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-r.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-r40.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c | 3 +-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c | 3 +-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80.c | 2 +-
+ drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c | 2 +-
+ drivers/clk/sunxi-ng/ccu_common.c | 89 ++++++++++++++++++++----
+ drivers/clk/sunxi-ng/ccu_common.h | 6 +-
+ 23 files changed, 100 insertions(+), 41 deletions(-)
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
+index f32366d9336e..bd9a8782fec3 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
++++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
+@@ -1464,7 +1464,7 @@ static void __init sun4i_ccu_init(struct device_node *node,
+ val &= ~GENMASK(7, 6);
+ writel(val | (2 << 6), reg + SUN4I_AHB_REG);
+
+- sunxi_ccu_probe(node, reg, desc);
++ of_sunxi_ccu_probe(node, reg, desc);
+ }
+
+ static void __init sun4i_a10_ccu_setup(struct device_node *node)
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+index a56142b90993..6f2a58970556 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+@@ -196,7 +196,7 @@ static int sun50i_a100_r_ccu_probe(struct platform_device *pdev)
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+- return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_r_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_r_ccu_desc);
+ }
+
+ static const struct of_device_id sun50i_a100_r_ccu_ids[] = {
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+index 81b48c73d389..913bb08e6dee 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+@@ -1247,7 +1247,7 @@ static int sun50i_a100_ccu_probe(struct platform_device *pdev)
+ writel(val, reg + sun50i_a100_usb2_clk_regs[i]);
+ }
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_ccu_desc);
+ if (ret)
+ return ret;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+index 149cfde817cb..54f25c624f02 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+@@ -955,7 +955,7 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev)
+
+ writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc);
+ if (ret)
+ return ret;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
+index f8909a7ed553..f30d7eb5424d 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
+@@ -232,7 +232,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node,
+ return;
+ }
+
+- sunxi_ccu_probe(node, reg, desc);
++ of_sunxi_ccu_probe(node, reg, desc);
+ }
+
+ static void __init sun50i_h6_r_ccu_setup(struct device_node *node)
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+index bff446b78290..c0800da2fa3d 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+@@ -1240,7 +1240,7 @@ static int sun50i_h6_ccu_probe(struct platform_device *pdev)
+ val |= BIT(24);
+ writel(val, reg + SUN50I_H6_HDMI_CEC_CLK_REG);
+
+- return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h6_ccu_desc);
+ }
+
+ static const struct of_device_id sun50i_h6_ccu_ids[] = {
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
+index 225307305880..22eb18079a15 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
+@@ -1141,9 +1141,7 @@ static void __init sun50i_h616_ccu_setup(struct device_node *node)
+ val |= BIT(24);
+ writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG);
+
+- i = sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
+- if (i)
+- pr_err("%pOF: probing clocks fails: %d\n", node, i);
++ of_sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
+ }
+
+ CLK_OF_DECLARE(sun50i_h616_ccu, "allwinner,sun50i-h616-ccu",
+diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c
+index b78e9b507c1c..1f4bc0e773a7 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun5i.c
++++ b/drivers/clk/sunxi-ng/ccu-sun5i.c
+@@ -1012,7 +1012,7 @@ static void __init sun5i_ccu_init(struct device_node *node,
+ val &= ~GENMASK(7, 6);
+ writel(val | (2 << 6), reg + SUN5I_AHB_REG);
+
+- sunxi_ccu_probe(node, reg, desc);
++ of_sunxi_ccu_probe(node, reg, desc);
+ }
+
+ static void __init sun5i_a10s_ccu_setup(struct device_node *node)
+diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+index 9b40d53266a3..3df5c0b41580 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
++++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+@@ -1257,7 +1257,7 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node)
+ val |= 0x3 << 12;
+ writel(val, reg + SUN6I_A31_AHB1_REG);
+
+- sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
++ of_sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
+
+ ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
+ &sun6i_a31_cpu_nb);
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+index 103aa504f6c8..577bb235d658 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+@@ -745,7 +745,7 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node)
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A23_PLL_MIPI_REG);
+
+- sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
++ of_sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
+ }
+ CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu",
+ sun8i_a23_ccu_setup);
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+index 91838cd11037..8f65cd03f5ac 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+@@ -805,7 +805,7 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
+
+- sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
++ of_sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb);
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+index 2b434521c5cc..c2ddcd2ddab4 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+@@ -906,7 +906,7 @@ static int sun8i_a83t_ccu_probe(struct platform_device *pdev)
+ sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C0CPUX_REG);
+ sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C1CPUX_REG);
+
+- return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_a83t_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a83t_ccu_desc);
+ }
+
+ static const struct of_device_id sun8i_a83t_ccu_ids[] = {
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
+index 524f33275bc7..4b94b6041b27 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
+@@ -342,7 +342,7 @@ static int sunxi_de2_clk_probe(struct platform_device *pdev)
+ goto err_disable_mod_clk;
+ }
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
+ if (ret)
+ goto err_assert_reset;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+index 7e629a4493af..d2fc2903787d 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+@@ -1154,7 +1154,7 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node,
+ val &= ~GENMASK(19, 16);
+ writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
+
+- sunxi_ccu_probe(node, reg, desc);
++ of_sunxi_ccu_probe(node, reg, desc);
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb);
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c
+index 4c8c491b87c2..9e754d1f754a 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c
+@@ -265,7 +265,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node,
+ return;
+ }
+
+- sunxi_ccu_probe(node, reg, desc);
++ of_sunxi_ccu_probe(node, reg, desc);
+ }
+
+ static void __init sun8i_a83t_r_ccu_setup(struct device_node *node)
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+index 84153418453f..002e0c3a04db 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+@@ -1346,7 +1346,7 @@ static int sun8i_r40_ccu_probe(struct platform_device *pdev)
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_r40_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_r40_ccu_desc);
+ if (ret)
+ return ret;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+index f49724a22540..ce150f83ab54 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+@@ -822,7 +822,7 @@ static void __init sun8i_v3_v3s_ccu_init(struct device_node *node,
+ val &= ~GENMASK(19, 16);
+ writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG);
+
+- sunxi_ccu_probe(node, reg, ccu_desc);
++ of_sunxi_ccu_probe(node, reg, ccu_desc);
+ }
+
+ static void __init sun8i_v3s_ccu_setup(struct device_node *node)
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+index 6616e8114f62..261e64416f26 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+@@ -246,8 +246,7 @@ static int sun9i_a80_de_clk_probe(struct platform_device *pdev)
+ goto err_disable_clk;
+ }
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
+- &sun9i_a80_de_clk_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_de_clk_desc);
+ if (ret)
+ goto err_assert_reset;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
+index 4b4a507d04ed..596243b3e0fa 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
+@@ -117,8 +117,7 @@ static int sun9i_a80_usb_clk_probe(struct platform_device *pdev)
+ return ret;
+ }
+
+- ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
+- &sun9i_a80_usb_clk_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_usb_clk_desc);
+ if (ret)
+ goto err_disable_clk;
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+index ef29582676f6..97aaed0e6850 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+@@ -1231,7 +1231,7 @@ static int sun9i_a80_ccu_probe(struct platform_device *pdev)
+ sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C0CPUX_REG);
+ sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C1CPUX_REG);
+
+- return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_ccu_desc);
+ }
+
+ static const struct of_device_id sun9i_a80_ccu_ids[] = {
+diff --git a/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c b/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
+index 7ecc3a5a5b5e..61ad7ee91c11 100644
+--- a/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
++++ b/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
+@@ -538,7 +538,7 @@ static void __init suniv_f1c100s_ccu_setup(struct device_node *node)
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUNIV_PLL_AUDIO_REG);
+
+- sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
++ of_sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&suniv_pll_cpu_nb);
+diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
+index 2e20e650b6c0..88cb569e5835 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.c
++++ b/drivers/clk/sunxi-ng/ccu_common.c
+@@ -7,6 +7,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
++#include <linux/device.h>
+ #include <linux/iopoll.h>
+ #include <linux/slab.h>
+
+@@ -14,6 +15,11 @@
+ #include "ccu_gate.h"
+ #include "ccu_reset.h"
+
++struct sunxi_ccu {
++ const struct sunxi_ccu_desc *desc;
++ struct ccu_reset reset;
++};
++
+ static DEFINE_SPINLOCK(ccu_lock);
+
+ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
+@@ -79,12 +85,15 @@ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
+ &pll_nb->clk_nb);
+ }
+
+-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+- const struct sunxi_ccu_desc *desc)
++static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
++ struct device_node *node, void __iomem *reg,
++ const struct sunxi_ccu_desc *desc)
+ {
+ struct ccu_reset *reset;
+ int i, ret;
+
++ ccu->desc = desc;
++
+ for (i = 0; i < desc->num_ccu_clks; i++) {
+ struct ccu_common *cclk = desc->ccu_clks[i];
+
+@@ -103,7 +112,10 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+ continue;
+
+ name = hw->init->name;
+- ret = of_clk_hw_register(node, hw);
++ if (dev)
++ ret = clk_hw_register(dev, hw);
++ else
++ ret = of_clk_hw_register(node, hw);
+ if (ret) {
+ pr_err("Couldn't register clock %d - %s\n", i, name);
+ goto err_clk_unreg;
+@@ -115,15 +127,10 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+ if (ret)
+ goto err_clk_unreg;
+
+- reset = kzalloc(sizeof(*reset), GFP_KERNEL);
+- if (!reset) {
+- ret = -ENOMEM;
+- goto err_alloc_reset;
+- }
+-
++ reset = &ccu->reset;
+ reset->rcdev.of_node = node;
+ reset->rcdev.ops = &ccu_reset_ops;
+- reset->rcdev.owner = THIS_MODULE;
++ reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
+ reset->rcdev.nr_resets = desc->num_resets;
+ reset->base = reg;
+ reset->lock = &ccu_lock;
+@@ -131,13 +138,11 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+
+ ret = reset_controller_register(&reset->rcdev);
+ if (ret)
+- goto err_of_clk_unreg;
++ goto err_del_provider;
+
+ return 0;
+
+-err_of_clk_unreg:
+- kfree(reset);
+-err_alloc_reset:
++err_del_provider:
+ of_clk_del_provider(node);
+ err_clk_unreg:
+ while (--i >= 0) {
+@@ -149,3 +154,59 @@ int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+ }
+ return ret;
+ }
++
++static void devm_sunxi_ccu_release(struct device *dev, void *res)
++{
++ struct sunxi_ccu *ccu = res;
++ const struct sunxi_ccu_desc *desc = ccu->desc;
++ int i;
++
++ reset_controller_unregister(&ccu->reset.rcdev);
++ of_clk_del_provider(dev->of_node);
++
++ for (i = 0; i < desc->hw_clks->num; i++) {
++ struct clk_hw *hw = desc->hw_clks->hws[i];
++
++ if (!hw)
++ continue;
++ clk_hw_unregister(hw);
++ }
++}
++
++int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
++ const struct sunxi_ccu_desc *desc)
++{
++ struct sunxi_ccu *ccu;
++ int ret;
++
++ ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL);
++ if (!ccu)
++ return -ENOMEM;
++
++ ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc);
++ if (ret) {
++ devres_free(ccu);
++ return ret;
++ }
++
++ devres_add(dev, ccu);
++
++ return 0;
++}
++
++void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
++ const struct sunxi_ccu_desc *desc)
++{
++ struct sunxi_ccu *ccu;
++ int ret;
++
++ ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
++ if (!ccu)
++ return;
++
++ ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc);
++ if (ret) {
++ pr_err("%pOF: probing clocks failed: %d\n", node, ret);
++ kfree(ccu);
++ }
++}
+diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h
+index 04e7a12200a2..98a1834b58bb 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.h
++++ b/drivers/clk/sunxi-ng/ccu_common.h
+@@ -63,7 +63,9 @@ struct ccu_pll_nb {
+
+ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb);
+
+-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+- const struct sunxi_ccu_desc *desc);
++int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
++ const struct sunxi_ccu_desc *desc);
++void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
++ const struct sunxi_ccu_desc *desc);
+
+ #endif /* _COMMON_H_ */
+--
+2.20.1
+
--- /dev/null
+From 98249d575dc3956e61214da83098cab4983aebd3 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 1 Sep 2021 00:05:20 -0500
+Subject: [PATCH 002/124] clk: sunxi-ng: Prevent unbinding CCUs via sysfs
+
+The CCU drivers are not really designed to be unbound. Unbinding a SoC's
+main CCU is especially pointless, as very few of the peripherals on the
+SoC will work without it. Let's avoid any potential problems by removing
+the bind/unbind attributes from sysfs for these drivers.
+
+This change is not applied to the "secondary" CCUs (DE, USB) as those
+could reasonably be unbound without making the system useless.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+Link: https://lore.kernel.org/r/20210901050526.45673-3-samuel@sholland.org
+---
+ drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun50i-a100.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun8i-r40.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c | 1 +
+ drivers/clk/sunxi-ng/ccu-sun9i-a80.c | 1 +
+ 8 files changed, 8 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+index 6f2a58970556..804729e0a208 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+@@ -208,6 +208,7 @@ static struct platform_driver sun50i_a100_r_ccu_driver = {
+ .probe = sun50i_a100_r_ccu_probe,
+ .driver = {
+ .name = "sun50i-a100-r-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun50i_a100_r_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+index 913bb08e6dee..1d475d5a3d91 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+@@ -1270,6 +1270,7 @@ static struct platform_driver sun50i_a100_ccu_driver = {
+ .probe = sun50i_a100_ccu_probe,
+ .driver = {
+ .name = "sun50i-a100-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun50i_a100_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+index 54f25c624f02..fcbd914e84e0 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+@@ -978,6 +978,7 @@ static struct platform_driver sun50i_a64_ccu_driver = {
+ .probe = sun50i_a64_ccu_probe,
+ .driver = {
+ .name = "sun50i-a64-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun50i_a64_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+index c0800da2fa3d..9a8902f702c5 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+@@ -1252,6 +1252,7 @@ static struct platform_driver sun50i_h6_ccu_driver = {
+ .probe = sun50i_h6_ccu_probe,
+ .driver = {
+ .name = "sun50i-h6-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun50i_h6_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+index c2ddcd2ddab4..e663ab0c9935 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+@@ -918,6 +918,7 @@ static struct platform_driver sun8i_a83t_ccu_driver = {
+ .probe = sun8i_a83t_ccu_probe,
+ .driver = {
+ .name = "sun8i-a83t-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun8i_a83t_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+index 002e0c3a04db..a2144ee728a0 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+@@ -1369,6 +1369,7 @@ static struct platform_driver sun8i_r40_ccu_driver = {
+ .probe = sun8i_r40_ccu_probe,
+ .driver = {
+ .name = "sun8i-r40-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun8i_r40_ccu_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+index 261e64416f26..d2072972b614 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+@@ -268,6 +268,7 @@ static struct platform_driver sun9i_a80_de_clk_driver = {
+ .probe = sun9i_a80_de_clk_probe,
+ .driver = {
+ .name = "sun9i-a80-de-clks",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun9i_a80_de_clk_ids,
+ },
+ };
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+index 97aaed0e6850..68b30fdc60fd 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+@@ -1243,6 +1243,7 @@ static struct platform_driver sun9i_a80_ccu_driver = {
+ .probe = sun9i_a80_ccu_probe,
+ .driver = {
+ .name = "sun9i-a80-ccu",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun9i_a80_ccu_ids,
+ },
+ };
+--
+2.20.1
+
--- /dev/null
+From 93395c038bc0a1750fec54eec531c5d36eec1aaf Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 1 Sep 2021 00:05:21 -0500
+Subject: [PATCH 003/124] clk: sunxi-ng: Use a separate lock for each CCU
+ instance
+
+Some platforms have more than one CCU driver loaded: the main CCU, the
+PRCM, the display engine, and possibly others. All of these hardware
+blocks have separate MMIO spaces, so there is no need to synchronize
+between them.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+Link: https://lore.kernel.org/r/20210901050526.45673-4-samuel@sholland.org
+---
+ drivers/clk/sunxi-ng/ccu_common.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
+index 88cb569e5835..31af8b6b5286 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.c
++++ b/drivers/clk/sunxi-ng/ccu_common.c
+@@ -17,11 +17,10 @@
+
+ struct sunxi_ccu {
+ const struct sunxi_ccu_desc *desc;
++ spinlock_t lock;
+ struct ccu_reset reset;
+ };
+
+-static DEFINE_SPINLOCK(ccu_lock);
+-
+ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
+ {
+ void __iomem *addr;
+@@ -94,6 +93,8 @@ static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+
+ ccu->desc = desc;
+
++ spin_lock_init(&ccu->lock);
++
+ for (i = 0; i < desc->num_ccu_clks; i++) {
+ struct ccu_common *cclk = desc->ccu_clks[i];
+
+@@ -101,7 +102,7 @@ static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+ continue;
+
+ cclk->base = reg;
+- cclk->lock = &ccu_lock;
++ cclk->lock = &ccu->lock;
+ }
+
+ for (i = 0; i < desc->hw_clks->num ; i++) {
+@@ -133,7 +134,7 @@ static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+ reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
+ reset->rcdev.nr_resets = desc->num_resets;
+ reset->base = reg;
+- reset->lock = &ccu_lock;
++ reset->lock = &ccu->lock;
+ reset->reset_map = desc->resets;
+
+ ret = reset_controller_register(&reset->rcdev);
+--
+2.20.1
+
--- /dev/null
+From 0dfc140602711a3d2a72cdc9bd24f2937b14ef01 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Tue, 28 Sep 2021 03:03:32 -0500
+Subject: [PATCH 004/124] rtc: sun6i: Allow probing without an early clock
+ provider
+
+Some SoCs have an RTC supported by this RTC driver, but do not have an
+early clock provider declared here. Currently, this prevents the RTC
+driver from probing, because it expects a global struct to already be
+allocated. Fix probing the driver by copying the missing pieces from the
+clock provider setup function, replacing them with the devm variants.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
+Link: https://lore.kernel.org/r/20210928080335.36706-7-samuel@sholland.org
+---
+ drivers/rtc/rtc-sun6i.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index adec1b14a8de..711832c758ae 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -673,8 +673,17 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
+ struct sun6i_rtc_dev *chip = sun6i_rtc;
+ int ret;
+
+- if (!chip)
+- return -ENODEV;
++ if (!chip) {
++ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
++ if (!chip)
++ return -ENOMEM;
++
++ spin_lock_init(&chip->lock);
++
++ chip->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(chip->base))
++ return PTR_ERR(chip->base);
++ }
+
+ platform_set_drvdata(pdev, chip);
+
+--
+2.20.1
+
--- /dev/null
+From cdbe9f3ed5fd2fa0395f317b81e653781c768d7e Mon Sep 17 00:00:00 2001
+From: Vitaly Wool <vitaly.wool@konsulko.com>
+Date: Mon, 11 Oct 2021 11:14:14 +0200
+Subject: [PATCH 005/124] riscv: remove .text section size limitation for XIP
+
+Currently there's a limit of 8MB for the .text section of a RISC-V
+image in the XIP case. This breaks compilation of many automatic
+builds and is generally inconvenient. This patch removes that
+limitation and optimizes XIP image file size at the same time.
+
+Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.com>
+Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
+---
+ arch/riscv/include/asm/pgtable.h | 6 ++++--
+ arch/riscv/kernel/head.S | 12 ++++++++++++
+ arch/riscv/kernel/vmlinux-xip.lds.S | 10 +++++++---
+ arch/riscv/mm/init.c | 7 +++----
+ 4 files changed, 26 insertions(+), 9 deletions(-)
+
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index 39b550310ec6..bf204e7c1f74 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -75,7 +75,8 @@
+ #endif
+
+ #ifdef CONFIG_XIP_KERNEL
+-#define XIP_OFFSET SZ_8M
++#define XIP_OFFSET SZ_32M
++#define XIP_OFFSET_MASK (SZ_32M - 1)
+ #else
+ #define XIP_OFFSET 0
+ #endif
+@@ -97,7 +98,8 @@
+ #ifdef CONFIG_XIP_KERNEL
+ #define XIP_FIXUP(addr) ({ \
+ uintptr_t __a = (uintptr_t)(addr); \
+- (__a >= CONFIG_XIP_PHYS_ADDR && __a < CONFIG_XIP_PHYS_ADDR + SZ_16M) ? \
++ (__a >= CONFIG_XIP_PHYS_ADDR && \
++ __a < CONFIG_XIP_PHYS_ADDR + XIP_OFFSET * 2) ? \
+ __a - CONFIG_XIP_PHYS_ADDR + CONFIG_PHYS_RAM_BASE - XIP_OFFSET :\
+ __a; \
+ })
+diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
+index 52c5ff9804c5..ebd577b51c5c 100644
+--- a/arch/riscv/kernel/head.S
++++ b/arch/riscv/kernel/head.S
+@@ -20,10 +20,20 @@
+ REG_L t0, _xip_fixup
+ add \reg, \reg, t0
+ .endm
++.macro XIP_FIXUP_FLASH_OFFSET reg
++ la t1, __data_loc
++ li t0, XIP_OFFSET_MASK
++ and t1, t1, t0
++ li t1, XIP_OFFSET
++ sub t0, t0, t1
++ sub \reg, \reg, t0
++.endm
+ _xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
+ #else
+ .macro XIP_FIXUP_OFFSET reg
+ .endm
++.macro XIP_FIXUP_FLASH_OFFSET reg
++.endm
+ #endif /* CONFIG_XIP_KERNEL */
+
+ __HEAD
+@@ -267,6 +277,7 @@ pmp_done:
+ la a3, hart_lottery
+ mv a2, a3
+ XIP_FIXUP_OFFSET a2
++ XIP_FIXUP_FLASH_OFFSET a3
+ lw t1, (a3)
+ amoswap.w t0, t1, (a2)
+ /* first time here if hart_lottery in RAM is not set */
+@@ -305,6 +316,7 @@ clear_bss_done:
+ XIP_FIXUP_OFFSET sp
+ #ifdef CONFIG_BUILTIN_DTB
+ la a0, __dtb_start
++ XIP_FIXUP_OFFSET a0
+ #else
+ mv a0, s1
+ #endif /* CONFIG_BUILTIN_DTB */
+diff --git a/arch/riscv/kernel/vmlinux-xip.lds.S b/arch/riscv/kernel/vmlinux-xip.lds.S
+index 9c9f35091ef0..f5ed08262139 100644
+--- a/arch/riscv/kernel/vmlinux-xip.lds.S
++++ b/arch/riscv/kernel/vmlinux-xip.lds.S
+@@ -64,8 +64,11 @@ SECTIONS
+ /*
+ * From this point, stuff is considered writable and will be copied to RAM
+ */
+- __data_loc = ALIGN(16); /* location in file */
+- . = LOAD_OFFSET + XIP_OFFSET; /* location in memory */
++ __data_loc = ALIGN(PAGE_SIZE); /* location in file */
++ . = KERNEL_LINK_ADDR + XIP_OFFSET; /* location in memory */
++
++#undef LOAD_OFFSET
++#define LOAD_OFFSET (KERNEL_LINK_ADDR + XIP_OFFSET - (__data_loc & XIP_OFFSET_MASK))
+
+ _sdata = .; /* Start of data section */
+ _data = .;
+@@ -96,7 +99,6 @@ SECTIONS
+ KEEP(*(__soc_builtin_dtb_table))
+ __soc_builtin_dtb_table_end = .;
+ }
+- PERCPU_SECTION(L1_CACHE_BYTES)
+
+ . = ALIGN(8);
+ .alternative : {
+@@ -122,6 +124,8 @@ SECTIONS
+
+ BSS_SECTION(PAGE_SIZE, PAGE_SIZE, 0)
+
++ PERCPU_SECTION(L1_CACHE_BYTES)
++
+ .rel.dyn : AT(ADDR(.rel.dyn) - LOAD_OFFSET) {
+ *(.rel.dyn*)
+ }
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index c0cddf0fc22d..24b2b8044602 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -41,7 +41,7 @@ phys_addr_t phys_ram_base __ro_after_init;
+ EXPORT_SYMBOL(phys_ram_base);
+
+ #ifdef CONFIG_XIP_KERNEL
+-extern char _xiprom[], _exiprom[];
++extern char _xiprom[], _exiprom[], __data_loc;
+ #endif
+
+ unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]
+@@ -454,10 +454,9 @@ static uintptr_t __init best_map_size(phys_addr_t base, phys_addr_t size)
+ /* called from head.S with MMU off */
+ asmlinkage void __init __copy_data(void)
+ {
+- void *from = (void *)(&_sdata);
+- void *end = (void *)(&_end);
++ void *from = (void *)(&__data_loc);
+ void *to = (void *)CONFIG_PHYS_RAM_BASE;
+- size_t sz = (size_t)(end - from + 1);
++ size_t sz = (size_t)((uintptr_t)(&_end) - (uintptr_t)(&_sdata));
+
+ memcpy(to, from, sz);
+ }
+--
+2.20.1
+
--- /dev/null
+From 2f0335228bf4bf224f5535464a940753c18af8f9 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Tue, 19 Oct 2021 10:40:45 +0100
+Subject: [PATCH 006/124] irq: simplify handle_domain_{irq,nmi}()
+
+There's no need for handle_domain_{irq,nmi}() to open-code the NULL
+check performed by handle_irq_desc(), nor the resolution of the desc
+performed by generic_handle_domain_irq().
+
+Use generic_handle_domain_irq() directly, as this is functioanlly
+equivalent and clearer. At the same time, delete the stale comments,
+which are no longer helpful.
+
+There should be no functional change as a result of this patch.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ kernel/irq/irqdesc.c | 24 ++++--------------------
+ 1 file changed, 4 insertions(+), 20 deletions(-)
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index 4e3c29bb603c..b07d0e1552bc 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -690,17 +690,11 @@ int handle_domain_irq(struct irq_domain *domain,
+ unsigned int hwirq, struct pt_regs *regs)
+ {
+ struct pt_regs *old_regs = set_irq_regs(regs);
+- struct irq_desc *desc;
+- int ret = 0;
++ int ret;
+
+ irq_enter();
+
+- /* The irqdomain code provides boundary checks */
+- desc = irq_resolve_mapping(domain, hwirq);
+- if (likely(desc))
+- handle_irq_desc(desc);
+- else
+- ret = -EINVAL;
++ ret = generic_handle_domain_irq(domain, hwirq);
+
+ irq_exit();
+ set_irq_regs(old_regs);
+@@ -721,24 +715,14 @@ int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
+ struct pt_regs *regs)
+ {
+ struct pt_regs *old_regs = set_irq_regs(regs);
+- struct irq_desc *desc;
+- int ret = 0;
++ int ret;
+
+ /*
+ * NMI context needs to be setup earlier in order to deal with tracing.
+ */
+ WARN_ON(!in_nmi());
+
+- desc = irq_resolve_mapping(domain, hwirq);
+-
+- /*
+- * ack_bad_irq is not NMI-safe, just report
+- * an invalid interrupt.
+- */
+- if (likely(desc))
+- handle_irq_desc(desc);
+- else
+- ret = -EINVAL;
++ ret = generic_handle_domain_irq(domain, hwirq);
+
+ set_irq_regs(old_regs);
+ return ret;
+--
+2.20.1
+
--- /dev/null
+From 5066cc5deaca0171b8b27079f81a924471be0e78 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Fri, 22 Oct 2021 15:35:04 +0100
+Subject: [PATCH 007/124] irq: unexport handle_irq_desc()
+
+There are no modular users of handle_irq_desc(). Remove the export
+before we gain any.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Suggested-by: Marc Zyngier <maz@kernel.org>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ kernel/irq/irqdesc.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index b07d0e1552bc..e25d4bddf3d8 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -646,7 +646,6 @@ int handle_irq_desc(struct irq_desc *desc)
+ generic_handle_irq_desc(desc);
+ return 0;
+ }
+-EXPORT_SYMBOL_GPL(handle_irq_desc);
+
+ /**
+ * generic_handle_irq - Invoke the handler for a particular irq
+--
+2.20.1
+
--- /dev/null
+From 48f86fb2e394a3daf227faaf2ada4b130d0da6ca Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Tue, 19 Oct 2021 11:12:31 +0100
+Subject: [PATCH 008/124] irq: add a (temporary)
+ CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY
+
+Going forward we want architecture/entry code to perform all the
+necessary work to enter/exit IRQ context, with irqchip code merely
+handling the mapping of the interrupt to any handler(s). Among other
+reasons, this is necessary to consistently fix some longstanding issues
+with the ordering of lockdep/RCU/tracing instrumentation which many
+architectures get wrong today in their entry code.
+
+Importantly, rcu_irq_{enter,exit}() must be called precisely once per
+IRQ exception, so that rcu_is_cpu_rrupt_from_idle() can correctly
+identify when an interrupt was taken from an idle context which must be
+explicitly preempted. Currently handle_domain_irq() calls
+rcu_irq_{enter,exit}() via irq_{enter,exit}(), but entry code needs to
+be able to call rcu_irq_{enter,exit}() earlier for correct ordering
+across lockdep/RCU/tracing updates for sequences such as:
+
+ lockdep_hardirqs_off(CALLER_ADDR0);
+ rcu_irq_enter();
+ trace_hardirqs_off_finish();
+
+To permit each architecture to be converted to the new style in turn,
+this patch adds a new CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY selected by all
+current users of HANDLE_DOMAIN_IRQ, which gates the existing behaviour.
+When CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY is not selected,
+handle_domain_irq() requires entry code to perform the
+irq_{enter,exit}() work, with an explicit check for this matching the
+style of handle_domain_nmi().
+
+Subsequent patches will:
+
+1) Add the necessary IRQ entry accounting to each architecture in turn,
+ dropping CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY from that architecture's
+ Kconfig.
+
+2) Remove CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY once it is no longer
+ selected.
+
+3) Convert irqchip drivers to consistently use
+ generic_handle_domain_irq() rather than handle_domain_irq().
+
+4) Remove handle_domain_irq() and CONFIG_HANDLE_DOMAIN_IRQ.
+
+... which should leave us with a clear split of responsiblity across the
+entry and irqchip code, making it possible to perform additional
+cleanups and fixes for the aforementioned longstanding issues with entry
+code.
+
+There should be no functional change as a result of this patch.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ arch/arm/Kconfig | 1 +
+ arch/arm64/Kconfig | 1 +
+ arch/csky/Kconfig | 1 +
+ arch/openrisc/Kconfig | 1 +
+ arch/riscv/Kconfig | 1 +
+ kernel/irq/Kconfig | 4 ++++
+ kernel/irq/irqdesc.c | 30 ++++++++++++++++++++++++++++++
+ 7 files changed, 39 insertions(+)
+
+diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
+index dcf2df6da98f..2a84c0c7e161 100644
+--- a/arch/arm/Kconfig
++++ b/arch/arm/Kconfig
+@@ -65,6 +65,7 @@ config ARM
+ select GENERIC_SCHED_CLOCK
+ select GENERIC_SMP_IDLE_THREAD
+ select HANDLE_DOMAIN_IRQ
++ select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select HARDIRQS_SW_RESEND
+ select HAVE_ARCH_AUDITSYSCALL if AEABI && !OABI_COMPAT
+ select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6
+diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
+index fee914c716aa..e7fbea796ed5 100644
+--- a/arch/arm64/Kconfig
++++ b/arch/arm64/Kconfig
+@@ -134,6 +134,7 @@ config ARM64
+ select GENERIC_GETTIMEOFDAY
+ select GENERIC_VDSO_TIME_NS
+ select HANDLE_DOMAIN_IRQ
++ select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select HARDIRQS_SW_RESEND
+ select HAVE_MOVE_PMD
+ select HAVE_MOVE_PUD
+diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig
+index 823d3d5a9e11..d8453dcff94e 100644
+--- a/arch/csky/Kconfig
++++ b/arch/csky/Kconfig
+@@ -18,6 +18,7 @@ config CSKY
+ select DMA_DIRECT_REMAP
+ select IRQ_DOMAIN
+ select HANDLE_DOMAIN_IRQ
++ select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select DW_APB_TIMER_OF
+ select GENERIC_IOREMAP
+ select GENERIC_LIB_ASHLDI3
+diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
+index e804026b4797..ed783a67065e 100644
+--- a/arch/openrisc/Kconfig
++++ b/arch/openrisc/Kconfig
+@@ -14,6 +14,7 @@ config OPENRISC
+ select OF_EARLY_FLATTREE
+ select IRQ_DOMAIN
+ select HANDLE_DOMAIN_IRQ
++ select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select GPIOLIB
+ select HAVE_ARCH_TRACEHOOK
+ select SPARSE_IRQ
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index f076cee11af6..ff159d5eb33e 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -63,6 +63,7 @@ config RISCV
+ select GENERIC_SMP_IDLE_THREAD
+ select GENERIC_TIME_VSYSCALL if MMU && 64BIT
+ select HANDLE_DOMAIN_IRQ
++ select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
+ select HAVE_ARCH_JUMP_LABEL_RELATIVE if !XIP_KERNEL
+diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
+index fbc54c2a7f23..897dfc552bb0 100644
+--- a/kernel/irq/Kconfig
++++ b/kernel/irq/Kconfig
+@@ -100,6 +100,10 @@ config IRQ_MSI_IOMMU
+ config HANDLE_DOMAIN_IRQ
+ bool
+
++# Legacy behaviour; architectures should call irq_{enter,exit}() themselves
++config HANDLE_DOMAIN_IRQ_IRQENTRY
++ bool
++
+ config IRQ_TIMINGS
+ bool
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index e25d4bddf3d8..5677a849cf1f 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -676,6 +676,7 @@ int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
+ EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
+
+ #ifdef CONFIG_HANDLE_DOMAIN_IRQ
++#ifdef CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY
+ /**
+ * handle_domain_irq - Invoke the handler for a HW irq belonging to a domain,
+ * usually for a root interrupt controller
+@@ -699,6 +700,35 @@ int handle_domain_irq(struct irq_domain *domain,
+ set_irq_regs(old_regs);
+ return ret;
+ }
++#else
++/**
++ * handle_domain_irq - Invoke the handler for a HW irq belonging to a domain,
++ * usually for a root interrupt controller
++ * @domain: The domain where to perform the lookup
++ * @hwirq: The HW irq number to convert to a logical one
++ * @regs: Register file coming from the low-level handling code
++ *
++ * This function must be called from an IRQ context.
++ *
++ * Returns: 0 on success, or -EINVAL if conversion has failed
++ */
++int handle_domain_irq(struct irq_domain *domain,
++ unsigned int hwirq, struct pt_regs *regs)
++{
++ struct pt_regs *old_regs = set_irq_regs(regs);
++ int ret;
++
++ /*
++ * IRQ context needs to be setup earlier.
++ */
++ WARN_ON(!in_irq());
++
++ ret = generic_handle_domain_irq(domain, hwirq);
++
++ set_irq_regs(old_regs);
++ return ret;
++}
++#endif
+
+ /**
+ * handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
+--
+2.20.1
+
--- /dev/null
+From 0a7b76c9ec957c83a5b2f4b06ec66220b4804c03 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Wed, 20 Oct 2021 15:49:03 +0100
+Subject: [PATCH 009/124] irq: remove CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY
+
+Now that all users of CONFIG_HANDLE_DOMAIN_IRQ perform the irq entry
+work themselves, we can remove the legacy
+CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY behaviour.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ kernel/irq/irqdesc.c | 26 --------------------------
+ 1 file changed, 26 deletions(-)
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index 5677a849cf1f..7041698a7bff 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -676,31 +676,6 @@ int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
+ EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
+
+ #ifdef CONFIG_HANDLE_DOMAIN_IRQ
+-#ifdef CONFIG_HANDLE_DOMAIN_IRQ_IRQENTRY
+-/**
+- * handle_domain_irq - Invoke the handler for a HW irq belonging to a domain,
+- * usually for a root interrupt controller
+- * @domain: The domain where to perform the lookup
+- * @hwirq: The HW irq number to convert to a logical one
+- * @regs: Register file coming from the low-level handling code
+- *
+- * Returns: 0 on success, or -EINVAL if conversion has failed
+- */
+-int handle_domain_irq(struct irq_domain *domain,
+- unsigned int hwirq, struct pt_regs *regs)
+-{
+- struct pt_regs *old_regs = set_irq_regs(regs);
+- int ret;
+-
+- irq_enter();
+-
+- ret = generic_handle_domain_irq(domain, hwirq);
+-
+- irq_exit();
+- set_irq_regs(old_regs);
+- return ret;
+-}
+-#else
+ /**
+ * handle_domain_irq - Invoke the handler for a HW irq belonging to a domain,
+ * usually for a root interrupt controller
+@@ -728,7 +703,6 @@ int handle_domain_irq(struct irq_domain *domain,
+ set_irq_regs(old_regs);
+ return ret;
+ }
+-#endif
+
+ /**
+ * handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
+--
+2.20.1
+
--- /dev/null
+From 226328e72b232a1702e29f8f6747c4d889d4425c Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Wed, 20 Oct 2021 11:24:06 +0100
+Subject: [PATCH 010/124] irq: add generic_handle_arch_irq()
+
+Several architectures select GENERIC_IRQ_MULTI_HANDLER and branch to
+handle_arch_irq() without performing any entry accounting.
+
+Add a generic wrapper to handle the common irqentry work when invoking
+handle_arch_irq(). Where an architecture needs to perform some entry
+accounting itself, it will need to invoke handle_arch_irq() itself.
+
+In subsequent patches it will become the responsibilty of the entry code
+to set the irq regs when entering an IRQ (rather than deferring this to
+an irqchip handler), so generic_handle_arch_irq() is made to set the irq
+regs now. This can be redundant in some cases, but is never harmful as
+saving/restoring the old regs nests safely.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Reviewed-by: Guo Ren <guoren@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ include/linux/irq.h | 1 +
+ kernel/irq/handle.c | 18 ++++++++++++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/include/linux/irq.h b/include/linux/irq.h
+index c8293c817646..988c225eef2d 100644
+--- a/include/linux/irq.h
++++ b/include/linux/irq.h
+@@ -1261,6 +1261,7 @@ int __init set_handle_irq(void (*handle_irq)(struct pt_regs *));
+ * top-level IRQ handler.
+ */
+ extern void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
++asmlinkage void generic_handle_arch_irq(struct pt_regs *regs);
+ #else
+ #ifndef set_handle_irq
+ #define set_handle_irq(handle_irq) \
+diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
+index 221d80c31e94..27182003b879 100644
+--- a/kernel/irq/handle.c
++++ b/kernel/irq/handle.c
+@@ -14,6 +14,8 @@
+ #include <linux/interrupt.h>
+ #include <linux/kernel_stat.h>
+
++#include <asm/irq_regs.h>
++
+ #include <trace/events/irq.h>
+
+ #include "internals.h"
+@@ -226,4 +228,20 @@ int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
+ handle_arch_irq = handle_irq;
+ return 0;
+ }
++
++/**
++ * generic_handle_arch_irq - root irq handler for architectures which do no
++ * entry accounting themselves
++ * @regs: Register file coming from the low-level handling code
++ */
++asmlinkage void noinstr generic_handle_arch_irq(struct pt_regs *regs)
++{
++ struct pt_regs *old_regs;
++
++ irq_enter();
++ old_regs = set_irq_regs(regs);
++ handle_arch_irq(regs);
++ set_irq_regs(old_regs);
++ irq_exit();
++}
+ #endif
+--
+2.20.1
+
--- /dev/null
+From ef1f47034f47324dfdb12e97253401438a9ff18d Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Wed, 20 Oct 2021 11:33:49 +0100
+Subject: [PATCH 011/124] irq: riscv: perform irqentry in entry code
+
+In preparation for removing HANDLE_DOMAIN_IRQ_IRQENTRY, have arch/riscv
+perform all the irqentry accounting in its entry code. As arch/riscv
+uses GENERIC_IRQ_MULTI_HANDLER, we can use generic_handle_arch_irq() to
+do so.
+
+Since generic_handle_arch_irq() handles the irq entry and setting the
+irq regs, and happens before the irqchip code calls handle_IPI(), we can
+remove the redundant irq entry and irq regs manipulation from
+handle_IPI().
+
+There should be no functional change as a result of this patch.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Guo Ren <guoren@kernel.org>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Albert Ou <aou@eecs.berkeley.edu>
+Cc: Palmer Dabbelt <palmer@dabbelt.com>
+Cc: Paul Walmsley <paul.walmsley@sifive.com>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ arch/riscv/Kconfig | 1 -
+ arch/riscv/kernel/entry.S | 3 +--
+ arch/riscv/kernel/smp.c | 9 +--------
+ 3 files changed, 2 insertions(+), 11 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index ff159d5eb33e..f076cee11af6 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -63,7 +63,6 @@ config RISCV
+ select GENERIC_SMP_IDLE_THREAD
+ select GENERIC_TIME_VSYSCALL if MMU && 64BIT
+ select HANDLE_DOMAIN_IRQ
+- select HANDLE_DOMAIN_IRQ_IRQENTRY
+ select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
+ select HAVE_ARCH_JUMP_LABEL_RELATIVE if !XIP_KERNEL
+diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
+index 98f502654edd..64236f7efde5 100644
+--- a/arch/riscv/kernel/entry.S
++++ b/arch/riscv/kernel/entry.S
+@@ -130,8 +130,7 @@ skip_context_tracking:
+
+ /* Handle interrupts */
+ move a0, sp /* pt_regs */
+- la a1, handle_arch_irq
+- REG_L a1, (a1)
++ la a1, generic_handle_arch_irq
+ jr a1
+ 1:
+ /*
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index 921d9d7df400..2f6da845c9ae 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -140,12 +140,9 @@ void arch_irq_work_raise(void)
+
+ void handle_IPI(struct pt_regs *regs)
+ {
+- struct pt_regs *old_regs = set_irq_regs(regs);
+ unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits;
+ unsigned long *stats = ipi_data[smp_processor_id()].stats;
+
+- irq_enter();
+-
+ riscv_clear_ipi();
+
+ while (true) {
+@@ -156,7 +153,7 @@ void handle_IPI(struct pt_regs *regs)
+
+ ops = xchg(pending_ipis, 0);
+ if (ops == 0)
+- goto done;
++ return;
+
+ if (ops & (1 << IPI_RESCHEDULE)) {
+ stats[IPI_RESCHEDULE]++;
+@@ -189,10 +186,6 @@ void handle_IPI(struct pt_regs *regs)
+ /* Order data access and bit testing. */
+ mb();
+ }
+-
+-done:
+- irq_exit();
+- set_irq_regs(old_regs);
+ }
+
+ static const char * const ipi_names[] = {
+--
+2.20.1
+
--- /dev/null
+From 1c0b62a931ffc5ef35421dc8410dcf5213a135f0 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Wed, 20 Oct 2021 20:23:09 +0100
+Subject: [PATCH 012/124] irq: remove handle_domain_{irq,nmi}()
+
+Now that entry code handles IRQ entry (including setting the IRQ regs)
+before calling irqchip code, irqchip code can safely call
+generic_handle_domain_irq(), and there's no functional reason for it to
+call handle_domain_irq().
+
+Let's cement this split of responsibility and remove handle_domain_irq()
+entirely, updating irqchip drivers to call generic_handle_domain_irq().
+
+For consistency, handle_domain_nmi() is similarly removed and replaced
+with a generic_handle_domain_nmi() function which also does not perform
+any entry logic.
+
+Previously handle_domain_{irq,nmi}() had a WARN_ON() which would fire
+when they were called in an inappropriate context. So that we can
+identify similar issues going forward, similar WARN_ON_ONCE() logic is
+added to the generic_handle_*() functions, and comments are updated for
+clarity and consistency.
+
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+---
+ arch/riscv/Kconfig | 1 -
+ drivers/irqchip/irq-riscv-intc.c | 2 +-
+ drivers/irqchip/irq-sun4i.c | 2 +-
+ include/linux/irqdesc.h | 9 +----
+ kernel/irq/Kconfig | 7 ----
+ kernel/irq/irqdesc.c | 68 ++++++++------------------------
+ 6 files changed, 20 insertions(+), 69 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index f076cee11af6..c28b743eba57 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -62,7 +62,6 @@ config RISCV
+ select GENERIC_SCHED_CLOCK
+ select GENERIC_SMP_IDLE_THREAD
+ select GENERIC_TIME_VSYSCALL if MMU && 64BIT
+- select HANDLE_DOMAIN_IRQ
+ select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
+ select HAVE_ARCH_JUMP_LABEL_RELATIVE if !XIP_KERNEL
+diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c
+index 8017f6d32d52..b65bd8878d4f 100644
+--- a/drivers/irqchip/irq-riscv-intc.c
++++ b/drivers/irqchip/irq-riscv-intc.c
+@@ -37,7 +37,7 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
+ break;
+ #endif
+ default:
+- handle_domain_irq(intc_domain, cause, regs);
++ generic_handle_domain_irq(intc_domain, cause);
+ break;
+ }
+ }
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index 8a315d6a3399..dd506ebfdacb 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -195,7 +195,7 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
+ return;
+
+ do {
+- handle_domain_irq(irq_ic_data->irq_domain, hwirq, regs);
++ generic_handle_domain_irq(irq_ic_data->irq_domain, hwirq);
+ hwirq = readl(irq_ic_data->irq_base +
+ SUN4I_IRQ_VECTOR_REG) >> 2;
+ } while (hwirq != 0);
+diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
+index 59aea39785bf..93d270ca0c56 100644
+--- a/include/linux/irqdesc.h
++++ b/include/linux/irqdesc.h
+@@ -168,14 +168,7 @@ int generic_handle_irq(unsigned int irq);
+ * conversion failed.
+ */
+ int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq);
+-
+-#ifdef CONFIG_HANDLE_DOMAIN_IRQ
+-int handle_domain_irq(struct irq_domain *domain,
+- unsigned int hwirq, struct pt_regs *regs);
+-
+-int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
+- struct pt_regs *regs);
+-#endif
++int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq);
+ #endif
+
+ /* Test to see if a driver has successfully requested an irq */
+diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
+index 897dfc552bb0..1b41078222f3 100644
+--- a/kernel/irq/Kconfig
++++ b/kernel/irq/Kconfig
+@@ -97,13 +97,6 @@ config GENERIC_MSI_IRQ_DOMAIN
+ config IRQ_MSI_IOMMU
+ bool
+
+-config HANDLE_DOMAIN_IRQ
+- bool
+-
+-# Legacy behaviour; architectures should call irq_{enter,exit}() themselves
+-config HANDLE_DOMAIN_IRQ_IRQENTRY
+- bool
+-
+ config IRQ_TIMINGS
+ bool
+
+diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
+index 7041698a7bff..2267e6527db3 100644
+--- a/kernel/irq/irqdesc.c
++++ b/kernel/irq/irqdesc.c
+@@ -651,7 +651,11 @@ int handle_irq_desc(struct irq_desc *desc)
+ * generic_handle_irq - Invoke the handler for a particular irq
+ * @irq: The irq number to handle
+ *
+- */
++ * Returns: 0 on success, or -EINVAL if conversion has failed
++ *
++ * This function must be called from an IRQ context with irq regs
++ * initialized.
++ */
+ int generic_handle_irq(unsigned int irq)
+ {
+ return handle_irq_desc(irq_to_desc(irq));
+@@ -661,77 +665,39 @@ EXPORT_SYMBOL_GPL(generic_handle_irq);
+ #ifdef CONFIG_IRQ_DOMAIN
+ /**
+ * generic_handle_domain_irq - Invoke the handler for a HW irq belonging
+- * to a domain, usually for a non-root interrupt
+- * controller
++ * to a domain.
+ * @domain: The domain where to perform the lookup
+ * @hwirq: The HW irq number to convert to a logical one
+ *
+ * Returns: 0 on success, or -EINVAL if conversion has failed
+ *
++ * This function must be called from an IRQ context with irq regs
++ * initialized.
+ */
+ int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
+ {
++ WARN_ON_ONCE(!in_irq());
+ return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
+ }
+ EXPORT_SYMBOL_GPL(generic_handle_domain_irq);
+
+-#ifdef CONFIG_HANDLE_DOMAIN_IRQ
+ /**
+- * handle_domain_irq - Invoke the handler for a HW irq belonging to a domain,
+- * usually for a root interrupt controller
++ * generic_handle_domain_nmi - Invoke the handler for a HW nmi belonging
++ * to a domain.
+ * @domain: The domain where to perform the lookup
+ * @hwirq: The HW irq number to convert to a logical one
+- * @regs: Register file coming from the low-level handling code
+- *
+- * This function must be called from an IRQ context.
+ *
+ * Returns: 0 on success, or -EINVAL if conversion has failed
+- */
+-int handle_domain_irq(struct irq_domain *domain,
+- unsigned int hwirq, struct pt_regs *regs)
+-{
+- struct pt_regs *old_regs = set_irq_regs(regs);
+- int ret;
+-
+- /*
+- * IRQ context needs to be setup earlier.
+- */
+- WARN_ON(!in_irq());
+-
+- ret = generic_handle_domain_irq(domain, hwirq);
+-
+- set_irq_regs(old_regs);
+- return ret;
+-}
+-
+-/**
+- * handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
+- * @domain: The domain where to perform the lookup
+- * @hwirq: The HW irq number to convert to a logical one
+- * @regs: Register file coming from the low-level handling code
+- *
+- * This function must be called from an NMI context.
+ *
+- * Returns: 0 on success, or -EINVAL if conversion has failed
+- */
+-int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
+- struct pt_regs *regs)
++ * This function must be called from an NMI context with irq regs
++ * initialized.
++ **/
++int generic_handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq)
+ {
+- struct pt_regs *old_regs = set_irq_regs(regs);
+- int ret;
+-
+- /*
+- * NMI context needs to be setup earlier in order to deal with tracing.
+- */
+- WARN_ON(!in_nmi());
+-
+- ret = generic_handle_domain_irq(domain, hwirq);
+-
+- set_irq_regs(old_regs);
+- return ret;
++ WARN_ON_ONCE(!in_nmi());
++ return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
+ }
+ #endif
+-#endif
+
+ /* Dynamic interrupt handling */
+
+--
+2.20.1
+
--- /dev/null
+From cbb36ef09be83f9e0fdd97d33d28e90292860814 Mon Sep 17 00:00:00 2001
+From: Cai Huoqing <caihuoqing@baidu.com>
+Date: Wed, 8 Sep 2021 15:17:15 +0800
+Subject: [PATCH 013/124] soc: sunxi_sram: Make use of the helper function
+ devm_platform_ioremap_resource()
+
+Use the devm_platform_ioremap_resource() helper instead of
+calling platform_get_resource() and devm_ioremap_resource()
+separately
+
+Signed-off-by: Cai Huoqing <caihuoqing@baidu.com>
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+Link: https://lore.kernel.org/r/20210908071716.772-1-caihuoqing@baidu.com
+---
+ drivers/soc/sunxi/sunxi_sram.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index 42833e33a96c..a8f3876963a0 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -331,7 +331,6 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = {
+
+ static int sunxi_sram_probe(struct platform_device *pdev)
+ {
+- struct resource *res;
+ struct dentry *d;
+ struct regmap *emac_clock;
+ const struct sunxi_sramc_variant *variant;
+@@ -342,8 +341,7 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ if (!variant)
+ return -EINVAL;
+
+- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- base = devm_ioremap_resource(&pdev->dev, res);
++ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+--
+2.20.1
+
--- /dev/null
+From cacc7b781648cdcfe9866a0709143e05789b6555 Mon Sep 17 00:00:00 2001
+From: Ondrej Jirman <megous@megous.com>
+Date: Tue, 22 Oct 2019 00:15:41 +0200
+Subject: [PATCH 014/124] input: sun4i-lradc-keys - Add wakup support
+
+Allow the driver to wake the system on key press if the "wakeup-source"
+property is provided in the device tree. Using the LRADC as a wakeup
+source requires keeping the AVCC domain active during sleep. Since this
+has a nontrivial impact on power consumption (sometimes doubling it),
+disable the LRADC wakeup source by default.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Ondrej Jirman <megous@megous.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/input/keyboard/sun4i-lradc-keys.c | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c
+index 4a796bed48ac..af1683d68c8c 100644
+--- a/drivers/input/keyboard/sun4i-lradc-keys.c
++++ b/drivers/input/keyboard/sun4i-lradc-keys.c
+@@ -22,6 +22,8 @@
+ #include <linux/module.h>
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
++#include <linux/pm_wakeirq.h>
++#include <linux/pm_wakeup.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+
+@@ -226,8 +228,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
+ {
+ struct sun4i_lradc_data *lradc;
+ struct device *dev = &pdev->dev;
+- int i;
+- int error;
++ int error, i, irq;
+
+ lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
+ if (!lradc)
+@@ -272,8 +273,11 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
+ if (IS_ERR(lradc->base))
+ return PTR_ERR(lradc->base);
+
+- error = devm_request_irq(dev, platform_get_irq(pdev, 0),
+- sun4i_lradc_irq, 0,
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return irq;
++
++ error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0,
+ "sun4i-a10-lradc-keys", lradc);
+ if (error)
+ return error;
+@@ -282,6 +286,14 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
+ if (error)
+ return error;
+
++ if (device_property_read_bool(dev, "wakeup-source")) {
++ device_set_wakeup_capable(dev, true);
++
++ error = dev_pm_set_wake_irq(dev, irq);
++ if (error)
++ dev_warn(dev, "Failed to set wake IRQ\n");
++ }
++
+ return 0;
+ }
+
+--
+2.20.1
+
--- /dev/null
+From 9dec9d2aec1607dffe516c901d137adb0e8839e1 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:04:52 -0500
+Subject: [PATCH 015/124] thermal: sun8i: Document the unknown field
+
+The H616 and newer user manuals document this field as the ADC
+acquisition time. Update the defintion and the timing calculations
+using the diagram in the manual.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/sun8i_thermal.c | 18 ++++++++----------
+ 1 file changed, 8 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
+index d9cd23cbb671..f05e70e280dd 100644
+--- a/drivers/thermal/sun8i_thermal.c
++++ b/drivers/thermal/sun8i_thermal.c
+@@ -50,7 +50,8 @@
+ #define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16)
+ #define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8)
+
+-#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16)
++#define SUN50I_THS_CTRL0_FS_DIV(x) ((GENMASK(15, 0) & (x)) << 16)
++#define SUN50I_THS_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x))
+ #define SUN50I_THS_FILTER_EN BIT(2)
+ #define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
+ #define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12)
+@@ -417,25 +418,22 @@ static int sun8i_h3_thermal_init(struct ths_device *tmdev)
+ return 0;
+ }
+
+-/*
+- * Without this undocumented value, the returned temperatures would
+- * be higher than real ones by about 20C.
+- */
+-#define SUN50I_H6_CTRL0_UNK 0x0000002f
+-
+ static int sun50i_h6_thermal_init(struct ths_device *tmdev)
+ {
+ int val;
+
+ /*
+- * T_acq = 20us
++ * T_sample = 20us > T_acq + T_conv
++ * T_acq = 2us
++ * T_conv = 14 cycles
+ * clkin = 24MHz
+ *
+- * x = T_acq * clkin - 1
++ * x = T_sample * clkin - 1
+ * = 479
+ */
+ regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
+- SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479));
++ SUN50I_THS_CTRL0_FS_DIV(479) |
++ SUN50I_THS_CTRL0_T_ACQ(47));
+ /* average over 4 samples */
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
+ SUN50I_THS_FILTER_EN |
+--
+2.20.1
+
--- /dev/null
+From 307dad7755974e3712d87b08972bff67ac9ce652 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 19:29:37 -0500
+Subject: [PATCH 016/124] thermal: sun8i: Set the event type for new samples
+
+Currently, an IRQ is only configured to trigger when a new sample is
+available. So report this event type to the thermal core.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/sun8i_thermal.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
+index f05e70e280dd..ddd21ef4e3eb 100644
+--- a/drivers/thermal/sun8i_thermal.c
++++ b/drivers/thermal/sun8i_thermal.c
+@@ -192,7 +192,7 @@ static irqreturn_t sun8i_irq_thread(int irq, void *data)
+
+ for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) {
+ thermal_zone_device_update(tmdev->sensor[i].tzd,
+- THERMAL_EVENT_UNSPECIFIED);
++ THERMAL_EVENT_TEMP_SAMPLE);
+ }
+
+ return IRQ_HANDLED;
+--
+2.20.1
+
--- /dev/null
+From 02ca2ba7daef709e1271dc5176c6efe7c635b15a Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 19:30:10 -0500
+Subject: [PATCH 017/124] thermal: sun8i: Use optional clock/reset getters
+
+The driver does not need to care about what variants have which clocks
+and resets; the devicetree binding already enforces that the necessary
+resources are provided. Simplify the logic by always calling the
+optional version of the getters, to pick up whatever references exist.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/sun8i_thermal.c | 34 +++++++++------------------------
+ 1 file changed, 9 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
+index ddd21ef4e3eb..be8064e89906 100644
+--- a/drivers/thermal/sun8i_thermal.c
++++ b/drivers/thermal/sun8i_thermal.c
+@@ -66,8 +66,6 @@ struct tsensor {
+ };
+
+ struct ths_thermal_chip {
+- bool has_mod_clk;
+- bool has_bus_clk_reset;
+ int sensor_num;
+ int offset;
+ int scale;
+@@ -335,21 +333,17 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
+ if (IS_ERR(tmdev->regmap))
+ return PTR_ERR(tmdev->regmap);
+
+- if (tmdev->chip->has_bus_clk_reset) {
+- tmdev->reset = devm_reset_control_get(dev, NULL);
+- if (IS_ERR(tmdev->reset))
+- return PTR_ERR(tmdev->reset);
++ tmdev->reset = devm_reset_control_get_optional(dev, NULL);
++ if (IS_ERR(tmdev->reset))
++ return PTR_ERR(tmdev->reset);
+
+- tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus");
+- if (IS_ERR(tmdev->bus_clk))
+- return PTR_ERR(tmdev->bus_clk);
+- }
++ tmdev->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
++ if (IS_ERR(tmdev->bus_clk))
++ return PTR_ERR(tmdev->bus_clk);
+
+- if (tmdev->chip->has_mod_clk) {
+- tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod");
+- if (IS_ERR(tmdev->mod_clk))
+- return PTR_ERR(tmdev->mod_clk);
+- }
++ tmdev->mod_clk = devm_clk_get_optional(&pdev->dev, "mod");
++ if (IS_ERR(tmdev->mod_clk))
++ return PTR_ERR(tmdev->mod_clk);
+
+ ret = reset_control_deassert(tmdev->reset);
+ if (ret)
+@@ -554,8 +548,6 @@ static const struct ths_thermal_chip sun8i_h3_ths = {
+ .sensor_num = 1,
+ .scale = 1211,
+ .offset = 217000,
+- .has_mod_clk = true,
+- .has_bus_clk_reset = true,
+ .temp_data_base = SUN8I_THS_TEMP_DATA,
+ .calibrate = sun8i_h3_ths_calibrate,
+ .init = sun8i_h3_thermal_init,
+@@ -567,8 +559,6 @@ static const struct ths_thermal_chip sun8i_r40_ths = {
+ .sensor_num = 2,
+ .offset = 251086,
+ .scale = 1130,
+- .has_mod_clk = true,
+- .has_bus_clk_reset = true,
+ .temp_data_base = SUN8I_THS_TEMP_DATA,
+ .calibrate = sun8i_h3_ths_calibrate,
+ .init = sun8i_h3_thermal_init,
+@@ -580,8 +570,6 @@ static const struct ths_thermal_chip sun50i_a64_ths = {
+ .sensor_num = 3,
+ .offset = 260890,
+ .scale = 1170,
+- .has_mod_clk = true,
+- .has_bus_clk_reset = true,
+ .temp_data_base = SUN8I_THS_TEMP_DATA,
+ .calibrate = sun8i_h3_ths_calibrate,
+ .init = sun8i_h3_thermal_init,
+@@ -591,7 +579,6 @@ static const struct ths_thermal_chip sun50i_a64_ths = {
+
+ static const struct ths_thermal_chip sun50i_a100_ths = {
+ .sensor_num = 3,
+- .has_bus_clk_reset = true,
+ .ft_deviation = 8000,
+ .offset = 187744,
+ .scale = 672,
+@@ -604,8 +591,6 @@ static const struct ths_thermal_chip sun50i_a100_ths = {
+
+ static const struct ths_thermal_chip sun50i_h5_ths = {
+ .sensor_num = 2,
+- .has_mod_clk = true,
+- .has_bus_clk_reset = true,
+ .temp_data_base = SUN8I_THS_TEMP_DATA,
+ .calibrate = sun8i_h3_ths_calibrate,
+ .init = sun8i_h3_thermal_init,
+@@ -615,7 +600,6 @@ static const struct ths_thermal_chip sun50i_h5_ths = {
+
+ static const struct ths_thermal_chip sun50i_h6_ths = {
+ .sensor_num = 2,
+- .has_bus_clk_reset = true,
+ .ft_deviation = 7000,
+ .offset = 187744,
+ .scale = 672,
+--
+2.20.1
+
--- /dev/null
+From 8bd42cf0216141eee71e303895a9e011f9951286 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 20:17:46 -0500
+Subject: [PATCH 018/124] thermal: sun8i: Ensure vref is powered
+
+On some boards, the supply for the reference voltage (AVCC) is not
+always on. For the thermal sensor to work, that supply must be powered.
+So add the necessary regulator consumer.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/sun8i_thermal.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
+index be8064e89906..fa88fd91f997 100644
+--- a/drivers/thermal/sun8i_thermal.c
++++ b/drivers/thermal/sun8i_thermal.c
+@@ -17,6 +17,7 @@
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
+ #include <linux/reset.h>
+ #include <linux/slab.h>
+ #include <linux/thermal.h>
+@@ -84,6 +85,7 @@ struct ths_device {
+ struct device *dev;
+ struct regmap *regmap;
+ struct reset_control *reset;
++ struct regulator *vref_supply;
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct tsensor sensor[MAX_SENSOR_NUM];
+@@ -333,6 +335,10 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
+ if (IS_ERR(tmdev->regmap))
+ return PTR_ERR(tmdev->regmap);
+
++ tmdev->vref_supply = devm_regulator_get(dev, "vref");
++ if (IS_ERR(tmdev->vref_supply))
++ return PTR_ERR(tmdev->vref_supply);
++
+ tmdev->reset = devm_reset_control_get_optional(dev, NULL);
+ if (IS_ERR(tmdev->reset))
+ return PTR_ERR(tmdev->reset);
+@@ -345,10 +351,14 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
+ if (IS_ERR(tmdev->mod_clk))
+ return PTR_ERR(tmdev->mod_clk);
+
+- ret = reset_control_deassert(tmdev->reset);
++ ret = regulator_enable(tmdev->vref_supply);
+ if (ret)
+ return ret;
+
++ ret = reset_control_deassert(tmdev->reset);
++ if (ret)
++ goto disable_vref_supply;
++
+ ret = clk_prepare_enable(tmdev->bus_clk);
+ if (ret)
+ goto assert_reset;
+@@ -373,6 +383,8 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
+ clk_disable_unprepare(tmdev->bus_clk);
+ assert_reset:
+ reset_control_assert(tmdev->reset);
++disable_vref_supply:
++ regulator_disable(tmdev->vref_supply);
+
+ return ret;
+ }
+@@ -529,6 +541,7 @@ static int sun8i_ths_remove(struct platform_device *pdev)
+ clk_disable_unprepare(tmdev->mod_clk);
+ clk_disable_unprepare(tmdev->bus_clk);
+ reset_control_assert(tmdev->reset);
++ regulator_disable(tmdev->vref_supply);
+
+ return 0;
+ }
+--
+2.20.1
+
--- /dev/null
+From 9e6cbc170439341082c5dfc36ac2d47a621152f2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:05:08 -0500
+Subject: [PATCH 019/124] thermal: sun8i: Add support for the D1 variant
+
+D1 has a thermal sensor block like other SoCs in the H6 generation.
+It has a single sensor channel and a unique temperature formula.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/sun8i_thermal.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
+index fa88fd91f997..1994e503bf15 100644
+--- a/drivers/thermal/sun8i_thermal.c
++++ b/drivers/thermal/sun8i_thermal.c
+@@ -579,6 +579,17 @@ static const struct ths_thermal_chip sun8i_r40_ths = {
+ .calc_temp = sun8i_ths_calc_temp,
+ };
+
++static const struct ths_thermal_chip sun20i_d1_ths = {
++ .sensor_num = 1,
++ .offset = 188147,
++ .scale = 672,
++ .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
++ .calibrate = sun50i_h6_ths_calibrate,
++ .init = sun50i_h6_thermal_init,
++ .irq_ack = sun50i_h6_irq_ack,
++ .calc_temp = sun8i_ths_calc_temp,
++};
++
+ static const struct ths_thermal_chip sun50i_a64_ths = {
+ .sensor_num = 3,
+ .offset = 260890,
+@@ -627,6 +638,7 @@ static const struct of_device_id of_ths_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths },
+ { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
+ { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths },
++ { .compatible = "allwinner,sun20i-d1-ths", .data = &sun20i_d1_ths },
+ { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths },
+ { .compatible = "allwinner,sun50i-a100-ths", .data = &sun50i_a100_ths },
+ { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths },
+--
+2.20.1
+
--- /dev/null
+From 617298fc711e9f5611dbfb44622cef4b330c66f0 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 13 Nov 2021 11:12:14 -0600
+Subject: [PATCH 020/124] ASoC: sun4i-spdif: Assert reset when removing the
+ device
+
+This completes reversing the process done in the probe function.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-spdif.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index a10949bf0ca1..ab42df6da8d8 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -589,10 +589,14 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
+
+ static int sun4i_spdif_remove(struct platform_device *pdev)
+ {
++ struct sun4i_spdif_dev *host = dev_get_drvdata(&pdev->dev);
++
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ sun4i_spdif_runtime_suspend(&pdev->dev);
+
++ reset_control_assert(host->rst);
++
+ return 0;
+ }
+
+--
+2.20.1
+
--- /dev/null
+From 5ca31032fb45e99ead74963601492fce57a261b6 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 13 Nov 2021 11:14:30 -0600
+Subject: [PATCH 021/124] ASoC: sun4i-spdif: Simplify code around optional
+ resets
+
+The driver does not need to care about which variants have a reset;
+the devicetree binding already enforces that the necessary resources are
+provided. Simplify the logic by always calling the optional getter,
+which will return NULL if no reset reference is found.
+
+Also clean up the error handling, which should not print a misleading
+error in the EPROBE_DEFER case.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-spdif.c | 22 ++++++----------------
+ 1 file changed, 6 insertions(+), 16 deletions(-)
+
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index ab42df6da8d8..a0255802be5f 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -168,12 +168,10 @@
+ * struct sun4i_spdif_quirks - Differences between SoC variants.
+ *
+ * @reg_dac_txdata: TX FIFO offset for DMA config.
+- * @has_reset: SoC needs reset deasserted.
+ * @val_fctl_ftx: TX FIFO flush bitmask.
+ */
+ struct sun4i_spdif_quirks {
+ unsigned int reg_dac_txdata;
+- bool has_reset;
+ unsigned int val_fctl_ftx;
+ };
+
+@@ -432,19 +430,16 @@ static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
+ static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
+ .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+- .has_reset = true,
+ };
+
+ static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
+ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+- .has_reset = true,
+ };
+
+ static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = {
+ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX,
+- .has_reset = true,
+ };
+
+ static const struct of_device_id sun4i_spdif_of_match[] = {
+@@ -551,17 +546,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, host);
+
+- if (quirks->has_reset) {
+- host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
+- NULL);
+- if (PTR_ERR(host->rst) == -EPROBE_DEFER) {
+- ret = -EPROBE_DEFER;
+- dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
+- return ret;
+- }
+- if (!IS_ERR(host->rst))
+- reset_control_deassert(host->rst);
+- }
++ host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
++ if (IS_ERR(host->rst))
++ return dev_err_probe(&pdev->dev, PTR_ERR(host->rst),
++ "Failed to get reset\n");
++
++ reset_control_deassert(host->rst);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_spdif_component, &sun4i_spdif_dai, 1);
+--
+2.20.1
+
--- /dev/null
+From 3153907498e377c97f0eb3026a87b6834e48b166 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:53:16 -0500
+Subject: [PATCH 022/124] ASoC: sun4i-spdif: Add support for separate RX/TX
+ clocks
+
+On older variants of the hardware, the RX and TX blocks share a single
+module clock, named "spdif" in the DT binding. The D1 variant has
+separate RX and TX clocks, so the TX module clock is named "tx" in the
+binding. To support this, supply the clock name in the quirks structure.
+
+Since the driver supports only TX, only the TX clock name is needed.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-spdif.c | 24 +++++++++++++++---------
+ 1 file changed, 15 insertions(+), 9 deletions(-)
+
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index a0255802be5f..748fb048a1f8 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -167,18 +167,20 @@
+ /**
+ * struct sun4i_spdif_quirks - Differences between SoC variants.
+ *
++ * @tx_clk_name: firmware name for the TX clock reference.
+ * @reg_dac_txdata: TX FIFO offset for DMA config.
+ * @val_fctl_ftx: TX FIFO flush bitmask.
+ */
+ struct sun4i_spdif_quirks {
++ const char *tx_clk_name;
+ unsigned int reg_dac_txdata;
+ unsigned int val_fctl_ftx;
+ };
+
+ struct sun4i_spdif_dev {
+ struct platform_device *pdev;
+- struct clk *spdif_clk;
+ struct clk *apb_clk;
++ struct clk *tx_clk;
+ struct reset_control *rst;
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct regmap *regmap;
+@@ -310,7 +312,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream,
+ return -EINVAL;
+ }
+
+- ret = clk_set_rate(host->spdif_clk, mclk);
++ ret = clk_set_rate(host->tx_clk, mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Setting SPDIF clock rate for %d Hz failed!\n", mclk);
+@@ -423,21 +425,25 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = {
+ };
+
+ static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = {
++ .tx_clk_name = "spdif",
+ .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+ };
+
+ static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = {
++ .tx_clk_name = "spdif",
+ .reg_dac_txdata = SUN4I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+ };
+
+ static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
++ .tx_clk_name = "spdif",
+ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+ };
+
+ static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = {
++ .tx_clk_name = "spdif",
+ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+ .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX,
+ };
+@@ -471,7 +477,7 @@ static int sun4i_spdif_runtime_suspend(struct device *dev)
+ {
+ struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
+
+- clk_disable_unprepare(host->spdif_clk);
++ clk_disable_unprepare(host->tx_clk);
+ clk_disable_unprepare(host->apb_clk);
+
+ return 0;
+@@ -482,12 +488,12 @@ static int sun4i_spdif_runtime_resume(struct device *dev)
+ struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
+ int ret;
+
+- ret = clk_prepare_enable(host->spdif_clk);
++ ret = clk_prepare_enable(host->tx_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(host->apb_clk);
+ if (ret)
+- clk_disable_unprepare(host->spdif_clk);
++ clk_disable_unprepare(host->tx_clk);
+
+ return ret;
+ }
+@@ -534,10 +540,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
+ return PTR_ERR(host->apb_clk);
+ }
+
+- host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
+- if (IS_ERR(host->spdif_clk)) {
+- dev_err(&pdev->dev, "failed to get a spdif clock.\n");
+- return PTR_ERR(host->spdif_clk);
++ host->tx_clk = devm_clk_get(&pdev->dev, quirks->tx_clk_name);
++ if (IS_ERR(host->tx_clk)) {
++ dev_err(&pdev->dev, "failed to get TX module clock.\n");
++ return PTR_ERR(host->tx_clk);
+ }
+
+ host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata;
+--
+2.20.1
+
--- /dev/null
+From 1fb53b6fb639f45d2ba4bf2a885e3fd5fbe95d8c Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:53:26 -0500
+Subject: [PATCH 023/124] ASoC: sun4i-spdif: Add support for the D1 variant
+
+The D1 variant is similar to the H6 variant, except for its clock setup.
+The clock tree changes impact some register fields on the RX side, but
+those are not yet relevant, because RX is not supported by this driver.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-spdif.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index 748fb048a1f8..7c6d596da84f 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -442,6 +442,12 @@ static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = {
+ .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX,
+ };
+
++static const struct sun4i_spdif_quirks sun20i_d1_spdif_quirks = {
++ .tx_clk_name = "tx",
++ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
++ .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX,
++};
++
+ static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = {
+ .tx_clk_name = "spdif",
+ .reg_dac_txdata = SUN8I_SPDIF_TXFIFO,
+@@ -461,6 +467,10 @@ static const struct of_device_id sun4i_spdif_of_match[] = {
+ .compatible = "allwinner,sun8i-h3-spdif",
+ .data = &sun8i_h3_spdif_quirks,
+ },
++ {
++ .compatible = "allwinner,sun20i-d1-spdif",
++ .data = &sun20i_d1_spdif_quirks,
++ },
+ {
+ .compatible = "allwinner,sun50i-h6-spdif",
+ .data = &sun50i_h6_spdif_quirks,
+--
+2.20.1
+
--- /dev/null
+From d69154525ee615f7f24b772658416f3a1093df23 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:43:20 -0500
+Subject: [PATCH 024/124] nvmem: sunxi_sid: Add support for D1 variant
+
+D1 has a smaller eFuse block than some other recent SoCs, and it no
+longer requires a workaround to read the eFuse data.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/nvmem/sunxi_sid.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
+index 275b9155e473..5750e1f4bcdb 100644
+--- a/drivers/nvmem/sunxi_sid.c
++++ b/drivers/nvmem/sunxi_sid.c
+@@ -184,6 +184,11 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = {
+ .need_register_readout = true,
+ };
+
++static const struct sunxi_sid_cfg sun20i_d1_cfg = {
++ .value_offset = 0x200,
++ .size = 0x100,
++};
++
+ static const struct sunxi_sid_cfg sun50i_a64_cfg = {
+ .value_offset = 0x200,
+ .size = 0x100,
+@@ -200,6 +205,7 @@ static const struct of_device_id sunxi_sid_of_match[] = {
+ { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg },
+ { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg },
++ { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg },
+ { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg },
+--
+2.20.1
+
--- /dev/null
+From 92e9840b41f64e5f98bb846b8db061276898e791 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 09:30:39 -0500
+Subject: [PATCH 025/124] Input: sun4i-lradc-keys: Add optional clock/reset
+ support
+
+Until the R329, the LRADC hardware was always active. Now it requires
+enabling a clock gate and deasserting a reset line. Do this if the clock
+and reset are provided in the device tree, but keep them optional to
+maintain support for the existing binding.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/input/keyboard/sun4i-lradc-keys.c | 29 +++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c
+index af1683d68c8c..cf8813d56208 100644
+--- a/drivers/input/keyboard/sun4i-lradc-keys.c
++++ b/drivers/input/keyboard/sun4i-lradc-keys.c
+@@ -14,6 +14,7 @@
+ * there are no boards known to use channel 1.
+ */
+
++#include <linux/clk.h>
+ #include <linux/err.h>
+ #include <linux/init.h>
+ #include <linux/input.h>
+@@ -25,6 +26,7 @@
+ #include <linux/pm_wakeirq.h>
+ #include <linux/pm_wakeup.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/reset.h>
+ #include <linux/slab.h>
+
+ #define LRADC_CTRL 0x00
+@@ -85,6 +87,8 @@ struct sun4i_lradc_data {
+ struct device *dev;
+ struct input_dev *input;
+ void __iomem *base;
++ struct clk *clk;
++ struct reset_control *reset;
+ struct regulator *vref_supply;
+ struct sun4i_lradc_keymap *chan0_map;
+ const struct lradc_variant *variant;
+@@ -142,6 +146,14 @@ static int sun4i_lradc_open(struct input_dev *dev)
+ if (error)
+ return error;
+
++ error = reset_control_deassert(lradc->reset);
++ if (error)
++ goto err_disable_reg;
++
++ error = clk_prepare_enable(lradc->clk);
++ if (error)
++ goto err_assert_reset;
++
+ lradc->vref = regulator_get_voltage(lradc->vref_supply) *
+ lradc->variant->divisor_numerator /
+ lradc->variant->divisor_denominator;
+@@ -155,6 +167,13 @@ static int sun4i_lradc_open(struct input_dev *dev)
+ writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
+
+ return 0;
++
++err_assert_reset:
++ reset_control_assert(lradc->reset);
++err_disable_reg:
++ regulator_disable(lradc->vref_supply);
++
++ return error;
+ }
+
+ static void sun4i_lradc_close(struct input_dev *dev)
+@@ -166,6 +185,8 @@ static void sun4i_lradc_close(struct input_dev *dev)
+ SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
+ writel(0, lradc->base + LRADC_INTC);
+
++ clk_disable_unprepare(lradc->clk);
++ reset_control_assert(lradc->reset);
+ regulator_disable(lradc->vref_supply);
+ }
+
+@@ -244,6 +265,14 @@ static int sun4i_lradc_probe(struct platform_device *pdev)
+ return -EINVAL;
+ }
+
++ lradc->clk = devm_clk_get_optional(dev, NULL);
++ if (IS_ERR(lradc->clk))
++ return PTR_ERR(lradc->clk);
++
++ lradc->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
++ if (IS_ERR(lradc->reset))
++ return PTR_ERR(lradc->reset);
++
+ lradc->vref_supply = devm_regulator_get(dev, "vref");
+ if (IS_ERR(lradc->vref_supply))
+ return PTR_ERR(lradc->vref_supply);
+--
+2.20.1
+
--- /dev/null
+From a592f18da0fa6a1cdcc3b06302c78d0b32f20d62 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 09:31:10 -0500
+Subject: [PATCH 026/124] Input: sun4i-lradc-keys: Add support for R329 and D1
+
+This LRADC variant uses the same 3/4*AVCC reference voltage as the A83T
+variant. The R329 and D1 LRADCs appear to be identical, so D1 support is
+accomplished through having the R329 LRADC as a fallback compatible.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/input/keyboard/sun4i-lradc-keys.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c
+index cf8813d56208..bec53c7a89b1 100644
+--- a/drivers/input/keyboard/sun4i-lradc-keys.c
++++ b/drivers/input/keyboard/sun4i-lradc-keys.c
+@@ -331,6 +331,8 @@ static const struct of_device_id sun4i_lradc_of_match[] = {
+ .data = &lradc_variant_a10 },
+ { .compatible = "allwinner,sun8i-a83t-r-lradc",
+ .data = &r_lradc_variant_a83t },
++ { .compatible = "allwinner,sun50i-r329-lradc",
++ .data = &r_lradc_variant_a83t },
+ { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
+--
+2.20.1
+
--- /dev/null
+From 929bb1c60474dc25b8c8f2d855ee704e16fe97b9 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 26 Jun 2021 11:02:49 -0500
+Subject: [PATCH 027/124] leds: sunxi: New driver for the R329/D1 LED
+ controller
+
+Some Allwinner sunxi SoCs, starting with the R329, contain an LED
+controller designed to drive RGB LED pixels. Add a driver for it using
+the multicolor LED framework, and with LEDs defined in the device tree.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/leds/Kconfig | 8 +
+ drivers/leds/Makefile | 1 +
+ drivers/leds/leds-sun50i-r329.c | 553 ++++++++++++++++++++++++++++++++
+ 3 files changed, 562 insertions(+)
+ create mode 100644 drivers/leds/leds-sun50i-r329.c
+
+diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
+index ed800f5da7d8..d5c1396788fe 100644
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -297,6 +297,14 @@ config LEDS_SUNFIRE
+ This option enables support for the Left, Middle, and Right
+ LEDs on the I/O and CPU boards of SunFire UltraSPARC servers.
+
++config LEDS_SUN50I_R329
++ tristate "LED support for Allwinner R329 LED controller"
++ depends on LEDS_CLASS
++ depends on ARCH_SUNXI || COMPILE_TEST
++ help
++ This option enables support for the RGB LED controller
++ provided in some Allwinner sunxi SoCs, like the R329.
++
+ config LEDS_IPAQ_MICRO
+ tristate "LED Support for the Compaq iPAQ h3xxx"
+ depends on LEDS_CLASS
+diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
+index c636ec069612..506b2617099d 100644
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
+ obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
+ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
+ obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
++obj-$(CONFIG_LEDS_SUN50I_R329) += leds-sun50i-r329.o
+ obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
+ obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
+ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
+diff --git a/drivers/leds/leds-sun50i-r329.c b/drivers/leds/leds-sun50i-r329.c
+new file mode 100644
+index 000000000000..ada6625e2bbc
+--- /dev/null
++++ b/drivers/leds/leds-sun50i-r329.c
+@@ -0,0 +1,553 @@
++// SPDX-License-Identifier: GPL-2.0
++//
++// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
++//
++// Partly based on drivers/leds/leds-turris-omnia.c, which is:
++// Copyright (c) 2020 by Marek Behún <kabel@kernel.org>
++//
++
++#include <linux/clk.h>
++#include <linux/dma-mapping.h>
++#include <linux/dmaengine.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/led-class-multicolor.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/reset.h>
++#include <linux/spinlock.h>
++
++#define LEDC_CTRL_REG 0x0000
++#define LEDC_CTRL_REG_DATA_LENGTH (0x1fff << 16)
++#define LEDC_CTRL_REG_RGB_MODE (0x7 << 6)
++#define LEDC_CTRL_REG_LEDC_EN BIT(0)
++#define LEDC_T01_TIMING_CTRL_REG 0x0004
++#define LEDC_T01_TIMING_CTRL_REG_T1H (0x3f << 21)
++#define LEDC_T01_TIMING_CTRL_REG_T1L (0x1f << 16)
++#define LEDC_T01_TIMING_CTRL_REG_T0H (0x1f << 6)
++#define LEDC_T01_TIMING_CTRL_REG_T0L (0x3f << 0)
++#define LEDC_RESET_TIMING_CTRL_REG 0x000c
++#define LEDC_RESET_TIMING_CTRL_REG_LED_NUM (0x3ff << 0)
++#define LEDC_DATA_REG 0x0014
++#define LEDC_DMA_CTRL_REG 0x0018
++#define LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL (0x1f << 0)
++#define LEDC_INT_CTRL_REG 0x001c
++#define LEDC_INT_CTRL_REG_GLOBAL_INT_EN BIT(5)
++#define LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN BIT(1)
++#define LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN BIT(0)
++#define LEDC_INT_STS_REG 0x0020
++#define LEDC_INT_STS_REG_FIFO_CPUREQ_INT BIT(1)
++#define LEDC_INT_STS_REG_TRANS_FINISH_INT BIT(0)
++
++#define LEDC_FIFO_DEPTH 32
++#define LEDC_MAX_LEDS 1024
++
++#define LEDS_TO_BYTES(n) ((n) * sizeof(u32))
++
++struct sun50i_r329_ledc_led {
++ struct led_classdev_mc mc_cdev;
++ struct mc_subled subled_info[3];
++};
++#define to_ledc_led(mc) container_of(mc, struct sun50i_r329_ledc_led, mc_cdev)
++
++struct sun50i_r329_ledc_timing {
++ u32 t0h_ns;
++ u32 t0l_ns;
++ u32 t1h_ns;
++ u32 t1l_ns;
++ u32 treset_ns;
++};
++
++struct sun50i_r329_ledc {
++ struct device *dev;
++ void __iomem *base;
++ struct clk *bus_clk;
++ struct clk *mod_clk;
++ struct reset_control *reset;
++
++ u32 *buffer;
++ struct dma_chan *dma_chan;
++ dma_addr_t dma_handle;
++ int pio_length;
++ int pio_offset;
++
++ spinlock_t lock;
++ int next_length;
++ bool xfer_active;
++
++ u32 format;
++ struct sun50i_r329_ledc_timing timing;
++
++ int num_leds;
++ struct sun50i_r329_ledc_led leds[];
++};
++
++static int sun50i_r329_ledc_dma_xfer(struct sun50i_r329_ledc *priv, int length)
++{
++ struct dma_async_tx_descriptor *desc;
++ dma_cookie_t cookie;
++
++ desc = dmaengine_prep_slave_single(priv->dma_chan, priv->dma_handle,
++ LEDS_TO_BYTES(length),
++ DMA_MEM_TO_DEV, 0);
++ if (!desc)
++ return -ENOMEM;
++
++ cookie = dmaengine_submit(desc);
++ if (dma_submit_error(cookie))
++ return -EIO;
++
++ dma_async_issue_pending(priv->dma_chan);
++
++ return 0;
++}
++
++static void sun50i_r329_ledc_pio_xfer(struct sun50i_r329_ledc *priv, int length)
++{
++ u32 burst, offset, val;
++
++ if (length) {
++ /* New transfer (FIFO is empty). */
++ offset = 0;
++ burst = min(length, LEDC_FIFO_DEPTH);
++ } else {
++ /* Existing transfer (FIFO is half-full). */
++ length = priv->pio_length;
++ offset = priv->pio_offset;
++ burst = min(length, LEDC_FIFO_DEPTH / 2);
++ }
++
++ iowrite32_rep(priv->base + LEDC_DATA_REG, priv->buffer + offset, burst);
++
++ if (burst < length) {
++ priv->pio_length = length - burst;
++ priv->pio_offset = offset + burst;
++
++ if (!offset) {
++ val = readl(priv->base + LEDC_INT_CTRL_REG);
++ val |= LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN;
++ writel(val, priv->base + LEDC_INT_CTRL_REG);
++ }
++ } else {
++ /* Disable the request IRQ once all data is written. */
++ val = readl(priv->base + LEDC_INT_CTRL_REG);
++ val &= ~LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN;
++ writel(val, priv->base + LEDC_INT_CTRL_REG);
++ }
++}
++
++static void sun50i_r329_ledc_start_xfer(struct sun50i_r329_ledc *priv,
++ int length)
++{
++ u32 val;
++
++ dev_dbg(priv->dev, "Updating %d LEDs\n", length);
++
++ val = readl(priv->base + LEDC_CTRL_REG);
++ val &= ~LEDC_CTRL_REG_DATA_LENGTH;
++ val |= length << 16 | LEDC_CTRL_REG_LEDC_EN;
++ writel(val, priv->base + LEDC_CTRL_REG);
++
++ if (length > LEDC_FIFO_DEPTH) {
++ int ret = sun50i_r329_ledc_dma_xfer(priv, length);
++
++ if (!ret)
++ return;
++
++ dev_warn(priv->dev, "Failed to set up DMA: %d\n", ret);
++ }
++
++ sun50i_r329_ledc_pio_xfer(priv, length);
++}
++
++static irqreturn_t sun50i_r329_ledc_irq(int irq, void *dev_id)
++{
++ struct sun50i_r329_ledc *priv = dev_id;
++ u32 val;
++
++ val = readl(priv->base + LEDC_INT_STS_REG);
++
++ if (val & LEDC_INT_STS_REG_TRANS_FINISH_INT) {
++ int next_length;
++
++ /* Start the next transfer if needed. */
++ spin_lock(&priv->lock);
++ next_length = priv->next_length;
++ if (next_length)
++ priv->next_length = 0;
++ else
++ priv->xfer_active = false;
++ spin_unlock(&priv->lock);
++
++ if (next_length)
++ sun50i_r329_ledc_start_xfer(priv, next_length);
++ } else if (val & LEDC_INT_STS_REG_FIFO_CPUREQ_INT) {
++ /* Continue the current transfer. */
++ sun50i_r329_ledc_pio_xfer(priv, 0);
++ }
++
++ writel(val, priv->base + LEDC_INT_STS_REG);
++
++ return IRQ_HANDLED;
++}
++
++static void sun50i_r329_ledc_brightness_set(struct led_classdev *cdev,
++ enum led_brightness brightness)
++{
++ struct sun50i_r329_ledc *priv = dev_get_drvdata(cdev->dev->parent);
++ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev);
++ struct sun50i_r329_ledc_led *led = to_ledc_led(mc_cdev);
++ int addr = led - priv->leds;
++ unsigned long flags;
++ bool xfer_active;
++ int next_length;
++
++ led_mc_calc_color_components(mc_cdev, brightness);
++
++ priv->buffer[addr] = led->subled_info[0].brightness << 16 |
++ led->subled_info[1].brightness << 8 |
++ led->subled_info[2].brightness;
++
++ dev_dbg(priv->dev, "LED %d -> #%06x\n", addr, priv->buffer[addr]);
++
++ spin_lock_irqsave(&priv->lock, flags);
++ next_length = max(priv->next_length, addr + 1);
++ xfer_active = priv->xfer_active;
++ if (xfer_active)
++ priv->next_length = next_length;
++ else
++ priv->xfer_active = true;
++ spin_unlock_irqrestore(&priv->lock, flags);
++
++ if (!xfer_active)
++ sun50i_r329_ledc_start_xfer(priv, next_length);
++}
++
++static const char *const sun50i_r329_ledc_formats[] = {
++ "rgb",
++ "rbg",
++ "grb",
++ "gbr",
++ "brg",
++ "bgr",
++};
++
++static int sun50i_r329_ledc_parse_format(const struct device_node *np,
++ struct sun50i_r329_ledc *priv)
++{
++ const char *format = "grb";
++ u32 i;
++
++ of_property_read_string(np, "allwinner,pixel-format", &format);
++
++ for (i = 0; i < ARRAY_SIZE(sun50i_r329_ledc_formats); ++i) {
++ if (!strcmp(format, sun50i_r329_ledc_formats[i])) {
++ priv->format = i;
++ return 0;
++ }
++ }
++
++ dev_err(priv->dev, "Bad pixel format '%s'\n", format);
++
++ return -EINVAL;
++}
++
++static void sun50i_r329_ledc_set_format(struct sun50i_r329_ledc *priv)
++{
++ u32 val;
++
++ val = readl(priv->base + LEDC_CTRL_REG);
++ val &= ~LEDC_CTRL_REG_RGB_MODE;
++ val |= priv->format << 6;
++ writel(val, priv->base + LEDC_CTRL_REG);
++}
++
++static const struct sun50i_r329_ledc_timing sun50i_r329_ledc_default_timing = {
++ .t0h_ns = 336,
++ .t0l_ns = 840,
++ .t1h_ns = 882,
++ .t1l_ns = 294,
++ .treset_ns = 300000,
++};
++
++static int sun50i_r329_ledc_parse_timing(const struct device_node *np,
++ struct sun50i_r329_ledc *priv)
++{
++ struct sun50i_r329_ledc_timing *timing = &priv->timing;
++
++ *timing = sun50i_r329_ledc_default_timing;
++
++ of_property_read_u32(np, "allwinner,t0h-ns", &timing->t0h_ns);
++ of_property_read_u32(np, "allwinner,t0l-ns", &timing->t0l_ns);
++ of_property_read_u32(np, "allwinner,t1h-ns", &timing->t1h_ns);
++ of_property_read_u32(np, "allwinner,t1l-ns", &timing->t1l_ns);
++ of_property_read_u32(np, "allwinner,treset-ns", &timing->treset_ns);
++
++ return 0;
++}
++
++static void sun50i_r329_ledc_set_timing(struct sun50i_r329_ledc *priv)
++{
++ const struct sun50i_r329_ledc_timing *timing = &priv->timing;
++ unsigned long mod_freq = clk_get_rate(priv->mod_clk);
++ u32 cycle_ns = NSEC_PER_SEC / mod_freq;
++ u32 val;
++
++ val = (timing->t1h_ns / cycle_ns) << 21 |
++ (timing->t1l_ns / cycle_ns) << 16 |
++ (timing->t0h_ns / cycle_ns) << 6 |
++ (timing->t0l_ns / cycle_ns);
++ writel(val, priv->base + LEDC_T01_TIMING_CTRL_REG);
++
++ val = (timing->treset_ns / cycle_ns) << 16 |
++ (priv->num_leds - 1);
++ writel(val, priv->base + LEDC_RESET_TIMING_CTRL_REG);
++}
++
++static int sun50i_r329_ledc_resume(struct device *dev)
++{
++ struct sun50i_r329_ledc *priv = dev_get_drvdata(dev);
++ u32 val;
++ int ret;
++
++ ret = reset_control_deassert(priv->reset);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(priv->bus_clk);
++ if (ret)
++ goto err_assert_reset;
++
++ ret = clk_prepare_enable(priv->mod_clk);
++ if (ret)
++ goto err_disable_bus_clk;
++
++ sun50i_r329_ledc_set_format(priv);
++ sun50i_r329_ledc_set_timing(priv);
++
++ /* The trigger level must be at least the burst length. */
++ val = readl(priv->base + LEDC_DMA_CTRL_REG);
++ val &= ~LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL;
++ val |= LEDC_FIFO_DEPTH / 2;
++ writel(val, priv->base + LEDC_DMA_CTRL_REG);
++
++ val = LEDC_INT_CTRL_REG_GLOBAL_INT_EN |
++ LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN;
++ writel(val, priv->base + LEDC_INT_CTRL_REG);
++
++ return 0;
++
++err_disable_bus_clk:
++ clk_disable_unprepare(priv->bus_clk);
++err_assert_reset:
++ reset_control_assert(priv->reset);
++
++ return ret;
++}
++
++static int sun50i_r329_ledc_suspend(struct device *dev)
++{
++ struct sun50i_r329_ledc *priv = dev_get_drvdata(dev);
++
++ clk_disable_unprepare(priv->mod_clk);
++ clk_disable_unprepare(priv->bus_clk);
++ reset_control_assert(priv->reset);
++
++ return 0;
++}
++
++static void sun50i_r329_ledc_dma_cleanup(void *data)
++{
++ struct sun50i_r329_ledc *priv = data;
++ struct device *dma_dev = dmaengine_get_dma_device(priv->dma_chan);
++
++ if (priv->buffer)
++ dma_free_wc(dma_dev, LEDS_TO_BYTES(priv->num_leds),
++ priv->buffer, priv->dma_handle);
++ dma_release_channel(priv->dma_chan);
++}
++
++static int sun50i_r329_ledc_probe(struct platform_device *pdev)
++{
++ const struct device_node *np = pdev->dev.of_node;
++ struct dma_slave_config dma_cfg = {};
++ struct led_init_data init_data = {};
++ struct device *dev = &pdev->dev;
++ struct device_node *child;
++ struct sun50i_r329_ledc *priv;
++ struct resource *mem;
++ int count, irq, ret;
++
++ count = of_get_available_child_count(np);
++ if (!count)
++ return -ENODEV;
++ if (count > LEDC_MAX_LEDS) {
++ dev_err(dev, "Too many LEDs! (max is %d)\n", LEDC_MAX_LEDS);
++ return -EINVAL;
++ }
++
++ priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ priv->dev = dev;
++ priv->num_leds = count;
++ spin_lock_init(&priv->lock);
++ dev_set_drvdata(dev, priv);
++
++ ret = sun50i_r329_ledc_parse_format(np, priv);
++ if (ret)
++ return ret;
++
++ ret = sun50i_r329_ledc_parse_timing(np, priv);
++ if (ret)
++ return ret;
++
++ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
++ if (IS_ERR(priv->base))
++ return PTR_ERR(priv->base);
++
++ priv->bus_clk = devm_clk_get(dev, "bus");
++ if (IS_ERR(priv->bus_clk))
++ return PTR_ERR(priv->bus_clk);
++
++ priv->mod_clk = devm_clk_get(dev, "mod");
++ if (IS_ERR(priv->mod_clk))
++ return PTR_ERR(priv->mod_clk);
++
++ priv->reset = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(priv->reset))
++ return PTR_ERR(priv->reset);
++
++ priv->dma_chan = dma_request_chan(dev, "tx");
++ if (IS_ERR(priv->dma_chan))
++ return PTR_ERR(priv->dma_chan);
++
++ ret = devm_add_action_or_reset(dev, sun50i_r329_ledc_dma_cleanup, priv);
++ if (ret)
++ return ret;
++
++ dma_cfg.dst_addr = mem->start + LEDC_DATA_REG;
++ dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ dma_cfg.dst_maxburst = LEDC_FIFO_DEPTH / 2;
++ ret = dmaengine_slave_config(priv->dma_chan, &dma_cfg);
++ if (ret)
++ return ret;
++
++ priv->buffer = dma_alloc_wc(dmaengine_get_dma_device(priv->dma_chan),
++ LEDS_TO_BYTES(priv->num_leds),
++ &priv->dma_handle, GFP_KERNEL);
++ if (!priv->buffer)
++ return -ENOMEM;
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return irq;
++
++ ret = devm_request_irq(dev, irq, sun50i_r329_ledc_irq,
++ 0, dev_name(dev), priv);
++ if (ret)
++ return ret;
++
++ ret = sun50i_r329_ledc_resume(dev);
++ if (ret)
++ return ret;
++
++ for_each_available_child_of_node(np, child) {
++ struct sun50i_r329_ledc_led *led;
++ struct led_classdev *cdev;
++ u32 addr, color;
++
++ ret = of_property_read_u32(child, "reg", &addr);
++ if (ret || addr >= count) {
++ dev_err(dev, "LED 'reg' values must be from 0 to %d\n",
++ priv->num_leds - 1);
++ ret = -EINVAL;
++ goto err_put_child;
++ }
++
++ ret = of_property_read_u32(child, "color", &color);
++ if (ret || color != LED_COLOR_ID_RGB) {
++ dev_err(dev, "LED 'color' must be LED_COLOR_ID_RGB\n");
++ ret = -EINVAL;
++ goto err_put_child;
++ }
++
++ led = &priv->leds[addr];
++
++ led->subled_info[0].color_index = LED_COLOR_ID_RED;
++ led->subled_info[0].channel = 0;
++ led->subled_info[1].color_index = LED_COLOR_ID_GREEN;
++ led->subled_info[1].channel = 1;
++ led->subled_info[2].color_index = LED_COLOR_ID_BLUE;
++ led->subled_info[2].channel = 2;
++
++ led->mc_cdev.num_colors = ARRAY_SIZE(led->subled_info);
++ led->mc_cdev.subled_info = led->subled_info;
++
++ cdev = &led->mc_cdev.led_cdev;
++ cdev->max_brightness = U8_MAX;
++ cdev->brightness_set = sun50i_r329_ledc_brightness_set;
++
++ init_data.fwnode = of_fwnode_handle(child);
++
++ ret = devm_led_classdev_multicolor_register_ext(dev,
++ &led->mc_cdev,
++ &init_data);
++ if (ret) {
++ dev_err(dev, "Failed to register LED %u: %d\n",
++ addr, ret);
++ goto err_put_child;
++ }
++ }
++
++ dev_info(dev, "Registered %d LEDs\n", priv->num_leds);
++
++ return 0;
++
++err_put_child:
++ of_node_put(child);
++ sun50i_r329_ledc_suspend(&pdev->dev);
++
++ return ret;
++}
++
++static int sun50i_r329_ledc_remove(struct platform_device *pdev)
++{
++ sun50i_r329_ledc_suspend(&pdev->dev);
++
++ return 0;
++}
++
++static void sun50i_r329_ledc_shutdown(struct platform_device *pdev)
++{
++ sun50i_r329_ledc_suspend(&pdev->dev);
++}
++
++static const struct of_device_id sun50i_r329_ledc_of_match[] = {
++ { .compatible = "allwinner,sun50i-r329-ledc" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, sun50i_r329_ledc_of_match);
++
++static SIMPLE_DEV_PM_OPS(sun50i_r329_ledc_pm,
++ sun50i_r329_ledc_suspend, sun50i_r329_ledc_resume);
++
++static struct platform_driver sun50i_r329_ledc_driver = {
++ .probe = sun50i_r329_ledc_probe,
++ .remove = sun50i_r329_ledc_remove,
++ .shutdown = sun50i_r329_ledc_shutdown,
++ .driver = {
++ .name = "sun50i-r329-ledc",
++ .of_match_table = sun50i_r329_ledc_of_match,
++ .pm = pm_ptr(&sun50i_r329_ledc_pm),
++ },
++};
++module_platform_driver(sun50i_r329_ledc_driver);
++
++MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
++MODULE_DESCRIPTION("Allwinner R329 LED controller driver");
++MODULE_LICENSE("GPL");
+--
+2.20.1
+
--- /dev/null
+From 8a568791d2dcb2f81df3e0664418dddc71d42500 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:50:41 -0500
+Subject: [PATCH 028/124] ASoC: sun4i-i2s: Update registers for more channels
+
+H6 expands the number of channels in each direction to 16, so the slot
+number fields need to be expanded from 3 to 4 bits each.
+
+R329/D1 expand that further by allowing each of the 16 slots to map to
+any of 4 data pins. For TX, the configuration of each pin is
+independent, so there is a copy of the mapping registers for each pin.
+For RX, each of the 16 slots can map to only one pin, so the registers
+were changed to add the pin selection inline with the channel mapping.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-i2s.c | 28 +++++++++++++++++-----------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
+index 1e9116cd365e..7da8a16955a1 100644
+--- a/sound/soc/sunxi/sun4i-i2s.c
++++ b/sound/soc/sunxi/sun4i-i2s.c
+@@ -115,9 +115,9 @@
+ #define SUN8I_I2S_FIFO_TX_REG 0x20
+
+ #define SUN8I_I2S_CHAN_CFG_REG 0x30
+-#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4)
++#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(7, 4)
+ #define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) ((chan - 1) << 4)
+-#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0)
++#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(3, 0)
+ #define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1)
+
+ #define SUN8I_I2S_TX_CHAN_MAP_REG 0x44
+@@ -138,13 +138,19 @@
+ #define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0)
+ #define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1))
+
+-#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44
+-#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48
++#define SUN50I_H6_I2S_TX_CHAN_SEL_REG(pin) (0x34 + 4 * (pin))
++#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG(pin) (0x44 + 8 * (pin))
++#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG(pin) (0x48 + 8 * (pin))
+
+ #define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64
+ #define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68
+ #define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C
+
++#define SUN50I_R329_I2S_RX_CHAN_MAP0_REG 0x68
++#define SUN50I_R329_I2S_RX_CHAN_MAP1_REG 0x6c
++#define SUN50I_R329_I2S_RX_CHAN_MAP2_REG 0x70
++#define SUN50I_R329_I2S_RX_CHAN_MAP3_REG 0x74
++
+ struct sun4i_i2s;
+
+ /**
+@@ -523,13 +529,13 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+ unsigned int lrck_period;
+
+ /* Map the channels for playback and capture */
+- regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0xFEDCBA98);
+- regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x76543210);
++ regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98);
++ regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210);
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98);
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210);
+
+ /* Configure the channels */
+- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
++ regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0),
+ SUN50I_H6_I2S_TX_CHAN_SEL_MASK,
+ SUN50I_H6_I2S_TX_CHAN_SEL(channels));
+ regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG,
+@@ -563,7 +569,7 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+ SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
+ SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period));
+
+- regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
++ regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0),
+ SUN50I_H6_I2S_TX_CHAN_EN_MASK,
+ SUN50I_H6_I2S_TX_CHAN_EN(channels));
+
+@@ -1210,9 +1216,9 @@ static const struct reg_default sun50i_h6_i2s_reg_defaults[] = {
+ { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
+ { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
+ { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
+- { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
+- { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 },
+- { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 },
++ { SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), 0x00000000 },
++ { SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0x00000000 },
++ { SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 },
+ { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 },
+--
+2.20.1
+
--- /dev/null
+From 8da658ca3f0aa0b4727333d544518bf985c72e8f Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:51:37 -0500
+Subject: [PATCH 029/124] ASoC: sun4i-i2s: Add support for the R329/D1 variant
+
+This adds a new set of quirks to set the right RX channel map. Since
+that is the only change to the register layout, reuse the H6 regmap
+config by extending its last register. R329 support is added by its
+compatible string. D1 uses R329 as its fallback compatible, so no
+additional code change is needed for it.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-i2s.c | 40 ++++++++++++++++++++++++++++++++++---
+ 1 file changed, 37 insertions(+), 3 deletions(-)
+
+diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
+index 7da8a16955a1..7047f71629ab 100644
+--- a/sound/soc/sunxi/sun4i-i2s.c
++++ b/sound/soc/sunxi/sun4i-i2s.c
+@@ -181,6 +181,9 @@ struct sun4i_i2s_quirks {
+ struct reg_field field_fmt_wss;
+ struct reg_field field_fmt_sr;
+
++ unsigned int num_din_pins;
++ unsigned int num_dout_pins;
++
+ const struct sun4i_i2s_clk_div *bclk_dividers;
+ unsigned int num_bclk_dividers;
+ const struct sun4i_i2s_clk_div *mclk_dividers;
+@@ -531,8 +534,15 @@ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s,
+ /* Map the channels for playback and capture */
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98);
+ regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210);
+- regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98);
+- regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210);
++ if (i2s->variant->num_din_pins > 1) {
++ regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP0_REG, 0x0F0E0D0C);
++ regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP1_REG, 0x0B0A0908);
++ regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP2_REG, 0x07060504);
++ regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP3_REG, 0x03020100);
++ } else {
++ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98);
++ regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210);
++ }
+
+ /* Configure the channels */
+ regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0),
+@@ -1255,7 +1265,7 @@ static const struct regmap_config sun50i_h6_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+- .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG,
++ .max_register = SUN50I_R329_I2S_RX_CHAN_MAP3_REG,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = sun50i_h6_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sun50i_h6_i2s_reg_defaults),
+@@ -1440,6 +1450,26 @@ static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = {
+ .set_fmt = sun50i_h6_i2s_set_soc_fmt,
+ };
+
++static const struct sun4i_i2s_quirks sun50i_r329_i2s_quirks = {
++ .has_reset = true,
++ .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
++ .sun4i_i2s_regmap = &sun50i_h6_i2s_regmap_config,
++ .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
++ .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
++ .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
++ .num_din_pins = 4,
++ .num_dout_pins = 4,
++ .bclk_dividers = sun8i_i2s_clk_div,
++ .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
++ .mclk_dividers = sun8i_i2s_clk_div,
++ .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div),
++ .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate,
++ .get_sr = sun8i_i2s_get_sr_wss,
++ .get_wss = sun8i_i2s_get_sr_wss,
++ .set_chan_cfg = sun50i_h6_i2s_set_chan_cfg,
++ .set_fmt = sun50i_h6_i2s_set_soc_fmt,
++};
++
+ static int sun4i_i2s_init_regmap_fields(struct device *dev,
+ struct sun4i_i2s *i2s)
+ {
+@@ -1612,6 +1642,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
+ .compatible = "allwinner,sun50i-h6-i2s",
+ .data = &sun50i_h6_i2s_quirks,
+ },
++ {
++ .compatible = "allwinner,sun50i-r329-i2s",
++ .data = &sun50i_r329_i2s_quirks,
++ },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
+--
+2.20.1
+
--- /dev/null
+From 2040266a0a941ec34949f049a2245f88a0b58ebc Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:42:19 -0500
+Subject: [PATCH 030/124] hwspinlock: sun6i: Clarify bank counting logic
+
+In some of the most recent datasheets, the register definition was
+updated in a way that resolves the conflict here: the field is only two
+bits wide, and a value of "4" really means a bit pattern of "0". Correct
+the code to reflect this, but leave an updated comment because some
+datasheets still have incorrect information in them.
+
+Fixes: 3c881e05c814 ("hwspinlock: add sun6i hardware spinlock support")
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/hwspinlock/sun6i_hwspinlock.c | 36 +++++++++++----------------
+ 1 file changed, 14 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/hwspinlock/sun6i_hwspinlock.c b/drivers/hwspinlock/sun6i_hwspinlock.c
+index c2d314588046..21dfbbc7a8ae 100644
+--- a/drivers/hwspinlock/sun6i_hwspinlock.c
++++ b/drivers/hwspinlock/sun6i_hwspinlock.c
+@@ -129,30 +129,22 @@ static int sun6i_hwspinlock_probe(struct platform_device *pdev)
+ }
+
+ /*
+- * bit 28 and 29 represents the hwspinlock setup
++ * Bits 28 and 29 represent the number of available locks.
+ *
+- * every datasheet (A64, A80, A83T, H3, H5, H6 ...) says the default value is 0x1 and 0x1
+- * to 0x4 represent 32, 64, 128 and 256 locks
+- * but later datasheets (H5, H6) say 00, 01, 10, 11 represent 32, 64, 128 and 256 locks,
+- * but that would mean H5 and H6 have 64 locks, while their datasheets talk about 32 locks
+- * all the time, not a single mentioning of 64 locks
+- * the 0x4 value is also not representable by 2 bits alone, so some datasheets are not
+- * correct
+- * one thing have all in common, default value of the sysstatus register is 0x10000000,
+- * which results in bit 28 being set
+- * this is the reason 0x1 is considered being 32 locks and bit 30 is taken into account
+- * verified on H2+ (datasheet 0x1 = 32 locks) and H5 (datasheet 01 = 64 locks)
++ * The datasheets have two conflicting interpretations for these bits:
++ * | 00 | 01 | 10 | 11 |
++ * +-----+----+-----+-----+
++ * | 256 | 32 | 64 | 128 | A80, A83T, H3, A64, A50, D1
++ * | 32 | 64 | 128 | 256 | H5, H6, R329
++ * where some datasheets use "4" instead of "0" for the first column.
++ *
++ * Experiments shows that the first interpretation is correct, as all
++ * known implementations report the value "1" and have 32 spinlocks.
+ */
+- num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28;
+- switch (num_banks) {
+- case 1 ... 4:
+- priv->nlocks = 1 << (4 + num_banks);
+- break;
+- default:
+- err = -EINVAL;
+- dev_err(&pdev->dev, "unsupported hwspinlock setup (%d)\n", num_banks);
+- goto bank_fail;
+- }
++ num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28 & 0x3;
++ if (!num_banks)
++ num_banks = 4;
++ priv->nlocks = 1 << (4 + num_banks);
+
+ priv->bank = devm_kzalloc(&pdev->dev, struct_size(priv->bank, lock, priv->nlocks),
+ GFP_KERNEL);
+--
+2.20.1
+
--- /dev/null
+From d9e58fa4c5704d94f3fec0d73eb018e7684b634b Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:41:44 -0500
+Subject: [PATCH 031/124] hwspinlock: sun6i: Fix driver to match binding
+
+The binding for this device does not allow using the clock-names and
+reset-names properties, so the driver should not reference the clock or
+reset by name.
+
+Fixes: 3c881e05c814 ("hwspinlock: add sun6i hardware spinlock support")
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/hwspinlock/sun6i_hwspinlock.c | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/hwspinlock/sun6i_hwspinlock.c b/drivers/hwspinlock/sun6i_hwspinlock.c
+index 21dfbbc7a8ae..11b63ce3bd46 100644
+--- a/drivers/hwspinlock/sun6i_hwspinlock.c
++++ b/drivers/hwspinlock/sun6i_hwspinlock.c
+@@ -104,14 +104,12 @@ static int sun6i_hwspinlock_probe(struct platform_device *pdev)
+ if (!priv)
+ return -ENOMEM;
+
+- priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+- if (IS_ERR(priv->ahb_clk)) {
+- err = PTR_ERR(priv->ahb_clk);
+- dev_err(&pdev->dev, "unable to get AHB clock (%d)\n", err);
+- return err;
+- }
++ priv->ahb_clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(priv->ahb_clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->ahb_clk),
++ "unable to get AHB clock\n");
+
+- priv->reset = devm_reset_control_get(&pdev->dev, "ahb");
++ priv->reset = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset),
+ "unable to get reset control\n");
+--
+2.20.1
+
--- /dev/null
+From a79bb5d39c26fa96ceb3e306ab2b1a418ec96541 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 14:50:51 -0500
+Subject: [PATCH 032/124] dmaengine: sun6i: Do not use virt_to_phys
+
+This breaks on RISC-V, because dma_pool_alloc returns addresses which
+are not in the linear map. Instead, plumb through the physical address
+which is already known anyway.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/dma/sun6i-dma.c | 38 +++++++++++++++++++-------------------
+ 1 file changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 5cadd4d2b824..a9334f969b28 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -241,9 +241,7 @@ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
+ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
+ struct sun6i_pchan *pchan)
+ {
+- phys_addr_t reg = virt_to_phys(pchan->base);
+-
+- dev_dbg(sdev->slave.dev, "Chan %d reg: %pa\n"
++ dev_dbg(sdev->slave.dev, "Chan %d reg: 0x%lx\n"
+ "\t___en(%04x): \t0x%08x\n"
+ "\tpause(%04x): \t0x%08x\n"
+ "\tstart(%04x): \t0x%08x\n"
+@@ -252,7 +250,7 @@ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
+ "\t__dst(%04x): \t0x%08x\n"
+ "\tcount(%04x): \t0x%08x\n"
+ "\t_para(%04x): \t0x%08x\n\n",
+- pchan->idx, ®,
++ pchan->idx, pchan->base - sdev->base,
+ DMA_CHAN_ENABLE,
+ readl(pchan->base + DMA_CHAN_ENABLE),
+ DMA_CHAN_PAUSE,
+@@ -385,17 +383,16 @@ static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
+ }
+
+ static inline void sun6i_dma_dump_lli(struct sun6i_vchan *vchan,
+- struct sun6i_dma_lli *lli)
++ struct sun6i_dma_lli *v_lli,
++ dma_addr_t p_lli)
+ {
+- phys_addr_t p_lli = virt_to_phys(lli);
+-
+ dev_dbg(chan2dev(&vchan->vc.chan),
+- "\n\tdesc: p - %pa v - 0x%p\n"
++ "\n\tdesc:\tp - %pad v - 0x%p\n"
+ "\t\tc - 0x%08x s - 0x%08x d - 0x%08x\n"
+ "\t\tl - 0x%08x p - 0x%08x n - 0x%08x\n",
+- &p_lli, lli,
+- lli->cfg, lli->src, lli->dst,
+- lli->len, lli->para, lli->p_lli_next);
++ &p_lli, v_lli,
++ v_lli->cfg, v_lli->src, v_lli->dst,
++ v_lli->len, v_lli->para, v_lli->p_lli_next);
+ }
+
+ static void sun6i_dma_free_desc(struct virt_dma_desc *vd)
+@@ -445,7 +442,7 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
+ pchan->desc = to_sun6i_desc(&desc->tx);
+ pchan->done = NULL;
+
+- sun6i_dma_dump_lli(vchan, pchan->desc->v_lli);
++ sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli);
+
+ irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
+ irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
+@@ -670,7 +667,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
+
+ sun6i_dma_lli_add(NULL, v_lli, p_lli, txd);
+
+- sun6i_dma_dump_lli(vchan, v_lli);
++ sun6i_dma_dump_lli(vchan, v_lli, p_lli);
+
+ return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+
+@@ -746,14 +743,16 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
+ }
+
+ dev_dbg(chan2dev(chan), "First: %pad\n", &txd->p_lli);
+- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
+- sun6i_dma_dump_lli(vchan, prev);
++ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
++ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
++ sun6i_dma_dump_lli(vchan, v_lli, p_lli);
+
+ return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+
+ err_lli_free:
+- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
+- dma_pool_free(sdev->pool, prev, virt_to_phys(prev));
++ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
++ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
++ dma_pool_free(sdev->pool, v_lli, p_lli);
+ kfree(txd);
+ return NULL;
+ }
+@@ -820,8 +819,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
+ return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+
+ err_lli_free:
+- for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
+- dma_pool_free(sdev->pool, prev, virt_to_phys(prev));
++ for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli;
++ p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next)
++ dma_pool_free(sdev->pool, v_lli, p_lli);
+ kfree(txd);
+ return NULL;
+ }
+--
+2.20.1
+
--- /dev/null
+From c14eb7526a01f87568c5f063eff3e628299b09e4 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 14:44:12 -0500
+Subject: [PATCH 033/124] dmaengine: sun6i: Add support for 34-bit physical
+ addresses
+
+Recent Allwinner SoCs support >4 GiB of DRAM, so those variants of the
+DMA engine support >32 bit physical addresses. This is accomplished by
+placing the high bits in the "para" word in the DMA descriptor.
+
+DMA descriptors themselves can be located at >32 bit addresses by
+putting the high bits in the LSBs of the descriptor address register,
+taking advantage of the required DMA desciptor alignment. However,
+support for this is not really necessary, so we can avoid the
+complication by allocating them from the DMA_32 zone.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/dma/sun6i-dma.c | 39 ++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 34 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index a9334f969b28..8c7cce643cdc 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -90,6 +90,14 @@
+
+ #define DMA_CHAN_CUR_PARA 0x1c
+
++/*
++ * LLI address mangling
++ *
++ * The LLI link physical address is also mangled, but we avoid dealing
++ * with that by allocating LLIs from the DMA32 zone.
++ */
++#define SET_SRC_HIGH_ADDR(x) ((((x) >> 32) & 0x3U) << 16)
++#define SET_DST_HIGH_ADDR(x) ((((x) >> 32) & 0x3U) << 18)
+
+ /*
+ * Various hardware related defines
+@@ -132,6 +140,7 @@ struct sun6i_dma_config {
+ u32 dst_burst_lengths;
+ u32 src_addr_widths;
+ u32 dst_addr_widths;
++ bool has_high_addr;
+ bool has_mbus_clk;
+ };
+
+@@ -223,6 +232,12 @@ to_sun6i_desc(struct dma_async_tx_descriptor *tx)
+ return container_of(tx, struct sun6i_desc, vd.tx);
+ }
+
++static inline bool sun6i_dma_has_high_addr(struct sun6i_dma_dev *sdev)
++{
++ return IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
++ sdev->cfg->has_high_addr;
++}
++
+ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
+ {
+ dev_dbg(sdev->slave.dev, "Common register:\n"
+@@ -645,7 +660,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
+ if (!txd)
+ return NULL;
+
+- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
++ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli);
+ if (!v_lli) {
+ dev_err(sdev->slave.dev, "Failed to alloc lli memory\n");
+ goto err_txd_free;
+@@ -655,6 +670,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
+ v_lli->dst = dest;
+ v_lli->len = len;
+ v_lli->para = NORMAL_WAIT;
++ if (sun6i_dma_has_high_addr(sdev))
++ v_lli->para |= SET_SRC_HIGH_ADDR(src) |
++ SET_DST_HIGH_ADDR(dest);
+
+ burst = convert_burst(8);
+ width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
+@@ -705,7 +723,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
+ return NULL;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
++ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli);
+ if (!v_lli)
+ goto err_lli_free;
+
+@@ -715,6 +733,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
+ if (dir == DMA_MEM_TO_DEV) {
+ v_lli->src = sg_dma_address(sg);
+ v_lli->dst = sconfig->dst_addr;
++ if (sun6i_dma_has_high_addr(sdev))
++ v_lli->para |= SET_SRC_HIGH_ADDR(sg_dma_address(sg)) |
++ SET_DST_HIGH_ADDR(sconfig->dst_addr);
+ v_lli->cfg = lli_cfg;
+ sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port);
+ sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE);
+@@ -728,6 +749,9 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
+ } else {
+ v_lli->src = sconfig->src_addr;
+ v_lli->dst = sg_dma_address(sg);
++ if (sun6i_dma_has_high_addr(sdev))
++ v_lli->para |= SET_SRC_HIGH_ADDR(sconfig->src_addr) |
++ SET_DST_HIGH_ADDR(sg_dma_address(sg));
+ v_lli->cfg = lli_cfg;
+ sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM);
+ sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE);
+@@ -786,7 +810,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
+ return NULL;
+
+ for (i = 0; i < periods; i++) {
+- v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
++ v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli);
+ if (!v_lli) {
+ dev_err(sdev->slave.dev, "Failed to alloc lli memory\n");
+ goto err_lli_free;
+@@ -798,12 +822,18 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
+ if (dir == DMA_MEM_TO_DEV) {
+ v_lli->src = buf_addr + period_len * i;
+ v_lli->dst = sconfig->dst_addr;
++ if (sun6i_dma_has_high_addr(sdev))
++ v_lli->para |= SET_SRC_HIGH_ADDR(buf_addr + period_len * i) |
++ SET_DST_HIGH_ADDR(sconfig->dst_addr);
+ v_lli->cfg = lli_cfg;
+ sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port);
+ sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE);
+ } else {
+ v_lli->src = sconfig->src_addr;
+ v_lli->dst = buf_addr + period_len * i;
++ if (sun6i_dma_has_high_addr(sdev))
++ v_lli->para |= SET_SRC_HIGH_ADDR(sconfig->src_addr) |
++ SET_DST_HIGH_ADDR(buf_addr + period_len * i);
+ v_lli->cfg = lli_cfg;
+ sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM);
+ sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE);
+@@ -1174,8 +1204,6 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
+ };
+
+ /*
+- * TODO: Add support for more than 4g physical addressing.
+- *
+ * The A100 binding uses the number of dma channels from the
+ * device tree node.
+ */
+@@ -1194,6 +1222,7 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES),
++ .has_high_addr = true,
+ .has_mbus_clk = true,
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 2d56be1a7b337a5b73afbf5bb762a314835e9ed4 Mon Sep 17 00:00:00 2001
+From: Corentin Labbe <clabbe.montjoie@gmail.com>
+Date: Mon, 14 Jun 2021 20:54:01 +0200
+Subject: [PATCH 034/124] crypto: sun8i-ce: Add support for the D1 variant
+
+The Allwinner D1 SoC has a crypto engine compatible with sun8i-ce.
+Add support for it.
+
+Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ .../crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 21 +++++++++++++++++++
+ drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 1 +
+ 2 files changed, 22 insertions(+)
+
+diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
+index 00194d1d9ae6..d8623c7e0d1d 100644
+--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
++++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
+@@ -106,6 +106,24 @@ static const struct ce_variant ce_a64_variant = {
+ .trng = CE_ID_NOTSUPP,
+ };
+
++static const struct ce_variant ce_d1_variant = {
++ .alg_cipher = { CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
++ },
++ .alg_hash = { CE_ALG_MD5, CE_ALG_SHA1, CE_ALG_SHA224, CE_ALG_SHA256,
++ CE_ALG_SHA384, CE_ALG_SHA512
++ },
++ .op_mode = { CE_OP_ECB, CE_OP_CBC
++ },
++ .ce_clks = {
++ { "bus", 0, 200000000 },
++ { "mod", 300000000, 0 },
++ { "ram", 0, 400000000 },
++ },
++ .esr = ESR_D1,
++ .prng = CE_ALG_PRNG,
++ .trng = CE_ALG_TRNG,
++};
++
+ static const struct ce_variant ce_r40_variant = {
+ .alg_cipher = { CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES,
+ },
+@@ -192,6 +210,7 @@ int sun8i_ce_run_task(struct sun8i_ce_dev *ce, int flow, const char *name)
+ dev_err(ce->dev, "CE ERROR: keysram access error for AES\n");
+ break;
+ case ESR_A64:
++ case ESR_D1:
+ case ESR_H5:
+ case ESR_R40:
+ v >>= (flow * 4);
+@@ -990,6 +1009,8 @@ static const struct of_device_id sun8i_ce_crypto_of_match_table[] = {
+ .data = &ce_h3_variant },
+ { .compatible = "allwinner,sun8i-r40-crypto",
+ .data = &ce_r40_variant },
++ { .compatible = "allwinner,sun20i-d1-crypto",
++ .data = &ce_d1_variant },
+ { .compatible = "allwinner,sun50i-a64-crypto",
+ .data = &ce_a64_variant },
+ { .compatible = "allwinner,sun50i-h5-crypto",
+diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
+index cec781d5063c..624a5926f21f 100644
+--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
++++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
+@@ -94,6 +94,7 @@
+ #define ESR_R40 2
+ #define ESR_H5 3
+ #define ESR_H6 4
++#define ESR_D1 5
+
+ #define PRNG_DATA_SIZE (160 / 8)
+ #define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)
+--
+2.20.1
+
--- /dev/null
+From 692f382f2d8e87d65e19ef44f7c1794ceac14126 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 13 Nov 2021 11:39:01 -0600
+Subject: [PATCH 035/124] bus: sun50i-de2: Prevent driver from being unbound
+
+Currently, the driver can be unbound via sysfs. Because it does not call
+of_platform_depopulate, unbinding the driver will ...
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/bus/sun50i-de2.c | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/drivers/bus/sun50i-de2.c b/drivers/bus/sun50i-de2.c
+index 672518741f86..740957de1077 100644
+--- a/drivers/bus/sun50i-de2.c
++++ b/drivers/bus/sun50i-de2.c
+@@ -25,12 +25,6 @@ static int sun50i_de2_bus_probe(struct platform_device *pdev)
+ return 0;
+ }
+
+-static int sun50i_de2_bus_remove(struct platform_device *pdev)
+-{
+- sunxi_sram_release(&pdev->dev);
+- return 0;
+-}
+-
+ static const struct of_device_id sun50i_de2_bus_of_match[] = {
+ { .compatible = "allwinner,sun50i-a64-de2", },
+ { /* sentinel */ }
+@@ -38,9 +32,9 @@ static const struct of_device_id sun50i_de2_bus_of_match[] = {
+
+ static struct platform_driver sun50i_de2_bus_driver = {
+ .probe = sun50i_de2_bus_probe,
+- .remove = sun50i_de2_bus_remove,
+ .driver = {
+ .name = "sun50i-de2-bus",
++ .suppress_bind_attrs = true,
+ .of_match_table = sun50i_de2_bus_of_match,
+ },
+ };
+--
+2.20.1
+
--- /dev/null
+From 6ff4e726d98f36bcd5669cf5dac3f84cb9f8f9cf Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 18 Jul 2021 18:12:53 -0500
+Subject: [PATCH 036/124] clk: sunxi-ng: Export symbols used by CCU drivers
+
+For the individual CCU drivers to be built as modules, the ops structs,
+helper functions, and callback registration functions must be exported.
+These symbols are intended for use only by the adjacent CCU drivers, so
+export them into the SUNXI_CCU namespace.
+
+of_sunxi_ccu_probe is not exported because it is only used by built-in
+OF clock providers.
+
+Cover-changes: 1
+ - Patches 1-3 of 8 were merged.
+ - Name modules using Makefile logic, not by renaming the source files.
+ (Drop patch 4 of 8.)
+
+Series-changes: 2
+ - Export symbols to the SUNXI_CCU namespace.
+
+Series-changes: 3
+ - Also export helper functions.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_common.c | 3 +++
+ drivers/clk/sunxi-ng/ccu_div.c | 1 +
+ drivers/clk/sunxi-ng/ccu_frac.c | 6 ++++++
+ drivers/clk/sunxi-ng/ccu_gate.c | 4 ++++
+ drivers/clk/sunxi-ng/ccu_mp.c | 2 ++
+ drivers/clk/sunxi-ng/ccu_mult.c | 1 +
+ drivers/clk/sunxi-ng/ccu_mux.c | 6 ++++++
+ drivers/clk/sunxi-ng/ccu_nk.c | 1 +
+ drivers/clk/sunxi-ng/ccu_nkm.c | 1 +
+ drivers/clk/sunxi-ng/ccu_nkmp.c | 1 +
+ drivers/clk/sunxi-ng/ccu_nm.c | 1 +
+ drivers/clk/sunxi-ng/ccu_phase.c | 1 +
+ drivers/clk/sunxi-ng/ccu_reset.c | 1 +
+ drivers/clk/sunxi-ng/ccu_sdm.c | 6 ++++++
+ 14 files changed, 35 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
+index 31af8b6b5286..6afdedbce6a2 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.c
++++ b/drivers/clk/sunxi-ng/ccu_common.c
+@@ -36,6 +36,7 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
+
+ WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU);
+
+ /*
+ * This clock notifier is called when the frequency of a PLL clock is
+@@ -83,6 +84,7 @@ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
+ return clk_notifier_register(pll_nb->common->hw.clk,
+ &pll_nb->clk_nb);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU);
+
+ static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+ struct device_node *node, void __iomem *reg,
+@@ -194,6 +196,7 @@ int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
+
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU);
+
+ void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+ const struct sunxi_ccu_desc *desc)
+diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
+index 4c297089483c..cb10a3ea23f9 100644
+--- a/drivers/clk/sunxi-ng/ccu_div.c
++++ b/drivers/clk/sunxi-ng/ccu_div.c
+@@ -141,3 +141,4 @@ const struct clk_ops ccu_div_ops = {
+ .recalc_rate = ccu_div_recalc_rate,
+ .set_rate = ccu_div_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_div_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c
+index 44fcded8b354..b31f3ad946d6 100644
+--- a/drivers/clk/sunxi-ng/ccu_frac.c
++++ b/drivers/clk/sunxi-ng/ccu_frac.c
+@@ -18,6 +18,7 @@ bool ccu_frac_helper_is_enabled(struct ccu_common *common,
+
+ return !(readl(common->base + common->reg) & cf->enable);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_is_enabled, SUNXI_CCU);
+
+ void ccu_frac_helper_enable(struct ccu_common *common,
+ struct ccu_frac_internal *cf)
+@@ -33,6 +34,7 @@ void ccu_frac_helper_enable(struct ccu_common *common,
+ writel(reg & ~cf->enable, common->base + common->reg);
+ spin_unlock_irqrestore(common->lock, flags);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_enable, SUNXI_CCU);
+
+ void ccu_frac_helper_disable(struct ccu_common *common,
+ struct ccu_frac_internal *cf)
+@@ -48,6 +50,7 @@ void ccu_frac_helper_disable(struct ccu_common *common,
+ writel(reg | cf->enable, common->base + common->reg);
+ spin_unlock_irqrestore(common->lock, flags);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_disable, SUNXI_CCU);
+
+ bool ccu_frac_helper_has_rate(struct ccu_common *common,
+ struct ccu_frac_internal *cf,
+@@ -58,6 +61,7 @@ bool ccu_frac_helper_has_rate(struct ccu_common *common,
+
+ return (cf->rates[0] == rate) || (cf->rates[1] == rate);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_has_rate, SUNXI_CCU);
+
+ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
+ struct ccu_frac_internal *cf)
+@@ -79,6 +83,7 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
+
+ return (reg & cf->select) ? cf->rates[1] : cf->rates[0];
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_read_rate, SUNXI_CCU);
+
+ int ccu_frac_helper_set_rate(struct ccu_common *common,
+ struct ccu_frac_internal *cf,
+@@ -107,3 +112,4 @@ int ccu_frac_helper_set_rate(struct ccu_common *common,
+
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_set_rate, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_gate.c b/drivers/clk/sunxi-ng/ccu_gate.c
+index 3d5ca092b08f..a2115a21807d 100644
+--- a/drivers/clk/sunxi-ng/ccu_gate.c
++++ b/drivers/clk/sunxi-ng/ccu_gate.c
+@@ -24,6 +24,7 @@ void ccu_gate_helper_disable(struct ccu_common *common, u32 gate)
+
+ spin_unlock_irqrestore(common->lock, flags);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_disable, SUNXI_CCU);
+
+ static void ccu_gate_disable(struct clk_hw *hw)
+ {
+@@ -49,6 +50,7 @@ int ccu_gate_helper_enable(struct ccu_common *common, u32 gate)
+
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_enable, SUNXI_CCU);
+
+ static int ccu_gate_enable(struct clk_hw *hw)
+ {
+@@ -64,6 +66,7 @@ int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
+
+ return readl(common->base + common->reg) & gate;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_is_enabled, SUNXI_CCU);
+
+ static int ccu_gate_is_enabled(struct clk_hw *hw)
+ {
+@@ -124,3 +127,4 @@ const struct clk_ops ccu_gate_ops = {
+ .set_rate = ccu_gate_set_rate,
+ .recalc_rate = ccu_gate_recalc_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_gate_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
+index 9d3a76604d94..57cf2d615148 100644
+--- a/drivers/clk/sunxi-ng/ccu_mp.c
++++ b/drivers/clk/sunxi-ng/ccu_mp.c
+@@ -245,6 +245,7 @@ const struct clk_ops ccu_mp_ops = {
+ .recalc_rate = ccu_mp_recalc_rate,
+ .set_rate = ccu_mp_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_mp_ops, SUNXI_CCU);
+
+ /*
+ * Support for MMC timing mode switching
+@@ -325,3 +326,4 @@ const struct clk_ops ccu_mp_mmc_ops = {
+ .recalc_rate = ccu_mp_mmc_recalc_rate,
+ .set_rate = ccu_mp_mmc_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_mp_mmc_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
+index 7c8cf2e04e94..7bee217ef111 100644
+--- a/drivers/clk/sunxi-ng/ccu_mult.c
++++ b/drivers/clk/sunxi-ng/ccu_mult.c
+@@ -170,3 +170,4 @@ const struct clk_ops ccu_mult_ops = {
+ .recalc_rate = ccu_mult_recalc_rate,
+ .set_rate = ccu_mult_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_mult_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
+index 7d75da9a1f2e..2306a1cd83e4 100644
+--- a/drivers/clk/sunxi-ng/ccu_mux.c
++++ b/drivers/clk/sunxi-ng/ccu_mux.c
+@@ -64,6 +64,7 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
+ {
+ return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_apply_prediv, SUNXI_CCU);
+
+ static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+@@ -152,6 +153,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
+ req->rate = best_rate;
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_determine_rate, SUNXI_CCU);
+
+ u8 ccu_mux_helper_get_parent(struct ccu_common *common,
+ struct ccu_mux_internal *cm)
+@@ -174,6 +176,7 @@ u8 ccu_mux_helper_get_parent(struct ccu_common *common,
+
+ return parent;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_get_parent, SUNXI_CCU);
+
+ int ccu_mux_helper_set_parent(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+@@ -195,6 +198,7 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
+
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_set_parent, SUNXI_CCU);
+
+ static void ccu_mux_disable(struct clk_hw *hw)
+ {
+@@ -251,6 +255,7 @@ const struct clk_ops ccu_mux_ops = {
+ .determine_rate = __clk_mux_determine_rate,
+ .recalc_rate = ccu_mux_recalc_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU);
+
+ /*
+ * This clock notifier is called when the frequency of the of the parent
+@@ -285,3 +290,4 @@ int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb)
+
+ return clk_notifier_register(clk, &mux_nb->clk_nb);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_mux_notifier_register, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_nk.c b/drivers/clk/sunxi-ng/ccu_nk.c
+index aee68b00f3b2..c4fb82af97e8 100644
+--- a/drivers/clk/sunxi-ng/ccu_nk.c
++++ b/drivers/clk/sunxi-ng/ccu_nk.c
+@@ -157,3 +157,4 @@ const struct clk_ops ccu_nk_ops = {
+ .round_rate = ccu_nk_round_rate,
+ .set_rate = ccu_nk_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_nk_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
+index b9cfee0276ea..67da2c189b53 100644
+--- a/drivers/clk/sunxi-ng/ccu_nkm.c
++++ b/drivers/clk/sunxi-ng/ccu_nkm.c
+@@ -206,3 +206,4 @@ const struct clk_ops ccu_nkm_ops = {
+ .recalc_rate = ccu_nkm_recalc_rate,
+ .set_rate = ccu_nkm_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_nkm_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
+index bda87b38c45c..39413cb0985c 100644
+--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
++++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
+@@ -230,3 +230,4 @@ const struct clk_ops ccu_nkmp_ops = {
+ .round_rate = ccu_nkmp_round_rate,
+ .set_rate = ccu_nkmp_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_nkmp_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
+index e6bcc0a7170c..9ca9257f4426 100644
+--- a/drivers/clk/sunxi-ng/ccu_nm.c
++++ b/drivers/clk/sunxi-ng/ccu_nm.c
+@@ -238,3 +238,4 @@ const struct clk_ops ccu_nm_ops = {
+ .round_rate = ccu_nm_round_rate,
+ .set_rate = ccu_nm_set_rate,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_nm_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_phase.c b/drivers/clk/sunxi-ng/ccu_phase.c
+index 92ab8bd66427..e4cae2afe9db 100644
+--- a/drivers/clk/sunxi-ng/ccu_phase.c
++++ b/drivers/clk/sunxi-ng/ccu_phase.c
+@@ -121,3 +121,4 @@ const struct clk_ops ccu_phase_ops = {
+ .get_phase = ccu_phase_get_phase,
+ .set_phase = ccu_phase_set_phase,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_phase_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_reset.c b/drivers/clk/sunxi-ng/ccu_reset.c
+index 483100e45df3..6577aa18cb01 100644
+--- a/drivers/clk/sunxi-ng/ccu_reset.c
++++ b/drivers/clk/sunxi-ng/ccu_reset.c
+@@ -75,3 +75,4 @@ const struct reset_control_ops ccu_reset_ops = {
+ .reset = ccu_reset_reset,
+ .status = ccu_reset_status,
+ };
++EXPORT_SYMBOL_NS_GPL(ccu_reset_ops, SUNXI_CCU);
+diff --git a/drivers/clk/sunxi-ng/ccu_sdm.c b/drivers/clk/sunxi-ng/ccu_sdm.c
+index 79581a1c649a..41937ed0766d 100644
+--- a/drivers/clk/sunxi-ng/ccu_sdm.c
++++ b/drivers/clk/sunxi-ng/ccu_sdm.c
+@@ -20,6 +20,7 @@ bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
+
+ return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU);
+
+ void ccu_sdm_helper_enable(struct ccu_common *common,
+ struct ccu_sdm_internal *sdm,
+@@ -49,6 +50,7 @@ void ccu_sdm_helper_enable(struct ccu_common *common,
+ writel(reg | sdm->enable, common->base + common->reg);
+ spin_unlock_irqrestore(common->lock, flags);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU);
+
+ void ccu_sdm_helper_disable(struct ccu_common *common,
+ struct ccu_sdm_internal *sdm)
+@@ -69,6 +71,7 @@ void ccu_sdm_helper_disable(struct ccu_common *common,
+ writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
+ spin_unlock_irqrestore(common->lock, flags);
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, SUNXI_CCU);
+
+ /*
+ * Sigma delta modulation provides a way to do fractional-N frequency
+@@ -102,6 +105,7 @@ bool ccu_sdm_helper_has_rate(struct ccu_common *common,
+
+ return false;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU);
+
+ unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
+ struct ccu_sdm_internal *sdm,
+@@ -132,6 +136,7 @@ unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
+ /* We can't calculate the effective clock rate, so just fail. */
+ return 0;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU);
+
+ int ccu_sdm_helper_get_factors(struct ccu_common *common,
+ struct ccu_sdm_internal *sdm,
+@@ -153,3 +158,4 @@ int ccu_sdm_helper_get_factors(struct ccu_common *common,
+ /* nothing found */
+ return -EINVAL;
+ }
++EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU);
+--
+2.20.1
+
--- /dev/null
+From de552526eb4e1add4a2d4b616c46435e01650022 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 18 Jul 2021 17:48:02 -0500
+Subject: [PATCH 037/124] clk: sunxi-ng: Allow drivers to be built as modules
+
+While it is useful to build all of the CCU drivers at once, only 1-3 of
+them will be loaded at a time, or possibly none of them if the kernel is
+booted on a non-sunxi platform. These CCU drivers are relatively large;
+32-bit drivers have 30-50k of data each, while the 64-bit ones are
+50-75k due to the increased pointer overhead. About half of that data
+comes from relocations. Let's allow the user to build these drivers as
+modules so only the necessary data is loaded.
+
+As a first step, convert the CCUs that are already platform drivers.
+
+When the drivers are built as modules, normally the file name becomes
+the module name. However, the current file names are inconsistent with
+the <platform>-<peripheral> name used everywhere else: the devicetree
+bindings, the platform driver names, and the Kconfig symbols. Use
+Makfile logic to rename the modules so they follow the usual pattern.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/Kconfig | 16 +++---
+ drivers/clk/sunxi-ng/Makefile | 64 ++++++++++++++++--------
+ drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c | 4 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-a100.c | 4 +-
+ drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 7 ++-
+ drivers/clk/sunxi-ng/ccu-sun50i-h6.c | 7 ++-
+ drivers/clk/sunxi-ng/ccu-sun8i-a83t.c | 7 ++-
+ drivers/clk/sunxi-ng/ccu-sun8i-de2.c | 9 ++--
+ drivers/clk/sunxi-ng/ccu-sun8i-r40.c | 6 ++-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c | 7 ++-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c | 7 ++-
+ drivers/clk/sunxi-ng/ccu-sun9i-a80.c | 7 ++-
+ 12 files changed, 98 insertions(+), 47 deletions(-)
+
+diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
+index cd46d8853876..7cfdf21717f7 100644
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -13,22 +13,22 @@ config SUNIV_F1C100S_CCU
+ depends on MACH_SUNIV || COMPILE_TEST
+
+ config SUN50I_A64_CCU
+- bool "Support for the Allwinner A64 CCU"
++ tristate "Support for the Allwinner A64 CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN50I_A100_CCU
+- bool "Support for the Allwinner A100 CCU"
++ tristate "Support for the Allwinner A100 CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN50I_A100_R_CCU
+- bool "Support for the Allwinner A100 PRCM CCU"
++ tristate "Support for the Allwinner A100 PRCM CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN50I_H6_CCU
+- bool "Support for the Allwinner H6 CCU"
++ tristate "Support for the Allwinner H6 CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+@@ -69,7 +69,7 @@ config SUN8I_A33_CCU
+ depends on MACH_SUN8I || COMPILE_TEST
+
+ config SUN8I_A83T_CCU
+- bool "Support for the Allwinner A83T CCU"
++ tristate "Support for the Allwinner A83T CCU"
+ default MACH_SUN8I
+
+ config SUN8I_H3_CCU
+@@ -83,16 +83,16 @@ config SUN8I_V3S_CCU
+ depends on MACH_SUN8I || COMPILE_TEST
+
+ config SUN8I_DE2_CCU
+- bool "Support for the Allwinner SoCs DE2 CCU"
++ tristate "Support for the Allwinner SoCs DE2 CCU"
+ default MACH_SUN8I || (ARM64 && ARCH_SUNXI)
+
+ config SUN8I_R40_CCU
+- bool "Support for the Allwinner R40 CCU"
++ tristate "Support for the Allwinner R40 CCU"
+ default MACH_SUN8I
+ depends on MACH_SUN8I || COMPILE_TEST
+
+ config SUN9I_A80_CCU
+- bool "Support for the Allwinner A80 CCU"
++ tristate "Support for the Allwinner A80 CCU"
+ default MACH_SUN9I
+ depends on MACH_SUN9I || COMPILE_TEST
+
+diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
+index 96c324306d97..1020ed49a588 100644
+--- a/drivers/clk/sunxi-ng/Makefile
++++ b/drivers/clk/sunxi-ng/Makefile
+@@ -21,24 +21,46 @@ obj-y += ccu_nm.o
+ obj-y += ccu_mp.o
+
+ # SoC support
+-obj-$(CONFIG_SUNIV_F1C100S_CCU) += ccu-suniv-f1c100s.o
+-obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o
+-obj-$(CONFIG_SUN50I_A100_CCU) += ccu-sun50i-a100.o
+-obj-$(CONFIG_SUN50I_A100_R_CCU) += ccu-sun50i-a100-r.o
+-obj-$(CONFIG_SUN50I_H6_CCU) += ccu-sun50i-h6.o
+-obj-$(CONFIG_SUN50I_H616_CCU) += ccu-sun50i-h616.o
+-obj-$(CONFIG_SUN50I_H6_R_CCU) += ccu-sun50i-h6-r.o
+-obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o
+-obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o
+-obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
+-obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o
+-obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o
+-obj-$(CONFIG_SUN8I_A83T_CCU) += ccu-sun8i-a83t.o
+-obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o
+-obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o
+-obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o
+-obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o
+-obj-$(CONFIG_SUN8I_R40_CCU) += ccu-sun8i-r40.o
+-obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o
+-obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o
+-obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o
++obj-$(CONFIG_SUNIV_F1C100S_CCU) += suniv-f1c100s-ccu.o
++obj-$(CONFIG_SUN50I_A64_CCU) += sun50i-a64-ccu.o
++obj-$(CONFIG_SUN50I_A100_CCU) += sun50i-a100-ccu.o
++obj-$(CONFIG_SUN50I_A100_R_CCU) += sun50i-a100-r-ccu.o
++obj-$(CONFIG_SUN50I_H6_CCU) += sun50i-h6-ccu.o
++obj-$(CONFIG_SUN50I_H6_R_CCU) += sun50i-h6-r-ccu.o
++obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
++obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
++obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
++obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
++obj-$(CONFIG_SUN8I_A23_CCU) += sun8i-a23-ccu.o
++obj-$(CONFIG_SUN8I_A33_CCU) += sun8i-a33-ccu.o
++obj-$(CONFIG_SUN8I_A83T_CCU) += sun8i-a83t-ccu.o
++obj-$(CONFIG_SUN8I_H3_CCU) += sun8i-h3-ccu.o
++obj-$(CONFIG_SUN8I_R40_CCU) += sun8i-r40-ccu.o
++obj-$(CONFIG_SUN8I_V3S_CCU) += sun8i-v3s-ccu.o
++obj-$(CONFIG_SUN8I_DE2_CCU) += sun8i-de2-ccu.o
++obj-$(CONFIG_SUN8I_R_CCU) += sun8i-r-ccu.o
++obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-ccu.o
++obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-de-ccu.o
++obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-usb-ccu.o
++
++suniv-f1c100s-ccu-y += ccu-suniv-f1c100s.o
++sun50i-a64-ccu-y += ccu-sun50i-a64.o
++sun50i-a100-ccu-y += ccu-sun50i-a100.o
++sun50i-a100-r-ccu-y += ccu-sun50i-a100-r.o
++sun50i-h6-ccu-y += ccu-sun50i-h6.o
++sun50i-h6-r-ccu-y += ccu-sun50i-h6-r.o
++sun50i-h616-ccu-y += ccu-sun50i-h616.o
++sun4i-a10-ccu-y += ccu-sun4i-a10.o
++sun5i-ccu-y += ccu-sun5i.o
++sun6i-a31-ccu-y += ccu-sun6i-a31.o
++sun8i-a23-ccu-y += ccu-sun8i-a23.o
++sun8i-a33-ccu-y += ccu-sun8i-a33.o
++sun8i-a83t-ccu-y += ccu-sun8i-a83t.o
++sun8i-h3-ccu-y += ccu-sun8i-h3.o
++sun8i-r40-ccu-y += ccu-sun8i-r40.o
++sun8i-v3s-ccu-y += ccu-sun8i-v3s.o
++sun8i-de2-ccu-y += ccu-sun8i-de2.o
++sun8i-r-ccu-y += ccu-sun8i-r.o
++sun9i-a80-ccu-y += ccu-sun9i-a80.o
++sun9i-a80-de-ccu-y += ccu-sun9i-a80-de.o
++sun9i-a80-usb-ccu-y += ccu-sun9i-a80-usb.o
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+index 804729e0a208..fddd6c877cec 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c
+@@ -5,7 +5,6 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/module.h>
+-#include <linux/of_address.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -213,3 +212,6 @@ static struct platform_driver sun50i_a100_r_ccu_driver = {
+ },
+ };
+ module_platform_driver(sun50i_a100_r_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+index 1d475d5a3d91..5f93b5526e13 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a100.c
+@@ -6,7 +6,6 @@
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+ #include <linux/module.h>
+-#include <linux/of_address.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -1275,3 +1274,6 @@ static struct platform_driver sun50i_a100_ccu_driver = {
+ },
+ };
+ module_platform_driver(sun50i_a100_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+index fcbd914e84e0..df0395d5542d 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -982,4 +982,7 @@ static struct platform_driver sun50i_a64_ccu_driver = {
+ .of_match_table = sun50i_a64_ccu_ids,
+ },
+ };
+-builtin_platform_driver(sun50i_a64_ccu_driver);
++module_platform_driver(sun50i_a64_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+index 9a8902f702c5..db81d1db0378 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -1256,4 +1256,7 @@ static struct platform_driver sun50i_h6_ccu_driver = {
+ .of_match_table = sun50i_h6_ccu_ids,
+ },
+ };
+-builtin_platform_driver(sun50i_h6_ccu_driver);
++module_platform_driver(sun50i_h6_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+index e663ab0c9935..c6b936ee8435 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a83t.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -922,4 +922,7 @@ static struct platform_driver sun8i_a83t_ccu_driver = {
+ .of_match_table = sun8i_a83t_ccu_ids,
+ },
+ };
+-builtin_platform_driver(sun8i_a83t_ccu_driver);
++module_platform_driver(sun8i_a83t_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
+index 4b94b6041b27..e67ba1febedb 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c
+@@ -5,8 +5,8 @@
+
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
+-#include <linux/of_address.h>
+-#include <linux/of_platform.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset.h>
+
+@@ -396,4 +396,7 @@ static struct platform_driver sunxi_de2_clk_driver = {
+ .of_match_table = sunxi_de2_clk_ids,
+ },
+ };
+-builtin_platform_driver(sunxi_de2_clk_driver);
++module_platform_driver(sunxi_de2_clk_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+index a2144ee728a0..1a06bc518f9e 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c
+@@ -5,6 +5,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
+
+@@ -1373,4 +1374,7 @@ static struct platform_driver sun8i_r40_ccu_driver = {
+ .of_match_table = sun8i_r40_ccu_ids,
+ },
+ };
+-builtin_platform_driver(sun8i_r40_ccu_driver);
++module_platform_driver(sun8i_r40_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+index d2072972b614..690d944a7ab0 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/reset.h>
+
+@@ -272,4 +272,7 @@ static struct platform_driver sun9i_a80_de_clk_driver = {
+ .of_match_table = sun9i_a80_de_clk_ids,
+ },
+ };
+-builtin_platform_driver(sun9i_a80_de_clk_driver);
++module_platform_driver(sun9i_a80_de_clk_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
+index 596243b3e0fa..31d1c00b69e8 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -140,4 +140,7 @@ static struct platform_driver sun9i_a80_usb_clk_driver = {
+ .of_match_table = sun9i_a80_usb_clk_ids,
+ },
+ };
+-builtin_platform_driver(sun9i_a80_usb_clk_driver);
++module_platform_driver(sun9i_a80_usb_clk_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+index 68b30fdc60fd..02550ecbd5c8 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
++++ b/drivers/clk/sunxi-ng/ccu-sun9i-a80.c
+@@ -5,7 +5,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -1247,4 +1247,7 @@ static struct platform_driver sun9i_a80_ccu_driver = {
+ .of_match_table = sun9i_a80_ccu_ids,
+ },
+ };
+-builtin_platform_driver(sun9i_a80_ccu_driver);
++module_platform_driver(sun9i_a80_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+--
+2.20.1
+
--- /dev/null
+From f5fb7ccecb5c4404eeb96d6d5662d1e4d84f80ba Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 17 Jul 2021 19:24:36 -0500
+Subject: [PATCH 038/124] clk: sunxi-ng: Convert early providers to platform
+ drivers
+
+The PRCM CCU drivers depend on clocks provided by other CCU drivers. For
+example, the sun8i-r-ccu driver uses the "pll-periph" clock provided by
+the SoC's main CCU.
+
+However, sun8i-r-ccu is an early OF clock provider, and many of the
+main CCUs (e.g. sun50i-a64-ccu) use platform drivers. This means that
+the consumer clocks will be orphaned until the supplier driver is bound.
+This can be avoided by converting the remaining CCUs to use platform
+drivers. Then fw_devlink will ensure the drivers are bound in the
+optimal order.
+
+The sun5i CCU is the only one which actually needs to be an early clock
+provider, because it provides the clock for the system timer. That one
+is left alone.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/Kconfig | 20 ++++----
+ drivers/clk/sunxi-ng/ccu-sun4i-a10.c | 58 +++++++++++++--------
+ drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c | 56 ++++++++++++--------
+ drivers/clk/sunxi-ng/ccu-sun50i-h616.c | 33 ++++++++----
+ drivers/clk/sunxi-ng/ccu-sun6i-a31.c | 40 +++++++++++----
+ drivers/clk/sunxi-ng/ccu-sun8i-a23.c | 35 +++++++++----
+ drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 40 +++++++++++----
+ drivers/clk/sunxi-ng/ccu-sun8i-h3.c | 62 ++++++++++++++--------
+ drivers/clk/sunxi-ng/ccu-sun8i-r.c | 65 ++++++++++++++----------
+ drivers/clk/sunxi-ng/ccu-sun8i-v3s.c | 57 +++++++++++++--------
+ drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c | 38 ++++++++++----
+ 11 files changed, 332 insertions(+), 172 deletions(-)
+
+diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
+index 7cfdf21717f7..cbae3612a9ef 100644
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -8,7 +8,7 @@ config SUNXI_CCU
+ if SUNXI_CCU
+
+ config SUNIV_F1C100S_CCU
+- bool "Support for the Allwinner newer F1C100s CCU"
++ tristate "Support for the Allwinner newer F1C100s CCU"
+ default MACH_SUNIV
+ depends on MACH_SUNIV || COMPILE_TEST
+
+@@ -33,17 +33,17 @@ config SUN50I_H6_CCU
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN50I_H616_CCU
+- bool "Support for the Allwinner H616 CCU"
++ tristate "Support for the Allwinner H616 CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN50I_H6_R_CCU
+- bool "Support for the Allwinner H6 and H616 PRCM CCU"
++ tristate "Support for the Allwinner H6 and H616 PRCM CCU"
+ default ARM64 && ARCH_SUNXI
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN4I_A10_CCU
+- bool "Support for the Allwinner A10/A20 CCU"
++ tristate "Support for the Allwinner A10/A20 CCU"
+ default MACH_SUN4I
+ default MACH_SUN7I
+ depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
+@@ -54,17 +54,17 @@ config SUN5I_CCU
+ depends on MACH_SUN5I || COMPILE_TEST
+
+ config SUN6I_A31_CCU
+- bool "Support for the Allwinner A31/A31s CCU"
++ tristate "Support for the Allwinner A31/A31s CCU"
+ default MACH_SUN6I
+ depends on MACH_SUN6I || COMPILE_TEST
+
+ config SUN8I_A23_CCU
+- bool "Support for the Allwinner A23 CCU"
++ tristate "Support for the Allwinner A23 CCU"
+ default MACH_SUN8I
+ depends on MACH_SUN8I || COMPILE_TEST
+
+ config SUN8I_A33_CCU
+- bool "Support for the Allwinner A33 CCU"
++ tristate "Support for the Allwinner A33 CCU"
+ default MACH_SUN8I
+ depends on MACH_SUN8I || COMPILE_TEST
+
+@@ -73,12 +73,12 @@ config SUN8I_A83T_CCU
+ default MACH_SUN8I
+
+ config SUN8I_H3_CCU
+- bool "Support for the Allwinner H3 CCU"
++ tristate "Support for the Allwinner H3 CCU"
+ default MACH_SUN8I || (ARM64 && ARCH_SUNXI)
+ depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+
+ config SUN8I_V3S_CCU
+- bool "Support for the Allwinner V3s CCU"
++ tristate "Support for the Allwinner V3s CCU"
+ default MACH_SUN8I
+ depends on MACH_SUN8I || COMPILE_TEST
+
+@@ -97,7 +97,7 @@ config SUN9I_A80_CCU
+ depends on MACH_SUN9I || COMPILE_TEST
+
+ config SUN8I_R_CCU
+- bool "Support for Allwinner SoCs' PRCM CCUs"
++ tristate "Support for Allwinner SoCs' PRCM CCUs"
+ default MACH_SUN8I || (ARCH_SUNXI && ARM64)
+
+ endif
+diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
+index bd9a8782fec3..c19828f1aa0f 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
++++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c
+@@ -7,7 +7,9 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -1425,18 +1427,19 @@ static const struct sunxi_ccu_desc sun7i_a20_ccu_desc = {
+ .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets),
+ };
+
+-static void __init sun4i_ccu_init(struct device_node *node,
+- const struct sunxi_ccu_desc *desc)
++static int sun4i_a10_ccu_probe(struct platform_device *pdev)
+ {
++ const struct sunxi_ccu_desc *desc;
+ void __iomem *reg;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%s: Could not map the clock registers\n",
+- of_node_full_name(node));
+- return;
+- }
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
++
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ val = readl(reg + SUN4I_PLL_AUDIO_REG);
+
+@@ -1464,19 +1467,30 @@ static void __init sun4i_ccu_init(struct device_node *node,
+ val &= ~GENMASK(7, 6);
+ writel(val | (2 << 6), reg + SUN4I_AHB_REG);
+
+- of_sunxi_ccu_probe(node, reg, desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, desc);
+ }
+
+-static void __init sun4i_a10_ccu_setup(struct device_node *node)
+-{
+- sun4i_ccu_init(node, &sun4i_a10_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun4i_a10_ccu, "allwinner,sun4i-a10-ccu",
+- sun4i_a10_ccu_setup);
++static const struct of_device_id sun4i_a10_ccu_ids[] = {
++ {
++ .compatible = "allwinner,sun4i-a10-ccu",
++ .data = &sun4i_a10_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun7i-a20-ccu",
++ .data = &sun7i_a20_ccu_desc,
++ },
++ { }
++};
+
+-static void __init sun7i_a20_ccu_setup(struct device_node *node)
+-{
+- sun4i_ccu_init(node, &sun7i_a20_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun7i_a20_ccu, "allwinner,sun7i-a20-ccu",
+- sun7i_a20_ccu_setup);
++static struct platform_driver sun4i_a10_ccu_driver = {
++ .probe = sun4i_a10_ccu_probe,
++ .driver = {
++ .name = "sun4i-a10-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun4i_a10_ccu_ids,
++ },
++};
++module_platform_driver(sun4i_a10_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
+index f30d7eb5424d..712e103382d8 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c
+@@ -4,7 +4,8 @@
+ */
+
+ #include <linux/clk-provider.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -221,30 +222,43 @@ static const struct sunxi_ccu_desc sun50i_h616_r_ccu_desc = {
+ .num_resets = ARRAY_SIZE(sun50i_h616_r_ccu_resets),
+ };
+
+-static void __init sunxi_r_ccu_init(struct device_node *node,
+- const struct sunxi_ccu_desc *desc)
++static int sun50i_h6_r_ccu_probe(struct platform_device *pdev)
+ {
++ const struct sunxi_ccu_desc *desc;
+ void __iomem *reg;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
+
+- of_sunxi_ccu_probe(node, reg, desc);
+-}
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+-static void __init sun50i_h6_r_ccu_setup(struct device_node *node)
+-{
+- sunxi_r_ccu_init(node, &sun50i_h6_r_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, desc);
+ }
+-CLK_OF_DECLARE(sun50i_h6_r_ccu, "allwinner,sun50i-h6-r-ccu",
+- sun50i_h6_r_ccu_setup);
+
+-static void __init sun50i_h616_r_ccu_setup(struct device_node *node)
+-{
+- sunxi_r_ccu_init(node, &sun50i_h616_r_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun50i_h616_r_ccu, "allwinner,sun50i-h616-r-ccu",
+- sun50i_h616_r_ccu_setup);
++static const struct of_device_id sun50i_h6_r_ccu_ids[] = {
++ {
++ .compatible = "allwinner,sun50i-h6-r-ccu",
++ .data = &sun50i_h6_r_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun50i-h616-r-ccu",
++ .data = &sun50i_h616_r_ccu_desc,
++ },
++ { }
++};
++
++static struct platform_driver sun50i_h6_r_ccu_driver = {
++ .probe = sun50i_h6_r_ccu_probe,
++ .driver = {
++ .name = "sun50i-h6-r-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun50i_h6_r_ccu_ids,
++ },
++};
++module_platform_driver(sun50i_h6_r_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
+index 22eb18079a15..49a2474cf314 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
++++ b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c
+@@ -7,7 +7,7 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -1082,17 +1082,15 @@ static const u32 usb2_clk_regs[] = {
+ SUN50I_H616_USB3_CLK_REG,
+ };
+
+-static void __init sun50i_h616_ccu_setup(struct device_node *node)
++static int sun50i_h616_ccu_probe(struct platform_device *pdev)
+ {
+ void __iomem *reg;
+ u32 val;
+ int i;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map clock registers\n", node);
+- return;
+- }
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Enable the lock bits and the output enable bits on all PLLs */
+ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) {
+@@ -1141,8 +1139,23 @@ static void __init sun50i_h616_ccu_setup(struct device_node *node)
+ val |= BIT(24);
+ writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG);
+
+- of_sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h616_ccu_desc);
+ }
+
+-CLK_OF_DECLARE(sun50i_h616_ccu, "allwinner,sun50i-h616-ccu",
+- sun50i_h616_ccu_setup);
++static const struct of_device_id sun50i_h616_ccu_ids[] = {
++ { .compatible = "allwinner,sun50i-h616-ccu" },
++ { }
++};
++
++static struct platform_driver sun50i_h616_ccu_driver = {
++ .probe = sun50i_h616_ccu_probe,
++ .driver = {
++ .name = "sun50i-h616-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun50i_h616_ccu_ids,
++ },
++};
++module_platform_driver(sun50i_h616_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+index 3df5c0b41580..0762deffb33c 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
++++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
+@@ -9,7 +9,8 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -1226,16 +1227,15 @@ static struct ccu_mux_nb sun6i_a31_cpu_nb = {
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+ };
+
+-static void __init sun6i_a31_ccu_setup(struct device_node *node)
++static int sun6i_a31_ccu_probe(struct platform_device *pdev)
+ {
+ void __iomem *reg;
++ int ret;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 1 */
+ val = readl(reg + SUN6I_A31_PLL_AUDIO_REG);
+@@ -1257,10 +1257,30 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node)
+ val |= 0x3 << 12;
+ writel(val, reg + SUN6I_A31_AHB1_REG);
+
+- of_sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun6i_a31_ccu_desc);
++ if (ret)
++ return ret;
+
+ ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
+ &sun6i_a31_cpu_nb);
++
++ return 0;
+ }
+-CLK_OF_DECLARE(sun6i_a31_ccu, "allwinner,sun6i-a31-ccu",
+- sun6i_a31_ccu_setup);
++
++static const struct of_device_id sun6i_a31_ccu_ids[] = {
++ { .compatible = "allwinner,sun6i-a31-ccu" },
++ { }
++};
++
++static struct platform_driver sun6i_a31_ccu_driver = {
++ .probe = sun6i_a31_ccu_probe,
++ .driver = {
++ .name = "sun6i-a31-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun6i_a31_ccu_ids,
++ },
++};
++module_platform_driver(sun6i_a31_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+index 577bb235d658..e80cc3864e44 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
+@@ -5,7 +5,8 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -724,16 +725,14 @@ static const struct sunxi_ccu_desc sun8i_a23_ccu_desc = {
+ .num_resets = ARRAY_SIZE(sun8i_a23_ccu_resets),
+ };
+
+-static void __init sun8i_a23_ccu_setup(struct device_node *node)
++static int sun8i_a23_ccu_probe(struct platform_device *pdev)
+ {
+ void __iomem *reg;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 1 */
+ val = readl(reg + SUN8I_A23_PLL_AUDIO_REG);
+@@ -745,7 +744,23 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node)
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A23_PLL_MIPI_REG);
+
+- of_sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a23_ccu_desc);
+ }
+-CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu",
+- sun8i_a23_ccu_setup);
++
++static const struct of_device_id sun8i_a23_ccu_ids[] = {
++ { .compatible = "allwinner,sun8i-a23-ccu" },
++ { }
++};
++
++static struct platform_driver sun8i_a23_ccu_driver = {
++ .probe = sun8i_a23_ccu_probe,
++ .driver = {
++ .name = "sun8i-a23-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun8i_a23_ccu_ids,
++ },
++};
++module_platform_driver(sun8i_a23_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+index 8f65cd03f5ac..d12878a1ba9e 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+@@ -5,7 +5,8 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -784,16 +785,15 @@ static struct ccu_mux_nb sun8i_a33_cpu_nb = {
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+ };
+
+-static void __init sun8i_a33_ccu_setup(struct device_node *node)
++static int sun8i_a33_ccu_probe(struct platform_device *pdev)
+ {
+ void __iomem *reg;
++ int ret;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 1 */
+ val = readl(reg + SUN8I_A33_PLL_AUDIO_REG);
+@@ -805,7 +805,9 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
+
+- of_sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a33_ccu_desc);
++ if (ret)
++ return ret;
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb);
+@@ -813,6 +815,24 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
+ /* Reparent CPU during PLL CPU rate changes */
+ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+ &sun8i_a33_cpu_nb);
++
++ return 0;
+ }
+-CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu",
+- sun8i_a33_ccu_setup);
++
++static const struct of_device_id sun8i_a33_ccu_ids[] = {
++ { .compatible = "allwinner,sun8i-a33-ccu" },
++ { }
++};
++
++static struct platform_driver sun8i_a33_ccu_driver = {
++ .probe = sun8i_a33_ccu_probe,
++ .driver = {
++ .name = "sun8i-a33-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun8i_a33_ccu_ids,
++ },
++};
++module_platform_driver(sun8i_a33_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+index d2fc2903787d..e058cf691aea 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+@@ -5,7 +5,9 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -1137,24 +1139,29 @@ static struct ccu_mux_nb sun8i_h3_cpu_nb = {
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+ };
+
+-static void __init sunxi_h3_h5_ccu_init(struct device_node *node,
+- const struct sunxi_ccu_desc *desc)
++static int sun8i_h3_ccu_probe(struct platform_device *pdev)
+ {
++ const struct sunxi_ccu_desc *desc;
+ void __iomem *reg;
++ int ret;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
++
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 1 */
+ val = readl(reg + SUN8I_H3_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
+
+- of_sunxi_ccu_probe(node, reg, desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, desc);
++ if (ret)
++ return ret;
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb);
+@@ -1162,18 +1169,31 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node,
+ /* Reparent CPU during PLL CPU rate changes */
+ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+ &sun8i_h3_cpu_nb);
+-}
+
+-static void __init sun8i_h3_ccu_setup(struct device_node *node)
+-{
+- sunxi_h3_h5_ccu_init(node, &sun8i_h3_ccu_desc);
++ return 0;
+ }
+-CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu",
+- sun8i_h3_ccu_setup);
+
+-static void __init sun50i_h5_ccu_setup(struct device_node *node)
+-{
+- sunxi_h3_h5_ccu_init(node, &sun50i_h5_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun50i_h5_ccu, "allwinner,sun50i-h5-ccu",
+- sun50i_h5_ccu_setup);
++static const struct of_device_id sun8i_h3_ccu_ids[] = {
++ {
++ .compatible = "allwinner,sun8i-h3-ccu",
++ .data = &sun8i_h3_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun50i-h5-ccu",
++ .data = &sun50i_h5_ccu_desc,
++ },
++ { }
++};
++
++static struct platform_driver sun8i_h3_ccu_driver = {
++ .probe = sun8i_h3_ccu_probe,
++ .driver = {
++ .name = "sun8i-h3-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun8i_h3_ccu_ids,
++ },
++};
++module_platform_driver(sun8i_h3_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c
+index 9e754d1f754a..5b7fab832a52 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c
+@@ -4,7 +4,8 @@
+ */
+
+ #include <linux/clk-provider.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
+ #include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+@@ -254,37 +255,47 @@ static const struct sunxi_ccu_desc sun50i_a64_r_ccu_desc = {
+ .num_resets = ARRAY_SIZE(sun50i_a64_r_ccu_resets),
+ };
+
+-static void __init sunxi_r_ccu_init(struct device_node *node,
+- const struct sunxi_ccu_desc *desc)
++static int sun8i_r_ccu_probe(struct platform_device *pdev)
+ {
++ const struct sunxi_ccu_desc *desc;
+ void __iomem *reg;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
+
+- of_sunxi_ccu_probe(node, reg, desc);
+-}
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+-static void __init sun8i_a83t_r_ccu_setup(struct device_node *node)
+-{
+- sunxi_r_ccu_init(node, &sun8i_a83t_r_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, desc);
+ }
+-CLK_OF_DECLARE(sun8i_a83t_r_ccu, "allwinner,sun8i-a83t-r-ccu",
+- sun8i_a83t_r_ccu_setup);
+
+-static void __init sun8i_h3_r_ccu_setup(struct device_node *node)
+-{
+- sunxi_r_ccu_init(node, &sun8i_h3_r_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun8i_h3_r_ccu, "allwinner,sun8i-h3-r-ccu",
+- sun8i_h3_r_ccu_setup);
++static const struct of_device_id sun8i_r_ccu_ids[] = {
++ {
++ .compatible = "allwinner,sun8i-a83t-r-ccu",
++ .data = &sun8i_a83t_r_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun8i-h3-r-ccu",
++ .data = &sun8i_h3_r_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun50i-a64-r-ccu",
++ .data = &sun50i_a64_r_ccu_desc,
++ },
++ { }
++};
+
+-static void __init sun50i_a64_r_ccu_setup(struct device_node *node)
+-{
+- sunxi_r_ccu_init(node, &sun50i_a64_r_ccu_desc);
+-}
+-CLK_OF_DECLARE(sun50i_a64_r_ccu, "allwinner,sun50i-a64-r-ccu",
+- sun50i_a64_r_ccu_setup);
++static struct platform_driver sun8i_r_ccu_driver = {
++ .probe = sun8i_r_ccu_probe,
++ .driver = {
++ .name = "sun8i-r-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun8i_r_ccu_ids,
++ },
++};
++module_platform_driver(sun8i_r_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+index ce150f83ab54..87f87d6ea3ad 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
++++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c
+@@ -8,7 +8,9 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -805,38 +807,49 @@ static const struct sunxi_ccu_desc sun8i_v3_ccu_desc = {
+ .num_resets = ARRAY_SIZE(sun8i_v3_ccu_resets),
+ };
+
+-static void __init sun8i_v3_v3s_ccu_init(struct device_node *node,
+- const struct sunxi_ccu_desc *ccu_desc)
++static int sun8i_v3s_ccu_probe(struct platform_device *pdev)
+ {
++ const struct sunxi_ccu_desc *desc;
+ void __iomem *reg;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ desc = of_device_get_match_data(&pdev->dev);
++ if (!desc)
++ return -EINVAL;
++
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 1 */
+ val = readl(reg + SUN8I_V3S_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG);
+
+- of_sunxi_ccu_probe(node, reg, ccu_desc);
+-}
+-
+-static void __init sun8i_v3s_ccu_setup(struct device_node *node)
+-{
+- sun8i_v3_v3s_ccu_init(node, &sun8i_v3s_ccu_desc);
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, desc);
+ }
+
+-static void __init sun8i_v3_ccu_setup(struct device_node *node)
+-{
+- sun8i_v3_v3s_ccu_init(node, &sun8i_v3_ccu_desc);
+-}
++static const struct of_device_id sun8i_v3s_ccu_ids[] = {
++ {
++ .compatible = "allwinner,sun8i-v3-ccu",
++ .data = &sun8i_v3_ccu_desc,
++ },
++ {
++ .compatible = "allwinner,sun8i-v3s-ccu",
++ .data = &sun8i_v3s_ccu_desc,
++ },
++ { }
++};
+
+-CLK_OF_DECLARE(sun8i_v3s_ccu, "allwinner,sun8i-v3s-ccu",
+- sun8i_v3s_ccu_setup);
++static struct platform_driver sun8i_v3s_ccu_driver = {
++ .probe = sun8i_v3s_ccu_probe,
++ .driver = {
++ .name = "sun8i-v3s-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun8i_v3s_ccu_ids,
++ },
++};
++module_platform_driver(sun8i_v3s_ccu_driver);
+
+-CLK_OF_DECLARE(sun8i_v3_ccu, "allwinner,sun8i-v3-ccu",
+- sun8i_v3_ccu_setup);
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c b/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
+index 61ad7ee91c11..a36dcab517e3 100644
+--- a/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
++++ b/drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c
+@@ -6,7 +6,8 @@
+
+ #include <linux/clk-provider.h>
+ #include <linux/io.h>
+-#include <linux/of_address.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
+
+ #include "ccu_common.h"
+ #include "ccu_reset.h"
+@@ -525,20 +526,21 @@ static struct ccu_mux_nb suniv_cpu_nb = {
+ static void __init suniv_f1c100s_ccu_setup(struct device_node *node)
+ {
+ void __iomem *reg;
++ int ret;
+ u32 val;
+
+- reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+- if (IS_ERR(reg)) {
+- pr_err("%pOF: Could not map the clock registers\n", node);
+- return;
+- }
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUNIV_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUNIV_PLL_AUDIO_REG);
+
+- of_sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &suniv_ccu_desc);
++ if (ret)
++ return ret;
+
+ /* Gate then ungate PLL CPU after any rate changes */
+ ccu_pll_notifier_register(&suniv_pll_cpu_nb);
+@@ -546,6 +548,24 @@ static void __init suniv_f1c100s_ccu_setup(struct device_node *node)
+ /* Reparent CPU during PLL CPU rate changes */
+ ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
+ &suniv_cpu_nb);
++
++ return 0;
+ }
+-CLK_OF_DECLARE(suniv_f1c100s_ccu, "allwinner,suniv-f1c100s-ccu",
+- suniv_f1c100s_ccu_setup);
++
++static const struct of_device_id suniv_f1c100s_ccu_ids[] = {
++ { .compatible = "allwinner,suniv-f1c100s-ccu" },
++ { }
++};
++
++static struct platform_driver suniv_f1c100s_ccu_driver = {
++ .probe = suniv_f1c100s_ccu_probe,
++ .driver = {
++ .name = "suniv-f1c100s-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = suniv_f1c100s_ccu_ids,
++ },
++};
++module_platform_driver(suniv_f1c100s_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+--
+2.20.1
+
--- /dev/null
+From c1ddd74ba3ddff59352333477f66a85bbfb414eb Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 18 Jul 2021 18:10:44 -0500
+Subject: [PATCH 039/124] clk: sunxi-ng: Allow the CCU core to be built as a
+ module
+
+Like the individual CCU drivers, it can be beneficial for memory
+consumption of cross-platform configurations to only load the CCU core
+on the relevant platform. For example, a generic arm64 kernel sees the
+following improvement when building the CCU core and drivers as modules:
+
+ before:
+ text data bss dec hex filename
+ 13882360 5251670 360800 19494830 12977ae vmlinux
+
+ after:
+ text data bss dec hex filename
+ 13734787 5086442 360800 19182029 124b1cd vmlinux
+
+So the result is a 390KB total reduction in kernel image size.
+
+The one early clock provider (sun5i) requires the core to be built in.
+
+Now that loading the MMC driver will trigger loading the CCU core, the
+MMC timing mode functions do not need a compile-time fallback.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/Makefile | 2 +-
+ drivers/clk/sunxi-ng/Kconfig | 3 ++-
+ drivers/clk/sunxi-ng/Makefile | 33 +++++++++++++++++--------------
+ drivers/clk/sunxi-ng/ccu_common.c | 3 +++
+ drivers/mmc/host/Kconfig | 1 +
+ include/linux/clk/sunxi-ng.h | 15 --------------
+ 6 files changed, 25 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
+index e42312121e51..6afe36bd2c0a 100644
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -110,7 +110,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/
+ obj-y += sprd/
+ obj-$(CONFIG_ARCH_STI) += st/
+ obj-$(CONFIG_ARCH_SUNXI) += sunxi/
+-obj-$(CONFIG_SUNXI_CCU) += sunxi-ng/
++obj-y += sunxi-ng/
+ obj-$(CONFIG_ARCH_TEGRA) += tegra/
+ obj-y += ti/
+ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
+diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
+index cbae3612a9ef..ee383658ff4d 100644
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config SUNXI_CCU
+- bool "Clock support for Allwinner SoCs"
++ tristate "Clock support for Allwinner SoCs"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ select RESET_CONTROLLER
+ default ARCH_SUNXI
+@@ -52,6 +52,7 @@ config SUN5I_CCU
+ bool "Support for the Allwinner sun5i family CCM"
+ default MACH_SUN5I
+ depends on MACH_SUN5I || COMPILE_TEST
++ depends on SUNXI_CCU=y
+
+ config SUN6I_A31_CCU
+ tristate "Support for the Allwinner A31/A31s CCU"
+diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
+index 1020ed49a588..659d55150c32 100644
+--- a/drivers/clk/sunxi-ng/Makefile
++++ b/drivers/clk/sunxi-ng/Makefile
+@@ -1,24 +1,27 @@
+ # SPDX-License-Identifier: GPL-2.0
++
++obj-$(CONFIG_SUNXI_CCU) += sunxi-ccu.o
++
+ # Common objects
+-obj-y += ccu_common.o
+-obj-y += ccu_mmc_timing.o
+-obj-y += ccu_reset.o
++sunxi-ccu-y += ccu_common.o
++sunxi-ccu-y += ccu_mmc_timing.o
++sunxi-ccu-y += ccu_reset.o
+
+ # Base clock types
+-obj-y += ccu_div.o
+-obj-y += ccu_frac.o
+-obj-y += ccu_gate.o
+-obj-y += ccu_mux.o
+-obj-y += ccu_mult.o
+-obj-y += ccu_phase.o
+-obj-y += ccu_sdm.o
++sunxi-ccu-y += ccu_div.o
++sunxi-ccu-y += ccu_frac.o
++sunxi-ccu-y += ccu_gate.o
++sunxi-ccu-y += ccu_mux.o
++sunxi-ccu-y += ccu_mult.o
++sunxi-ccu-y += ccu_phase.o
++sunxi-ccu-y += ccu_sdm.o
+
+ # Multi-factor clocks
+-obj-y += ccu_nk.o
+-obj-y += ccu_nkm.o
+-obj-y += ccu_nkmp.o
+-obj-y += ccu_nm.o
+-obj-y += ccu_mp.o
++sunxi-ccu-y += ccu_nk.o
++sunxi-ccu-y += ccu_nkm.o
++sunxi-ccu-y += ccu_nkmp.o
++sunxi-ccu-y += ccu_nm.o
++sunxi-ccu-y += ccu_mp.o
+
+ # SoC support
+ obj-$(CONFIG_SUNIV_F1C100S_CCU) += suniv-f1c100s-ccu.o
+diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
+index 6afdedbce6a2..8d28a7a079d0 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.c
++++ b/drivers/clk/sunxi-ng/ccu_common.c
+@@ -9,6 +9,7 @@
+ #include <linux/clk-provider.h>
+ #include <linux/device.h>
+ #include <linux/iopoll.h>
++#include <linux/module.h>
+ #include <linux/slab.h>
+
+ #include "ccu_common.h"
+@@ -214,3 +215,5 @@ void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+ kfree(ccu);
+ }
+ }
++
++MODULE_LICENSE("GPL");
+diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
+index ccc148cdb5ee..cd9f3c626053 100644
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -964,6 +964,7 @@ config MMC_REALTEK_USB
+ config MMC_SUNXI
+ tristate "Allwinner sunxi SD/MMC Host Controller support"
+ depends on ARCH_SUNXI || COMPILE_TEST
++ depends on SUNXI_CCU
+ help
+ This selects support for the SD/MMC Host Controller on
+ Allwinner sunxi SoCs.
+diff --git a/include/linux/clk/sunxi-ng.h b/include/linux/clk/sunxi-ng.h
+index 3cd14acde0a1..cf32123b39f5 100644
+--- a/include/linux/clk/sunxi-ng.h
++++ b/include/linux/clk/sunxi-ng.h
+@@ -6,22 +6,7 @@
+ #ifndef _LINUX_CLK_SUNXI_NG_H_
+ #define _LINUX_CLK_SUNXI_NG_H_
+
+-#include <linux/errno.h>
+-
+-#ifdef CONFIG_SUNXI_CCU
+ int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode);
+ int sunxi_ccu_get_mmc_timing_mode(struct clk *clk);
+-#else
+-static inline int sunxi_ccu_set_mmc_timing_mode(struct clk *clk,
+- bool new_mode)
+-{
+- return -ENOTSUPP;
+-}
+-
+-static inline int sunxi_ccu_get_mmc_timing_mode(struct clk *clk)
+-{
+- return -ENOTSUPP;
+-}
+-#endif
+
+ #endif
+--
+2.20.1
+
--- /dev/null
+From 88f4f9b057dda3d521fb13d7a7fbd09cf95fda41 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Thu, 20 May 2021 00:07:32 -0500
+Subject: [PATCH 040/124] clk: sunxi-ng: mux: Allow muxes to have keys
+
+The muxes in the RTC can only be updated when setting a key field to a
+specific value. Add a feature flag to denote muxes with this property.
+
+Since so far the key value is always the same, it does not need to be
+provided separately for each mux.
+
+Series-changes: 3
+ - Drop the SUNXI_CCU_MUX_HW_WITH_KEY macro, since it is no longer used.
+
+Cover-changes: 3
+ - Also drop the patch adding the SUNXI_CCU_MUX_DATA_WITH_GATE macro.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_common.h | 1 +
+ drivers/clk/sunxi-ng/ccu_mux.c | 7 +++++++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h
+index 98a1834b58bb..fbf16c6b896d 100644
+--- a/drivers/clk/sunxi-ng/ccu_common.h
++++ b/drivers/clk/sunxi-ng/ccu_common.h
+@@ -17,6 +17,7 @@
+ #define CCU_FEATURE_LOCK_REG BIT(5)
+ #define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
+ #define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7)
++#define CCU_FEATURE_KEY_FIELD BIT(8)
+
+ /* MMC timing mode switch bit */
+ #define CCU_MMC_NEW_TIMING_MODE BIT(30)
+diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
+index 2306a1cd83e4..1d557e323169 100644
+--- a/drivers/clk/sunxi-ng/ccu_mux.c
++++ b/drivers/clk/sunxi-ng/ccu_mux.c
+@@ -12,6 +12,8 @@
+ #include "ccu_gate.h"
+ #include "ccu_mux.h"
+
++#define CCU_MUX_KEY_VALUE 0x16aa0000
++
+ static u16 ccu_mux_get_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index)
+@@ -191,6 +193,11 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
+ spin_lock_irqsave(common->lock, flags);
+
+ reg = readl(common->base + common->reg);
++
++ /* The key field always reads as zero. */
++ if (common->features & CCU_FEATURE_KEY_FIELD)
++ reg |= CCU_MUX_KEY_VALUE;
++
+ reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift);
+ writel(reg | (index << cm->shift), common->base + common->reg);
+
+--
+2.20.1
+
--- /dev/null
+From 442fb5f5b81fbf5535d77859af73d2c05c930956 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:43:58 -0500
+Subject: [PATCH 041/124] of/irq: Use interrupts-extended to find parent
+
+Some OF irqchips, such as the RISC-V PLIC, use interrupts-extended to
+specify their parent domain(s). So that binding does not allow using the
+interrupt-parent property in the irqchip node. That prevents of_irq_init
+from properly detecting the irqchip hierarchy.
+
+If no interrupt-parent property is present in the enclosing bus or root
+node, then desc->interrupt_parent will be NULL for both the per-CPU
+RISC-V INTCs (the actual root domains) and the PLIC. Similarly, if the
+bus or root node specifies `interrupt-parent = <&plic>`, then
+of_irq_init will hit the `desc->interrupt_parent == np` check, and again
+all parents will be NULL. So things happen to work today for some boards
+due to Makefile ordering.
+
+However, things break when another irqchip (call it "foo") is stacked
+with the PLIC. The bus/root node will have `interrupt-parent = <&foo>`,
+since that is what all of the other peripherals need. When of_irq_init
+runs, it will try to find the PLIC's parent domain. But because
+of_irq_find_parent ignores interrupts-extended, it will fall back to the
+interrupt-parent of the PLIC's parent node (i.e. the bus or root node),
+and see "foo" as the PLIC's parent domain. But this is wrong, because
+"foo" is actually the PLIC's child domain!
+
+So of_irq_init wrongly attempts to init the stacked irqchip before the
+PLIC. This fails and prevents boot.
+
+Fix this by having of_irq_find_parent return the first node referenced
+by interrupts-extended when that property is present. Even if the
+property references multiple different IRQ domains, this will still work
+reliably in of_irq_init as long as all referenced domains are the same
+distance from some root domain (e.g. the RISC-V INTCs referenced by the
+PLIC are always all root domains).
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/of/irq.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/of/irq.c b/drivers/of/irq.c
+index 352e14b007e7..d84d27fb2203 100644
+--- a/drivers/of/irq.c
++++ b/drivers/of/irq.c
+@@ -60,7 +60,8 @@ struct device_node *of_irq_find_parent(struct device_node *child)
+ return NULL;
+
+ do {
+- if (of_property_read_u32(child, "interrupt-parent", &parent)) {
++ if (of_property_read_u32(child, "interrupt-parent", &parent) &&
++ of_property_read_u32(child, "interrupts-extended", &parent)) {
+ p = of_get_parent(child);
+ } else {
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+--
+2.20.1
+
--- /dev/null
+From 10c33c9b9734fc0c93d4766c1c7bfa87739ef712 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Fri, 25 Sep 2020 11:40:49 +0530
+Subject: [PATCH 042/124] RISC-V: Use SBI SRST extension when available
+
+The SBI SRST extension provides a standard way to poweroff and
+reboot the system irrespective to whether Linux RISC-V S-mode
+is running natively (HS-mode) or inside Guest/VM (VS-mode).
+
+The SBI SRST extension is available in the SBI v0.3 specification.
+(Refer, https://github.com/riscv-non-isa/riscv-sbi-doc/releases/tag/v0.3.0)
+
+This patch extends Linux RISC-V SBI implementation to detect
+and use SBI SRST extension.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Reviewed-by: Atish Patra <atish.patra@wdc.com>
+---
+ arch/riscv/include/asm/sbi.h | 24 ++++++++++++++++++++++++
+ arch/riscv/kernel/sbi.c | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 59 insertions(+)
+
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index 0d42693cb65e..289621da4a2a 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -27,6 +27,7 @@ enum sbi_ext_id {
+ SBI_EXT_IPI = 0x735049,
+ SBI_EXT_RFENCE = 0x52464E43,
+ SBI_EXT_HSM = 0x48534D,
++ SBI_EXT_SRST = 0x53525354,
+ };
+
+ enum sbi_ext_base_fid {
+@@ -70,6 +71,21 @@ enum sbi_hsm_hart_status {
+ SBI_HSM_HART_STATUS_STOP_PENDING,
+ };
+
++enum sbi_ext_srst_fid {
++ SBI_EXT_SRST_RESET = 0,
++};
++
++enum sbi_srst_reset_type {
++ SBI_SRST_RESET_TYPE_SHUTDOWN = 0,
++ SBI_SRST_RESET_TYPE_COLD_REBOOT,
++ SBI_SRST_RESET_TYPE_WARM_REBOOT,
++};
++
++enum sbi_srst_reset_reason {
++ SBI_SRST_RESET_REASON_NONE = 0,
++ SBI_SRST_RESET_REASON_SYS_FAILURE,
++};
++
+ #define SBI_SPEC_VERSION_DEFAULT 0x1
+ #define SBI_SPEC_VERSION_MAJOR_SHIFT 24
+ #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
+@@ -148,6 +164,14 @@ static inline unsigned long sbi_minor_version(void)
+ return sbi_spec_version & SBI_SPEC_VERSION_MINOR_MASK;
+ }
+
++/* Make SBI version */
++static inline unsigned long sbi_mk_version(unsigned long major,
++ unsigned long minor)
++{
++ return ((major & SBI_SPEC_VERSION_MAJOR_MASK) <<
++ SBI_SPEC_VERSION_MAJOR_SHIFT) | minor;
++}
++
+ int sbi_err_map_linux_errno(int err);
+ #else /* CONFIG_RISCV_SBI */
+ static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; }
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 7402a417f38e..9a84f0cb5175 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -7,6 +7,7 @@
+
+ #include <linux/init.h>
+ #include <linux/pm.h>
++#include <linux/reboot.h>
+ #include <asm/sbi.h>
+ #include <asm/smp.h>
+
+@@ -501,6 +502,32 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
+ }
+ EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid);
+
++static void sbi_srst_reset(unsigned long type, unsigned long reason)
++{
++ sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason,
++ 0, 0, 0, 0);
++ pr_warn("%s: type=0x%lx reason=0x%lx failed\n",
++ __func__, type, reason);
++}
++
++static int sbi_srst_reboot(struct notifier_block *this,
++ unsigned long mode, void *cmd)
++{
++ sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ?
++ SBI_SRST_RESET_TYPE_WARM_REBOOT :
++ SBI_SRST_RESET_TYPE_COLD_REBOOT,
++ SBI_SRST_RESET_REASON_NONE);
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block sbi_srst_reboot_nb;
++
++static void sbi_srst_power_off(void)
++{
++ sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN,
++ SBI_SRST_RESET_REASON_NONE);
++}
++
+ /**
+ * sbi_probe_extension() - Check if an SBI extension ID is supported or not.
+ * @extid: The extension ID to be probed.
+@@ -608,6 +635,14 @@ void __init sbi_init(void)
+ } else {
+ __sbi_rfence = __sbi_rfence_v01;
+ }
++ if ((sbi_spec_version >= sbi_mk_version(0, 3)) &&
++ (sbi_probe_extension(SBI_EXT_SRST) > 0)) {
++ pr_info("SBI SRST extension detected\n");
++ pm_power_off = sbi_srst_power_off;
++ sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot;
++ sbi_srst_reboot_nb.priority = 192;
++ register_restart_handler(&sbi_srst_reboot_nb);
++ }
+ } else {
+ __sbi_set_timer = __sbi_set_timer_v01;
+ __sbi_send_ipi = __sbi_send_ipi_v01;
+--
+2.20.1
+
--- /dev/null
+From 1efcd21929800a5435e6f6cfcf8397f7fe621a33 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Mon, 8 Feb 2021 17:00:06 +0530
+Subject: [PATCH 043/124] RISC-V: Enable CPU_IDLE drivers
+
+We force select CPU_PM and provide asm/cpuidle.h so that we can
+use CPU IDLE drivers for Linux RISC-V kernel.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/Kconfig | 7 +++++++
+ arch/riscv/configs/defconfig | 1 +
+ arch/riscv/configs/rv32_defconfig | 1 +
+ arch/riscv/include/asm/cpuidle.h | 24 ++++++++++++++++++++++++
+ arch/riscv/kernel/process.c | 3 ++-
+ 5 files changed, 35 insertions(+), 1 deletion(-)
+ create mode 100644 arch/riscv/include/asm/cpuidle.h
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index c28b743eba57..655f35ea2e74 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -45,6 +45,7 @@ config RISCV
+ select CLONE_BACKWARDS
+ select CLINT_TIMER if !MMU
+ select COMMON_CLK
++ select CPU_PM if CPU_IDLE
+ select EDAC_SUPPORT
+ select GENERIC_ARCH_TOPOLOGY if SMP
+ select GENERIC_ATOMIC64 if !64BIT
+@@ -566,3 +567,9 @@ menu "Power management options"
+ source "kernel/power/Kconfig"
+
+ endmenu
++
++menu "CPU Power Management"
++
++source "drivers/cpuidle/Kconfig"
++
++endmenu
+diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
+index 4ebc80315f01..66552914be3f 100644
+--- a/arch/riscv/configs/defconfig
++++ b/arch/riscv/configs/defconfig
+@@ -19,6 +19,7 @@ CONFIG_SOC_VIRT=y
+ CONFIG_SOC_MICROCHIP_POLARFIRE=y
+ CONFIG_SMP=y
+ CONFIG_HOTPLUG_CPU=y
++CONFIG_CPU_IDLE=y
+ CONFIG_JUMP_LABEL=y
+ CONFIG_MODULES=y
+ CONFIG_MODULE_UNLOAD=y
+diff --git a/arch/riscv/configs/rv32_defconfig b/arch/riscv/configs/rv32_defconfig
+index 434ef5b64599..3093f8efd7e5 100644
+--- a/arch/riscv/configs/rv32_defconfig
++++ b/arch/riscv/configs/rv32_defconfig
+@@ -19,6 +19,7 @@ CONFIG_SOC_VIRT=y
+ CONFIG_ARCH_RV32I=y
+ CONFIG_SMP=y
+ CONFIG_HOTPLUG_CPU=y
++CONFIG_CPU_IDLE=y
+ CONFIG_JUMP_LABEL=y
+ CONFIG_MODULES=y
+ CONFIG_MODULE_UNLOAD=y
+diff --git a/arch/riscv/include/asm/cpuidle.h b/arch/riscv/include/asm/cpuidle.h
+new file mode 100644
+index 000000000000..71fdc607d4bc
+--- /dev/null
++++ b/arch/riscv/include/asm/cpuidle.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021 Allwinner Ltd
++ * Copyright (C) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#ifndef _ASM_RISCV_CPUIDLE_H
++#define _ASM_RISCV_CPUIDLE_H
++
++#include <asm/barrier.h>
++#include <asm/processor.h>
++
++static inline void cpu_do_idle(void)
++{
++ /*
++ * Add mb() here to ensure that all
++ * IO/MEM accesses are completed prior
++ * to entering WFI.
++ */
++ mb();
++ wait_for_interrupt();
++}
++
++#endif
+diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
+index 03ac3aa611f5..504b496787aa 100644
+--- a/arch/riscv/kernel/process.c
++++ b/arch/riscv/kernel/process.c
+@@ -23,6 +23,7 @@
+ #include <asm/string.h>
+ #include <asm/switch_to.h>
+ #include <asm/thread_info.h>
++#include <asm/cpuidle.h>
+
+ register unsigned long gp_in_global __asm__("gp");
+
+@@ -37,7 +38,7 @@ extern asmlinkage void ret_from_kernel_thread(void);
+
+ void arch_cpu_idle(void)
+ {
+- wait_for_interrupt();
++ cpu_do_idle();
+ raw_local_irq_enable();
+ }
+
+--
+2.20.1
+
--- /dev/null
+From 10b6c02d5a9baadfeb9b1d16ef2c62de35d8bfa5 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Sun, 10 Oct 2021 10:52:13 +0530
+Subject: [PATCH 044/124] RISC-V: Rename relocate() and make it global
+
+The low-level relocate() function enables mmu and relocates
+execution to link-time addresses. We rename relocate() function
+to relocate_enable_mmu() function which is more informative.
+
+Also, the relocate_enable_mmu() function will be used in the
+resume path when a CPU wakes-up from a non-retentive suspend
+so we make it global symbol.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+---
+ arch/riscv/kernel/head.S | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
+index ebd577b51c5c..ab3248f9cd25 100644
+--- a/arch/riscv/kernel/head.S
++++ b/arch/riscv/kernel/head.S
+@@ -89,7 +89,8 @@ pe_head_start:
+
+ .align 2
+ #ifdef CONFIG_MMU
+-relocate:
++ .global relocate_enable_mmu
++relocate_enable_mmu:
+ /* Relocate return address */
+ la a1, kernel_map
+ XIP_FIXUP_OFFSET a1
+@@ -184,7 +185,7 @@ secondary_start_common:
+ /* Enable virtual memory and relocate to virtual address */
+ la a0, swapper_pg_dir
+ XIP_FIXUP_OFFSET a0
+- call relocate
++ call relocate_enable_mmu
+ #endif
+ call setup_trap_vector
+ tail smp_callin
+@@ -324,7 +325,7 @@ clear_bss_done:
+ #ifdef CONFIG_MMU
+ la a0, early_pg_dir
+ XIP_FIXUP_OFFSET a0
+- call relocate
++ call relocate_enable_mmu
+ #endif /* CONFIG_MMU */
+
+ call setup_trap_vector
+--
+2.20.1
+
--- /dev/null
+From c79b186f87c991b22271451520d211de26e5fd7d Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Wed, 10 Feb 2021 09:13:08 +0530
+Subject: [PATCH 045/124] RISC-V: Add arch functions for non-retentive suspend
+ entry/exit
+
+The hart registers and CSRs are not preserved in non-retentative
+suspend state so we provide arch specific helper functions which
+will save/restore hart context upon entry/exit to non-retentive
+suspend state. These helper functions can be used by cpuidle
+drivers for non-retentive suspend entry/exit.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/include/asm/asm.h | 27 +++++++
+ arch/riscv/include/asm/suspend.h | 35 +++++++++
+ arch/riscv/kernel/Makefile | 2 +
+ arch/riscv/kernel/asm-offsets.c | 3 +
+ arch/riscv/kernel/head.S | 21 -----
+ arch/riscv/kernel/suspend.c | 86 +++++++++++++++++++++
+ arch/riscv/kernel/suspend_entry.S | 123 ++++++++++++++++++++++++++++++
+ 7 files changed, 276 insertions(+), 21 deletions(-)
+ create mode 100644 arch/riscv/include/asm/suspend.h
+ create mode 100644 arch/riscv/kernel/suspend.c
+ create mode 100644 arch/riscv/kernel/suspend_entry.S
+
+diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
+index 618d7c5af1a2..8afd81ad0c1e 100644
+--- a/arch/riscv/include/asm/asm.h
++++ b/arch/riscv/include/asm/asm.h
+@@ -67,4 +67,31 @@
+ #error "Unexpected __SIZEOF_SHORT__"
+ #endif
+
++#ifdef __ASSEMBLY__
++
++/* Common assembly source macros */
++
++#ifdef CONFIG_XIP_KERNEL
++.macro XIP_FIXUP_OFFSET reg
++ REG_L t0, _xip_fixup
++ add \reg, \reg, t0
++.endm
++.macro XIP_FIXUP_FLASH_OFFSET reg
++ la t1, __data_loc
++ li t0, XIP_OFFSET_MASK
++ and t1, t1, t0
++ li t1, XIP_OFFSET
++ sub t0, t0, t1
++ sub \reg, \reg, t0
++.endm
++_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
++#else
++.macro XIP_FIXUP_OFFSET reg
++.endm
++.macro XIP_FIXUP_FLASH_OFFSET reg
++.endm
++#endif /* CONFIG_XIP_KERNEL */
++
++#endif
++
+ #endif /* _ASM_RISCV_ASM_H */
+diff --git a/arch/riscv/include/asm/suspend.h b/arch/riscv/include/asm/suspend.h
+new file mode 100644
+index 000000000000..63e9f434fb89
+--- /dev/null
++++ b/arch/riscv/include/asm/suspend.h
+@@ -0,0 +1,35 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#ifndef _ASM_RISCV_SUSPEND_H
++#define _ASM_RISCV_SUSPEND_H
++
++#include <asm/ptrace.h>
++
++struct suspend_context {
++ /* Saved and restored by low-level functions */
++ struct pt_regs regs;
++ /* Saved and restored by high-level functions */
++ unsigned long scratch;
++ unsigned long tvec;
++ unsigned long ie;
++#ifdef CONFIG_MMU
++ unsigned long satp;
++#endif
++};
++
++/* Low-level CPU suspend entry function */
++int __cpu_suspend_enter(struct suspend_context *context);
++
++/* High-level CPU suspend which will save context and call finish() */
++int cpu_suspend(unsigned long arg,
++ int (*finish)(unsigned long arg,
++ unsigned long entry,
++ unsigned long context));
++
++/* Low-level CPU resume entry function */
++int __cpu_resume_enter(unsigned long hartid, unsigned long context);
++
++#endif
+diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
+index 3397ddac1a30..4320629c07c3 100644
+--- a/arch/riscv/kernel/Makefile
++++ b/arch/riscv/kernel/Makefile
+@@ -47,6 +47,8 @@ obj-$(CONFIG_SMP) += cpu_ops_spinwait.o
+ obj-$(CONFIG_MODULES) += module.o
+ obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
+
++obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o
++
+ obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
+ obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
+
+diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c
+index 90f8ce64fa6f..cf64dab398da 100644
+--- a/arch/riscv/kernel/asm-offsets.c
++++ b/arch/riscv/kernel/asm-offsets.c
+@@ -10,6 +10,7 @@
+ #include <linux/sched.h>
+ #include <asm/thread_info.h>
+ #include <asm/ptrace.h>
++#include <asm/suspend.h>
+
+ void asm_offsets(void);
+
+@@ -306,6 +307,8 @@ void asm_offsets(void)
+ - offsetof(struct task_struct, thread.fstate.f[0])
+ );
+
++ OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs);
++
+ /*
+ * We allocate a pt_regs on the stack when entering the kernel. This
+ * ensures the alignment is sane.
+diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
+index ab3248f9cd25..141a1ac308f7 100644
+--- a/arch/riscv/kernel/head.S
++++ b/arch/riscv/kernel/head.S
+@@ -15,27 +15,6 @@
+ #include <asm/image.h>
+ #include "efi-header.S"
+
+-#ifdef CONFIG_XIP_KERNEL
+-.macro XIP_FIXUP_OFFSET reg
+- REG_L t0, _xip_fixup
+- add \reg, \reg, t0
+-.endm
+-.macro XIP_FIXUP_FLASH_OFFSET reg
+- la t1, __data_loc
+- li t0, XIP_OFFSET_MASK
+- and t1, t1, t0
+- li t1, XIP_OFFSET
+- sub t0, t0, t1
+- sub \reg, \reg, t0
+-.endm
+-_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
+-#else
+-.macro XIP_FIXUP_OFFSET reg
+-.endm
+-.macro XIP_FIXUP_FLASH_OFFSET reg
+-.endm
+-#endif /* CONFIG_XIP_KERNEL */
+-
+ __HEAD
+ ENTRY(_start)
+ /*
+diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c
+new file mode 100644
+index 000000000000..49dddec30e99
+--- /dev/null
++++ b/arch/riscv/kernel/suspend.c
+@@ -0,0 +1,86 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#include <linux/ftrace.h>
++#include <asm/csr.h>
++#include <asm/suspend.h>
++
++static void suspend_save_csrs(struct suspend_context *context)
++{
++ context->scratch = csr_read(CSR_SCRATCH);
++ context->tvec = csr_read(CSR_TVEC);
++ context->ie = csr_read(CSR_IE);
++
++ /*
++ * No need to save/restore IP CSR (i.e. MIP or SIP) because:
++ *
++ * 1. For no-MMU (M-mode) kernel, the bits in MIP are set by
++ * external devices (such as interrupt controller, timer, etc).
++ * 2. For MMU (S-mode) kernel, the bits in SIP are set by
++ * M-mode firmware and external devices (such as interrupt
++ * controller, etc).
++ */
++
++#ifdef CONFIG_MMU
++ context->satp = csr_read(CSR_SATP);
++#endif
++}
++
++static void suspend_restore_csrs(struct suspend_context *context)
++{
++ csr_write(CSR_SCRATCH, context->scratch);
++ csr_write(CSR_TVEC, context->tvec);
++ csr_write(CSR_IE, context->ie);
++
++#ifdef CONFIG_MMU
++ csr_write(CSR_SATP, context->satp);
++#endif
++}
++
++int cpu_suspend(unsigned long arg,
++ int (*finish)(unsigned long arg,
++ unsigned long entry,
++ unsigned long context))
++{
++ int rc = 0;
++ struct suspend_context context = { 0 };
++
++ /* Finisher should be non-NULL */
++ if (!finish)
++ return -EINVAL;
++
++ /* Save additional CSRs*/
++ suspend_save_csrs(&context);
++
++ /*
++ * Function graph tracer state gets incosistent when the kernel
++ * calls functions that never return (aka finishers) hence disable
++ * graph tracing during their execution.
++ */
++ pause_graph_tracing();
++
++ /* Save context on stack */
++ if (__cpu_suspend_enter(&context)) {
++ /* Call the finisher */
++ rc = finish(arg, __pa_symbol(__cpu_resume_enter),
++ (ulong)&context);
++
++ /*
++ * Should never reach here, unless the suspend finisher
++ * fails. Successful cpu_suspend() should return from
++ * __cpu_resume_entry()
++ */
++ if (!rc)
++ rc = -EOPNOTSUPP;
++ }
++
++ /* Enable function graph tracer */
++ unpause_graph_tracing();
++
++ /* Restore additional CSRs */
++ suspend_restore_csrs(&context);
++
++ return rc;
++}
+diff --git a/arch/riscv/kernel/suspend_entry.S b/arch/riscv/kernel/suspend_entry.S
+new file mode 100644
+index 000000000000..b8d20decfc28
+--- /dev/null
++++ b/arch/riscv/kernel/suspend_entry.S
+@@ -0,0 +1,123 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#include <linux/linkage.h>
++#include <asm/asm.h>
++#include <asm/asm-offsets.h>
++#include <asm/csr.h>
++
++ .text
++ .altmacro
++ .option norelax
++
++ENTRY(__cpu_suspend_enter)
++ /* Save registers (except A0 and T0-T6) */
++ REG_S ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0)
++ REG_S sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0)
++ REG_S gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0)
++ REG_S tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0)
++ REG_S s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0)
++ REG_S s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0)
++ REG_S a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0)
++ REG_S a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0)
++ REG_S a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0)
++ REG_S a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0)
++ REG_S a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0)
++ REG_S a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0)
++ REG_S a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0)
++ REG_S s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0)
++ REG_S s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0)
++ REG_S s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0)
++ REG_S s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0)
++ REG_S s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0)
++ REG_S s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0)
++ REG_S s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0)
++ REG_S s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0)
++ REG_S s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0)
++ REG_S s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0)
++
++ /* Save CSRs */
++ csrr t0, CSR_EPC
++ REG_S t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0)
++ csrr t0, CSR_STATUS
++ REG_S t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0)
++ csrr t0, CSR_TVAL
++ REG_S t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0)
++ csrr t0, CSR_CAUSE
++ REG_S t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0)
++
++ /* Return non-zero value */
++ li a0, 1
++
++ /* Return to C code */
++ ret
++END(__cpu_suspend_enter)
++
++ENTRY(__cpu_resume_enter)
++ /* Load the global pointer */
++ .option push
++ .option norelax
++ la gp, __global_pointer$
++ .option pop
++
++#ifdef CONFIG_MMU
++ /* Save A0 and A1 */
++ add t0, a0, zero
++ add t1, a1, zero
++
++ /* Enable MMU */
++ la a0, swapper_pg_dir
++ XIP_FIXUP_OFFSET a0
++ call relocate_enable_mmu
++
++ /* Restore A0 and A1 */
++ add a0, t0, zero
++ add a1, t1, zero
++#endif
++
++ /* Make A0 point to suspend context */
++ add a0, a1, zero
++
++ /* Restore CSRs */
++ REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0)
++ csrw CSR_EPC, t0
++ REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0)
++ csrw CSR_STATUS, t0
++ REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0)
++ csrw CSR_TVAL, t0
++ REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0)
++ csrw CSR_CAUSE, t0
++
++ /* Restore registers (except A0 and T0-T6) */
++ REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0)
++ REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0)
++ REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0)
++ REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0)
++ REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0)
++ REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0)
++ REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0)
++ REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0)
++ REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0)
++ REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0)
++ REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0)
++ REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0)
++ REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0)
++ REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0)
++ REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0)
++ REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0)
++ REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0)
++ REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0)
++ REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0)
++ REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0)
++ REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0)
++ REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0)
++ REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0)
++
++ /* Return zero value */
++ add a0, zero, zero
++
++ /* Return to C code */
++ ret
++END(__cpu_resume_enter)
+--
+2.20.1
+
--- /dev/null
+From 6a6733ae3666b12d2288e8f75e4406c3e2137cf4 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Mon, 15 Feb 2021 10:43:39 +0530
+Subject: [PATCH 046/124] RISC-V: Add SBI HSM suspend related defines
+
+We add defines related to SBI HSM suspend call and also
+update HSM states naming as-per latest SBI specification.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+---
+ arch/riscv/include/asm/sbi.h | 27 ++++++++++++++++++++++-----
+ arch/riscv/kernel/cpu_ops_sbi.c | 2 +-
+ 2 files changed, 23 insertions(+), 6 deletions(-)
+
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index 289621da4a2a..ab9782f8da52 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -62,15 +62,32 @@ enum sbi_ext_hsm_fid {
+ SBI_EXT_HSM_HART_START = 0,
+ SBI_EXT_HSM_HART_STOP,
+ SBI_EXT_HSM_HART_STATUS,
++ SBI_EXT_HSM_HART_SUSPEND,
+ };
+
+-enum sbi_hsm_hart_status {
+- SBI_HSM_HART_STATUS_STARTED = 0,
+- SBI_HSM_HART_STATUS_STOPPED,
+- SBI_HSM_HART_STATUS_START_PENDING,
+- SBI_HSM_HART_STATUS_STOP_PENDING,
++enum sbi_hsm_hart_state {
++ SBI_HSM_STATE_STARTED = 0,
++ SBI_HSM_STATE_STOPPED,
++ SBI_HSM_STATE_START_PENDING,
++ SBI_HSM_STATE_STOP_PENDING,
++ SBI_HSM_STATE_SUSPENDED,
++ SBI_HSM_STATE_SUSPEND_PENDING,
++ SBI_HSM_STATE_RESUME_PENDING,
+ };
+
++#define SBI_HSM_SUSP_BASE_MASK 0x7fffffff
++#define SBI_HSM_SUSP_NON_RET_BIT 0x80000000
++#define SBI_HSM_SUSP_PLAT_BASE 0x10000000
++
++#define SBI_HSM_SUSPEND_RET_DEFAULT 0x00000000
++#define SBI_HSM_SUSPEND_RET_PLATFORM SBI_HSM_SUSP_PLAT_BASE
++#define SBI_HSM_SUSPEND_RET_LAST SBI_HSM_SUSP_BASE_MASK
++#define SBI_HSM_SUSPEND_NON_RET_DEFAULT SBI_HSM_SUSP_NON_RET_BIT
++#define SBI_HSM_SUSPEND_NON_RET_PLATFORM (SBI_HSM_SUSP_NON_RET_BIT | \
++ SBI_HSM_SUSP_PLAT_BASE)
++#define SBI_HSM_SUSPEND_NON_RET_LAST (SBI_HSM_SUSP_NON_RET_BIT | \
++ SBI_HSM_SUSP_BASE_MASK)
++
+ enum sbi_ext_srst_fid {
+ SBI_EXT_SRST_RESET = 0,
+ };
+diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c
+index 685fae72b7f5..5fd90f03a3e9 100644
+--- a/arch/riscv/kernel/cpu_ops_sbi.c
++++ b/arch/riscv/kernel/cpu_ops_sbi.c
+@@ -97,7 +97,7 @@ static int sbi_cpu_is_stopped(unsigned int cpuid)
+
+ rc = sbi_hsm_hart_get_status(hartid);
+
+- if (rc == SBI_HSM_HART_STATUS_STOPPED)
++ if (rc == SBI_HSM_STATE_STOPPED)
+ return 0;
+ return rc;
+ }
+--
+2.20.1
+
--- /dev/null
+From 21cdb37bf5cdf1b4466b2a5f9fb1494adc55feac Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Wed, 17 Feb 2021 18:52:00 +0530
+Subject: [PATCH 047/124] cpuidle: Factor-out power domain related code from
+ PSCI domain driver
+
+The generic power domain related code in PSCI domain driver is largely
+independent of PSCI and can be shared with RISC-V SBI domain driver
+hence we factor-out this code into dt_idle_genpd.c and dt_idle_genpd.h.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
+---
+ MAINTAINERS | 7 +
+ drivers/cpuidle/Kconfig | 4 +
+ drivers/cpuidle/Kconfig.arm | 1 +
+ drivers/cpuidle/Makefile | 1 +
+ drivers/cpuidle/cpuidle-psci-domain.c | 138 +-------------------
+ drivers/cpuidle/cpuidle-psci.h | 15 ++-
+ drivers/cpuidle/dt_idle_genpd.c | 177 ++++++++++++++++++++++++++
+ drivers/cpuidle/dt_idle_genpd.h | 50 ++++++++
+ 8 files changed, 258 insertions(+), 135 deletions(-)
+ create mode 100644 drivers/cpuidle/dt_idle_genpd.c
+ create mode 100644 drivers/cpuidle/dt_idle_genpd.h
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 3b79fd441dde..b209020decb9 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -4911,6 +4911,13 @@ S: Supported
+ F: drivers/cpuidle/cpuidle-psci.h
+ F: drivers/cpuidle/cpuidle-psci-domain.c
+
++CPUIDLE DRIVER - DT IDLE PM DOMAIN
++M: Ulf Hansson <ulf.hansson@linaro.org>
++L: linux-pm@vger.kernel.org
++S: Supported
++F: drivers/cpuidle/dt_idle_genpd.c
++F: drivers/cpuidle/dt_idle_genpd.h
++
+ CRAMFS FILESYSTEM
+ M: Nicolas Pitre <nico@fluxnic.net>
+ S: Maintained
+diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
+index c0aeedd66f02..f1afe7ab6b54 100644
+--- a/drivers/cpuidle/Kconfig
++++ b/drivers/cpuidle/Kconfig
+@@ -47,6 +47,10 @@ config CPU_IDLE_GOV_HALTPOLL
+ config DT_IDLE_STATES
+ bool
+
++config DT_IDLE_GENPD
++ depends on PM_GENERIC_DOMAINS_OF
++ bool
++
+ menu "ARM CPU Idle Drivers"
+ depends on ARM || ARM64
+ source "drivers/cpuidle/Kconfig.arm"
+diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
+index 334f83e56120..be12a9ca78f0 100644
+--- a/drivers/cpuidle/Kconfig.arm
++++ b/drivers/cpuidle/Kconfig.arm
+@@ -27,6 +27,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN
+ bool "PSCI CPU idle Domain"
+ depends on ARM_PSCI_CPUIDLE
+ depends on PM_GENERIC_DOMAINS_OF
++ select DT_IDLE_GENPD
+ default y
+ help
+ Select this to enable the PSCI based CPUidle driver to use PM domains,
+diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
+index 26bbc5e74123..11a26cef279f 100644
+--- a/drivers/cpuidle/Makefile
++++ b/drivers/cpuidle/Makefile
+@@ -6,6 +6,7 @@
+ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
+ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+ obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
++obj-$(CONFIG_DT_IDLE_GENPD) += dt_idle_genpd.o
+ obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o
+ obj-$(CONFIG_HALTPOLL_CPUIDLE) += cpuidle-haltpoll.o
+
+diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c
+index ff2c3f8e4668..755bbdfc5b82 100644
+--- a/drivers/cpuidle/cpuidle-psci-domain.c
++++ b/drivers/cpuidle/cpuidle-psci-domain.c
+@@ -47,73 +47,14 @@ static int psci_pd_power_off(struct generic_pm_domain *pd)
+ return 0;
+ }
+
+-static int psci_pd_parse_state_nodes(struct genpd_power_state *states,
+- int state_count)
+-{
+- int i, ret;
+- u32 psci_state, *psci_state_buf;
+-
+- for (i = 0; i < state_count; i++) {
+- ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode),
+- &psci_state);
+- if (ret)
+- goto free_state;
+-
+- psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
+- if (!psci_state_buf) {
+- ret = -ENOMEM;
+- goto free_state;
+- }
+- *psci_state_buf = psci_state;
+- states[i].data = psci_state_buf;
+- }
+-
+- return 0;
+-
+-free_state:
+- i--;
+- for (; i >= 0; i--)
+- kfree(states[i].data);
+- return ret;
+-}
+-
+-static int psci_pd_parse_states(struct device_node *np,
+- struct genpd_power_state **states, int *state_count)
+-{
+- int ret;
+-
+- /* Parse the domain idle states. */
+- ret = of_genpd_parse_idle_states(np, states, state_count);
+- if (ret)
+- return ret;
+-
+- /* Fill out the PSCI specifics for each found state. */
+- ret = psci_pd_parse_state_nodes(*states, *state_count);
+- if (ret)
+- kfree(*states);
+-
+- return ret;
+-}
+-
+-static void psci_pd_free_states(struct genpd_power_state *states,
+- unsigned int state_count)
+-{
+- int i;
+-
+- for (i = 0; i < state_count; i++)
+- kfree(states[i].data);
+- kfree(states);
+-}
+-
+ static int psci_pd_init(struct device_node *np, bool use_osi)
+ {
+ struct generic_pm_domain *pd;
+ struct psci_pd_provider *pd_provider;
+ struct dev_power_governor *pd_gov;
+- struct genpd_power_state *states = NULL;
+ int ret = -ENOMEM, state_count = 0;
+
+- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
++ pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node);
+ if (!pd)
+ goto out;
+
+@@ -121,22 +62,6 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
+ if (!pd_provider)
+ goto free_pd;
+
+- pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
+- if (!pd->name)
+- goto free_pd_prov;
+-
+- /*
+- * Parse the domain idle states and let genpd manage the state selection
+- * for those being compatible with "domain-idle-state".
+- */
+- ret = psci_pd_parse_states(np, &states, &state_count);
+- if (ret)
+- goto free_name;
+-
+- pd->free_states = psci_pd_free_states;
+- pd->name = kbasename(pd->name);
+- pd->states = states;
+- pd->state_count = state_count;
+ pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
+
+ /* Allow power off when OSI has been successfully enabled. */
+@@ -149,10 +74,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
+ pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;
+
+ ret = pm_genpd_init(pd, pd_gov, false);
+- if (ret) {
+- psci_pd_free_states(states, state_count);
+- goto free_name;
+- }
++ if (ret)
++ goto free_pd_prov;
+
+ ret = of_genpd_add_provider_simple(np, pd);
+ if (ret)
+@@ -166,12 +89,10 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
+
+ remove_pd:
+ pm_genpd_remove(pd);
+-free_name:
+- kfree(pd->name);
+ free_pd_prov:
+ kfree(pd_provider);
+ free_pd:
+- kfree(pd);
++ dt_idle_pd_free(pd);
+ out:
+ pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
+ return ret;
+@@ -195,30 +116,6 @@ static void psci_pd_remove(void)
+ }
+ }
+
+-static int psci_pd_init_topology(struct device_node *np)
+-{
+- struct device_node *node;
+- struct of_phandle_args child, parent;
+- int ret;
+-
+- for_each_child_of_node(np, node) {
+- if (of_parse_phandle_with_args(node, "power-domains",
+- "#power-domain-cells", 0, &parent))
+- continue;
+-
+- child.np = node;
+- child.args_count = 0;
+- ret = of_genpd_add_subdomain(&parent, &child);
+- of_node_put(parent.np);
+- if (ret) {
+- of_node_put(node);
+- return ret;
+- }
+- }
+-
+- return 0;
+-}
+-
+ static bool psci_pd_try_set_osi_mode(void)
+ {
+ int ret;
+@@ -282,7 +179,7 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev)
+ goto no_pd;
+
+ /* Link genpd masters/subdomains to model the CPU topology. */
+- ret = psci_pd_init_topology(np);
++ ret = dt_idle_pd_init_topology(np);
+ if (ret)
+ goto remove_pd;
+
+@@ -314,28 +211,3 @@ static int __init psci_idle_init_domains(void)
+ return platform_driver_register(&psci_cpuidle_domain_driver);
+ }
+ subsys_initcall(psci_idle_init_domains);
+-
+-struct device *psci_dt_attach_cpu(int cpu)
+-{
+- struct device *dev;
+-
+- dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci");
+- if (IS_ERR_OR_NULL(dev))
+- return dev;
+-
+- pm_runtime_irq_safe(dev);
+- if (cpu_online(cpu))
+- pm_runtime_get_sync(dev);
+-
+- dev_pm_syscore_device(dev, true);
+-
+- return dev;
+-}
+-
+-void psci_dt_detach_cpu(struct device *dev)
+-{
+- if (IS_ERR_OR_NULL(dev))
+- return;
+-
+- dev_pm_domain_detach(dev, false);
+-}
+diff --git a/drivers/cpuidle/cpuidle-psci.h b/drivers/cpuidle/cpuidle-psci.h
+index d8e925e84c27..4e132640ed64 100644
+--- a/drivers/cpuidle/cpuidle-psci.h
++++ b/drivers/cpuidle/cpuidle-psci.h
+@@ -10,8 +10,19 @@ void psci_set_domain_state(u32 state);
+ int psci_dt_parse_state_node(struct device_node *np, u32 *state);
+
+ #ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN
+-struct device *psci_dt_attach_cpu(int cpu);
+-void psci_dt_detach_cpu(struct device *dev);
++
++#include "dt_idle_genpd.h"
++
++static inline struct device *psci_dt_attach_cpu(int cpu)
++{
++ return dt_idle_attach_cpu(cpu, "psci");
++}
++
++static inline void psci_dt_detach_cpu(struct device *dev)
++{
++ dt_idle_detach_cpu(dev);
++}
++
+ #else
+ static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; }
+ static inline void psci_dt_detach_cpu(struct device *dev) { }
+diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c
+new file mode 100644
+index 000000000000..db385fd2507e
+--- /dev/null
++++ b/drivers/cpuidle/dt_idle_genpd.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * PM domains for CPUs via genpd.
++ *
++ * Copyright (C) 2019 Linaro Ltd.
++ * Author: Ulf Hansson <ulf.hansson@linaro.org>
++ *
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#define pr_fmt(fmt) "dt-idle-genpd: " fmt
++
++#include <linux/cpu.h>
++#include <linux/device.h>
++#include <linux/kernel.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_runtime.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++
++#include "dt_idle_genpd.h"
++
++static int pd_parse_state_nodes(
++ int (*parse_state)(struct device_node *, u32 *),
++ struct genpd_power_state *states, int state_count)
++{
++ int i, ret;
++ u32 state, *state_buf;
++
++ for (i = 0; i < state_count; i++) {
++ ret = parse_state(to_of_node(states[i].fwnode), &state);
++ if (ret)
++ goto free_state;
++
++ state_buf = kmalloc(sizeof(u32), GFP_KERNEL);
++ if (!state_buf) {
++ ret = -ENOMEM;
++ goto free_state;
++ }
++ *state_buf = state;
++ states[i].data = state_buf;
++ }
++
++ return 0;
++
++free_state:
++ i--;
++ for (; i >= 0; i--)
++ kfree(states[i].data);
++ return ret;
++}
++
++static int pd_parse_states(struct device_node *np,
++ int (*parse_state)(struct device_node *, u32 *),
++ struct genpd_power_state **states,
++ int *state_count)
++{
++ int ret;
++
++ /* Parse the domain idle states. */
++ ret = of_genpd_parse_idle_states(np, states, state_count);
++ if (ret)
++ return ret;
++
++ /* Fill out the dt specifics for each found state. */
++ ret = pd_parse_state_nodes(parse_state, *states, *state_count);
++ if (ret)
++ kfree(*states);
++
++ return ret;
++}
++
++static void pd_free_states(struct genpd_power_state *states,
++ unsigned int state_count)
++{
++ int i;
++
++ for (i = 0; i < state_count; i++)
++ kfree(states[i].data);
++ kfree(states);
++}
++
++void dt_idle_pd_free(struct generic_pm_domain *pd)
++{
++ pd_free_states(pd->states, pd->state_count);
++ kfree(pd->name);
++ kfree(pd);
++}
++
++struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
++ int (*parse_state)(struct device_node *, u32 *))
++{
++ struct generic_pm_domain *pd;
++ struct genpd_power_state *states = NULL;
++ int ret, state_count = 0;
++
++ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
++ if (!pd)
++ goto out;
++
++ pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
++ if (!pd->name)
++ goto free_pd;
++
++ /*
++ * Parse the domain idle states and let genpd manage the state selection
++ * for those being compatible with "domain-idle-state".
++ */
++ ret = pd_parse_states(np, parse_state, &states, &state_count);
++ if (ret)
++ goto free_name;
++
++ pd->free_states = pd_free_states;
++ pd->name = kbasename(pd->name);
++ pd->states = states;
++ pd->state_count = state_count;
++
++ pr_debug("alloc PM domain %s\n", pd->name);
++ return pd;
++
++free_name:
++ kfree(pd->name);
++free_pd:
++ kfree(pd);
++out:
++ pr_err("failed to alloc PM domain %pOF\n", np);
++ return NULL;
++}
++
++int dt_idle_pd_init_topology(struct device_node *np)
++{
++ struct device_node *node;
++ struct of_phandle_args child, parent;
++ int ret;
++
++ for_each_child_of_node(np, node) {
++ if (of_parse_phandle_with_args(node, "power-domains",
++ "#power-domain-cells", 0, &parent))
++ continue;
++
++ child.np = node;
++ child.args_count = 0;
++ ret = of_genpd_add_subdomain(&parent, &child);
++ of_node_put(parent.np);
++ if (ret) {
++ of_node_put(node);
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++struct device *dt_idle_attach_cpu(int cpu, const char *name)
++{
++ struct device *dev;
++
++ dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), name);
++ if (IS_ERR_OR_NULL(dev))
++ return dev;
++
++ pm_runtime_irq_safe(dev);
++ if (cpu_online(cpu))
++ pm_runtime_get_sync(dev);
++
++ dev_pm_syscore_device(dev, true);
++
++ return dev;
++}
++
++void dt_idle_detach_cpu(struct device *dev)
++{
++ if (IS_ERR_OR_NULL(dev))
++ return;
++
++ dev_pm_domain_detach(dev, false);
++}
+diff --git a/drivers/cpuidle/dt_idle_genpd.h b/drivers/cpuidle/dt_idle_genpd.h
+new file mode 100644
+index 000000000000..a95483d08a02
+--- /dev/null
++++ b/drivers/cpuidle/dt_idle_genpd.h
+@@ -0,0 +1,50 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __DT_IDLE_GENPD
++#define __DT_IDLE_GENPD
++
++struct device_node;
++struct generic_pm_domain;
++
++#ifdef CONFIG_DT_IDLE_GENPD
++
++void dt_idle_pd_free(struct generic_pm_domain *pd);
++
++struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
++ int (*parse_state)(struct device_node *, u32 *));
++
++int dt_idle_pd_init_topology(struct device_node *np);
++
++struct device *dt_idle_attach_cpu(int cpu, const char *name);
++
++void dt_idle_detach_cpu(struct device *dev);
++
++#else
++
++static inline void dt_idle_pd_free(struct generic_pm_domain *pd)
++{
++}
++
++static inline struct generic_pm_domain *dt_idle_pd_alloc(
++ struct device_node *np,
++ int (*parse_state)(struct device_node *, u32 *))
++{
++ return NULL;
++}
++
++static inline int dt_idle_pd_init_topology(struct device_node *np)
++{
++ return 0;
++}
++
++static inline struct device *dt_idle_attach_cpu(int cpu, const char *name)
++{
++ return NULL;
++}
++
++static inline void dt_idle_detach_cpu(struct device *dev)
++{
++}
++
++#endif
++
++#endif
+--
+2.20.1
+
--- /dev/null
+From 583adf3464089e7f164d45bbddae08fc2e60fe2a Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Fri, 12 Feb 2021 09:55:34 +0530
+Subject: [PATCH 048/124] cpuidle: Add RISC-V SBI CPU idle driver
+
+The RISC-V SBI HSM extension provides HSM suspend call which can
+be used by Linux RISC-V to enter platform specific low-power state.
+
+This patch adds a CPU idle driver based on RISC-V SBI calls which
+will populate idle states from device tree and use SBI calls to
+entry these idle states.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+---
+ MAINTAINERS | 7 +
+ drivers/cpuidle/Kconfig | 5 +
+ drivers/cpuidle/Kconfig.riscv | 15 +
+ drivers/cpuidle/Makefile | 4 +
+ drivers/cpuidle/cpuidle-riscv-sbi.c | 626 ++++++++++++++++++++++++++++
+ 5 files changed, 657 insertions(+)
+ create mode 100644 drivers/cpuidle/Kconfig.riscv
+ create mode 100644 drivers/cpuidle/cpuidle-riscv-sbi.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index b209020decb9..8b4809d2c789 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -4918,6 +4918,13 @@ S: Supported
+ F: drivers/cpuidle/dt_idle_genpd.c
+ F: drivers/cpuidle/dt_idle_genpd.h
+
++CPUIDLE DRIVER - RISC-V SBI
++M: Anup Patel <anup.patel@wdc.com>
++L: linux-pm@vger.kernel.org
++L: linux-riscv@lists.infradead.org
++S: Maintained
++F: drivers/cpuidle/cpuidle-riscv-sbi.c
++
+ CRAMFS FILESYSTEM
+ M: Nicolas Pitre <nico@fluxnic.net>
+ S: Maintained
+diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
+index f1afe7ab6b54..ff71dd662880 100644
+--- a/drivers/cpuidle/Kconfig
++++ b/drivers/cpuidle/Kconfig
+@@ -66,6 +66,11 @@ depends on PPC
+ source "drivers/cpuidle/Kconfig.powerpc"
+ endmenu
+
++menu "RISC-V CPU Idle Drivers"
++depends on RISCV
++source "drivers/cpuidle/Kconfig.riscv"
++endmenu
++
+ config HALTPOLL_CPUIDLE
+ tristate "Halt poll cpuidle driver"
+ depends on X86 && KVM_GUEST
+diff --git a/drivers/cpuidle/Kconfig.riscv b/drivers/cpuidle/Kconfig.riscv
+new file mode 100644
+index 000000000000..78518c26af74
+--- /dev/null
++++ b/drivers/cpuidle/Kconfig.riscv
+@@ -0,0 +1,15 @@
++# SPDX-License-Identifier: GPL-2.0-only
++#
++# RISC-V CPU Idle drivers
++#
++
++config RISCV_SBI_CPUIDLE
++ bool "RISC-V SBI CPU idle Driver"
++ depends on RISCV_SBI
++ select DT_IDLE_STATES
++ select CPU_IDLE_MULTIPLE_DRIVERS
++ select DT_IDLE_GENPD if PM_GENERIC_DOMAINS_OF
++ help
++ Select this option to enable RISC-V SBI firmware based CPU idle
++ driver for RISC-V systems. This drivers also supports hierarchical
++ DT based layout of the idle state.
+diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
+index 11a26cef279f..d103342b7cfc 100644
+--- a/drivers/cpuidle/Makefile
++++ b/drivers/cpuidle/Makefile
+@@ -35,3 +35,7 @@ obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
+ # POWERPC drivers
+ obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
+ obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
++
++###############################################################################
++# RISC-V drivers
++obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o
+diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c
+new file mode 100644
+index 000000000000..1f80e27c5cfb
+--- /dev/null
++++ b/drivers/cpuidle/cpuidle-riscv-sbi.c
+@@ -0,0 +1,626 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RISC-V SBI CPU idle driver.
++ *
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#define pr_fmt(fmt) "cpuidle-riscv-sbi: " fmt
++
++#include <linux/cpuidle.h>
++#include <linux/cpumask.h>
++#include <linux/cpu_pm.h>
++#include <linux/cpu_cooling.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_runtime.h>
++#include <asm/cpuidle.h>
++#include <asm/sbi.h>
++#include <asm/suspend.h>
++
++#include "dt_idle_states.h"
++#include "dt_idle_genpd.h"
++
++struct sbi_cpuidle_data {
++ u32 *states;
++ struct device *dev;
++};
++
++struct sbi_domain_state {
++ bool available;
++ u32 state;
++};
++
++static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data);
++static DEFINE_PER_CPU(struct sbi_domain_state, domain_state);
++static bool sbi_cpuidle_use_osi;
++static bool sbi_cpuidle_use_cpuhp;
++static bool sbi_cpuidle_pd_allow_domain_state;
++
++static inline void sbi_set_domain_state(u32 state)
++{
++ struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
++
++ data->available = true;
++ data->state = state;
++}
++
++static inline u32 sbi_get_domain_state(void)
++{
++ struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
++
++ return data->state;
++}
++
++static inline void sbi_clear_domain_state(void)
++{
++ struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
++
++ data->available = false;
++}
++
++static inline bool sbi_is_domain_state_available(void)
++{
++ struct sbi_domain_state *data = this_cpu_ptr(&domain_state);
++
++ return data->available;
++}
++
++static int sbi_suspend_finisher(unsigned long suspend_type,
++ unsigned long resume_addr,
++ unsigned long opaque)
++{
++ struct sbiret ret;
++
++ ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND,
++ suspend_type, resume_addr, opaque, 0, 0, 0);
++
++ return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0;
++}
++
++static int sbi_suspend(u32 state)
++{
++ if (state & SBI_HSM_SUSP_NON_RET_BIT)
++ return cpu_suspend(state, sbi_suspend_finisher);
++ else
++ return sbi_suspend_finisher(state, 0, 0);
++}
++
++static int sbi_cpuidle_enter_state(struct cpuidle_device *dev,
++ struct cpuidle_driver *drv, int idx)
++{
++ u32 *states = __this_cpu_read(sbi_cpuidle_data.states);
++
++ return CPU_PM_CPU_IDLE_ENTER_PARAM(sbi_suspend, idx, states[idx]);
++}
++
++static int __sbi_enter_domain_idle_state(struct cpuidle_device *dev,
++ struct cpuidle_driver *drv, int idx,
++ bool s2idle)
++{
++ struct sbi_cpuidle_data *data = this_cpu_ptr(&sbi_cpuidle_data);
++ u32 *states = data->states;
++ struct device *pd_dev = data->dev;
++ u32 state;
++ int ret;
++
++ ret = cpu_pm_enter();
++ if (ret)
++ return -1;
++
++ /* Do runtime PM to manage a hierarchical CPU toplogy. */
++ rcu_irq_enter_irqson();
++ if (s2idle)
++ dev_pm_genpd_suspend(pd_dev);
++ else
++ pm_runtime_put_sync_suspend(pd_dev);
++ rcu_irq_exit_irqson();
++
++ if (sbi_is_domain_state_available())
++ state = sbi_get_domain_state();
++ else
++ state = states[idx];
++
++ ret = sbi_suspend(state) ? -1 : idx;
++
++ rcu_irq_enter_irqson();
++ if (s2idle)
++ dev_pm_genpd_resume(pd_dev);
++ else
++ pm_runtime_get_sync(pd_dev);
++ rcu_irq_exit_irqson();
++
++ cpu_pm_exit();
++
++ /* Clear the domain state to start fresh when back from idle. */
++ sbi_clear_domain_state();
++ return ret;
++}
++
++static int sbi_enter_domain_idle_state(struct cpuidle_device *dev,
++ struct cpuidle_driver *drv, int idx)
++{
++ return __sbi_enter_domain_idle_state(dev, drv, idx, false);
++}
++
++static int sbi_enter_s2idle_domain_idle_state(struct cpuidle_device *dev,
++ struct cpuidle_driver *drv,
++ int idx)
++{
++ return __sbi_enter_domain_idle_state(dev, drv, idx, true);
++}
++
++static int sbi_cpuidle_cpuhp_up(unsigned int cpu)
++{
++ struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev);
++
++ if (pd_dev)
++ pm_runtime_get_sync(pd_dev);
++
++ return 0;
++}
++
++static int sbi_cpuidle_cpuhp_down(unsigned int cpu)
++{
++ struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev);
++
++ if (pd_dev) {
++ pm_runtime_put_sync(pd_dev);
++ /* Clear domain state to start fresh at next online. */
++ sbi_clear_domain_state();
++ }
++
++ return 0;
++}
++
++static void sbi_idle_init_cpuhp(void)
++{
++ int err;
++
++ if (!sbi_cpuidle_use_cpuhp)
++ return;
++
++ err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING,
++ "cpuidle/sbi:online",
++ sbi_cpuidle_cpuhp_up,
++ sbi_cpuidle_cpuhp_down);
++ if (err)
++ pr_warn("Failed %d while setup cpuhp state\n", err);
++}
++
++static const struct of_device_id sbi_cpuidle_state_match[] = {
++ { .compatible = "riscv,idle-state",
++ .data = sbi_cpuidle_enter_state },
++ { },
++};
++
++static bool sbi_suspend_state_is_valid(u32 state)
++{
++ if (state > SBI_HSM_SUSPEND_RET_DEFAULT &&
++ state < SBI_HSM_SUSPEND_RET_PLATFORM)
++ return false;
++ if (state > SBI_HSM_SUSPEND_NON_RET_DEFAULT &&
++ state < SBI_HSM_SUSPEND_NON_RET_PLATFORM)
++ return false;
++ return true;
++}
++
++static int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
++{
++ int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state);
++
++ if (err) {
++ pr_warn("%pOF missing riscv,sbi-suspend-param property\n", np);
++ return err;
++ }
++
++ if (!sbi_suspend_state_is_valid(*state)) {
++ pr_warn("Invalid SBI suspend state %#x\n", *state);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int sbi_dt_cpu_init_topology(struct cpuidle_driver *drv,
++ struct sbi_cpuidle_data *data,
++ unsigned int state_count, int cpu)
++{
++ /* Currently limit the hierarchical topology to be used in OSI mode. */
++ if (!sbi_cpuidle_use_osi)
++ return 0;
++
++ data->dev = dt_idle_attach_cpu(cpu, "sbi");
++ if (IS_ERR_OR_NULL(data->dev))
++ return PTR_ERR_OR_ZERO(data->dev);
++
++ /*
++ * Using the deepest state for the CPU to trigger a potential selection
++ * of a shared state for the domain, assumes the domain states are all
++ * deeper states.
++ */
++ drv->states[state_count - 1].enter = sbi_enter_domain_idle_state;
++ drv->states[state_count - 1].enter_s2idle =
++ sbi_enter_s2idle_domain_idle_state;
++ sbi_cpuidle_use_cpuhp = true;
++
++ return 0;
++}
++
++static int sbi_cpuidle_dt_init_states(struct device *dev,
++ struct cpuidle_driver *drv,
++ unsigned int cpu,
++ unsigned int state_count)
++{
++ struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu);
++ struct device_node *state_node;
++ struct device_node *cpu_node;
++ u32 *states;
++ int i, ret;
++
++ cpu_node = of_cpu_device_node_get(cpu);
++ if (!cpu_node)
++ return -ENODEV;
++
++ states = devm_kcalloc(dev, state_count, sizeof(*states), GFP_KERNEL);
++ if (!states) {
++ ret = -ENOMEM;
++ goto fail;
++ }
++
++ /* Parse SBI specific details from state DT nodes */
++ for (i = 1; i < state_count; i++) {
++ state_node = of_get_cpu_state_node(cpu_node, i - 1);
++ if (!state_node)
++ break;
++
++ ret = sbi_dt_parse_state_node(state_node, &states[i]);
++ of_node_put(state_node);
++
++ if (ret)
++ return ret;
++
++ pr_debug("sbi-state %#x index %d\n", states[i], i);
++ }
++ if (i != state_count) {
++ ret = -ENODEV;
++ goto fail;
++ }
++
++ /* Initialize optional data, used for the hierarchical topology. */
++ ret = sbi_dt_cpu_init_topology(drv, data, state_count, cpu);
++ if (ret < 0)
++ return ret;
++
++ /* Store states in the per-cpu struct. */
++ data->states = states;
++
++fail:
++ of_node_put(cpu_node);
++
++ return ret;
++}
++
++static void sbi_cpuidle_deinit_cpu(int cpu)
++{
++ struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu);
++
++ dt_idle_detach_cpu(data->dev);
++ sbi_cpuidle_use_cpuhp = false;
++}
++
++static int sbi_cpuidle_init_cpu(struct device *dev, int cpu)
++{
++ struct cpuidle_driver *drv;
++ unsigned int state_count = 0;
++ int ret = 0;
++
++ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
++ if (!drv)
++ return -ENOMEM;
++
++ drv->name = "sbi_cpuidle";
++ drv->owner = THIS_MODULE;
++ drv->cpumask = (struct cpumask *)cpumask_of(cpu);
++
++ /* RISC-V architectural WFI to be represented as state index 0. */
++ drv->states[0].enter = sbi_cpuidle_enter_state;
++ drv->states[0].exit_latency = 1;
++ drv->states[0].target_residency = 1;
++ drv->states[0].power_usage = UINT_MAX;
++ strcpy(drv->states[0].name, "WFI");
++ strcpy(drv->states[0].desc, "RISC-V WFI");
++
++ /*
++ * If no DT idle states are detected (ret == 0) let the driver
++ * initialization fail accordingly since there is no reason to
++ * initialize the idle driver if only wfi is supported, the
++ * default archictectural back-end already executes wfi
++ * on idle entry.
++ */
++ ret = dt_init_idle_driver(drv, sbi_cpuidle_state_match, 1);
++ if (ret <= 0) {
++ pr_debug("HART%ld: failed to parse DT idle states\n",
++ cpuid_to_hartid_map(cpu));
++ return ret ? : -ENODEV;
++ }
++ state_count = ret + 1; /* Include WFI state as well */
++
++ /* Initialize idle states from DT. */
++ ret = sbi_cpuidle_dt_init_states(dev, drv, cpu, state_count);
++ if (ret) {
++ pr_err("HART%ld: failed to init idle states\n",
++ cpuid_to_hartid_map(cpu));
++ return ret;
++ }
++
++ ret = cpuidle_register(drv, NULL);
++ if (ret)
++ goto deinit;
++
++ cpuidle_cooling_register(drv);
++
++ return 0;
++deinit:
++ sbi_cpuidle_deinit_cpu(cpu);
++ return ret;
++}
++
++static void sbi_cpuidle_domain_sync_state(struct device *dev)
++{
++ /*
++ * All devices have now been attached/probed to the PM domain
++ * topology, hence it's fine to allow domain states to be picked.
++ */
++ sbi_cpuidle_pd_allow_domain_state = true;
++}
++
++#ifdef CONFIG_DT_IDLE_GENPD
++
++static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd)
++{
++ struct genpd_power_state *state = &pd->states[pd->state_idx];
++ u32 *pd_state;
++
++ if (!state->data)
++ return 0;
++
++ if (!sbi_cpuidle_pd_allow_domain_state)
++ return -EBUSY;
++
++ /* OSI mode is enabled, set the corresponding domain state. */
++ pd_state = state->data;
++ sbi_set_domain_state(*pd_state);
++
++ return 0;
++}
++
++struct sbi_pd_provider {
++ struct list_head link;
++ struct device_node *node;
++};
++
++static LIST_HEAD(sbi_pd_providers);
++
++static int sbi_pd_init(struct device_node *np)
++{
++ struct generic_pm_domain *pd;
++ struct sbi_pd_provider *pd_provider;
++ struct dev_power_governor *pd_gov;
++ int ret = -ENOMEM, state_count = 0;
++
++ pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node);
++ if (!pd)
++ goto out;
++
++ pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL);
++ if (!pd_provider)
++ goto free_pd;
++
++ pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN;
++
++ /* Allow power off when OSI is available. */
++ if (sbi_cpuidle_use_osi)
++ pd->power_off = sbi_cpuidle_pd_power_off;
++ else
++ pd->flags |= GENPD_FLAG_ALWAYS_ON;
++
++ /* Use governor for CPU PM domains if it has some states to manage. */
++ pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL;
++
++ ret = pm_genpd_init(pd, pd_gov, false);
++ if (ret)
++ goto free_pd_prov;
++
++ ret = of_genpd_add_provider_simple(np, pd);
++ if (ret)
++ goto remove_pd;
++
++ pd_provider->node = of_node_get(np);
++ list_add(&pd_provider->link, &sbi_pd_providers);
++
++ pr_debug("init PM domain %s\n", pd->name);
++ return 0;
++
++remove_pd:
++ pm_genpd_remove(pd);
++free_pd_prov:
++ kfree(pd_provider);
++free_pd:
++ dt_idle_pd_free(pd);
++out:
++ pr_err("failed to init PM domain ret=%d %pOF\n", ret, np);
++ return ret;
++}
++
++static void sbi_pd_remove(void)
++{
++ struct sbi_pd_provider *pd_provider, *it;
++ struct generic_pm_domain *genpd;
++
++ list_for_each_entry_safe(pd_provider, it, &sbi_pd_providers, link) {
++ of_genpd_del_provider(pd_provider->node);
++
++ genpd = of_genpd_remove_last(pd_provider->node);
++ if (!IS_ERR(genpd))
++ kfree(genpd);
++
++ of_node_put(pd_provider->node);
++ list_del(&pd_provider->link);
++ kfree(pd_provider);
++ }
++}
++
++static int sbi_genpd_probe(struct device_node *np)
++{
++ struct device_node *node;
++ int ret = 0, pd_count = 0;
++
++ if (!np)
++ return -ENODEV;
++
++ /*
++ * Parse child nodes for the "#power-domain-cells" property and
++ * initialize a genpd/genpd-of-provider pair when it's found.
++ */
++ for_each_child_of_node(np, node) {
++ if (!of_find_property(node, "#power-domain-cells", NULL))
++ continue;
++
++ ret = sbi_pd_init(node);
++ if (ret)
++ goto put_node;
++
++ pd_count++;
++ }
++
++ /* Bail out if not using the hierarchical CPU topology. */
++ if (!pd_count)
++ goto no_pd;
++
++ /* Link genpd masters/subdomains to model the CPU topology. */
++ ret = dt_idle_pd_init_topology(np);
++ if (ret)
++ goto remove_pd;
++
++ return 0;
++
++put_node:
++ of_node_put(node);
++remove_pd:
++ sbi_pd_remove();
++ pr_err("failed to create CPU PM domains ret=%d\n", ret);
++no_pd:
++ return ret;
++}
++
++#else
++
++static inline int sbi_genpd_probe(struct device_node *np)
++{
++ return 0;
++}
++
++#endif
++
++static int sbi_cpuidle_probe(struct platform_device *pdev)
++{
++ int cpu, ret;
++ struct cpuidle_driver *drv;
++ struct cpuidle_device *dev;
++ struct device_node *np, *pds_node;
++
++ /* Detect OSI support based on CPU DT nodes */
++ sbi_cpuidle_use_osi = true;
++ for_each_possible_cpu(cpu) {
++ np = of_cpu_device_node_get(cpu);
++ if (np &&
++ of_find_property(np, "power-domains", NULL) &&
++ of_find_property(np, "power-domain-names", NULL)) {
++ continue;
++ } else {
++ sbi_cpuidle_use_osi = false;
++ break;
++ }
++ }
++
++ /* Populate generic power domains from DT nodes */
++ pds_node = of_find_node_by_path("/cpus/power-domains");
++ if (pds_node) {
++ ret = sbi_genpd_probe(pds_node);
++ of_node_put(pds_node);
++ if (ret)
++ return ret;
++ }
++
++ /* Initialize CPU idle driver for each CPU */
++ for_each_possible_cpu(cpu) {
++ ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu);
++ if (ret) {
++ pr_debug("HART%ld: idle driver init failed\n",
++ cpuid_to_hartid_map(cpu));
++ goto out_fail;
++ }
++ }
++
++ /* Setup CPU hotplut notifiers */
++ sbi_idle_init_cpuhp();
++
++ pr_info("idle driver registered for all CPUs\n");
++
++ return 0;
++
++out_fail:
++ while (--cpu >= 0) {
++ dev = per_cpu(cpuidle_devices, cpu);
++ drv = cpuidle_get_cpu_driver(dev);
++ cpuidle_unregister(drv);
++ sbi_cpuidle_deinit_cpu(cpu);
++ }
++
++ return ret;
++}
++
++static struct platform_driver sbi_cpuidle_driver = {
++ .probe = sbi_cpuidle_probe,
++ .driver = {
++ .name = "sbi-cpuidle",
++ .sync_state = sbi_cpuidle_domain_sync_state,
++ },
++};
++
++static int __init sbi_cpuidle_init(void)
++{
++ int ret;
++ struct platform_device *pdev;
++
++ /*
++ * The SBI HSM suspend function is only available when:
++ * 1) SBI version is 0.3 or higher
++ * 2) SBI HSM extension is available
++ */
++ if ((sbi_spec_version < sbi_mk_version(0, 3)) ||
++ sbi_probe_extension(SBI_EXT_HSM) <= 0) {
++ pr_info("HSM suspend not available\n");
++ return 0;
++ }
++
++ ret = platform_driver_register(&sbi_cpuidle_driver);
++ if (ret)
++ return ret;
++
++ pdev = platform_device_register_simple("sbi-cpuidle",
++ -1, NULL, 0);
++ if (IS_ERR(pdev)) {
++ platform_driver_unregister(&sbi_cpuidle_driver);
++ return PTR_ERR(pdev);
++ }
++
++ return 0;
++}
++device_initcall(sbi_cpuidle_init);
+--
+2.20.1
+
--- /dev/null
+From 908cff58b3a2185c8fa22adfc96a5a7ce33c3387 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Wed, 17 Feb 2021 14:53:25 +0530
+Subject: [PATCH 049/124] RISC-V: Enable RISC-V SBI CPU Idle driver for QEMU
+ virt machine
+
+We enable RISC-V SBI CPU Idle driver for QEMU virt machine to test
+SBI HSM Supend on QEMU.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+---
+ arch/riscv/Kconfig.socs | 3 +++
+ arch/riscv/configs/defconfig | 1 +
+ arch/riscv/configs/rv32_defconfig | 1 +
+ 3 files changed, 5 insertions(+)
+
+diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
+index 30676ebb16eb..56bafc3dad4c 100644
+--- a/arch/riscv/Kconfig.socs
++++ b/arch/riscv/Kconfig.socs
+@@ -28,6 +28,9 @@ config SOC_VIRT
+ select GOLDFISH
+ select RTC_DRV_GOLDFISH if RTC_CLASS
+ select SIFIVE_PLIC
++ select PM_GENERIC_DOMAINS if PM
++ select PM_GENERIC_DOMAINS_OF if PM && OF
++ select RISCV_SBI_CPUIDLE if CPU_IDLE
+ help
+ This enables support for QEMU Virt Machine.
+
+diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
+index 66552914be3f..2911f78240aa 100644
+--- a/arch/riscv/configs/defconfig
++++ b/arch/riscv/configs/defconfig
+@@ -19,6 +19,7 @@ CONFIG_SOC_VIRT=y
+ CONFIG_SOC_MICROCHIP_POLARFIRE=y
+ CONFIG_SMP=y
+ CONFIG_HOTPLUG_CPU=y
++CONFIG_PM=y
+ CONFIG_CPU_IDLE=y
+ CONFIG_JUMP_LABEL=y
+ CONFIG_MODULES=y
+diff --git a/arch/riscv/configs/rv32_defconfig b/arch/riscv/configs/rv32_defconfig
+index 3093f8efd7e5..a8cbae623fd8 100644
+--- a/arch/riscv/configs/rv32_defconfig
++++ b/arch/riscv/configs/rv32_defconfig
+@@ -19,6 +19,7 @@ CONFIG_SOC_VIRT=y
+ CONFIG_ARCH_RV32I=y
+ CONFIG_SMP=y
+ CONFIG_HOTPLUG_CPU=y
++CONFIG_PM=y
+ CONFIG_CPU_IDLE=y
+ CONFIG_JUMP_LABEL=y
+ CONFIG_MODULES=y
+--
+2.20.1
+
--- /dev/null
+From 65fb9bd8dc188403f9f922542ee2faae64f96a54 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Thu, 13 May 2021 14:10:34 +0530
+Subject: [PATCH 050/124] RISC-V: Clear SIP bit only when using SBI IPI
+ operations
+
+The software interrupt pending (i.e. [M|S]SIP) bit is writeable for
+S-mode but read-only for M-mode so we clear this bit only when using
+SBI IPI operations.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
+---
+ arch/riscv/kernel/sbi.c | 8 +++++++-
+ arch/riscv/kernel/smp.c | 2 --
+ 2 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 9a84f0cb5175..8aeca26198f2 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -598,8 +598,14 @@ static void sbi_send_cpumask_ipi(const struct cpumask *target)
+ sbi_send_ipi(cpumask_bits(&hartid_mask));
+ }
+
++static void sbi_ipi_clear(void)
++{
++ csr_clear(CSR_IP, IE_SIE);
++}
++
+ static const struct riscv_ipi_ops sbi_ipi_ops = {
+- .ipi_inject = sbi_send_cpumask_ipi
++ .ipi_inject = sbi_send_cpumask_ipi,
++ .ipi_clear = sbi_ipi_clear
+ };
+
+ void __init sbi_init(void)
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index 2f6da845c9ae..f2bbaa670f72 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -99,8 +99,6 @@ void riscv_clear_ipi(void)
+ {
+ if (ipi_ops && ipi_ops->ipi_clear)
+ ipi_ops->ipi_clear();
+-
+- csr_clear(CSR_IP, IE_SIE);
+ }
+ EXPORT_SYMBOL_GPL(riscv_clear_ipi);
+
+--
+2.20.1
+
--- /dev/null
+From b3977c0e4c7396a190e8d9636573a76bd7868656 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Wed, 16 Jun 2021 14:39:29 +0530
+Subject: [PATCH 051/124] RISC-V: Treat IPIs as normal Linux IRQs
+
+Currently, the RISC-V kernel provides arch specific hooks (i.e.
+struct riscv_ipi_ops) to register IPI handling methods. The stats
+gathering of IPIs is also arch specific in the RISC-V kernel.
+
+Other architectures (such as ARM, ARM64, and MIPS) have moved away
+from custom arch specific IPI handling methods. Currently, these
+architectures have Linux irqchip drivers providing a range of Linux
+IRQ numbers to be used as IPIs and IPI triggering is done using
+generic IPI APIs. This approach allows architectures to treat IPIs
+as normal Linux IRQs and IPI stats gathering is done by the generic
+Linux IRQ subsystem.
+
+We extend the RISC-V IPI handling as-per above approach so that
+arch specific IPI handling methods (struct riscv_ipi_ops) can be
+removed and the IPI handling is totally contained within Linux
+irqchip drivers.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+---
+ arch/riscv/Kconfig | 1 +
+ arch/riscv/include/asm/sbi.h | 2 +
+ arch/riscv/include/asm/smp.h | 35 +++--
+ arch/riscv/kernel/Makefile | 1 +
+ arch/riscv/kernel/cpu-hotplug.c | 3 +-
+ arch/riscv/kernel/irq.c | 3 +-
+ arch/riscv/kernel/sbi-ipi.c | 218 ++++++++++++++++++++++++++++++
+ arch/riscv/kernel/sbi.c | 21 ---
+ arch/riscv/kernel/smp.c | 153 +++++++++++----------
+ arch/riscv/kernel/smpboot.c | 5 +-
+ drivers/clocksource/timer-clint.c | 23 ----
+ drivers/irqchip/irq-riscv-intc.c | 55 ++++----
+ 12 files changed, 349 insertions(+), 171 deletions(-)
+ create mode 100644 arch/riscv/kernel/sbi-ipi.c
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 655f35ea2e74..1d7b5066ce80 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -54,6 +54,7 @@ config RISCV
+ select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
+ select GENERIC_IDLE_POLL_SETUP
+ select GENERIC_IOREMAP if MMU
++ select GENERIC_IRQ_IPI
+ select GENERIC_IRQ_MULTI_HANDLER
+ select GENERIC_IRQ_SHOW
+ select GENERIC_IRQ_SHOW_LEVEL
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index ab9782f8da52..ff5cecd13bda 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -123,6 +123,7 @@ struct sbiret {
+ };
+
+ void sbi_init(void);
++void sbi_ipi_init(void);
+ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+@@ -192,6 +193,7 @@ static inline unsigned long sbi_mk_version(unsigned long major,
+ int sbi_err_map_linux_errno(int err);
+ #else /* CONFIG_RISCV_SBI */
+ static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; }
++static inline void sbi_ipi_init(void) { }
+ static inline void sbi_init(void) {}
+ #endif /* CONFIG_RISCV_SBI */
+ #endif /* _ASM_RISCV_SBI_H */
+diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
+index a7d2811f3536..e5480d75b2d9 100644
+--- a/arch/riscv/include/asm/smp.h
++++ b/arch/riscv/include/asm/smp.h
+@@ -15,11 +15,6 @@
+ struct seq_file;
+ extern unsigned long boot_cpu_hartid;
+
+-struct riscv_ipi_ops {
+- void (*ipi_inject)(const struct cpumask *target);
+- void (*ipi_clear)(void);
+-};
+-
+ #ifdef CONFIG_SMP
+ /*
+ * Mapping between linux logical cpu index and hartid.
+@@ -33,9 +28,6 @@ void show_ipi_stats(struct seq_file *p, int prec);
+ /* SMP initialization hook for setup_arch */
+ void __init setup_smp(void);
+
+-/* Called from C code, this handles an IPI. */
+-void handle_IPI(struct pt_regs *regs);
+-
+ /* Hook for the generic smp_call_function_many() routine. */
+ void arch_send_call_function_ipi_mask(struct cpumask *mask);
+
+@@ -45,11 +37,17 @@ void arch_send_call_function_single_ipi(int cpu);
+ int riscv_hartid_to_cpuid(int hartid);
+ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out);
+
+-/* Set custom IPI operations */
+-void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops);
++/* Enable IPI for CPU hotplug */
++void riscv_ipi_enable(void);
++
++/* Disable IPI for CPU hotplug */
++void riscv_ipi_disable(void);
+
+-/* Clear IPI for current CPU */
+-void riscv_clear_ipi(void);
++/* Check if IPI interrupt numbers are available */
++bool riscv_ipi_have_virq_range(void);
++
++/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */
++void riscv_ipi_set_virq_range(int virq, int nr_irqs);
+
+ /* Secondary hart entry */
+ asmlinkage void smp_callin(void);
+@@ -92,11 +90,20 @@ static inline void riscv_cpuid_to_hartid_mask(const struct cpumask *in,
+ cpumask_set_cpu(boot_cpu_hartid, out);
+ }
+
+-static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
++static inline void riscv_ipi_enable(void)
+ {
+ }
+
+-static inline void riscv_clear_ipi(void)
++static inline void riscv_ipi_disable(void)
++{
++}
++
++static inline bool riscv_ipi_have_virq_range(void)
++{
++ return false;
++}
++
++static inline void riscv_ipi_set_virq_range(int virq, int nr)
+ {
+ }
+
+diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
+index 4320629c07c3..65bcd8e344b6 100644
+--- a/arch/riscv/kernel/Makefile
++++ b/arch/riscv/kernel/Makefile
+@@ -56,6 +56,7 @@ obj-$(CONFIG_RISCV_BASE_PMU) += perf_event.o
+ obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
+ obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
+ obj-$(CONFIG_RISCV_SBI) += sbi.o
++obj-$(CONFIG_RISCV_SBI) += sbi-ipi.o
+ ifeq ($(CONFIG_RISCV_SBI), y)
+ obj-$(CONFIG_SMP) += cpu_ops_sbi.o
+ endif
+diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c
+index df84e0c13db1..141f8a01d706 100644
+--- a/arch/riscv/kernel/cpu-hotplug.c
++++ b/arch/riscv/kernel/cpu-hotplug.c
+@@ -12,7 +12,7 @@
+ #include <linux/sched/hotplug.h>
+ #include <asm/irq.h>
+ #include <asm/cpu_ops.h>
+-#include <asm/sbi.h>
++#include <asm/smp.h>
+
+ void cpu_stop(void);
+ void arch_cpu_idle_dead(void)
+@@ -47,6 +47,7 @@ int __cpu_disable(void)
+
+ remove_cpu_topology(cpu);
+ set_cpu_online(cpu, false);
++ riscv_ipi_disable();
+ irq_migrate_all_off_this_cpu();
+
+ return ret;
+diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c
+index 7207fa08d78f..a179350d2208 100644
+--- a/arch/riscv/kernel/irq.c
++++ b/arch/riscv/kernel/irq.c
+@@ -8,7 +8,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/irqchip.h>
+ #include <linux/seq_file.h>
+-#include <asm/smp.h>
++#include <asm/sbi.h>
+
+ int arch_show_interrupts(struct seq_file *p, int prec)
+ {
+@@ -21,4 +21,5 @@ void __init init_IRQ(void)
+ irqchip_init();
+ if (!handle_arch_irq)
+ panic("No interrupt controller found.");
++ sbi_ipi_init();
+ }
+diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c
+new file mode 100644
+index 000000000000..8d06b1e3d682
+--- /dev/null
++++ b/arch/riscv/kernel/sbi-ipi.c
+@@ -0,0 +1,218 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * SBI based IPI support.
++ *
++ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
++ */
++
++#define pr_fmt(fmt) "riscv-sbi-ipi: " fmt
++#include <linux/cpu.h>
++#include <linux/cpumask.h>
++#include <linux/init.h>
++#include <linux/irq.h>
++#include <linux/irqchip.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/of.h>
++#include <linux/smp.h>
++#include <asm/sbi.h>
++
++static int intc_parent_irq __ro_after_init;
++static struct irq_domain *sbi_ipi_domain __ro_after_init;
++static DEFINE_PER_CPU(unsigned long, sbi_ipi_bits);
++
++static void sbi_ipi_dummy(struct irq_data *d)
++{
++}
++
++static void sbi_ipi_send_mask(struct irq_data *d, const struct cpumask *mask)
++{
++ int cpu;
++ struct cpumask hartid_mask;
++
++ /* Barrier before doing atomic bit update to IPI bits */
++ smp_mb__before_atomic();
++ for_each_cpu(cpu, mask)
++ set_bit(d->hwirq, per_cpu_ptr(&sbi_ipi_bits, cpu));
++ /* Barrier after doing atomic bit update to IPI bits */
++ smp_mb__after_atomic();
++
++ riscv_cpuid_to_hartid_mask(mask, &hartid_mask);
++
++ sbi_send_ipi(cpumask_bits(&hartid_mask));
++}
++
++static struct irq_chip sbi_ipi_chip = {
++ .name = "RISC-V SBI IPI",
++ .irq_mask = sbi_ipi_dummy,
++ .irq_unmask = sbi_ipi_dummy,
++ .ipi_send_mask = sbi_ipi_send_mask,
++};
++
++static int sbi_ipi_domain_map(struct irq_domain *d, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_percpu_devid(irq);
++ irq_domain_set_info(d, irq, hwirq, &sbi_ipi_chip, d->host_data,
++ handle_percpu_devid_irq, NULL, NULL);
++
++ return 0;
++}
++
++static int sbi_ipi_domain_alloc(struct irq_domain *d, unsigned int virq,
++ unsigned int nr_irqs, void *arg)
++{
++ int i, ret;
++ irq_hw_number_t hwirq;
++ unsigned int type = IRQ_TYPE_NONE;
++ struct irq_fwspec *fwspec = arg;
++
++ ret = irq_domain_translate_onecell(d, fwspec, &hwirq, &type);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < nr_irqs; i++) {
++ ret = sbi_ipi_domain_map(d, virq + i, hwirq + i);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct irq_domain_ops sbi_ipi_domain_ops = {
++ .translate = irq_domain_translate_onecell,
++ .alloc = sbi_ipi_domain_alloc,
++ .free = irq_domain_free_irqs_top,
++};
++
++static void sbi_ipi_handle_irq(struct irq_desc *desc)
++{
++ int err;
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ unsigned long irqs, *bits = this_cpu_ptr(&sbi_ipi_bits);
++ irq_hw_number_t hwirq;
++
++ chained_irq_enter(chip, desc);
++
++ while (true) {
++ csr_clear(CSR_IP, IE_SIE);
++
++ /* Order bit clearing and data access. */
++ mb();
++
++ irqs = xchg(bits, 0);
++ if (!irqs)
++ goto done;
++
++ for_each_set_bit(hwirq, &irqs, BITS_PER_LONG) {
++ err = generic_handle_domain_irq(sbi_ipi_domain,
++ hwirq);
++ if (unlikely(err))
++ pr_warn_ratelimited(
++ "can't find mapping for hwirq %lu\n",
++ hwirq);
++ }
++ }
++
++done:
++ chained_irq_exit(chip, desc);
++}
++
++static int sbi_ipi_dying_cpu(unsigned int cpu)
++{
++ disable_percpu_irq(intc_parent_irq);
++ return 0;
++}
++
++static int sbi_ipi_starting_cpu(unsigned int cpu)
++{
++ enable_percpu_irq(intc_parent_irq,
++ irq_get_trigger_type(intc_parent_irq));
++ return 0;
++}
++
++static int __init sbi_ipi_set_virq(void)
++{
++ int virq;
++ struct irq_fwspec ipi = {
++ .fwnode = sbi_ipi_domain->fwnode,
++ .param_count = 1,
++ .param[0] = 0,
++ };
++
++ virq = __irq_domain_alloc_irqs(sbi_ipi_domain, -1, BITS_PER_LONG,
++ NUMA_NO_NODE, &ipi,
++ false, NULL);
++ if (virq <= 0) {
++ pr_err("unable to alloc IRQs from SBI IPI IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ riscv_ipi_set_virq_range(virq, BITS_PER_LONG);
++
++ return 0;
++}
++
++static int __init sbi_ipi_domain_init(struct irq_domain *domain)
++{
++ struct irq_fwspec swi = {
++ .fwnode = domain->fwnode,
++ .param_count = 1,
++ .param[0] = RV_IRQ_SOFT,
++ };
++
++ intc_parent_irq = __irq_domain_alloc_irqs(domain, -1, 1,
++ NUMA_NO_NODE, &swi,
++ false, NULL);
++ if (intc_parent_irq <= 0) {
++ pr_err("unable to alloc IRQ from INTC IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ irq_set_chained_handler(intc_parent_irq, sbi_ipi_handle_irq);
++
++ sbi_ipi_domain = irq_domain_add_linear(NULL, BITS_PER_LONG,
++ &sbi_ipi_domain_ops, NULL);
++ if (!sbi_ipi_domain) {
++ pr_err("unable to add SBI IPI IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
++ "irqchip/riscv/sbi-ipi:starting",
++ sbi_ipi_starting_cpu, sbi_ipi_dying_cpu);
++
++ return sbi_ipi_set_virq();
++}
++
++void __init sbi_ipi_init(void)
++{
++ struct irq_domain *domain = NULL;
++ struct device_node *cpu, *child;
++
++ if (riscv_ipi_have_virq_range())
++ return;
++
++ for_each_of_cpu_node(cpu) {
++ child = of_get_compatible_child(cpu, "riscv,cpu-intc");
++ if (!child) {
++ pr_err("failed to find INTC node [%pOF]\n", cpu);
++ return;
++ }
++
++ domain = irq_find_host(child);
++ of_node_put(child);
++ if (domain)
++ break;
++ }
++ if (!domain) {
++ pr_err("can't find INTC IRQ domain\n");
++ return;
++ }
++
++ if (sbi_ipi_domain_init(domain))
++ pr_err("failed to register IPI domain\n");
++ else
++ pr_info("registered IPI domain\n");
++}
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 8aeca26198f2..372aa7e181d5 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -589,25 +589,6 @@ long sbi_get_mimpid(void)
+ return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID);
+ }
+
+-static void sbi_send_cpumask_ipi(const struct cpumask *target)
+-{
+- struct cpumask hartid_mask;
+-
+- riscv_cpuid_to_hartid_mask(target, &hartid_mask);
+-
+- sbi_send_ipi(cpumask_bits(&hartid_mask));
+-}
+-
+-static void sbi_ipi_clear(void)
+-{
+- csr_clear(CSR_IP, IE_SIE);
+-}
+-
+-static const struct riscv_ipi_ops sbi_ipi_ops = {
+- .ipi_inject = sbi_send_cpumask_ipi,
+- .ipi_clear = sbi_ipi_clear
+-};
+-
+ void __init sbi_init(void)
+ {
+ int ret;
+@@ -654,6 +635,4 @@ void __init sbi_init(void)
+ __sbi_send_ipi = __sbi_send_ipi_v01;
+ __sbi_rfence = __sbi_rfence_v01;
+ }
+-
+- riscv_set_ipi_ops(&sbi_ipi_ops);
+ }
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index f2bbaa670f72..53902cd1671f 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -17,9 +17,9 @@
+ #include <linux/sched.h>
+ #include <linux/seq_file.h>
+ #include <linux/delay.h>
++#include <linux/irq.h>
+ #include <linux/irq_work.h>
+
+-#include <asm/sbi.h>
+ #include <asm/tlbflush.h>
+ #include <asm/cacheflush.h>
+
+@@ -41,11 +41,9 @@ void __init smp_setup_processor_id(void)
+ cpuid_to_hartid_map(0) = boot_cpu_hartid;
+ }
+
+-/* A collection of single bit ipi messages. */
+-static struct {
+- unsigned long stats[IPI_MAX] ____cacheline_aligned;
+- unsigned long bits ____cacheline_aligned;
+-} ipi_data[NR_CPUS] __cacheline_aligned;
++static int ipi_virq_base __ro_after_init;
++static int nr_ipi __ro_after_init = IPI_MAX;
++static struct irq_desc *ipi_desc[IPI_MAX] __read_mostly;
+
+ int riscv_hartid_to_cpuid(int hartid)
+ {
+@@ -87,46 +85,14 @@ static void ipi_stop(void)
+ wait_for_interrupt();
+ }
+
+-static const struct riscv_ipi_ops *ipi_ops __ro_after_init;
+-
+-void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
+-{
+- ipi_ops = ops;
+-}
+-EXPORT_SYMBOL_GPL(riscv_set_ipi_ops);
+-
+-void riscv_clear_ipi(void)
+-{
+- if (ipi_ops && ipi_ops->ipi_clear)
+- ipi_ops->ipi_clear();
+-}
+-EXPORT_SYMBOL_GPL(riscv_clear_ipi);
+-
+ static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op)
+ {
+- int cpu;
+-
+- smp_mb__before_atomic();
+- for_each_cpu(cpu, mask)
+- set_bit(op, &ipi_data[cpu].bits);
+- smp_mb__after_atomic();
+-
+- if (ipi_ops && ipi_ops->ipi_inject)
+- ipi_ops->ipi_inject(mask);
+- else
+- pr_warn("SMP: IPI inject method not available\n");
++ __ipi_send_mask(ipi_desc[op], mask);
+ }
+
+ static void send_ipi_single(int cpu, enum ipi_message_type op)
+ {
+- smp_mb__before_atomic();
+- set_bit(op, &ipi_data[cpu].bits);
+- smp_mb__after_atomic();
+-
+- if (ipi_ops && ipi_ops->ipi_inject)
+- ipi_ops->ipi_inject(cpumask_of(cpu));
+- else
+- pr_warn("SMP: IPI inject method not available\n");
++ __ipi_send_mask(ipi_desc[op], cpumask_of(cpu));
+ }
+
+ #ifdef CONFIG_IRQ_WORK
+@@ -136,55 +102,88 @@ void arch_irq_work_raise(void)
+ }
+ #endif
+
+-void handle_IPI(struct pt_regs *regs)
++static irqreturn_t handle_IPI(int irq, void *data)
++{
++ int ipi = irq - ipi_virq_base;
++
++ switch (ipi) {
++ case IPI_RESCHEDULE:
++ scheduler_ipi();
++ break;
++ case IPI_CALL_FUNC:
++ generic_smp_call_function_interrupt();
++ break;
++ case IPI_CPU_STOP:
++ ipi_stop();
++ break;
++ case IPI_IRQ_WORK:
++ irq_work_run();
++ break;
++#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
++ case IPI_TIMER:
++ tick_receive_broadcast();
++ break;
++#endif
++ default:
++ pr_warn("CPU%d: unhandled IPI%d\n", smp_processor_id(), ipi);
++ break;
++ };
++
++ return IRQ_HANDLED;
++}
++
++void riscv_ipi_enable(void)
+ {
+- unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits;
+- unsigned long *stats = ipi_data[smp_processor_id()].stats;
++ int i;
+
+- riscv_clear_ipi();
++ if (WARN_ON_ONCE(!ipi_virq_base))
++ return;
+
+- while (true) {
+- unsigned long ops;
++ for (i = 0; i < nr_ipi; i++)
++ enable_percpu_irq(ipi_virq_base + i, 0);
++}
+
+- /* Order bit clearing and data access. */
+- mb();
++void riscv_ipi_disable(void)
++{
++ int i;
+
+- ops = xchg(pending_ipis, 0);
+- if (ops == 0)
+- return;
++ if (WARN_ON_ONCE(!ipi_virq_base))
++ return;
+
+- if (ops & (1 << IPI_RESCHEDULE)) {
+- stats[IPI_RESCHEDULE]++;
+- scheduler_ipi();
+- }
++ for (i = 0; i < nr_ipi; i++)
++ disable_percpu_irq(ipi_virq_base + i);
++}
+
+- if (ops & (1 << IPI_CALL_FUNC)) {
+- stats[IPI_CALL_FUNC]++;
+- generic_smp_call_function_interrupt();
+- }
++bool riscv_ipi_have_virq_range(void)
++{
++ return (ipi_virq_base) ? true : false;
++}
+
+- if (ops & (1 << IPI_CPU_STOP)) {
+- stats[IPI_CPU_STOP]++;
+- ipi_stop();
+- }
++void riscv_ipi_set_virq_range(int virq, int nr)
++{
++ int i, err;
+
+- if (ops & (1 << IPI_IRQ_WORK)) {
+- stats[IPI_IRQ_WORK]++;
+- irq_work_run();
+- }
++ if (WARN_ON(ipi_virq_base))
++ return;
+
+-#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+- if (ops & (1 << IPI_TIMER)) {
+- stats[IPI_TIMER]++;
+- tick_receive_broadcast();
+- }
+-#endif
+- BUG_ON((ops >> IPI_MAX) != 0);
++ WARN_ON(nr < IPI_MAX);
++ nr_ipi = min(nr, IPI_MAX);
++ ipi_virq_base = virq;
++
++ /* Request IPIs */
++ for (i = 0; i < nr_ipi; i++) {
++ err = request_percpu_irq(ipi_virq_base + i, handle_IPI,
++ "IPI", &ipi_virq_base);
++ WARN_ON(err);
+
+- /* Order data access and bit testing. */
+- mb();
++ ipi_desc[i] = irq_to_desc(ipi_virq_base + i);
++ irq_set_status_flags(ipi_virq_base + i, IRQ_HIDDEN);
+ }
++
++ /* Enabled IPIs for boot CPU immediately */
++ riscv_ipi_enable();
+ }
++EXPORT_SYMBOL_GPL(riscv_ipi_set_virq_range);
+
+ static const char * const ipi_names[] = {
+ [IPI_RESCHEDULE] = "Rescheduling interrupts",
+@@ -202,7 +201,7 @@ void show_ipi_stats(struct seq_file *p, int prec)
+ seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
+ prec >= 4 ? " " : "");
+ for_each_online_cpu(cpu)
+- seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]);
++ seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu));
+ seq_printf(p, " %s\n", ipi_names[i]);
+ }
+ }
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index bd82375db51a..775d0026efd4 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -30,7 +30,6 @@
+ #include <asm/numa.h>
+ #include <asm/tlbflush.h>
+ #include <asm/sections.h>
+-#include <asm/sbi.h>
+ #include <asm/smp.h>
+ #include <asm/alternative.h>
+
+@@ -159,12 +158,12 @@ asmlinkage __visible void smp_callin(void)
+ struct mm_struct *mm = &init_mm;
+ unsigned int curr_cpuid = smp_processor_id();
+
+- riscv_clear_ipi();
+-
+ /* All kernel threads share the same mm context. */
+ mmgrab(mm);
+ current->active_mm = mm;
+
++ riscv_ipi_enable();
++
+ notify_cpu_starting(curr_cpuid);
+ numa_add_cpu(curr_cpuid);
+ update_siblings_masks(curr_cpuid);
+diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
+index 6cfe2ab73eb0..3b68ed53fe4a 100644
+--- a/drivers/clocksource/timer-clint.c
++++ b/drivers/clocksource/timer-clint.c
+@@ -30,7 +30,6 @@
+ #define CLINT_TIMER_VAL_OFF 0xbff8
+
+ /* CLINT manages IPI and Timer for RISC-V M-mode */
+-static u32 __iomem *clint_ipi_base;
+ static u64 __iomem *clint_timer_cmp;
+ static u64 __iomem *clint_timer_val;
+ static unsigned long clint_timer_freq;
+@@ -41,24 +40,6 @@ u64 __iomem *clint_time_val;
+ EXPORT_SYMBOL(clint_time_val);
+ #endif
+
+-static void clint_send_ipi(const struct cpumask *target)
+-{
+- unsigned int cpu;
+-
+- for_each_cpu(cpu, target)
+- writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu));
+-}
+-
+-static void clint_clear_ipi(void)
+-{
+- writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id()));
+-}
+-
+-static struct riscv_ipi_ops clint_ipi_ops = {
+- .ipi_inject = clint_send_ipi,
+- .ipi_clear = clint_clear_ipi,
+-};
+-
+ #ifdef CONFIG_64BIT
+ #define clint_get_cycles() readq_relaxed(clint_timer_val)
+ #else
+@@ -189,7 +170,6 @@ static int __init clint_timer_init_dt(struct device_node *np)
+ return -ENODEV;
+ }
+
+- clint_ipi_base = base + CLINT_IPI_OFF;
+ clint_timer_cmp = base + CLINT_TIMER_CMP_OFF;
+ clint_timer_val = base + CLINT_TIMER_VAL_OFF;
+ clint_timer_freq = riscv_timebase;
+@@ -228,9 +208,6 @@ static int __init clint_timer_init_dt(struct device_node *np)
+ goto fail_free_irq;
+ }
+
+- riscv_set_ipi_ops(&clint_ipi_ops);
+- clint_clear_ipi();
+-
+ return 0;
+
+ fail_free_irq:
+diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c
+index b65bd8878d4f..c5380e3e2aa0 100644
+--- a/drivers/irqchip/irq-riscv-intc.c
++++ b/drivers/irqchip/irq-riscv-intc.c
+@@ -26,20 +26,7 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
+ if (unlikely(cause >= BITS_PER_LONG))
+ panic("unexpected interrupt cause");
+
+- switch (cause) {
+-#ifdef CONFIG_SMP
+- case RV_IRQ_SOFT:
+- /*
+- * We only use software interrupts to pass IPIs, so if a
+- * non-SMP system gets one, then we don't know what to do.
+- */
+- handle_IPI(regs);
+- break;
+-#endif
+- default:
+- generic_handle_domain_irq(intc_domain, cause);
+- break;
+- }
++ generic_handle_domain_irq(intc_domain, cause);
+ }
+
+ /*
+@@ -59,18 +46,6 @@ static void riscv_intc_irq_unmask(struct irq_data *d)
+ csr_set(CSR_IE, BIT(d->hwirq));
+ }
+
+-static int riscv_intc_cpu_starting(unsigned int cpu)
+-{
+- csr_set(CSR_IE, BIT(RV_IRQ_SOFT));
+- return 0;
+-}
+-
+-static int riscv_intc_cpu_dying(unsigned int cpu)
+-{
+- csr_clear(CSR_IE, BIT(RV_IRQ_SOFT));
+- return 0;
+-}
+-
+ static struct irq_chip riscv_intc_chip = {
+ .name = "RISC-V INTC",
+ .irq_mask = riscv_intc_irq_mask,
+@@ -87,9 +62,32 @@ static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq,
+ return 0;
+ }
+
++static int riscv_intc_domain_alloc(struct irq_domain *domain,
++ unsigned int virq, unsigned int nr_irqs,
++ void *arg)
++{
++ int i, ret;
++ irq_hw_number_t hwirq;
++ unsigned int type = IRQ_TYPE_NONE;
++ struct irq_fwspec *fwspec = arg;
++
++ ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < nr_irqs; i++) {
++ ret = riscv_intc_domain_map(domain, virq + i, hwirq + i);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
+ static const struct irq_domain_ops riscv_intc_domain_ops = {
+ .map = riscv_intc_domain_map,
+ .xlate = irq_domain_xlate_onecell,
++ .alloc = riscv_intc_domain_alloc
+ };
+
+ static int __init riscv_intc_init(struct device_node *node,
+@@ -125,11 +123,6 @@ static int __init riscv_intc_init(struct device_node *node,
+ return rc;
+ }
+
+- cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING,
+- "irqchip/riscv/intc:starting",
+- riscv_intc_cpu_starting,
+- riscv_intc_cpu_dying);
+-
+ pr_info("%d local interrupts mapped\n", BITS_PER_LONG);
+
+ return 0;
+--
+2.20.1
+
--- /dev/null
+From 3877938e9a34e561cb6564ea033454d7d5615ca8 Mon Sep 17 00:00:00 2001
+From: Anup Patel <anup.patel@wdc.com>
+Date: Thu, 17 Jun 2021 16:36:16 +0530
+Subject: [PATCH 052/124] clocksource: clint: Add support for ACLINT MTIMER
+ device
+
+The RISC-V ACLINT specification is a modular specification and the
+ACLINT MTIMER device is backward compatible with the M-mode timer
+functionality of the CLINT device. This patch extends the CLINT
+timer driver to support both CLINT device and ACLINT MTIMER device.
+
+Signed-off-by: Anup Patel <anup.patel@wdc.com>
+Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
+---
+ drivers/clocksource/timer-clint.c | 59 ++++++++++++++++++++++++-------
+ 1 file changed, 47 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
+index 3b68ed53fe4a..9d38dfce374e 100644
+--- a/drivers/clocksource/timer-clint.c
++++ b/drivers/clocksource/timer-clint.c
+@@ -2,8 +2,16 @@
+ /*
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ *
+- * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a
+- * CLINT MMIO timer device.
++ * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a CLINT
++ * MMIO device which is a composite device capable of injecting M-mode
++ * software interrupts and M-mode timer interrupts.
++ *
++ * The RISC-V ACLINT specification is modular in nature and defines
++ * separate devices for M-mode software interrupt (MSWI), M-mode timer
++ * (MTIMER) and S-mode software interrupt (SSWI).
++ *
++ * This is a common timer driver for the CLINT device and the ACLINT
++ * MTIMER device.
+ */
+
+ #define pr_fmt(fmt) "clint: " fmt
+@@ -17,12 +25,16 @@
+ #include <linux/sched_clock.h>
+ #include <linux/io-64-nonatomic-lo-hi.h>
+ #include <linux/interrupt.h>
++#include <linux/irqchip/irq-riscv-aclint-swi.h>
+ #include <linux/of_irq.h>
+ #include <linux/smp.h>
+ #include <linux/timex.h>
+
+-#ifndef CONFIG_RISCV_M_MODE
++#ifdef CONFIG_RISCV_M_MODE
+ #include <asm/clint.h>
++
++u64 __iomem *clint_time_val;
++EXPORT_SYMBOL(clint_time_val);
+ #endif
+
+ #define CLINT_IPI_OFF 0
+@@ -35,11 +47,6 @@ static u64 __iomem *clint_timer_val;
+ static unsigned long clint_timer_freq;
+ static unsigned int clint_timer_irq;
+
+-#ifdef CONFIG_RISCV_M_MODE
+-u64 __iomem *clint_time_val;
+-EXPORT_SYMBOL(clint_time_val);
+-#endif
+-
+ #ifdef CONFIG_64BIT
+ #define clint_get_cycles() readq_relaxed(clint_timer_val)
+ #else
+@@ -129,8 +136,10 @@ static int __init clint_timer_init_dt(struct device_node *np)
+ {
+ int rc;
+ u32 i, nr_irqs;
+- void __iomem *base;
++ void __iomem *base = NULL;
++ void __iomem *base1 = NULL;
+ struct of_phandle_args oirq;
++ bool is_aclint = of_device_is_compatible(np, "riscv,aclint-mtimer");
+
+ /*
+ * Ensure that CLINT device interrupts are either RV_IRQ_TIMER or
+@@ -170,8 +179,19 @@ static int __init clint_timer_init_dt(struct device_node *np)
+ return -ENODEV;
+ }
+
+- clint_timer_cmp = base + CLINT_TIMER_CMP_OFF;
+- clint_timer_val = base + CLINT_TIMER_VAL_OFF;
++ if (is_aclint) {
++ clint_timer_val = base;
++ base1 = of_iomap(np, 1);
++ if (!base1) {
++ rc = -ENODEV;
++ pr_err("%pOFP: could not map registers\n", np);
++ goto fail_iounmap;
++ }
++ clint_timer_cmp = base1;
++ } else {
++ clint_timer_cmp = base + CLINT_TIMER_CMP_OFF;
++ clint_timer_val = base + CLINT_TIMER_VAL_OFF;
++ }
+ clint_timer_freq = riscv_timebase;
+
+ #ifdef CONFIG_RISCV_M_MODE
+@@ -208,14 +228,29 @@ static int __init clint_timer_init_dt(struct device_node *np)
+ goto fail_free_irq;
+ }
+
++ if (!is_aclint) {
++ rc = aclint_swi_init(np, base + CLINT_IPI_OFF);
++ if (rc) {
++ pr_err("%pOFP: aclint swi init failed [%d]\n",
++ np, rc);
++ goto fail_remove_cpuhp;
++ }
++ }
++
+ return 0;
+
++fail_remove_cpuhp:
++ cpuhp_remove_state(CPUHP_AP_CLINT_TIMER_STARTING);
+ fail_free_irq:
+ free_irq(clint_timer_irq, &clint_clock_event);
+ fail_iounmap:
+- iounmap(base);
++ if (base1)
++ iounmap(base1);
++ if (base)
++ iounmap(base);
+ return rc;
+ }
+
+ TIMER_OF_DECLARE(clint_timer, "riscv,clint0", clint_timer_init_dt);
+ TIMER_OF_DECLARE(clint_timer1, "sifive,clint0", clint_timer_init_dt);
++TIMER_OF_DECLARE(clint_timer2, "riscv,aclint-mtimer", clint_timer_init_dt);
+--
+2.20.1
+
--- /dev/null
+From 41c15354d89edf12be64f19ababdaaee0cccaef2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:58:08 -0500
+Subject: [PATCH 053/124] irqchip/sun20i: Add Allwinner D1 stacked INTC driver
+
+The D1 SoC uses the RISC-V PLIC as its main interrupt controller, but
+the PLIC does not provide any interface for configuring IRQ trigger type
+or wakeup capability. So Allwinner added registers in a separate MMIO
+region (RISCV_CFG) to support these features.
+
+Neither of those features is needed at the moment: most peripherals use
+level interrupts, and RISC-V Linux does not yet support system suspend.
+Plus, there are some complications with this extra MMIO region requiring
+clocks/resets to access, which are not available during of_irq_init().
+
+However, adding this irqchip later would be a compatibility-breaking
+devicetree change, so at least a stub driver is needed for now.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/irqchip/Kconfig | 3 ++
+ drivers/irqchip/Makefile | 1 +
+ drivers/irqchip/irq-sun20i.c | 95 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 99 insertions(+)
+ create mode 100644 drivers/irqchip/irq-sun20i.c
+
+diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
+index aca7b595c4c7..ca982f2f6467 100644
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -522,6 +522,9 @@ config SIFIVE_PLIC
+
+ If you don't know what to do here, say Y.
+
++config SUN20I_INTC
++ bool
++
+ config EXYNOS_IRQ_COMBINER
+ bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST
+ depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST
+diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
+index f88cbf36a9d2..ba1097e01991 100644
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -98,6 +98,7 @@ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o
+ obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o
+ obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o
+ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o
++obj-$(CONFIG_SUN20I_INTC) += irq-sun20i.o
+ obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o
+ obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o
+ obj-$(CONFIG_MADERA_IRQ) += irq-madera.o
+diff --git a/drivers/irqchip/irq-sun20i.c b/drivers/irqchip/irq-sun20i.c
+new file mode 100644
+index 000000000000..ee1b81aa7ff7
+--- /dev/null
++++ b/drivers/irqchip/irq-sun20i.c
+@@ -0,0 +1,95 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Allwinner sun20i (D1) wakeup irqchip driver.
++ */
++
++#include <linux/bitmap.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/irqchip.h>
++#include <linux/irqdomain.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/syscore_ops.h>
++
++#define SUN20I_HWIRQ_OFFSET 16
++#define SUN20I_NR_HWIRQS 160
++
++static struct irq_chip sun20i_intc_chip = {
++ .name = "sun20i-intc",
++ .irq_mask = irq_chip_mask_parent,
++ .irq_unmask = irq_chip_unmask_parent,
++ .irq_eoi = irq_chip_eoi_parent,
++ .irq_set_affinity = irq_chip_set_affinity_parent,
++};
++
++static int sun20i_intc_domain_translate(struct irq_domain *domain,
++ struct irq_fwspec *fwspec,
++ unsigned long *hwirq,
++ unsigned int *type)
++{
++ if (fwspec->param_count < 2)
++ return -EINVAL;
++ if (fwspec->param[0] < SUN20I_HWIRQ_OFFSET)
++ return -EINVAL;
++
++ *hwirq = fwspec->param[0];
++ *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
++
++ return 0;
++}
++
++static int sun20i_intc_domain_alloc(struct irq_domain *domain,
++ unsigned int virq,
++ unsigned int nr_irqs, void *arg)
++{
++ struct irq_fwspec *fwspec = arg;
++ unsigned long hwirq;
++ unsigned int type;
++ int i, ret;
++
++ ret = sun20i_intc_domain_translate(domain, fwspec, &hwirq, &type);
++ if (ret)
++ return ret;
++ if (hwirq + nr_irqs > SUN20I_HWIRQ_OFFSET + SUN20I_NR_HWIRQS)
++ return -EINVAL;
++
++ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, fwspec);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < nr_irqs; ++i, ++hwirq, ++virq)
++ irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
++ &sun20i_intc_chip, 0);
++
++ return 0;
++}
++
++static const struct irq_domain_ops sun20i_intc_domain_ops = {
++ .translate = sun20i_intc_domain_translate,
++ .alloc = sun20i_intc_domain_alloc,
++ .free = irq_domain_free_irqs_common,
++};
++
++static int __init sun20i_intc_init(struct device_node *node,
++ struct device_node *parent)
++{
++ struct irq_domain *domain, *parent_domain;
++
++ parent_domain = irq_find_host(parent);
++ if (!parent_domain) {
++ pr_err("%pOF: Failed to obtain parent domain\n", node);
++ return -ENXIO;
++ }
++
++ domain = irq_domain_add_hierarchy(parent_domain, 0, 0, node,
++ &sun20i_intc_domain_ops, NULL);
++ if (!domain) {
++ pr_err("%pOF: Failed to allocate domain\n", node);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++IRQCHIP_DECLARE(sun20i_intc, "allwinner,sun20i-d1-intc", sun20i_intc_init);
+--
+2.20.1
+
--- /dev/null
+From 020c708867842e41e96d6a73081a7d32415b2301 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 01:08:23 -0500
+Subject: [PATCH 054/124] clk: sunxi-ng: div: Add macros using clk_parent_data
+ and clk_hw
+
+Referencing parents with clk_hw pointers is more efficient and removes
+the dependency on global clock names. clk_parent_data is needed when
+some parent clocks are provided from another driver. Add macros for
+declaring dividers that take advantage of these.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_div.h | 78 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 78 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
+index 6682fde6043c..948e2b0c0c3b 100644
+--- a/drivers/clk/sunxi-ng/ccu_div.h
++++ b/drivers/clk/sunxi-ng/ccu_div.h
+@@ -108,6 +108,22 @@ struct ccu_div {
+ _shift, _width, _table, 0, \
+ _flags)
+
++#define SUNXI_CCU_DIV_TABLE_HW(_struct, _name, _parent, _reg, \
++ _shift, _width, \
++ _table, _flags) \
++ struct ccu_div _struct = { \
++ .div = _SUNXI_CCU_DIV_TABLE(_shift, _width, \
++ _table), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_HW(_name, \
++ _parent, \
++ &ccu_div_ops, \
++ _flags), \
++ } \
++ }
++
++
+ #define SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \
+ _parents, _table, \
+ _reg, \
+@@ -166,6 +182,68 @@ struct ccu_div {
+ SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \
+ _mshift, _mwidth, 0, _flags)
+
++#define SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _muxshift, _muxwidth, \
++ _gate, _flags) \
++ struct ccu_div _struct = { \
++ .enable = _gate, \
++ .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
++ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
++ _parents, \
++ &ccu_div_ops, \
++ _flags), \
++ }, \
++ }
++
++#define SUNXI_CCU_M_DATA_WITH_MUX(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _muxshift, _muxwidth, \
++ _flags) \
++ SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _muxshift, _muxwidth, \
++ 0, _flags)
++
++#define SUNXI_CCU_M_HW_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, _muxshift, _muxwidth, \
++ _gate, _flags) \
++ struct ccu_div _struct = { \
++ .enable = _gate, \
++ .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
++ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \
++ _parents, \
++ &ccu_div_ops, \
++ _flags), \
++ }, \
++ }
++
++#define SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg, \
++ _mshift, _mwidth, _gate, \
++ _flags) \
++ struct ccu_div _struct = { \
++ .enable = _gate, \
++ .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_HWS(_name, \
++ _parent, \
++ &ccu_div_ops, \
++ _flags), \
++ }, \
++ }
++
++#define SUNXI_CCU_M_HWS(_struct, _name, _parent, _reg, _mshift, \
++ _mwidth, _flags) \
++ SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg, \
++ _mshift, _mwidth, 0, _flags)
++
+ static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw)
+ {
+ struct ccu_common *common = hw_to_ccu_common(hw);
+--
+2.20.1
+
--- /dev/null
+From 3ae434b57e6db1761db9403512a21cac890a8856 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 01:08:23 -0500
+Subject: [PATCH 055/124] clk: sunxi-ng: mp: Add macros using clk_parent_data
+ and clk_hw
+
+Referencing parents with clk_hw pointers is more efficient and removes
+the dependency on global clock names. clk_parent_data is needed when
+some parent clocks are provided from another driver. Add macros for
+declaring dividers that take advantage of these.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_mp.h | 49 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 49 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h
+index b392e0d575b5..6e50f3728fb5 100644
+--- a/drivers/clk/sunxi-ng/ccu_mp.h
++++ b/drivers/clk/sunxi-ng/ccu_mp.h
+@@ -82,6 +82,55 @@ struct ccu_mp {
+ _muxshift, _muxwidth, \
+ 0, _flags)
+
++#define SUNXI_CCU_MP_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _pshift, _pwidth, \
++ _muxshift, _muxwidth, \
++ _gate, _flags) \
++ struct ccu_mp _struct = { \
++ .enable = _gate, \
++ .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
++ .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
++ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
++ _parents, \
++ &ccu_mp_ops, \
++ _flags), \
++ } \
++ }
++
++#define SUNXI_CCU_MP_DATA_WITH_MUX(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _pshift, _pwidth, \
++ _muxshift, _muxwidth, \
++ _flags) \
++ SUNXI_CCU_MP_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _pshift, _pwidth, \
++ _muxshift, _muxwidth, \
++ 0, _flags)
++
++#define SUNXI_CCU_MP_HW_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
++ _mshift, _mwidth, \
++ _pshift, _pwidth, \
++ _muxshift, _muxwidth, \
++ _gate, _flags) \
++ struct ccu_mp _struct = { \
++ .enable = _gate, \
++ .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
++ .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
++ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \
++ _parents, \
++ &ccu_mp_ops, \
++ _flags), \
++ } \
++ }
++
+ static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw)
+ {
+ struct ccu_common *common = hw_to_ccu_common(hw);
+--
+2.20.1
+
--- /dev/null
+From 916bf0d78efdb771692043170aceaa87ebd29617 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 01:08:23 -0500
+Subject: [PATCH 056/124] clk: sunxi-ng: mux: Add macros using clk_parent_data
+ and clk_hw
+
+Referencing parents with clk_hw pointers is more efficient and removes
+the dependency on global clock names. clk_parent_data is needed when
+some parent clocks are provided from another driver. Add macros for
+declaring muxes that take advantage of these.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_mux.h | 33 +++++++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
+index f165395effb5..f9a674c69c65 100644
+--- a/drivers/clk/sunxi-ng/ccu_mux.h
++++ b/drivers/clk/sunxi-ng/ccu_mux.h
+@@ -73,6 +73,39 @@ struct ccu_mux {
+ SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \
+ _reg, _shift, _width, 0, _flags)
+
++#define SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg, \
++ _shift, _width, _gate, _flags) \
++ struct ccu_mux _struct = { \
++ .enable = _gate, \
++ .mux = _SUNXI_CCU_MUX(_shift, _width), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
++ _parents, \
++ &ccu_mux_ops, \
++ _flags), \
++ } \
++ }
++
++#define SUNXI_CCU_MUX_DATA(_struct, _name, _parents, _reg, \
++ _shift, _width, _flags) \
++ SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg, \
++ _shift, _width, 0, _flags)
++
++#define SUNXI_CCU_MUX_HW_WITH_GATE(_struct, _name, _parents, _reg, \
++ _shift, _width, _gate, _flags) \
++ struct ccu_mux _struct = { \
++ .enable = _gate, \
++ .mux = _SUNXI_CCU_MUX(_shift, _width), \
++ .common = { \
++ .reg = _reg, \
++ .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \
++ _parents, \
++ &ccu_mux_ops, \
++ _flags), \
++ } \
++ }
++
+ static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
+ {
+ struct ccu_common *common = hw_to_ccu_common(hw);
+--
+2.20.1
+
--- /dev/null
+From 6420720c7f1c456341f71a0a9da3b79f26d89f30 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 01:21:11 -0500
+Subject: [PATCH 057/124] clk: sunxi-ng: gate: Add macros for gates with fixed
+ dividers
+
+It is possible to declare a gate with a fixed divider, by using the
+CCU_FEATURE_ALL_PREDIV flag. Since this is not obvious, add a macro
+for declaring this type of clock.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu_gate.h | 32 +++++++++++++++++++++++++++++++-
+ 1 file changed, 31 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/clk/sunxi-ng/ccu_gate.h b/drivers/clk/sunxi-ng/ccu_gate.h
+index c386689a952b..dc05ce06737a 100644
+--- a/drivers/clk/sunxi-ng/ccu_gate.h
++++ b/drivers/clk/sunxi-ng/ccu_gate.h
+@@ -53,7 +53,7 @@ struct ccu_gate {
+ }
+
+ /*
+- * The following two macros allow the re-use of the data structure
++ * The following macros allow the re-use of the data structure
+ * holding the parent info.
+ */
+ #define SUNXI_CCU_GATE_HWS(_struct, _name, _parent, _reg, _gate, _flags) \
+@@ -68,6 +68,21 @@ struct ccu_gate {
+ } \
+ }
+
++#define SUNXI_CCU_GATE_HWS_WITH_PREDIV(_struct, _name, _parent, _reg, \
++ _gate, _prediv, _flags) \
++ struct ccu_gate _struct = { \
++ .enable = _gate, \
++ .common = { \
++ .reg = _reg, \
++ .prediv = _prediv, \
++ .features = CCU_FEATURE_ALL_PREDIV, \
++ .hw.init = CLK_HW_INIT_HWS(_name, \
++ _parent, \
++ &ccu_gate_ops, \
++ _flags), \
++ } \
++ }
++
+ #define SUNXI_CCU_GATE_DATA(_struct, _name, _data, _reg, _gate, _flags) \
+ struct ccu_gate _struct = { \
+ .enable = _gate, \
+@@ -81,6 +96,21 @@ struct ccu_gate {
+ } \
+ }
+
++#define SUNXI_CCU_GATE_DATA_WITH_PREDIV(_struct, _name, _parent, _reg, \
++ _gate, _prediv, _flags) \
++ struct ccu_gate _struct = { \
++ .enable = _gate, \
++ .common = { \
++ .reg = _reg, \
++ .prediv = _prediv, \
++ .features = CCU_FEATURE_ALL_PREDIV, \
++ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \
++ _parent, \
++ &ccu_gate_ops, \
++ _flags), \
++ } \
++ }
++
+ static inline struct ccu_gate *hw_to_ccu_gate(struct clk_hw *hw)
+ {
+ struct ccu_common *common = hw_to_ccu_common(hw);
+--
+2.20.1
+
--- /dev/null
+From 78feeb7c1c73da8480ca503812f289e0265a9345 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Thu, 20 May 2021 00:09:46 -0500
+Subject: [PATCH 058/124] clk: sunxi-ng: Add support for the D1 SoC clocks
+
+The D1 SoC contains a CCU and a R_CCU (PRCM CCU). Add support for them.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/Kconfig | 10 +
+ drivers/clk/sunxi-ng/Makefile | 4 +
+ drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c | 140 +++
+ drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h | 17 +
+ drivers/clk/sunxi-ng/ccu-sun20i-d1.c | 1390 ++++++++++++++++++++++++
+ drivers/clk/sunxi-ng/ccu-sun20i-d1.h | 15 +
+ 6 files changed, 1576 insertions(+)
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun20i-d1.c
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun20i-d1.h
+
+diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
+index ee383658ff4d..26555f59515d 100644
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -12,6 +12,16 @@ config SUNIV_F1C100S_CCU
+ default MACH_SUNIV
+ depends on MACH_SUNIV || COMPILE_TEST
+
++config SUN20I_D1_CCU
++ tristate "Support for the Allwinner D1 CCU"
++ default RISCV && ARCH_SUNXI
++ depends on (RISCV && ARCH_SUNXI) || COMPILE_TEST
++
++config SUN20I_D1_R_CCU
++ tristate "Support for the Allwinner D1 PRCM CCU"
++ default RISCV && ARCH_SUNXI
++ depends on (RISCV && ARCH_SUNXI) || COMPILE_TEST
++
+ config SUN50I_A64_CCU
+ tristate "Support for the Allwinner A64 CCU"
+ default ARM64 && ARCH_SUNXI
+diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
+index 659d55150c32..ec931cb7aa14 100644
+--- a/drivers/clk/sunxi-ng/Makefile
++++ b/drivers/clk/sunxi-ng/Makefile
+@@ -25,6 +25,8 @@ sunxi-ccu-y += ccu_mp.o
+
+ # SoC support
+ obj-$(CONFIG_SUNIV_F1C100S_CCU) += suniv-f1c100s-ccu.o
++obj-$(CONFIG_SUN20I_D1_CCU) += sun20i-d1-ccu.o
++obj-$(CONFIG_SUN20I_D1_R_CCU) += sun20i-d1-r-ccu.o
+ obj-$(CONFIG_SUN50I_A64_CCU) += sun50i-a64-ccu.o
+ obj-$(CONFIG_SUN50I_A100_CCU) += sun50i-a100-ccu.o
+ obj-$(CONFIG_SUN50I_A100_R_CCU) += sun50i-a100-r-ccu.o
+@@ -47,6 +49,8 @@ obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-de-ccu.o
+ obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-usb-ccu.o
+
+ suniv-f1c100s-ccu-y += ccu-suniv-f1c100s.o
++sun20i-d1-ccu-y += ccu-sun20i-d1.o
++sun20i-d1-r-ccu-y += ccu-sun20i-d1-r.o
+ sun50i-a64-ccu-y += ccu-sun50i-a64.o
+ sun50i-a100-ccu-y += ccu-sun50i-a100.o
+ sun50i-a100-r-ccu-y += ccu-sun50i-a100-r.o
+diff --git a/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c b/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c
+new file mode 100644
+index 000000000000..9d3ffd3fb2c1
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c
+@@ -0,0 +1,140 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "ccu_common.h"
++#include "ccu_reset.h"
++
++#include "ccu_gate.h"
++#include "ccu_mp.h"
++
++#include "ccu-sun20i-d1-r.h"
++
++static const struct clk_parent_data r_ahb_apb0_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .fw_name = "iosc" },
++ { .fw_name = "pll-periph" },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX(r_ahb_clk, "r-ahb",
++ r_ahb_apb0_parents, 0x000,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ 0);
++static const struct clk_hw *r_ahb_hw = &r_ahb_clk.common.hw;
++
++static SUNXI_CCU_MP_DATA_WITH_MUX(r_apb0_clk, "r-apb0",
++ r_ahb_apb0_parents, 0x00c,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ 0);
++static const struct clk_hw *r_apb0_hw = &r_apb0_clk.common.hw;
++
++static SUNXI_CCU_GATE_HWS(bus_r_timer_clk, "bus-r-timer", &r_apb0_hw,
++ 0x11c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_r_twd_clk, "bus-r-twd", &r_apb0_hw,
++ 0x12c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_r_ppu_clk, "bus-r-ppu", &r_apb0_hw,
++ 0x1ac, BIT(0), 0);
++
++static const struct clk_parent_data r_ir_rx_parents[] = {
++ { .fw_name = "losc" },
++ { .fw_name = "hosc" },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_ir_rx_clk, "r-ir-rx",
++ r_ir_rx_parents, 0x1c0,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 2, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_r_ir_rx_clk, "bus-r-ir-rx", &r_apb0_hw,
++ 0x1cc, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_r_rtc_clk, "bus-r-rtc", &r_ahb_hw,
++ 0x20c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_r_cpucfg_clk, "bus-r-cpucfg", &r_apb0_hw,
++ 0x22c, BIT(0), 0);
++
++static struct ccu_common *sun20i_d1_r_ccu_clks[] = {
++ &r_ahb_clk.common,
++ &r_apb0_clk.common,
++ &bus_r_timer_clk.common,
++ &bus_r_twd_clk.common,
++ &bus_r_ppu_clk.common,
++ &r_ir_rx_clk.common,
++ &bus_r_ir_rx_clk.common,
++ &bus_r_rtc_clk.common,
++ &bus_r_cpucfg_clk.common,
++};
++
++static struct clk_hw_onecell_data sun20i_d1_r_hw_clks = {
++ .num = CLK_NUMBER,
++ .hws = {
++ [CLK_R_AHB] = &r_ahb_clk.common.hw,
++ [CLK_R_APB0] = &r_apb0_clk.common.hw,
++ [CLK_BUS_R_TIMER] = &bus_r_timer_clk.common.hw,
++ [CLK_BUS_R_TWD] = &bus_r_twd_clk.common.hw,
++ [CLK_BUS_R_PPU] = &bus_r_ppu_clk.common.hw,
++ [CLK_R_IR_RX] = &r_ir_rx_clk.common.hw,
++ [CLK_BUS_R_IR_RX] = &bus_r_ir_rx_clk.common.hw,
++ [CLK_BUS_R_RTC] = &bus_r_rtc_clk.common.hw,
++ [CLK_BUS_R_CPUCFG] = &bus_r_cpucfg_clk.common.hw,
++ },
++};
++
++static struct ccu_reset_map sun20i_d1_r_ccu_resets[] = {
++ [RST_BUS_R_TIMER] = { 0x11c, BIT(16) },
++ [RST_BUS_R_TWD] = { 0x12c, BIT(16) },
++ [RST_BUS_R_PPU] = { 0x1ac, BIT(16) },
++ [RST_BUS_R_IR_RX] = { 0x1cc, BIT(16) },
++ [RST_BUS_R_RTC] = { 0x20c, BIT(16) },
++ [RST_BUS_R_CPUCFG] = { 0x22c, BIT(16) },
++};
++
++static const struct sunxi_ccu_desc sun20i_d1_r_ccu_desc = {
++ .ccu_clks = sun20i_d1_r_ccu_clks,
++ .num_ccu_clks = ARRAY_SIZE(sun20i_d1_r_ccu_clks),
++
++ .hw_clks = &sun20i_d1_r_hw_clks,
++
++ .resets = sun20i_d1_r_ccu_resets,
++ .num_resets = ARRAY_SIZE(sun20i_d1_r_ccu_resets),
++};
++
++static int sun20i_d1_r_ccu_probe(struct platform_device *pdev)
++{
++ void __iomem *reg;
++
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
++
++ return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun20i_d1_r_ccu_desc);
++}
++
++static const struct of_device_id sun20i_d1_r_ccu_ids[] = {
++ { .compatible = "allwinner,sun20i-d1-r-ccu" },
++ { }
++};
++
++static struct platform_driver sun20i_d1_r_ccu_driver = {
++ .probe = sun20i_d1_r_ccu_probe,
++ .driver = {
++ .name = "sun20i-d1-r-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun20i_d1_r_ccu_ids,
++ },
++};
++module_platform_driver(sun20i_d1_r_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h b/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h
+new file mode 100644
+index 000000000000..afd4342209ee
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2020 frank@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _CCU_SUN20I_D1_R_H
++#define _CCU_SUN20I_D1_R_H
++
++#include <dt-bindings/clock/sun20i-d1-r-ccu.h>
++#include <dt-bindings/reset/sun20i-d1-r-ccu.h>
++
++#define CLK_R_APB0 1
++
++#define CLK_NUMBER (CLK_BUS_R_CPUCFG + 1)
++
++#endif /* _CCU_SUN20I_D1_R_H */
+diff --git a/drivers/clk/sunxi-ng/ccu-sun20i-d1.c b/drivers/clk/sunxi-ng/ccu-sun20i-d1.c
+new file mode 100644
+index 000000000000..51058ba4db4d
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun20i-d1.c
+@@ -0,0 +1,1390 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "../clk.h"
++
++#include "ccu_common.h"
++#include "ccu_reset.h"
++
++#include "ccu_div.h"
++#include "ccu_gate.h"
++#include "ccu_mp.h"
++#include "ccu_mult.h"
++#include "ccu_nk.h"
++#include "ccu_nkm.h"
++#include "ccu_nkmp.h"
++#include "ccu_nm.h"
++
++#include "ccu-sun20i-d1.h"
++
++static const struct clk_parent_data osc24M[] = {
++ { .fw_name = "hosc" }
++};
++
++/*
++ * For the CPU PLL, the output divider is described as "only for testing"
++ * in the user manual. So it's not modelled and forced to 0.
++ */
++#define SUN20I_D1_PLL_CPUX_REG 0x000
++static struct ccu_mult pll_cpux_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .mult = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .common = {
++ .reg = 0x000,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-cpux", osc24M,
++ &ccu_mult_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++/* Some PLLs are input * N / div1 / P. Model them as NKMP with no K */
++#define SUN20I_D1_PLL_DDR0_REG 0x010
++static struct ccu_nkmp pll_ddr0_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
++ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
++ .common = {
++ .reg = 0x010,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-ddr0", osc24M,
++ &ccu_nkmp_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++#define SUN20I_D1_PLL_PERIPH0_REG 0x020
++static struct ccu_nm pll_periph0_4x_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
++ .common = {
++ .reg = 0x020,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-periph0-4x", osc24M,
++ &ccu_nm_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++static const struct clk_hw *pll_periph0_4x_hws[] = {
++ &pll_periph0_4x_clk.common.hw
++};
++static SUNXI_CCU_M_HWS(pll_periph0_2x_clk, "pll-periph0-2x",
++ pll_periph0_4x_hws, 0x020, 16, 3, 0);
++static SUNXI_CCU_M_HWS(pll_periph0_800M_clk, "pll-periph0-800M",
++ pll_periph0_4x_hws, 0x020, 20, 3, 0);
++
++static const struct clk_hw *pll_periph0_2x_hws[] = {
++ &pll_periph0_2x_clk.common.hw
++};
++static CLK_FIXED_FACTOR_HWS(pll_periph0_clk, "pll-periph0",
++ pll_periph0_2x_hws, 2, 1, 0);
++
++static const struct clk_hw *pll_periph0_hws[] = { &pll_periph0_clk.hw };
++static CLK_FIXED_FACTOR_HWS(pll_periph0_div3_clk, "pll-periph0-div3",
++ pll_periph0_2x_hws, 6, 1, 0);
++
++/*
++ * For Video PLLs, the output divider is described as "only for testing"
++ * in the user manual. So it's not modelled and forced to 0.
++ */
++#define SUN20I_D1_PLL_VIDEO0_REG 0x040
++static struct ccu_nm pll_video0_4x_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
++ .common = {
++ .reg = 0x040,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-video0-4x", osc24M,
++ &ccu_nm_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++static const struct clk_hw *pll_video0_4x_hws[] = {
++ &pll_video0_4x_clk.common.hw
++};
++static CLK_FIXED_FACTOR_HWS(pll_video0_2x_clk, "pll-video0-2x",
++ pll_video0_4x_hws, 2, 1, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR_HWS(pll_video0_clk, "pll-video0",
++ pll_video0_4x_hws, 4, 1, CLK_SET_RATE_PARENT);
++
++#define SUN20I_D1_PLL_VIDEO1_REG 0x048
++static struct ccu_nm pll_video1_4x_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
++ .common = {
++ .reg = 0x048,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-video1-4x", osc24M,
++ &ccu_nm_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++static const struct clk_hw *pll_video1_4x_hws[] = {
++ &pll_video1_4x_clk.common.hw
++};
++static CLK_FIXED_FACTOR_HWS(pll_video1_2x_clk, "pll-video1-2x",
++ pll_video1_4x_hws, 2, 1, CLK_SET_RATE_PARENT);
++static CLK_FIXED_FACTOR_HWS(pll_video1_clk, "pll-video1",
++ pll_video1_4x_hws, 4, 1, CLK_SET_RATE_PARENT);
++
++#define SUN20I_D1_PLL_VE_REG 0x058
++static struct ccu_nkmp pll_ve_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1), /* input divider */
++ .p = _SUNXI_CCU_DIV(0, 1), /* output divider */
++ .common = {
++ .reg = 0x058,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-ve", osc24M,
++ &ccu_nkmp_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++/*
++ * PLL_AUDIO0 has m0, m1 dividers in addition to the usual N, M factors.
++ * Since we only need one frequency from this PLL (22.5792 x 4 == 90.3168 MHz),
++ * ignore them for now. Enforce the default for them, which is m1 = 0, m0 = 0.
++ * The M factor must be an even number to produce a 50% duty cycle output.
++ */
++#define SUN20I_D1_PLL_AUDIO0_REG 0x078
++static struct ccu_sdm_setting pll_audio0_sdm_table[] = {
++ { .rate = 90316800, .pattern = 0xc001288d, .m = 6, .n = 22 },
++};
++
++static struct ccu_nm pll_audio0_4x_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(16, 6),
++ .sdm = _SUNXI_CCU_SDM(pll_audio0_sdm_table, BIT(24),
++ 0x178, BIT(31)),
++ .common = {
++ .reg = 0x078,
++ .features = CCU_FEATURE_SIGMA_DELTA_MOD,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio0-4x", osc24M,
++ &ccu_nm_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++static const struct clk_hw *pll_audio0_4x_hws[] = {
++ &pll_audio0_4x_clk.common.hw
++};
++static CLK_FIXED_FACTOR_HWS(pll_audio0_2x_clk, "pll-audio0-2x",
++ pll_audio0_4x_hws, 2, 1, 0);
++static CLK_FIXED_FACTOR_HWS(pll_audio0_clk, "pll-audio0",
++ pll_audio0_4x_hws, 4, 1, 0);
++
++/*
++ * PLL_AUDIO1 doesn't need Fractional-N. The output is usually 614.4 MHz for
++ * audio. The ADC or DAC should divide the PLL output further to 24.576 MHz.
++ */
++#define SUN20I_D1_PLL_AUDIO1_REG 0x080
++static struct ccu_nm pll_audio1_clk = {
++ .enable = BIT(27),
++ .lock = BIT(28),
++ .n = _SUNXI_CCU_MULT_MIN(8, 8, 12),
++ .m = _SUNXI_CCU_DIV(1, 1),
++ .common = {
++ .reg = 0x080,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio1", osc24M,
++ &ccu_nm_ops,
++ CLK_SET_RATE_UNGATE),
++ },
++};
++
++static const struct clk_hw *pll_audio1_hws[] = {
++ &pll_audio1_clk.common.hw
++};
++static SUNXI_CCU_M_HWS(pll_audio1_div2_clk, "pll-audio1-div2",
++ pll_audio1_hws, 0x080, 16, 3, 0);
++static SUNXI_CCU_M_HWS(pll_audio1_div5_clk, "pll-audio1-div5",
++ pll_audio1_hws, 0x080, 20, 3, 0);
++
++/*
++ * The CPUX gate is not modelled - it is in a separate register (0x504)
++ * and has a special key field. The clock does not need to be ungated anyway.
++ */
++static const struct clk_parent_data cpux_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .fw_name = "iosc" },
++ { .hw = &pll_cpux_clk.common.hw },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_periph0_800M_clk.common.hw },
++};
++static SUNXI_CCU_MUX_DATA(cpux_clk, "cpux", cpux_parents,
++ 0x500, 24, 3, CLK_SET_RATE_PARENT);
++
++static const struct clk_hw *cpux_hws[] = { &cpux_clk.common.hw };
++static SUNXI_CCU_M_HWS(cpux_axi_clk, "cpux-axi",
++ cpux_hws, 0x500, 0, 2, 0);
++static SUNXI_CCU_M_HWS(cpux_apb_clk, "cpux-apb",
++ cpux_hws, 0x500, 8, 2, 0);
++
++static const struct clk_parent_data psi_ahb_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .fw_name = "iosc" },
++ { .hw = &pll_periph0_clk.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX(psi_ahb_clk, "psi-ahb", psi_ahb_parents, 0x510,
++ 0, 2, /* M */
++ 8, 2, /* P */
++ 24, 2, /* mux */
++ 0);
++
++static const struct clk_parent_data apb0_apb1_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .hw = &psi_ahb_clk.common.hw },
++ { .hw = &pll_periph0_clk.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX(apb0_clk, "apb0", apb0_apb1_parents, 0x520,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 2, /* mux */
++ 0);
++
++static SUNXI_CCU_MP_DATA_WITH_MUX(apb1_clk, "apb1", apb0_apb1_parents, 0x524,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 2, /* mux */
++ 0);
++
++static const struct clk_hw *psi_ahb_hws[] = { &psi_ahb_clk.common.hw };
++static const struct clk_hw *apb0_hws[] = { &apb0_clk.common.hw };
++static const struct clk_hw *apb1_hws[] = { &apb1_clk.common.hw };
++
++static const struct clk_hw *de_di_g2d_parents[] = {
++ &pll_periph0_2x_clk.common.hw,
++ &pll_video0_4x_clk.common.hw,
++ &pll_video1_4x_clk.common.hw,
++ &pll_audio1_div2_clk.common.hw,
++};
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(de_clk, "de", de_di_g2d_parents, 0x600,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_de_clk, "bus-de", psi_ahb_hws,
++ 0x60c, BIT(0), 0);
++
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(di_clk, "di", de_di_g2d_parents, 0x620,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_di_clk, "bus-di", psi_ahb_hws,
++ 0x62c, BIT(0), 0);
++
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(g2d_clk, "g2d", de_di_g2d_parents, 0x630,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_g2d_clk, "bus-g2d", psi_ahb_hws,
++ 0x63c, BIT(0), 0);
++
++static const struct clk_parent_data ce_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_periph0_clk.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x680,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_ce_clk, "bus-ce", psi_ahb_hws,
++ 0x68c, BIT(0), 0);
++
++static const struct clk_hw *ve_parents[] = {
++ &pll_ve_clk.common.hw,
++ &pll_periph0_2x_clk.common.hw,
++};
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_clk, "ve", ve_parents, 0x690,
++ 0, 5, /* M */
++ 24, 1, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_ve_clk, "bus-ve", psi_ahb_hws,
++ 0x69c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dma_clk, "bus-dma", psi_ahb_hws,
++ 0x70c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_msgbox0_clk, "bus-msgbox0", psi_ahb_hws,
++ 0x71c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_msgbox1_clk, "bus-msgbox1", psi_ahb_hws,
++ 0x71c, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_msgbox2_clk, "bus-msgbox2", psi_ahb_hws,
++ 0x71c, BIT(2), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_spinlock_clk, "bus-spinlock", psi_ahb_hws,
++ 0x72c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_hstimer_clk, "bus-hstimer", psi_ahb_hws,
++ 0x73c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_DATA(avs_clk, "avs", osc24M,
++ 0x740, BIT(31), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dbg_clk, "bus-dbg", psi_ahb_hws,
++ 0x78c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_pwm_clk, "bus-pwm", apb0_hws,
++ 0x7ac, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_iommu_clk, "bus-iommu", apb0_hws,
++ 0x7bc, BIT(0), 0);
++
++static const struct clk_hw *dram_parents[] = {
++ &pll_ddr0_clk.common.hw,
++ &pll_audio1_div2_clk.common.hw,
++ &pll_periph0_2x_clk.common.hw,
++ &pll_periph0_800M_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(dram_clk, "dram", dram_parents, 0x800,
++ 0, 2, /* M */
++ 8, 2, /* P */
++ 24, 2, /* mux */
++ BIT(31), CLK_IS_CRITICAL);
++
++static CLK_FIXED_FACTOR_HW(mbus_clk, "mbus",
++ &dram_clk.common.hw, 4, 1, 0);
++
++static const struct clk_hw *mbus_hws[] = { &mbus_clk.hw };
++
++static SUNXI_CCU_GATE_HWS(mbus_dma_clk, "mbus-dma", mbus_hws,
++ 0x804, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(mbus_ve_clk, "mbus-ve", mbus_hws,
++ 0x804, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(mbus_ce_clk, "mbus-ce", mbus_hws,
++ 0x804, BIT(2), 0);
++static SUNXI_CCU_GATE_HWS(mbus_tvin_clk, "mbus-tvin", mbus_hws,
++ 0x804, BIT(7), 0);
++static SUNXI_CCU_GATE_HWS(mbus_csi_clk, "mbus-csi", mbus_hws,
++ 0x804, BIT(8), 0);
++static SUNXI_CCU_GATE_HWS(mbus_g2d_clk, "mbus-g2d", mbus_hws,
++ 0x804, BIT(10), 0);
++static SUNXI_CCU_GATE_HWS(mbus_riscv_clk, "mbus-riscv", mbus_hws,
++ 0x804, BIT(11), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dram_clk, "bus-dram", psi_ahb_hws,
++ 0x80c, BIT(0), CLK_IS_CRITICAL);
++
++static const struct clk_parent_data mmc0_mmc1_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc0_mmc1_parents, 0x830,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc0_mmc1_parents, 0x834,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static const struct clk_parent_data mmc2_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_periph0_800M_clk.common.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc2_parents, 0x838,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_mmc0_clk, "bus-mmc0", psi_ahb_hws,
++ 0x84c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_mmc1_clk, "bus-mmc1", psi_ahb_hws,
++ 0x84c, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_mmc2_clk, "bus-mmc2", psi_ahb_hws,
++ 0x84c, BIT(2), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_uart0_clk, "bus-uart0", apb1_hws,
++ 0x90c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_uart1_clk, "bus-uart1", apb1_hws,
++ 0x90c, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_uart2_clk, "bus-uart2", apb1_hws,
++ 0x90c, BIT(2), 0);
++static SUNXI_CCU_GATE_HWS(bus_uart3_clk, "bus-uart3", apb1_hws,
++ 0x90c, BIT(3), 0);
++static SUNXI_CCU_GATE_HWS(bus_uart4_clk, "bus-uart4", apb1_hws,
++ 0x90c, BIT(4), 0);
++static SUNXI_CCU_GATE_HWS(bus_uart5_clk, "bus-uart5", apb1_hws,
++ 0x90c, BIT(5), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_i2c0_clk, "bus-i2c0", apb1_hws,
++ 0x91c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_i2c1_clk, "bus-i2c1", apb1_hws,
++ 0x91c, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_i2c2_clk, "bus-i2c2", apb1_hws,
++ 0x91c, BIT(2), 0);
++static SUNXI_CCU_GATE_HWS(bus_i2c3_clk, "bus-i2c3", apb1_hws,
++ 0x91c, BIT(3), 0);
++
++static const struct clk_parent_data spi_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++ { .hw = &pll_audio1_div5_clk.common.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(spi0_clk, "spi0", spi_parents, 0x940,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(spi1_clk, "spi1", spi_parents, 0x944,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_spi0_clk, "bus-spi0", psi_ahb_hws,
++ 0x96c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_spi1_clk, "bus-spi1", psi_ahb_hws,
++ 0x96c, BIT(1), 0);
++
++static SUNXI_CCU_GATE_HWS_WITH_PREDIV(emac_25M_clk, "emac-25M", pll_periph0_hws,
++ 0x970, BIT(31) | BIT(30), 24, 0);
++
++static SUNXI_CCU_GATE_HWS(bus_emac_clk, "bus-emac", psi_ahb_hws,
++ 0x97c, BIT(0), 0);
++
++static const struct clk_parent_data ir_tx_ledc_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++};
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ir_tx_clk, "ir-tx", ir_tx_ledc_parents, 0x9c0,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_ir_tx_clk, "bus-ir-tx", apb0_hws,
++ 0x9cc, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_gpadc_clk, "bus-gpadc", apb0_hws,
++ 0x9ec, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_ths_clk, "bus-ths", apb0_hws,
++ 0x9fc, BIT(0), 0);
++
++static const struct clk_hw *i2s_spdif_tx_parents[] = {
++ &pll_audio0_clk.hw,
++ &pll_audio0_4x_clk.common.hw,
++ &pll_audio1_div2_clk.common.hw,
++ &pll_audio1_div5_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s0_clk, "i2s0", i2s_spdif_tx_parents, 0xa10,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s1_clk, "i2s1", i2s_spdif_tx_parents, 0xa14,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s2_clk, "i2s2", i2s_spdif_tx_parents, 0xa18,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static const struct clk_hw *i2s2_asrc_parents[] = {
++ &pll_audio0_4x_clk.common.hw,
++ &pll_periph0_clk.hw,
++ &pll_audio1_div2_clk.common.hw,
++ &pll_audio1_div5_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s2_asrc_clk, "i2s2-asrc", i2s2_asrc_parents, 0xa1c,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_i2s0_clk, "bus-i2s0", apb0_hws,
++ 0xa20, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_i2s1_clk, "bus-i2s1", apb0_hws,
++ 0xa20, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_i2s2_clk, "bus-i2s2", apb0_hws,
++ 0xa20, BIT(2), 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(spdif_tx_clk, "spdif-tx", i2s_spdif_tx_parents, 0xa24,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static const struct clk_hw *spdif_rx_parents[] = {
++ &pll_periph0_clk.hw,
++ &pll_audio1_div2_clk.common.hw,
++ &pll_audio1_div5_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(spdif_rx_clk, "spdif-rx", spdif_rx_parents, 0xa28,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_spdif_clk, "bus-spdif", apb0_hws,
++ 0xa2c, BIT(0), 0);
++
++static const struct clk_hw *dmic_codec_parents[] = {
++ &pll_audio0_clk.hw,
++ &pll_audio1_div2_clk.common.hw,
++ &pll_audio1_div5_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(dmic_clk, "dmic", dmic_codec_parents, 0xa40,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dmic_clk, "bus-dmic", apb0_hws,
++ 0xa4c, BIT(0), 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(audio_dac_clk, "audio-dac", dmic_codec_parents, 0xa50,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(audio_adc_clk, "audio-adc", dmic_codec_parents, 0xa54,
++ 0, 5, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_audio_clk, "bus-audio", apb0_hws,
++ 0xa5c, BIT(0), 0);
++
++
++/*
++ * The first parent is a 48 MHz input clock divided by 4. That 48 MHz clock is
++ * a 2x multiplier from osc24M synchronized by pll-periph0, and is also used by
++ * the OHCI module.
++ */
++static const struct clk_parent_data usb_ohci_parents[] = {
++ { .hw = &pll_periph0_clk.hw },
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++};
++static const struct ccu_mux_fixed_prediv usb_ohci_predivs[] = {
++ { .index = 0, .div = 50 },
++ { .index = 1, .div = 2 },
++};
++
++static struct ccu_mux usb_ohci0_clk = {
++ .enable = BIT(31),
++ .mux = {
++ .shift = 24,
++ .width = 2,
++ .fixed_predivs = usb_ohci_predivs,
++ .n_predivs = ARRAY_SIZE(usb_ohci_predivs),
++ },
++ .common = {
++ .reg = 0xa70,
++ .features = CCU_FEATURE_FIXED_PREDIV,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-ohci0",
++ usb_ohci_parents,
++ &ccu_mux_ops,
++ 0),
++ },
++};
++
++static struct ccu_mux usb_ohci1_clk = {
++ .enable = BIT(31),
++ .mux = {
++ .shift = 24,
++ .width = 2,
++ .fixed_predivs = usb_ohci_predivs,
++ .n_predivs = ARRAY_SIZE(usb_ohci_predivs),
++ },
++ .common = {
++ .reg = 0xa74,
++ .features = CCU_FEATURE_FIXED_PREDIV,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-ohci1",
++ usb_ohci_parents,
++ &ccu_mux_ops,
++ 0),
++ },
++};
++
++static SUNXI_CCU_GATE_HWS(bus_ohci0_clk, "bus-ohci0", psi_ahb_hws,
++ 0xa8c, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_ohci1_clk, "bus-ohci1", psi_ahb_hws,
++ 0xa8c, BIT(1), 0);
++static SUNXI_CCU_GATE_HWS(bus_ehci0_clk, "bus-ehci0", psi_ahb_hws,
++ 0xa8c, BIT(4), 0);
++static SUNXI_CCU_GATE_HWS(bus_ehci1_clk, "bus-ehci1", psi_ahb_hws,
++ 0xa8c, BIT(5), 0);
++static SUNXI_CCU_GATE_HWS(bus_otg_clk, "bus-otg", psi_ahb_hws,
++ 0xa8c, BIT(8), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_lradc_clk, "bus-lradc", apb0_hws,
++ 0xa9c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dpss_top_clk, "bus-dpss-top", psi_ahb_hws,
++ 0xabc, BIT(0), 0);
++
++static SUNXI_CCU_GATE_DATA(hdmi_24M_clk, "hdmi-24M", osc24M,
++ 0xb04, BIT(31), 0);
++
++static SUNXI_CCU_GATE_HWS_WITH_PREDIV(hdmi_cec_32k_clk, "hdmi-cec-32k",
++ pll_periph0_2x_hws,
++ 0xb10, BIT(30), 36621, 0);
++
++static const struct clk_parent_data hdmi_cec_parents[] = {
++ { .fw_name = "losc" },
++ { .hw = &hdmi_cec_32k_clk.common.hw },
++};
++static SUNXI_CCU_MUX_DATA_WITH_GATE(hdmi_cec_clk, "hdmi-cec", hdmi_cec_parents, 0xb10,
++ 24, 1, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_hdmi_clk, "bus-hdmi", psi_ahb_hws,
++ 0xb1c, BIT(0), 0);
++
++static const struct clk_parent_data mipi_dsi_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_video0_2x_clk.hw },
++ { .hw = &pll_video1_2x_clk.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++};
++static SUNXI_CCU_M_DATA_WITH_MUX_GATE(mipi_dsi_clk, "mipi-dsi", mipi_dsi_parents, 0xb24,
++ 0, 4, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_mipi_dsi_clk, "bus-mipi-dsi", psi_ahb_hws,
++ 0xb4c, BIT(0), 0);
++
++static const struct clk_hw *tcon_tve_parents[] = {
++ &pll_video0_clk.hw,
++ &pll_video0_4x_clk.common.hw,
++ &pll_video1_clk.hw,
++ &pll_video1_4x_clk.common.hw,
++ &pll_periph0_2x_clk.common.hw,
++ &pll_audio1_div2_clk.common.hw,
++};
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_tve_parents, 0xb60,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_tcon_lcd0_clk, "bus-tcon-lcd0", psi_ahb_hws,
++ 0xb7c, BIT(0), 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tcon_tv_clk, "tcon-tv", tcon_tve_parents, 0xb80,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ CLK_SET_RATE_PARENT);
++
++static SUNXI_CCU_GATE_HWS(bus_tcon_tv_clk, "bus-tcon-tv", psi_ahb_hws,
++ 0xb9c, BIT(0), 0);
++
++static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tve_clk, "tve", tcon_tve_parents, 0xbb0,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_tve_top_clk, "bus-tve-top", psi_ahb_hws,
++ 0xbbc, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_tve_clk, "bus-tve", psi_ahb_hws,
++ 0xbbc, BIT(1), 0);
++
++static const struct clk_parent_data tvd_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_video0_clk.hw },
++ { .hw = &pll_video1_clk.hw },
++ { .hw = &pll_periph0_clk.hw },
++};
++static SUNXI_CCU_M_DATA_WITH_MUX_GATE(tvd_clk, "tvd", tvd_parents, 0xbc0,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_tvd_top_clk, "bus-tvd-top", psi_ahb_hws,
++ 0xbdc, BIT(0), 0);
++static SUNXI_CCU_GATE_HWS(bus_tvd_clk, "bus-tvd", psi_ahb_hws,
++ 0xbdc, BIT(1), 0);
++
++static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ledc_clk, "ledc", ir_tx_ledc_parents, 0xbf0,
++ 0, 4, /* M */
++ 8, 2, /* P */
++ 24, 1, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_ledc_clk, "bus-ledc", psi_ahb_hws,
++ 0xbfc, BIT(0), 0);
++
++static const struct clk_hw *csi_top_parents[] = {
++ &pll_periph0_2x_clk.common.hw,
++ &pll_video0_2x_clk.hw,
++ &pll_video1_2x_clk.hw,
++};
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(csi_top_clk, "csi-top", csi_top_parents, 0xc04,
++ 0, 4, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static const struct clk_parent_data csi_mclk_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_video0_clk.hw },
++ { .hw = &pll_video1_clk.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++ { .hw = &pll_audio1_div5_clk.common.hw },
++};
++static SUNXI_CCU_M_DATA_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents, 0xc08,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_csi_clk, "bus-csi", psi_ahb_hws,
++ 0xc1c, BIT(0), 0);
++
++static const struct clk_parent_data tpadc_parents[] = {
++ { .fw_name = "hosc" },
++ { .hw = &pll_audio0_clk.hw },
++};
++static SUNXI_CCU_MUX_DATA_WITH_GATE(tpadc_clk, "tpadc", tpadc_parents, 0xc50,
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_tpadc_clk, "bus-tpadc", apb0_hws,
++ 0xc5c, BIT(0), 0);
++
++static SUNXI_CCU_GATE_HWS(bus_tzma_clk, "bus-tzma", apb0_hws,
++ 0xc6c, BIT(0), 0);
++
++static const struct clk_parent_data dsp_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .fw_name = "iosc" },
++ { .hw = &pll_periph0_2x_clk.common.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++};
++static SUNXI_CCU_M_DATA_WITH_MUX_GATE(dsp_clk, "dsp", dsp_parents, 0xc70,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_GATE_HWS(bus_dsp_cfg_clk, "bus-dsp-cfg", psi_ahb_hws,
++ 0xc7c, BIT(1), 0);
++
++/*
++ * The RISC-V gate is not modelled - it is in a separate register (0xd04)
++ * and has a special key field. The clock is critical anyway.
++ */
++static const struct clk_parent_data riscv_parents[] = {
++ { .fw_name = "hosc" },
++ { .fw_name = "losc" },
++ { .fw_name = "iosc" },
++ { .hw = &pll_periph0_800M_clk.common.hw },
++ { .hw = &pll_periph0_clk.hw },
++ { .hw = &pll_cpux_clk.common.hw },
++ { .hw = &pll_audio1_div2_clk.common.hw },
++};
++static SUNXI_CCU_M_DATA_WITH_MUX(riscv_clk, "riscv", riscv_parents, 0xd00,
++ 0, 5, /* M */
++ 24, 3, /* mux */
++ CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
++
++/* The riscv-axi clk must be divided by at least 2. */
++static struct clk_div_table riscv_axi_table[] = {
++ { .val = 1, .div = 2 },
++ { .val = 2, .div = 3 },
++ { .val = 3, .div = 4 },
++ { /* Sentinel */ }
++};
++static SUNXI_CCU_DIV_TABLE_HW(riscv_axi_clk, "riscv-axi", &riscv_clk.common.hw,
++ 0xd00, 8, 2, riscv_axi_table, 0);
++
++static SUNXI_CCU_GATE_HWS(bus_riscv_cfg_clk, "bus-riscv-cfg", psi_ahb_hws,
++ 0xd0c, BIT(0), CLK_IS_CRITICAL);
++
++static SUNXI_CCU_GATE_DATA(fanout_24M_clk, "fanout-24M", osc24M,
++ 0xf30, BIT(0), 0);
++static SUNXI_CCU_GATE_DATA_WITH_PREDIV(fanout_12M_clk, "fanout-12M", osc24M,
++ 0xf30, BIT(1), 2, 0);
++static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_16M_clk, "fanout-16M", pll_periph0_2x_hws,
++ 0xf30, BIT(2), 75, 0);
++static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_25M_clk, "fanout-25M", pll_periph0_hws,
++ 0xf30, BIT(3), 24, 0);
++static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_32k_clk, "fanout-32k", pll_periph0_2x_hws,
++ 0xf30, BIT(4), 36621, 0);
++
++/* This clock has a second divider that is not modelled and forced to 0. */
++#define SUN20I_D1_FANOUT_27M_REG 0xf34
++static const struct clk_hw *fanout_27M_parents[] = {
++ &pll_video0_clk.hw,
++ &pll_video1_clk.hw,
++};
++static SUNXI_CCU_M_HW_WITH_MUX_GATE(fanout_27M_clk, "fanout-27M", fanout_27M_parents, 0xf34,
++ 0, 5, /* M */
++ 24, 2, /* mux */
++ BIT(31), /* gate */
++ 0);
++
++static SUNXI_CCU_M_HWS_WITH_GATE(fanout_pclk_clk, "fanout-pclk", apb0_hws, 0xf38,
++ 0, 5, /* M */
++ BIT(31), /* gate */
++ 0);
++
++static const struct clk_hw *fanout_parents[] = {
++ &fanout_32k_clk.common.hw,
++ &fanout_12M_clk.common.hw,
++ &fanout_16M_clk.common.hw,
++ &fanout_24M_clk.common.hw,
++ &fanout_25M_clk.common.hw,
++ &fanout_27M_clk.common.hw,
++ &fanout_pclk_clk.common.hw,
++};
++static SUNXI_CCU_MUX_HW_WITH_GATE(fanout0_clk, "fanout0", fanout_parents, 0xf3c,
++ 0, 3, /* mux */
++ BIT(21), /* gate */
++ 0);
++static SUNXI_CCU_MUX_HW_WITH_GATE(fanout1_clk, "fanout1", fanout_parents, 0xf3c,
++ 3, 3, /* mux */
++ BIT(22), /* gate */
++ 0);
++static SUNXI_CCU_MUX_HW_WITH_GATE(fanout2_clk, "fanout2", fanout_parents, 0xf3c,
++ 6, 3, /* mux */
++ BIT(23), /* gate */
++ 0);
++
++static struct ccu_common *sun20i_d1_ccu_clks[] = {
++ &pll_cpux_clk.common,
++ &pll_ddr0_clk.common,
++ &pll_periph0_4x_clk.common,
++ &pll_periph0_2x_clk.common,
++ &pll_periph0_800M_clk.common,
++ &pll_video0_4x_clk.common,
++ &pll_video1_4x_clk.common,
++ &pll_ve_clk.common,
++ &pll_audio0_4x_clk.common,
++ &pll_audio1_clk.common,
++ &pll_audio1_div2_clk.common,
++ &pll_audio1_div5_clk.common,
++ &cpux_clk.common,
++ &cpux_axi_clk.common,
++ &cpux_apb_clk.common,
++ &psi_ahb_clk.common,
++ &apb0_clk.common,
++ &apb1_clk.common,
++ &de_clk.common,
++ &bus_de_clk.common,
++ &di_clk.common,
++ &bus_di_clk.common,
++ &g2d_clk.common,
++ &bus_g2d_clk.common,
++ &ce_clk.common,
++ &bus_ce_clk.common,
++ &ve_clk.common,
++ &bus_ve_clk.common,
++ &bus_dma_clk.common,
++ &bus_msgbox0_clk.common,
++ &bus_msgbox1_clk.common,
++ &bus_msgbox2_clk.common,
++ &bus_spinlock_clk.common,
++ &bus_hstimer_clk.common,
++ &avs_clk.common,
++ &bus_dbg_clk.common,
++ &bus_pwm_clk.common,
++ &bus_iommu_clk.common,
++ &dram_clk.common,
++ &mbus_dma_clk.common,
++ &mbus_ve_clk.common,
++ &mbus_ce_clk.common,
++ &mbus_tvin_clk.common,
++ &mbus_csi_clk.common,
++ &mbus_g2d_clk.common,
++ &mbus_riscv_clk.common,
++ &bus_dram_clk.common,
++ &mmc0_clk.common,
++ &mmc1_clk.common,
++ &mmc2_clk.common,
++ &bus_mmc0_clk.common,
++ &bus_mmc1_clk.common,
++ &bus_mmc2_clk.common,
++ &bus_uart0_clk.common,
++ &bus_uart1_clk.common,
++ &bus_uart2_clk.common,
++ &bus_uart3_clk.common,
++ &bus_uart4_clk.common,
++ &bus_uart5_clk.common,
++ &bus_i2c0_clk.common,
++ &bus_i2c1_clk.common,
++ &bus_i2c2_clk.common,
++ &bus_i2c3_clk.common,
++ &spi0_clk.common,
++ &spi1_clk.common,
++ &bus_spi0_clk.common,
++ &bus_spi1_clk.common,
++ &emac_25M_clk.common,
++ &bus_emac_clk.common,
++ &ir_tx_clk.common,
++ &bus_ir_tx_clk.common,
++ &bus_gpadc_clk.common,
++ &bus_ths_clk.common,
++ &i2s0_clk.common,
++ &i2s1_clk.common,
++ &i2s2_clk.common,
++ &i2s2_asrc_clk.common,
++ &bus_i2s0_clk.common,
++ &bus_i2s1_clk.common,
++ &bus_i2s2_clk.common,
++ &spdif_tx_clk.common,
++ &spdif_rx_clk.common,
++ &bus_spdif_clk.common,
++ &dmic_clk.common,
++ &bus_dmic_clk.common,
++ &audio_dac_clk.common,
++ &audio_adc_clk.common,
++ &bus_audio_clk.common,
++ &usb_ohci0_clk.common,
++ &usb_ohci1_clk.common,
++ &bus_ohci0_clk.common,
++ &bus_ohci1_clk.common,
++ &bus_ehci0_clk.common,
++ &bus_ehci1_clk.common,
++ &bus_otg_clk.common,
++ &bus_lradc_clk.common,
++ &bus_dpss_top_clk.common,
++ &hdmi_24M_clk.common,
++ &hdmi_cec_32k_clk.common,
++ &hdmi_cec_clk.common,
++ &bus_hdmi_clk.common,
++ &mipi_dsi_clk.common,
++ &bus_mipi_dsi_clk.common,
++ &tcon_lcd0_clk.common,
++ &bus_tcon_lcd0_clk.common,
++ &tcon_tv_clk.common,
++ &bus_tcon_tv_clk.common,
++ &tve_clk.common,
++ &bus_tve_top_clk.common,
++ &bus_tve_clk.common,
++ &tvd_clk.common,
++ &bus_tvd_top_clk.common,
++ &bus_tvd_clk.common,
++ &ledc_clk.common,
++ &bus_ledc_clk.common,
++ &csi_top_clk.common,
++ &csi_mclk_clk.common,
++ &bus_csi_clk.common,
++ &tpadc_clk.common,
++ &bus_tpadc_clk.common,
++ &bus_tzma_clk.common,
++ &dsp_clk.common,
++ &bus_dsp_cfg_clk.common,
++ &riscv_clk.common,
++ &riscv_axi_clk.common,
++ &bus_riscv_cfg_clk.common,
++ &fanout_24M_clk.common,
++ &fanout_12M_clk.common,
++ &fanout_16M_clk.common,
++ &fanout_25M_clk.common,
++ &fanout_32k_clk.common,
++ &fanout_27M_clk.common,
++ &fanout_pclk_clk.common,
++ &fanout0_clk.common,
++ &fanout1_clk.common,
++ &fanout2_clk.common,
++};
++
++static struct clk_hw_onecell_data sun20i_d1_hw_clks = {
++ .num = CLK_NUMBER,
++ .hws = {
++ [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw,
++ [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw,
++ [CLK_PLL_PERIPH0_4X] = &pll_periph0_4x_clk.common.hw,
++ [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.common.hw,
++ [CLK_PLL_PERIPH0_800M] = &pll_periph0_800M_clk.common.hw,
++ [CLK_PLL_PERIPH0] = &pll_periph0_clk.hw,
++ [CLK_PLL_PERIPH0_DIV3] = &pll_periph0_div3_clk.hw,
++ [CLK_PLL_VIDEO0_4X] = &pll_video0_4x_clk.common.hw,
++ [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw,
++ [CLK_PLL_VIDEO0] = &pll_video0_clk.hw,
++ [CLK_PLL_VIDEO1_4X] = &pll_video1_4x_clk.common.hw,
++ [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw,
++ [CLK_PLL_VIDEO1] = &pll_video1_clk.hw,
++ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
++ [CLK_PLL_AUDIO0_4X] = &pll_audio0_4x_clk.common.hw,
++ [CLK_PLL_AUDIO0_2X] = &pll_audio0_2x_clk.hw,
++ [CLK_PLL_AUDIO0] = &pll_audio0_clk.hw,
++ [CLK_PLL_AUDIO1] = &pll_audio1_clk.common.hw,
++ [CLK_PLL_AUDIO1_DIV2] = &pll_audio1_div2_clk.common.hw,
++ [CLK_PLL_AUDIO1_DIV5] = &pll_audio1_div5_clk.common.hw,
++ [CLK_CPUX] = &cpux_clk.common.hw,
++ [CLK_CPUX_AXI] = &cpux_axi_clk.common.hw,
++ [CLK_CPUX_APB] = &cpux_apb_clk.common.hw,
++ [CLK_PSI_AHB] = &psi_ahb_clk.common.hw,
++ [CLK_APB0] = &apb0_clk.common.hw,
++ [CLK_APB1] = &apb1_clk.common.hw,
++ [CLK_MBUS] = &mbus_clk.hw,
++ [CLK_DE] = &de_clk.common.hw,
++ [CLK_BUS_DE] = &bus_de_clk.common.hw,
++ [CLK_DI] = &di_clk.common.hw,
++ [CLK_BUS_DI] = &bus_di_clk.common.hw,
++ [CLK_G2D] = &g2d_clk.common.hw,
++ [CLK_BUS_G2D] = &bus_g2d_clk.common.hw,
++ [CLK_CE] = &ce_clk.common.hw,
++ [CLK_BUS_CE] = &bus_ce_clk.common.hw,
++ [CLK_VE] = &ve_clk.common.hw,
++ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
++ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
++ [CLK_BUS_MSGBOX0] = &bus_msgbox0_clk.common.hw,
++ [CLK_BUS_MSGBOX1] = &bus_msgbox1_clk.common.hw,
++ [CLK_BUS_MSGBOX2] = &bus_msgbox2_clk.common.hw,
++ [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw,
++ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
++ [CLK_AVS] = &avs_clk.common.hw,
++ [CLK_BUS_DBG] = &bus_dbg_clk.common.hw,
++ [CLK_BUS_PWM] = &bus_pwm_clk.common.hw,
++ [CLK_BUS_IOMMU] = &bus_iommu_clk.common.hw,
++ [CLK_DRAM] = &dram_clk.common.hw,
++ [CLK_MBUS_DMA] = &mbus_dma_clk.common.hw,
++ [CLK_MBUS_VE] = &mbus_ve_clk.common.hw,
++ [CLK_MBUS_CE] = &mbus_ce_clk.common.hw,
++ [CLK_MBUS_TVIN] = &mbus_tvin_clk.common.hw,
++ [CLK_MBUS_CSI] = &mbus_csi_clk.common.hw,
++ [CLK_MBUS_G2D] = &mbus_g2d_clk.common.hw,
++ [CLK_MBUS_RISCV] = &mbus_riscv_clk.common.hw,
++ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
++ [CLK_MMC0] = &mmc0_clk.common.hw,
++ [CLK_MMC1] = &mmc1_clk.common.hw,
++ [CLK_MMC2] = &mmc2_clk.common.hw,
++ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
++ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
++ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
++ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
++ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
++ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
++ [CLK_BUS_UART3] = &bus_uart3_clk.common.hw,
++ [CLK_BUS_UART4] = &bus_uart4_clk.common.hw,
++ [CLK_BUS_UART5] = &bus_uart5_clk.common.hw,
++ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
++ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
++ [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw,
++ [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw,
++ [CLK_SPI0] = &spi0_clk.common.hw,
++ [CLK_SPI1] = &spi1_clk.common.hw,
++ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
++ [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw,
++ [CLK_EMAC_25M] = &emac_25M_clk.common.hw,
++ [CLK_BUS_EMAC] = &bus_emac_clk.common.hw,
++ [CLK_IR_TX] = &ir_tx_clk.common.hw,
++ [CLK_BUS_IR_TX] = &bus_ir_tx_clk.common.hw,
++ [CLK_BUS_GPADC] = &bus_gpadc_clk.common.hw,
++ [CLK_BUS_THS] = &bus_ths_clk.common.hw,
++ [CLK_I2S0] = &i2s0_clk.common.hw,
++ [CLK_I2S1] = &i2s1_clk.common.hw,
++ [CLK_I2S2] = &i2s2_clk.common.hw,
++ [CLK_I2S2_ASRC] = &i2s2_asrc_clk.common.hw,
++ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
++ [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw,
++ [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw,
++ [CLK_SPDIF_TX] = &spdif_tx_clk.common.hw,
++ [CLK_SPDIF_RX] = &spdif_rx_clk.common.hw,
++ [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw,
++ [CLK_DMIC] = &dmic_clk.common.hw,
++ [CLK_BUS_DMIC] = &bus_dmic_clk.common.hw,
++ [CLK_AUDIO_DAC] = &audio_dac_clk.common.hw,
++ [CLK_AUDIO_ADC] = &audio_adc_clk.common.hw,
++ [CLK_BUS_AUDIO] = &bus_audio_clk.common.hw,
++ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
++ [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw,
++ [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw,
++ [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw,
++ [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw,
++ [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw,
++ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
++ [CLK_BUS_LRADC] = &bus_lradc_clk.common.hw,
++ [CLK_BUS_DPSS_TOP] = &bus_dpss_top_clk.common.hw,
++ [CLK_HDMI_24M] = &hdmi_24M_clk.common.hw,
++ [CLK_HDMI_CEC_32K] = &hdmi_cec_32k_clk.common.hw,
++ [CLK_HDMI_CEC] = &hdmi_cec_clk.common.hw,
++ [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw,
++ [CLK_MIPI_DSI] = &mipi_dsi_clk.common.hw,
++ [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw,
++ [CLK_TCON_LCD0] = &tcon_lcd0_clk.common.hw,
++ [CLK_BUS_TCON_LCD0] = &bus_tcon_lcd0_clk.common.hw,
++ [CLK_TCON_TV] = &tcon_tv_clk.common.hw,
++ [CLK_BUS_TCON_TV] = &bus_tcon_tv_clk.common.hw,
++ [CLK_TVE] = &tve_clk.common.hw,
++ [CLK_BUS_TVE_TOP] = &bus_tve_top_clk.common.hw,
++ [CLK_BUS_TVE] = &bus_tve_clk.common.hw,
++ [CLK_TVD] = &tvd_clk.common.hw,
++ [CLK_BUS_TVD_TOP] = &bus_tvd_top_clk.common.hw,
++ [CLK_BUS_TVD] = &bus_tvd_clk.common.hw,
++ [CLK_LEDC] = &ledc_clk.common.hw,
++ [CLK_BUS_LEDC] = &bus_ledc_clk.common.hw,
++ [CLK_CSI_TOP] = &csi_top_clk.common.hw,
++ [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw,
++ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
++ [CLK_TPADC] = &tpadc_clk.common.hw,
++ [CLK_BUS_TPADC] = &bus_tpadc_clk.common.hw,
++ [CLK_BUS_TZMA] = &bus_tzma_clk.common.hw,
++ [CLK_DSP] = &dsp_clk.common.hw,
++ [CLK_BUS_DSP_CFG] = &bus_dsp_cfg_clk.common.hw,
++ [CLK_RISCV] = &riscv_clk.common.hw,
++ [CLK_RISCV_AXI] = &riscv_axi_clk.common.hw,
++ [CLK_BUS_RISCV_CFG] = &bus_riscv_cfg_clk.common.hw,
++ [CLK_FANOUT_24M] = &fanout_24M_clk.common.hw,
++ [CLK_FANOUT_12M] = &fanout_12M_clk.common.hw,
++ [CLK_FANOUT_16M] = &fanout_16M_clk.common.hw,
++ [CLK_FANOUT_25M] = &fanout_25M_clk.common.hw,
++ [CLK_FANOUT_32K] = &fanout_32k_clk.common.hw,
++ [CLK_FANOUT_27M] = &fanout_27M_clk.common.hw,
++ [CLK_FANOUT_PCLK] = &fanout_pclk_clk.common.hw,
++ [CLK_FANOUT0] = &fanout0_clk.common.hw,
++ [CLK_FANOUT1] = &fanout1_clk.common.hw,
++ [CLK_FANOUT2] = &fanout2_clk.common.hw,
++ },
++};
++
++static struct ccu_reset_map sun20i_d1_ccu_resets[] = {
++ [RST_MBUS] = { 0x540, BIT(30) },
++ [RST_BUS_DE] = { 0x60c, BIT(16) },
++ [RST_BUS_DI] = { 0x62c, BIT(16) },
++ [RST_BUS_G2D] = { 0x63c, BIT(16) },
++ [RST_BUS_CE] = { 0x68c, BIT(16) },
++ [RST_BUS_VE] = { 0x69c, BIT(16) },
++ [RST_BUS_DMA] = { 0x70c, BIT(16) },
++ [RST_BUS_MSGBOX0] = { 0x71c, BIT(16) },
++ [RST_BUS_MSGBOX1] = { 0x71c, BIT(17) },
++ [RST_BUS_MSGBOX2] = { 0x71c, BIT(18) },
++ [RST_BUS_SPINLOCK] = { 0x72c, BIT(16) },
++ [RST_BUS_HSTIMER] = { 0x73c, BIT(16) },
++ [RST_BUS_DBG] = { 0x78c, BIT(16) },
++ [RST_BUS_PWM] = { 0x7ac, BIT(16) },
++ [RST_BUS_DRAM] = { 0x80c, BIT(16) },
++ [RST_BUS_MMC0] = { 0x84c, BIT(16) },
++ [RST_BUS_MMC1] = { 0x84c, BIT(17) },
++ [RST_BUS_MMC2] = { 0x84c, BIT(18) },
++ [RST_BUS_UART0] = { 0x90c, BIT(16) },
++ [RST_BUS_UART1] = { 0x90c, BIT(17) },
++ [RST_BUS_UART2] = { 0x90c, BIT(18) },
++ [RST_BUS_UART3] = { 0x90c, BIT(19) },
++ [RST_BUS_UART4] = { 0x90c, BIT(20) },
++ [RST_BUS_UART5] = { 0x90c, BIT(21) },
++ [RST_BUS_I2C0] = { 0x91c, BIT(16) },
++ [RST_BUS_I2C1] = { 0x91c, BIT(17) },
++ [RST_BUS_I2C2] = { 0x91c, BIT(18) },
++ [RST_BUS_I2C3] = { 0x91c, BIT(19) },
++ [RST_BUS_SPI0] = { 0x96c, BIT(16) },
++ [RST_BUS_SPI1] = { 0x96c, BIT(17) },
++ [RST_BUS_EMAC] = { 0x97c, BIT(16) },
++ [RST_BUS_IR_TX] = { 0x9cc, BIT(16) },
++ [RST_BUS_GPADC] = { 0x9ec, BIT(16) },
++ [RST_BUS_THS] = { 0x9fc, BIT(16) },
++ [RST_BUS_I2S0] = { 0xa20, BIT(16) },
++ [RST_BUS_I2S1] = { 0xa20, BIT(17) },
++ [RST_BUS_I2S2] = { 0xa20, BIT(18) },
++ [RST_BUS_SPDIF] = { 0xa2c, BIT(16) },
++ [RST_BUS_DMIC] = { 0xa4c, BIT(16) },
++ [RST_BUS_AUDIO] = { 0xa5c, BIT(16) },
++ [RST_USB_PHY0] = { 0xa70, BIT(30) },
++ [RST_USB_PHY1] = { 0xa74, BIT(30) },
++ [RST_BUS_OHCI0] = { 0xa8c, BIT(16) },
++ [RST_BUS_OHCI1] = { 0xa8c, BIT(17) },
++ [RST_BUS_EHCI0] = { 0xa8c, BIT(20) },
++ [RST_BUS_EHCI1] = { 0xa8c, BIT(21) },
++ [RST_BUS_OTG] = { 0xa8c, BIT(24) },
++ [RST_BUS_LRADC] = { 0xa9c, BIT(16) },
++ [RST_BUS_DPSS_TOP] = { 0xabc, BIT(16) },
++ [RST_BUS_HDMI_MAIN] = { 0xb1c, BIT(16) },
++ [RST_BUS_HDMI_SUB] = { 0xb1c, BIT(17) },
++ [RST_BUS_MIPI_DSI] = { 0xb4c, BIT(16) },
++ [RST_BUS_TCON_LCD0] = { 0xb7c, BIT(16) },
++ [RST_BUS_TCON_TV] = { 0xb9c, BIT(16) },
++ [RST_BUS_LVDS0] = { 0xbac, BIT(16) },
++ [RST_BUS_TVE_TOP] = { 0xbbc, BIT(16) },
++ [RST_BUS_TVE] = { 0xbbc, BIT(17) },
++ [RST_BUS_TVD_TOP] = { 0xbdc, BIT(16) },
++ [RST_BUS_TVD] = { 0xbdc, BIT(17) },
++ [RST_BUS_LEDC] = { 0xbfc, BIT(16) },
++ [RST_BUS_CSI] = { 0xc1c, BIT(16) },
++ [RST_BUS_TPADC] = { 0xc5c, BIT(16) },
++ [RST_DSP] = { 0xc7c, BIT(16) },
++ [RST_BUS_DSP_CFG] = { 0xc7c, BIT(17) },
++ [RST_BUS_DSP_DBG] = { 0xc7c, BIT(18) },
++ [RST_BUS_RISCV_CFG] = { 0xd0c, BIT(16) },
++};
++
++static const struct sunxi_ccu_desc sun20i_d1_ccu_desc = {
++ .ccu_clks = sun20i_d1_ccu_clks,
++ .num_ccu_clks = ARRAY_SIZE(sun20i_d1_ccu_clks),
++
++ .hw_clks = &sun20i_d1_hw_clks,
++
++ .resets = sun20i_d1_ccu_resets,
++ .num_resets = ARRAY_SIZE(sun20i_d1_ccu_resets),
++};
++
++static const u32 pll_regs[] = {
++ SUN20I_D1_PLL_CPUX_REG,
++ SUN20I_D1_PLL_DDR0_REG,
++ SUN20I_D1_PLL_PERIPH0_REG,
++ SUN20I_D1_PLL_VIDEO0_REG,
++ SUN20I_D1_PLL_VIDEO1_REG,
++ SUN20I_D1_PLL_VE_REG,
++ SUN20I_D1_PLL_AUDIO0_REG,
++ SUN20I_D1_PLL_AUDIO1_REG,
++};
++
++static const u32 pll_video_regs[] = {
++ SUN20I_D1_PLL_VIDEO0_REG,
++ SUN20I_D1_PLL_VIDEO1_REG,
++};
++
++static struct ccu_mux_nb sun20i_d1_riscv_nb = {
++ .common = &riscv_clk.common,
++ .cm = &riscv_clk.mux,
++ .delay_us = 1,
++ .bypass_index = 4, /* index of pll-periph0 */
++};
++
++static int sun20i_d1_ccu_probe(struct platform_device *pdev)
++{
++ void __iomem *reg;
++ u32 val;
++ int i, ret;
++
++ reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
++
++ /* Enable the enable, LDO, and lock bits on all PLLs. */
++ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) {
++ val = readl(reg + pll_regs[i]);
++ val |= BIT(31) | BIT(30) | BIT(29);
++ writel(val, reg + pll_regs[i]);
++ }
++
++ /* Force PLL_CPUX factor M to 0. */
++ val = readl(reg + SUN20I_D1_PLL_CPUX_REG);
++ val &= ~GENMASK(1, 0);
++ writel(val, reg + SUN20I_D1_PLL_CPUX_REG);
++
++ /*
++ * Force the output divider of video PLLs to 0.
++ *
++ * See the comment before pll-video0 definition for the reason.
++ */
++ for (i = 0; i < ARRAY_SIZE(pll_video_regs); i++) {
++ val = readl(reg + pll_video_regs[i]);
++ val &= ~BIT(0);
++ writel(val, reg + pll_video_regs[i]);
++ }
++
++ /* Enforce m1 = 0, m0 = 0 for PLL_AUDIO0 */
++ val = readl(reg + SUN20I_D1_PLL_AUDIO0_REG);
++ val &= ~BIT(1) | BIT(0);
++ writel(val, reg + SUN20I_D1_PLL_AUDIO0_REG);
++
++ /* Force fanout-27M factor N to 0. */
++ val = readl(reg + SUN20I_D1_FANOUT_27M_REG);
++ val &= ~GENMASK(9, 8);
++ writel(val, reg + SUN20I_D1_FANOUT_27M_REG);
++
++ ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun20i_d1_ccu_desc);
++ if (ret)
++ return ret;
++
++ /* Reparent CPU during PLL CPUX rate changes */
++ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
++ &sun20i_d1_riscv_nb);
++
++ return 0;
++}
++
++static const struct of_device_id sun20i_d1_ccu_ids[] = {
++ { .compatible = "allwinner,sun20i-d1-ccu" },
++ { }
++};
++
++static struct platform_driver sun20i_d1_ccu_driver = {
++ .probe = sun20i_d1_ccu_probe,
++ .driver = {
++ .name = "sun20i-d1-ccu",
++ .suppress_bind_attrs = true,
++ .of_match_table = sun20i_d1_ccu_ids,
++ },
++};
++module_platform_driver(sun20i_d1_ccu_driver);
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun20i-d1.h b/drivers/clk/sunxi-ng/ccu-sun20i-d1.h
+new file mode 100644
+index 000000000000..e303176f0d4e
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun20i-d1.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2020 frank@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _CCU_SUN20I_D1_H_
++#define _CCU_SUN20I_D1_H_
++
++#include <dt-bindings/clock/sun20i-d1-ccu.h>
++#include <dt-bindings/reset/sun20i-d1-ccu.h>
++
++#define CLK_NUMBER (CLK_FANOUT2 + 1)
++
++#endif /* _CCU_SUN20I_D1_H_ */
+--
+2.20.1
+
--- /dev/null
+From b3692a02024482ac18f684a4c38cfd4be2a2a12b Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Thu, 20 May 2021 00:08:20 -0500
+Subject: [PATCH 059/124] clk: sunxi-ng: Add support for the sun6i RTC clocks
+
+The RTC power domain in sun6i and newer SoCs manages the 16 MHz RC
+oscillator (called "IOSC" or "osc16M") and the optional 32 kHz crystal
+oscillator (called "LOSC" or "osc32k"). Starting with the H6, this power
+domain also handles the 24 MHz DCXO (called variously "HOSC", "dcxo24M",
+or "osc24M") as well. The H6 also adds a calibration circuit for IOSC.
+
+Later SoCs introduce further variations on the design:
+ - H616 adds an additional mux for the 32 kHz fanout source.
+ - R329 adds an additional mux for the RTC timekeeping clock, a clock
+ for the SPI bus between power domains inside the RTC, and removes the
+ IOSC calibration functionality.
+
+Take advantage of the CCU framework to handle this increased complexity.
+The CCU framework provides pre-made widgets for the mux/gate/divider
+combinations. And it allows plugging in different clocks for the same
+specifiers based on the compatible string.
+
+This driver is intended to be a drop-in replacement for the existing RTC
+clock provider. So some runtime adjustment of the clock parents is
+needed, both to handle hardware differences, and to support the old
+binding which omitted some of the input clocks.
+
+Series-changes: 2
+ - Rebase on v2 of the CCU module support series.
+ - Load the CCU driver from the RTC driver, not as an OF provider.
+
+Series-changes: 3
+ - Rebase on v3 of the CCU module support series.
+ - Move IOSC calibration control to prepare/unprepare operations.
+ - Declare several `struct clk_init_data`s as static variables (instead
+ of as anonymous) so they can be modified from the probe function.
+ - Instead of creating two copies of clocks which may or may not have
+ muxes, change the number of parents to 1 in the non-mux case.
+ - Only include clocks that are part of the binding in the OF provider.
+ - Use a single CCU description for all variants.
+ - Use IS_REACHABLE to guard the call to sun6i_rtc_ccu_probe.
+ - Allow the driver to be built on !ARM64 (i.e. RISCV).
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/Kconfig | 5 +
+ drivers/clk/sunxi-ng/Makefile | 2 +
+ drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 366 +++++++++++++++++++++++++++
+ drivers/clk/sunxi-ng/ccu-sun6i-rtc.h | 10 +
+ drivers/rtc/rtc-sun6i.c | 7 +
+ include/linux/clk/sunxi-ng.h | 2 +
+ 6 files changed, 392 insertions(+)
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+ create mode 100644 drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
+
+diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
+index 26555f59515d..7af83d75623b 100644
+--- a/drivers/clk/sunxi-ng/Kconfig
++++ b/drivers/clk/sunxi-ng/Kconfig
+@@ -69,6 +69,11 @@ config SUN6I_A31_CCU
+ default MACH_SUN6I
+ depends on MACH_SUN6I || COMPILE_TEST
+
++config SUN6I_RTC_CCU
++ tristate "Support for the Allwinner H616/R329 RTC CCU"
++ default ARCH_SUNXI
++ depends on ARCH_SUNXI || COMPILE_TEST
++
+ config SUN8I_A23_CCU
+ tristate "Support for the Allwinner A23 CCU"
+ default MACH_SUN8I
+diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
+index ec931cb7aa14..6b3ae2b620db 100644
+--- a/drivers/clk/sunxi-ng/Makefile
++++ b/drivers/clk/sunxi-ng/Makefile
+@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o
+ obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o
+ obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o
+ obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o
++obj-$(CONFIG_SUN6I_RTC_CCU) += sun6i-rtc-ccu.o
+ obj-$(CONFIG_SUN8I_A23_CCU) += sun8i-a23-ccu.o
+ obj-$(CONFIG_SUN8I_A33_CCU) += sun8i-a33-ccu.o
+ obj-$(CONFIG_SUN8I_A83T_CCU) += sun8i-a83t-ccu.o
+@@ -60,6 +61,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o
+ sun4i-a10-ccu-y += ccu-sun4i-a10.o
+ sun5i-ccu-y += ccu-sun5i.o
+ sun6i-a31-ccu-y += ccu-sun6i-a31.o
++sun6i-rtc-ccu-y += ccu-sun6i-rtc.o
+ sun8i-a23-ccu-y += ccu-sun8i-a23.o
+ sun8i-a33-ccu-y += ccu-sun8i-a33.o
+ sun8i-a83t-ccu-y += ccu-sun8i-a83t.o
+diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+new file mode 100644
+index 000000000000..f563cee58a20
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+@@ -0,0 +1,366 @@
++// SPDX-License-Identifier: GPL-2.0-only
++//
++// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
++//
++
++#include <linux/clk-provider.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++
++#include "ccu_common.h"
++
++#include "ccu_div.h"
++#include "ccu_gate.h"
++#include "ccu_mux.h"
++
++#include "ccu-sun6i-rtc.h"
++
++#define IOSC_ACCURACY 300000000 /* 30% */
++#define IOSC_RATE 16000000
++
++#define LOSC_RATE 32768
++#define LOSC_RATE_SHIFT 15
++
++#define LOSC_CTRL_REG 0x0
++#define LOSC_CTRL_KEY 0x16aa0000
++
++#define IOSC_32K_CLK_DIV_REG 0x8
++#define IOSC_32K_CLK_DIV GENMASK(4, 0)
++#define IOSC_32K_PRE_DIV 32
++
++#define IOSC_CLK_CALI_REG 0xc
++#define IOSC_CLK_CALI_DIV_ONES 22
++#define IOSC_CLK_CALI_EN BIT(1)
++#define IOSC_CLK_CALI_SRC_SEL BIT(0)
++
++#define LOSC_OUT_GATING_REG 0x60
++
++#define DCXO_CTRL_REG 0x160
++#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
++
++struct sun6i_rtc_match_data {
++ bool have_ext_osc32k : 1;
++ bool have_iosc_calibration : 1;
++ bool rtc_32k_single_parent : 1;
++ const struct clk_parent_data *osc32k_fanout_parents;
++ u8 osc32k_fanout_nparents;
++};
++
++static bool have_iosc_calibration;
++
++static int ccu_iosc_enable(struct clk_hw *hw)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++
++ return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
++}
++
++static void ccu_iosc_disable(struct clk_hw *hw)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++
++ return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
++}
++
++static int ccu_iosc_is_enabled(struct clk_hw *hw)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++
++ return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
++}
++
++static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++
++ if (have_iosc_calibration) {
++ u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
++
++ /*
++ * Recover the IOSC frequency by shifting the ones place of
++ * (fixed-point divider * 32768) into bit zero.
++ */
++ if (reg & IOSC_CLK_CALI_EN)
++ return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
++ }
++
++ return IOSC_RATE;
++}
++
++static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
++ unsigned long parent_accuracy)
++{
++ return IOSC_ACCURACY;
++}
++
++static const struct clk_ops ccu_iosc_ops = {
++ .enable = ccu_iosc_enable,
++ .disable = ccu_iosc_disable,
++ .is_enabled = ccu_iosc_is_enabled,
++ .recalc_rate = ccu_iosc_recalc_rate,
++ .recalc_accuracy = ccu_iosc_recalc_accuracy,
++};
++
++static struct ccu_common iosc_clk = {
++ .reg = DCXO_CTRL_REG,
++ .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
++ CLK_GET_RATE_NOCACHE),
++};
++
++static int ccu_iosc_32k_prepare(struct clk_hw *hw)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++ u32 val;
++
++ if (!have_iosc_calibration)
++ return 0;
++
++ val = readl(cm->base + IOSC_CLK_CALI_REG);
++ writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
++ cm->base + IOSC_CLK_CALI_REG);
++
++ return 0;
++}
++
++static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++ u32 val;
++
++ if (!have_iosc_calibration)
++ return;
++
++ val = readl(cm->base + IOSC_CLK_CALI_REG);
++ writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
++ cm->base + IOSC_CLK_CALI_REG);
++}
++
++static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++ u32 val;
++
++ if (have_iosc_calibration) {
++ val = readl(cm->base + IOSC_CLK_CALI_REG);
++
++ /* Assume the calibrated 32k clock is accurate. */
++ if (val & IOSC_CLK_CALI_SRC_SEL)
++ return LOSC_RATE;
++ }
++
++ val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
++
++ return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
++}
++
++static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
++ unsigned long parent_accuracy)
++{
++ struct ccu_common *cm = hw_to_ccu_common(hw);
++ u32 val;
++
++ if (have_iosc_calibration) {
++ val = readl(cm->base + IOSC_CLK_CALI_REG);
++
++ /* Assume the calibrated 32k clock is accurate. */
++ if (val & IOSC_CLK_CALI_SRC_SEL)
++ return 0;
++ }
++
++ return parent_accuracy;
++}
++
++static const struct clk_ops ccu_iosc_32k_ops = {
++ .prepare = ccu_iosc_32k_prepare,
++ .unprepare = ccu_iosc_32k_unprepare,
++ .recalc_rate = ccu_iosc_32k_recalc_rate,
++ .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
++};
++
++static struct ccu_common iosc_32k_clk = {
++ .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
++ &ccu_iosc_32k_ops,
++ CLK_GET_RATE_NOCACHE),
++};
++
++/* .fw_name will be cleared if the clock-names property is missing. */
++static struct clk_parent_data ext_osc32k[] = {
++ { .fw_name = "ext-osc32k", .index = 0 }
++};
++
++static SUNXI_CCU_GATE_DATA(ext_osc32k_gate_clk, "ext-osc32k-gate",
++ ext_osc32k, 0x0, BIT(4), 0);
++
++static const struct clk_hw *osc32k_parents[] = {
++ &iosc_32k_clk.hw,
++ &ext_osc32k_gate_clk.common.hw
++};
++
++static struct clk_init_data osc32k_init_data = {
++ .name = "osc32k",
++ .ops = &ccu_mux_ops,
++ .parent_hws = osc32k_parents,
++ .num_parents = ARRAY_SIZE(osc32k_parents), /* updated during probe */
++};
++
++static struct ccu_mux osc32k_clk = {
++ .mux = _SUNXI_CCU_MUX(0, 1),
++ .common = {
++ .reg = LOSC_CTRL_REG,
++ .features = CCU_FEATURE_KEY_FIELD,
++ .hw.init = &osc32k_init_data,
++ },
++};
++
++/* This falls back to the global name for fwnodes without a named reference. */
++static const struct clk_parent_data osc24M[] = {
++ { .fw_name = "hosc", .name = "osc24M" }
++};
++
++static struct ccu_gate osc24M_32k_clk = {
++ .enable = BIT(16),
++ .common = {
++ .reg = LOSC_OUT_GATING_REG,
++ .prediv = 750,
++ .features = CCU_FEATURE_ALL_PREDIV,
++ .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M,
++ &ccu_gate_ops, 0),
++ },
++};
++
++static const struct clk_hw *rtc_32k_parents[] = {
++ &osc32k_clk.common.hw,
++ &osc24M_32k_clk.common.hw
++};
++
++static struct clk_init_data rtc_32k_init_data = {
++ .name = "rtc-32k",
++ .ops = &ccu_mux_ops,
++ .parent_hws = rtc_32k_parents,
++ .num_parents = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */
++};
++
++static struct ccu_mux rtc_32k_clk = {
++ .mux = _SUNXI_CCU_MUX(1, 1),
++ .common = {
++ .reg = LOSC_CTRL_REG,
++ .features = CCU_FEATURE_KEY_FIELD,
++ .hw.init = &rtc_32k_init_data,
++ },
++};
++
++static struct clk_init_data osc32k_fanout_init_data = {
++ .name = "osc32k-fanout",
++ .ops = &ccu_mux_ops,
++ /* parents are set during probe */
++};
++
++static struct ccu_mux osc32k_fanout_clk = {
++ .enable = BIT(0),
++ .mux = _SUNXI_CCU_MUX(1, 2),
++ .common = {
++ .reg = LOSC_OUT_GATING_REG,
++ .hw.init = &osc32k_fanout_init_data,
++ },
++};
++
++static struct ccu_common *sun6i_rtc_ccu_clks[] = {
++ &iosc_clk,
++ &iosc_32k_clk,
++ &ext_osc32k_gate_clk.common, /* updated during probe */
++ &osc32k_clk.common,
++ &osc24M_32k_clk.common,
++ &rtc_32k_clk.common,
++ &osc32k_fanout_clk.common,
++};
++
++static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = {
++ .num = CLK_NUMBER,
++ .hws = {
++ [CLK_OSC32K] = &osc32k_clk.common.hw,
++ [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw,
++ [CLK_IOSC] = &iosc_clk.hw,
++ },
++};
++
++static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
++ .ccu_clks = sun6i_rtc_ccu_clks,
++ .num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks),
++
++ .hw_clks = &sun6i_rtc_ccu_hw_clks,
++};
++
++static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
++ { .hw = &osc32k_clk.common.hw },
++ { .fw_name = "pll-32k" },
++ { .hw = &osc24M_32k_clk.common.hw }
++};
++
++static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
++ { .hw = &osc32k_clk.common.hw },
++ { .hw = &ext_osc32k_gate_clk.common.hw },
++ { .hw = &osc24M_32k_clk.common.hw }
++};
++
++static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
++ .have_iosc_calibration = true,
++ .rtc_32k_single_parent = true,
++ .osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents,
++ .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents),
++};
++
++static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
++ .have_ext_osc32k = true,
++ .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents,
++ .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents),
++};
++
++static const struct of_device_id sun6i_rtc_ccu_match[] = {
++ {
++ .compatible = "allwinner,sun50i-h616-rtc",
++ .data = &sun50i_h616_rtc_ccu_data,
++ },
++ {
++ .compatible = "allwinner,sun50i-r329-rtc",
++ .data = &sun50i_r329_rtc_ccu_data,
++ },
++};
++
++int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg)
++{
++ struct device_node *node = dev->of_node;
++ const struct sun6i_rtc_match_data *data;
++ const struct of_device_id *match;
++
++ match = of_match_device(sun6i_rtc_ccu_match, dev);
++ if (!match)
++ return 0;
++
++ data = match->data;
++ have_iosc_calibration = data->have_iosc_calibration;
++
++ if (data->have_ext_osc32k) {
++ /* ext-osc32k was the only input clock in the old binding. */
++ if (!of_property_read_bool(node, "clock-names"))
++ ext_osc32k->fw_name = NULL;
++ } else {
++ /* Do not register the orphan ext-osc32k-gate clock. */
++ sun6i_rtc_ccu_clks[2] = NULL;
++
++ osc32k_init_data.num_parents = 1;
++ }
++
++ if (data->rtc_32k_single_parent)
++ rtc_32k_init_data.num_parents = 1;
++
++ osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents;
++ osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents;
++
++ return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc);
++}
++
++MODULE_IMPORT_NS(SUNXI_CCU);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
+new file mode 100644
+index 000000000000..0c1ac4c705a0
+--- /dev/null
++++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.h
+@@ -0,0 +1,10 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++
++#ifndef _CCU_SUN6I_RTC_H
++#define _CCU_SUN6I_RTC_H
++
++#include <dt-bindings/clock/sun6i-rtc.h>
++
++#define CLK_NUMBER (CLK_IOSC + 1)
++
++#endif /* _CCU_SUN6I_RTC_H */
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index 711832c758ae..12fd674b003d 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -13,6 +13,7 @@
+
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
++#include <linux/clk/sunxi-ng.h>
+ #include <linux/delay.h>
+ #include <linux/err.h>
+ #include <linux/fs.h>
+@@ -683,6 +684,12 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
+ chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
++
++ if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) {
++ ret = sun6i_rtc_ccu_probe(&pdev->dev, chip->base);
++ if (ret)
++ return ret;
++ }
+ }
+
+ platform_set_drvdata(pdev, chip);
+diff --git a/include/linux/clk/sunxi-ng.h b/include/linux/clk/sunxi-ng.h
+index cf32123b39f5..57c8ec44ab4e 100644
+--- a/include/linux/clk/sunxi-ng.h
++++ b/include/linux/clk/sunxi-ng.h
+@@ -9,4 +9,6 @@
+ int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode);
+ int sunxi_ccu_get_mmc_timing_mode(struct clk *clk);
+
++int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg);
++
+ #endif
+--
+2.20.1
+
--- /dev/null
+From 026ed9ed88c6cb6b2ae8f005f7b83304800a6171 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Tue, 31 Aug 2021 23:01:42 -0500
+Subject: [PATCH 060/124] clk: sunxi-ng: sun6i-rtc: Add support for H6
+
+H6 supports IOSC calibration and an ext-osc32k input. Unlike newer SoCs,
+it has a single parent for its fanout clock.
+
+Add support for H6 in the CCU driver, replacing the support in the
+existing early OF clock provider.
+
+Series-changes: 3
+ - Rebase example on top of driver changes, and drop the second example.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 15 +++++++++++++++
+ drivers/rtc/rtc-sun6i.c | 17 -----------------
+ 2 files changed, 15 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+index f563cee58a20..e5541da098f1 100644
+--- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
++++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
+@@ -293,6 +293,10 @@ static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = {
+ .hw_clks = &sun6i_rtc_ccu_hw_clks,
+ };
+
++static const struct clk_parent_data sun50i_h6_osc32k_fanout_parents[] = {
++ { .hw = &osc32k_clk.common.hw },
++};
++
+ static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = {
+ { .hw = &osc32k_clk.common.hw },
+ { .fw_name = "pll-32k" },
+@@ -305,6 +309,13 @@ static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = {
+ { .hw = &osc24M_32k_clk.common.hw }
+ };
+
++static const struct sun6i_rtc_match_data sun50i_h6_rtc_ccu_data = {
++ .have_ext_osc32k = true,
++ .have_iosc_calibration = true,
++ .osc32k_fanout_parents = sun50i_h6_osc32k_fanout_parents,
++ .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h6_osc32k_fanout_parents),
++};
++
+ static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = {
+ .have_iosc_calibration = true,
+ .rtc_32k_single_parent = true,
+@@ -319,6 +330,10 @@ static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = {
+ };
+
+ static const struct of_device_id sun6i_rtc_ccu_match[] = {
++ {
++ .compatible = "allwinner,sun50i-h6-rtc",
++ .data = &sun50i_h6_rtc_ccu_data,
++ },
+ {
+ .compatible = "allwinner,sun50i-h616-rtc",
+ .data = &sun50i_h616_rtc_ccu_data,
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index 12fd674b003d..4358cde143d4 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -364,23 +364,6 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
+ CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
+ sun8i_h3_rtc_clk_init);
+
+-static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
+- .rc_osc_rate = 16000000,
+- .fixed_prescaler = 32,
+- .has_prescaler = 1,
+- .has_out_clk = 1,
+- .export_iosc = 1,
+- .has_losc_en = 1,
+- .has_auto_swt = 1,
+-};
+-
+-static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
+-{
+- sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
+-}
+-CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
+- sun50i_h6_rtc_clk_init);
+-
+ /*
+ * The R40 user manual is self-conflicting on whether the prescaler is
+ * fixed or configurable. The clock diagram shows it as fixed, but there
+--
+2.20.1
+
--- /dev/null
+From 739535112738d67cfeada06baea94fbc40f84328 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 12 Jun 2021 08:04:28 -0500
+Subject: [PATCH 061/124] dmaengine: sun6i: Add support for the D1 variant
+
+So far it appears to work identically to the A100 variant.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/dma/Kconfig | 2 +-
+ drivers/dma/sun6i-dma.c | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
+index 80c2c03cb014..910ff963da60 100644
+--- a/drivers/dma/Kconfig
++++ b/drivers/dma/Kconfig
+@@ -163,7 +163,7 @@ config DMA_SUN4I
+
+ config DMA_SUN6I
+ tristate "Allwinner A31 SoCs DMA support"
+- depends on MACH_SUN6I || MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
++ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on RESET_CONTROLLER
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 8c7cce643cdc..795cce445532 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -1277,6 +1277,7 @@ static const struct of_device_id sun6i_dma_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg },
+ { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg },
+ { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg },
++ { .compatible = "allwinner,sun20i-d1-dma", .data = &sun50i_a100_dma_cfg },
+ { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg },
+ { .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg },
+ { .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg },
+--
+2.20.1
+
--- /dev/null
+From db0b9488559ef01603ff9bc5799d41d7bd346b39 Mon Sep 17 00:00:00 2001
+From: Guo Ren <guoren@linux.alibaba.com>
+Date: Sun, 6 Jun 2021 09:04:02 +0000
+Subject: [PATCH 062/124] riscv: pgtable: Fixup _PAGE_CHG_MASK usage
+
+We should masks all attributes BITS first, and then
+using '>> _PAGE_PFN_SHIFT' to get the final PFN value.
+
+Adding '& _PAGE_CHG_MASK' makes the code semantics more accurate.
+
+Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
+Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
+Cc: Anup Patel <anup.patel@wdc.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Drew Fustini <drew@beagleboard.org>
+Cc: Maxime Ripard <maxime@cerno.tech>
+Cc: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Wei Fu <wefu@redhat.com>
+Cc: Wei Wu <lazyparser@gmail.com>
+---
+ arch/riscv/include/asm/pgtable-64.h | 6 +++---
+ arch/riscv/include/asm/pgtable.h | 6 +++---
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h
+index 228261aa9628..6df493b55dde 100644
+--- a/arch/riscv/include/asm/pgtable-64.h
++++ b/arch/riscv/include/asm/pgtable-64.h
+@@ -61,12 +61,12 @@ static inline void pud_clear(pud_t *pudp)
+
+ static inline pmd_t *pud_pgtable(pud_t pud)
+ {
+- return (pmd_t *)pfn_to_virt(pud_val(pud) >> _PAGE_PFN_SHIFT);
++ return (pmd_t *)pfn_to_virt((pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
+ }
+
+ static inline struct page *pud_page(pud_t pud)
+ {
+- return pfn_to_page(pud_val(pud) >> _PAGE_PFN_SHIFT);
++ return pfn_to_page((pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
+ }
+
+ static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
+@@ -76,7 +76,7 @@ static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot)
+
+ static inline unsigned long _pmd_pfn(pmd_t pmd)
+ {
+- return pmd_val(pmd) >> _PAGE_PFN_SHIFT;
++ return (pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT;
+ }
+
+ #define mk_pmd(page, prot) pfn_pmd(page_to_pfn(page), prot)
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index bf204e7c1f74..4866597886f1 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -232,12 +232,12 @@ static inline unsigned long _pgd_pfn(pgd_t pgd)
+
+ static inline struct page *pmd_page(pmd_t pmd)
+ {
+- return pfn_to_page(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
++ return pfn_to_page((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
+ }
+
+ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
+ {
+- return (unsigned long)pfn_to_virt(pmd_val(pmd) >> _PAGE_PFN_SHIFT);
++ return (unsigned long)pfn_to_virt((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT);
+ }
+
+ static inline pte_t pmd_pte(pmd_t pmd)
+@@ -253,7 +253,7 @@ static inline pte_t pud_pte(pud_t pud)
+ /* Yields the page frame number (PFN) of a page table entry */
+ static inline unsigned long pte_pfn(pte_t pte)
+ {
+- return (pte_val(pte) >> _PAGE_PFN_SHIFT);
++ return (pte_val(pte) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT;
+ }
+
+ #define pte_page(x) pfn_to_page(pte_pfn(x))
+--
+2.20.1
+
--- /dev/null
+From 65bf9a94eed354bb356e61573b0a011e305e6fda Mon Sep 17 00:00:00 2001
+From: Guo Ren <guoren@linux.alibaba.com>
+Date: Sun, 6 Jun 2021 09:04:03 +0000
+Subject: [PATCH 063/124] riscv: pgtable: Add custom protection_map init
+
+Some RISC-V CPU vendors have defined their own PTE attributes to
+solve non-coherent DMA bus problems. That makes _P/SXXX definitions
+contain global variables which could be initialized at the early
+boot stage before setup_vm.
+
+This patch is similar to 316d097c4cd4 (x86/pti: Filter at
+vma->vm_page_prot population) which give a choice for arch custom
+implementation.
+
+Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Palmer Dabbelt <palmer@dabbelt.com>
+---
+ arch/riscv/Kconfig | 4 ++++
+ arch/riscv/mm/init.c | 22 ++++++++++++++++++++++
+ mm/mmap.c | 4 ++++
+ 3 files changed, 30 insertions(+)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 1d7b5066ce80..7e907aa32ac8 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -23,6 +23,7 @@ config RISCV
+ select ARCH_HAS_GIGANTIC_PAGE
+ select ARCH_HAS_KCOV
+ select ARCH_HAS_MMIOWB
++ select ARCH_HAS_PROTECTION_MAP_INIT
+ select ARCH_HAS_PTE_SPECIAL
+ select ARCH_HAS_SET_DIRECT_MAP if MMU
+ select ARCH_HAS_SET_MEMORY if MMU
+@@ -211,6 +212,9 @@ config GENERIC_HWEIGHT
+ config FIX_EARLYCON_MEM
+ def_bool MMU
+
++config ARCH_HAS_PROTECTION_MAP_INIT
++ def_bool y
++
+ config PGTABLE_LEVELS
+ int
+ default 3 if 64BIT
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index 24b2b8044602..6ba585a974a6 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -588,10 +588,32 @@ static void __init create_fdt_early_page_table(pgd_t *pgdir, uintptr_t dtb_pa)
+ dtb_early_pa = dtb_pa;
+ }
+
++static void __init setup_protection_map(void)
++{
++ protection_map[0] = __P000;
++ protection_map[1] = __P001;
++ protection_map[2] = __P010;
++ protection_map[3] = __P011;
++ protection_map[4] = __P100;
++ protection_map[5] = __P101;
++ protection_map[6] = __P110;
++ protection_map[7] = __P111;
++ protection_map[8] = __S000;
++ protection_map[9] = __S001;
++ protection_map[10] = __S010;
++ protection_map[11] = __S011;
++ protection_map[12] = __S100;
++ protection_map[13] = __S101;
++ protection_map[14] = __S110;
++ protection_map[15] = __S111;
++}
++
+ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
+ {
+ pmd_t __maybe_unused fix_bmap_spmd, fix_bmap_epmd;
+
++ setup_protection_map();
++
+ kernel_map.virt_addr = KERNEL_LINK_ADDR;
+
+ #ifdef CONFIG_XIP_KERNEL
+diff --git a/mm/mmap.c b/mm/mmap.c
+index 88dcc5c25225..55b9da1f8b56 100644
+--- a/mm/mmap.c
++++ b/mm/mmap.c
+@@ -100,10 +100,14 @@ static void unmap_region(struct mm_struct *mm,
+ * w: (no) no
+ * x: (yes) yes
+ */
++#ifdef CONFIG_ARCH_HAS_PROTECTION_MAP_INIT
++pgprot_t protection_map[16] __ro_after_init;
++#else
+ pgprot_t protection_map[16] __ro_after_init = {
+ __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
+ __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
+ };
++#endif
+
+ #ifndef CONFIG_ARCH_HAS_FILTER_PGPROT
+ static inline pgprot_t arch_filter_pgprot(pgprot_t prot)
+--
+2.20.1
+
--- /dev/null
+From d2579d2b8552501662320fbc57875f83f0d91a4f Mon Sep 17 00:00:00 2001
+From: Guo Ren <guoren@linux.alibaba.com>
+Date: Sun, 6 Jun 2021 09:04:04 +0000
+Subject: [PATCH 064/124] riscv: pgtable: Add DMA_COHERENT with custom PTE
+ attributes
+
+The dma-noncoherent SOCs need different virtual memory mappings
+with different attributes:
+ - noncached + Strong Order (for IO/DMA descriptor)
+ - noncached + Weak Order (for writecombine usage, eg: frame
+ buffer)
+
+All above base on PTE attributes by MMU hardware. That means
+address attributes are determined by PTE entry, not PMA. RISC-V
+soc vendors have defined their own custom PTE attributes for
+dma-noncoherency.
+
+Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
+Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
+Cc: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Anup Patel <anup.patel@wdc.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Drew Fustini <drew@beagleboard.org>
+Cc: Wei Fu <wefu@redhat.com>
+Cc: Wei Wu <lazyparser@gmail.com>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/riscv/include/asm/pgtable-bits.h | 22 +++++++++++++++++++++-
+ arch/riscv/include/asm/pgtable.h | 11 ++++-------
+ arch/riscv/include/asm/soc.h | 1 +
+ arch/riscv/include/asm/vendorid_list.h | 1 +
+ arch/riscv/kernel/soc.c | 22 ++++++++++++++++++++++
+ arch/riscv/mm/init.c | 4 ++++
+ 6 files changed, 53 insertions(+), 8 deletions(-)
+
+diff --git a/arch/riscv/include/asm/pgtable-bits.h b/arch/riscv/include/asm/pgtable-bits.h
+index 2ee413912926..e9e95666f9bd 100644
+--- a/arch/riscv/include/asm/pgtable-bits.h
++++ b/arch/riscv/include/asm/pgtable-bits.h
+@@ -24,6 +24,11 @@
+ #define _PAGE_DIRTY (1 << 7) /* Set by hardware on any write */
+ #define _PAGE_SOFT (1 << 8) /* Reserved for software */
+
++#define _PAGE_DMA_MASK __riscv_custom_pte.mask
++#define _PAGE_DMA_CACHE __riscv_custom_pte.cache
++#define _PAGE_DMA_IO __riscv_custom_pte.io
++#define _PAGE_DMA_WC __riscv_custom_pte.wc
++
+ #define _PAGE_SPECIAL _PAGE_SOFT
+ #define _PAGE_TABLE _PAGE_PRESENT
+
+@@ -35,10 +40,25 @@
+
+ #define _PAGE_PFN_SHIFT 10
+
++#ifndef __ASSEMBLY__
++
++struct riscv_custom_pte {
++ unsigned long cache;
++ unsigned long mask;
++ unsigned long io;
++ unsigned long wc;
++};
++
++extern struct riscv_custom_pte __riscv_custom_pte;
++
+ /* Set of bits to preserve across pte_modify() */
+ #define _PAGE_CHG_MASK (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ | \
+ _PAGE_WRITE | _PAGE_EXEC | \
+- _PAGE_USER | _PAGE_GLOBAL))
++ _PAGE_USER | _PAGE_GLOBAL | \
++ _PAGE_DMA_MASK))
++
++#endif
++
+ /*
+ * when all of R/W/X are zero, the PTE is a pointer to the next level
+ * of the page table; otherwise, it is a leaf PTE.
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index 4866597886f1..6f2bdab5bfa7 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -117,7 +117,7 @@
+ #define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE)
+
+ /* Page protection bits */
+-#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER)
++#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER | _PAGE_DMA_CACHE)
+
+ #define PAGE_NONE __pgprot(_PAGE_PROT_NONE)
+ #define PAGE_READ __pgprot(_PAGE_BASE | _PAGE_READ)
+@@ -138,7 +138,8 @@
+ | _PAGE_PRESENT \
+ | _PAGE_ACCESSED \
+ | _PAGE_DIRTY \
+- | _PAGE_GLOBAL)
++ | _PAGE_GLOBAL \
++ | _PAGE_DMA_CACHE)
+
+ #define PAGE_KERNEL __pgprot(_PAGE_KERNEL)
+ #define PAGE_KERNEL_READ __pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
+@@ -148,11 +149,7 @@
+
+ #define PAGE_TABLE __pgprot(_PAGE_TABLE)
+
+-/*
+- * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't
+- * change the properties of memory regions.
+- */
+-#define _PAGE_IOREMAP _PAGE_KERNEL
++#define _PAGE_IOREMAP ((_PAGE_KERNEL & ~_PAGE_DMA_MASK) | _PAGE_DMA_IO)
+
+ extern pgd_t swapper_pg_dir[];
+
+diff --git a/arch/riscv/include/asm/soc.h b/arch/riscv/include/asm/soc.h
+index f494066051a2..fc587d7d295d 100644
+--- a/arch/riscv/include/asm/soc.h
++++ b/arch/riscv/include/asm/soc.h
+@@ -17,6 +17,7 @@
+ = { .compatible = compat, .data = fn }
+
+ void soc_early_init(void);
++void soc_setup_vm(void);
+
+ extern unsigned long __soc_early_init_table_start;
+ extern unsigned long __soc_early_init_table_end;
+diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
+index 9d934215b3c8..c2710f391a89 100644
+--- a/arch/riscv/include/asm/vendorid_list.h
++++ b/arch/riscv/include/asm/vendorid_list.h
+@@ -6,5 +6,6 @@
+ #define ASM_VENDOR_LIST_H
+
+ #define SIFIVE_VENDOR_ID 0x489
++#define THEAD_VENDOR_ID 0x401
+
+ #endif
+diff --git a/arch/riscv/kernel/soc.c b/arch/riscv/kernel/soc.c
+index a0516172a33c..05fa76467e69 100644
+--- a/arch/riscv/kernel/soc.c
++++ b/arch/riscv/kernel/soc.c
+@@ -3,8 +3,10 @@
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ */
+ #include <linux/init.h>
++#include <linux/mm.h>
+ #include <linux/libfdt.h>
+ #include <linux/pgtable.h>
++#include <asm/image.h>
+ #include <asm/soc.h>
+
+ /*
+@@ -26,3 +28,23 @@ void __init soc_early_init(void)
+ }
+ }
+ }
++
++static void __init thead_init(void)
++{
++ __riscv_custom_pte.cache = 0x7000000000000000;
++ __riscv_custom_pte.mask = 0xf800000000000000;
++ __riscv_custom_pte.io = BIT(63);
++ __riscv_custom_pte.wc = 0;
++}
++
++void __init soc_setup_vm(void)
++{
++ unsigned long vendor_id =
++ ((struct riscv_image_header *)(&_start))->res1;
++
++ switch (vendor_id) {
++ case THEAD_VENDOR_ID:
++ thead_init();
++ break;
++ }
++};
+diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
+index 6ba585a974a6..4af87922c19f 100644
+--- a/arch/riscv/mm/init.c
++++ b/arch/riscv/mm/init.c
+@@ -612,6 +612,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
+ {
+ pmd_t __maybe_unused fix_bmap_spmd, fix_bmap_epmd;
+
++ soc_setup_vm();
+ setup_protection_map();
+
+ kernel_map.virt_addr = KERNEL_LINK_ADDR;
+@@ -876,3 +877,6 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
+ return vmemmap_populate_basepages(start, end, node, NULL);
+ }
+ #endif
++
++struct riscv_custom_pte __riscv_custom_pte __ro_after_init;
++EXPORT_SYMBOL(__riscv_custom_pte);
+--
+2.20.1
+
--- /dev/null
+From 43e3d184609e4a46d17a5c897e7e1da113233ca2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 15 Nov 2021 23:37:41 -0600
+Subject: [PATCH 065/124] riscv: fixup PAGE_NONE
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/include/asm/pgtable.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index 6f2bdab5bfa7..cb24ca059442 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -119,7 +119,7 @@
+ /* Page protection bits */
+ #define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER | _PAGE_DMA_CACHE)
+
+-#define PAGE_NONE __pgprot(_PAGE_PROT_NONE)
++#define PAGE_NONE __pgprot(_PAGE_DMA_CACHE | _PAGE_PROT_NONE)
+ #define PAGE_READ __pgprot(_PAGE_BASE | _PAGE_READ)
+ #define PAGE_WRITE __pgprot(_PAGE_BASE | _PAGE_READ | _PAGE_WRITE)
+ #define PAGE_EXEC __pgprot(_PAGE_BASE | _PAGE_EXEC)
+--
+2.20.1
+
--- /dev/null
+From 346b9731f8704f684aa1e74b7ced812864d78da2 Mon Sep 17 00:00:00 2001
+From: Guo Ren <guoren@linux.alibaba.com>
+Date: Sun, 6 Jun 2021 09:04:05 +0000
+Subject: [PATCH 066/124] riscv: cmo: Add dma-noncoherency support
+
+To support DMA device in a non-coherent interconnect SOC system,
+we need the below facilities:
+ - Changing a virtual memory mapping region attributes from
+ cacheable to noncache + strong order which used in DMA
+ descriptors.
+ - Add noncache + weakorder virtual memory attributes for dma
+ mapping.
+ - Syncing the cache with memory before DMA start and after DMA
+ end with vendor custom CMO instructions.
+
+This patch enables linux kernel generic dma-noncoherency
+infrastructure and introduces new sbi_ecall API for dma_sync.
+
+@@ -27,6 +27,7 @@ enum sbi_ext_id {
++ SBI_EXT_DMA = 0xAB150401,
+
+Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
+Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
+Cc: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Anup Patel <anup.patel@wdc.com>
+Cc: Arnd Bergmann <arnd@arndb.de>
+Cc: Drew Fustini <drew@beagleboard.org>
+Cc: Wei Fu <wefu@redhat.com>
+Cc: Wei Wu <lazyparser@gmail.com>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Maxime Ripard <maxime@cerno.tech>
+---
+ arch/riscv/Kconfig | 5 +++
+ arch/riscv/include/asm/pgtable.h | 26 ++++++++++++++++
+ arch/riscv/include/asm/sbi.h | 22 +++++++++++++
+ arch/riscv/kernel/sbi.c | 19 ++++++++++++
+ arch/riscv/mm/Makefile | 1 +
+ arch/riscv/mm/dma-mapping.c | 53 ++++++++++++++++++++++++++++++++
+ 6 files changed, 126 insertions(+)
+ create mode 100644 arch/riscv/mm/dma-mapping.c
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 7e907aa32ac8..fb462acf769b 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -18,6 +18,10 @@ config RISCV
+ select ARCH_HAS_DEBUG_VM_PGTABLE
+ select ARCH_HAS_DEBUG_VIRTUAL if MMU
+ select ARCH_HAS_DEBUG_WX
++ select ARCH_HAS_DMA_PREP_COHERENT
++ select ARCH_HAS_SYNC_DMA_FOR_CPU
++ select ARCH_HAS_SYNC_DMA_FOR_DEVICE
++ select ARCH_HAS_DMA_WRITE_COMBINE
+ select ARCH_HAS_FORTIFY_SOURCE
+ select ARCH_HAS_GCOV_PROFILE_ALL
+ select ARCH_HAS_GIGANTIC_PAGE
+@@ -47,6 +51,7 @@ config RISCV
+ select CLINT_TIMER if !MMU
+ select COMMON_CLK
+ select CPU_PM if CPU_IDLE
++ select DMA_DIRECT_REMAP
+ select EDAC_SUPPORT
+ select GENERIC_ARCH_TOPOLOGY if SMP
+ select GENERIC_ATOMIC64 if !64BIT
+diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
+index cb24ca059442..7f7047a32a82 100644
+--- a/arch/riscv/include/asm/pgtable.h
++++ b/arch/riscv/include/asm/pgtable.h
+@@ -489,6 +489,32 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
+ return ptep_test_and_clear_young(vma, address, ptep);
+ }
+
++#define pgprot_noncached pgprot_noncached
++static inline pgprot_t pgprot_noncached(pgprot_t _prot)
++{
++ unsigned long prot = pgprot_val(_prot);
++
++ prot &= ~_PAGE_DMA_MASK;
++ prot |= _PAGE_DMA_IO;
++
++ return __pgprot(prot);
++}
++
++#define pgprot_writecombine pgprot_writecombine
++static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
++{
++ unsigned long prot = pgprot_val(_prot);
++
++ prot &= ~_PAGE_DMA_MASK;
++ prot |= _PAGE_DMA_WC;
++
++ return __pgprot(prot);
++}
++
++#define __HAVE_PHYS_MEM_ACCESS_PROT
++extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
++ unsigned long size, pgprot_t vma_prot);
++
+ /*
+ * THP functions
+ */
+diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
+index ff5cecd13bda..9d4398595714 100644
+--- a/arch/riscv/include/asm/sbi.h
++++ b/arch/riscv/include/asm/sbi.h
+@@ -28,6 +28,7 @@ enum sbi_ext_id {
+ SBI_EXT_RFENCE = 0x52464E43,
+ SBI_EXT_HSM = 0x48534D,
+ SBI_EXT_SRST = 0x53525354,
++ SBI_EXT_DMA = 0xAB150401,
+ };
+
+ enum sbi_ext_base_fid {
+@@ -103,6 +104,24 @@ enum sbi_srst_reset_reason {
+ SBI_SRST_RESET_REASON_SYS_FAILURE,
+ };
+
++enum sbi_ext_dma_fid {
++ SBI_DMA_SYNC = 0,
++};
++
++enum sbi_dma_sync_data_direction {
++ SBI_DMA_BIDIRECTIONAL = 0,
++ SBI_DMA_TO_DEVICE = 1,
++ SBI_DMA_FROM_DEVICE = 2,
++ SBI_DMA_NONE = 3,
++};
++
++enum sbi_hsm_hart_status {
++ SBI_HSM_HART_STATUS_STARTED = 0,
++ SBI_HSM_HART_STATUS_STOPPED,
++ SBI_HSM_HART_STATUS_START_PENDING,
++ SBI_HSM_HART_STATUS_STOP_PENDING,
++};
++
+ #define SBI_SPEC_VERSION_DEFAULT 0x1
+ #define SBI_SPEC_VERSION_MAJOR_SHIFT 24
+ #define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
+@@ -162,6 +181,9 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
+ unsigned long size,
+ unsigned long asid);
+ int sbi_probe_extension(int ext);
++void sbi_dma_sync(unsigned long start,
++ unsigned long size,
++ enum sbi_dma_sync_data_direction dir);
+
+ /* Check if current SBI specification version is 0.1 or not */
+ static inline int sbi_spec_is_0_1(void)
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 372aa7e181d5..4daea631c078 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -548,6 +548,25 @@ int sbi_probe_extension(int extid)
+ }
+ EXPORT_SYMBOL(sbi_probe_extension);
+
++void sbi_dma_sync(unsigned long start,
++ unsigned long size,
++ enum sbi_dma_sync_data_direction dir)
++{
++#if 0
++ sbi_ecall(SBI_EXT_DMA, SBI_DMA_SYNC, start, size, dir,
++ 0, 0, 0);
++#else
++ /* Just for try, it should be in sbi ecall and will be removed before merged */
++ register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1);
++
++ for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES)
++ __asm__ __volatile__(".long 0x02b5000b");
++
++ __asm__ __volatile__(".long 0x01b0000b");
++#endif
++}
++EXPORT_SYMBOL(sbi_dma_sync);
++
+ static long __sbi_base_ecall(int fid)
+ {
+ struct sbiret ret;
+diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile
+index 7ebaef10ea1b..ca0ff9064953 100644
+--- a/arch/riscv/mm/Makefile
++++ b/arch/riscv/mm/Makefile
+@@ -13,6 +13,7 @@ obj-y += extable.o
+ obj-$(CONFIG_MMU) += fault.o pageattr.o
+ obj-y += cacheflush.o
+ obj-y += context.o
++obj-y += dma-mapping.o
+
+ ifeq ($(CONFIG_MMU),y)
+ obj-$(CONFIG_SMP) += tlbflush.o
+diff --git a/arch/riscv/mm/dma-mapping.c b/arch/riscv/mm/dma-mapping.c
+new file mode 100644
+index 000000000000..4afd9dc32618
+--- /dev/null
++++ b/arch/riscv/mm/dma-mapping.c
+@@ -0,0 +1,53 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <linux/dma-map-ops.h>
++#include <asm/sbi.h>
++
++void arch_dma_prep_coherent(struct page *page, size_t size)
++{
++ void *ptr = page_address(page);
++
++ memset(ptr, 0, size);
++ sbi_dma_sync(page_to_phys(page), size, SBI_DMA_BIDIRECTIONAL);
++}
++
++void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
++ enum dma_data_direction dir)
++{
++ switch (dir) {
++ case DMA_TO_DEVICE:
++ case DMA_FROM_DEVICE:
++ case DMA_BIDIRECTIONAL:
++ sbi_dma_sync(paddr, size, dir);
++ break;
++ default:
++ BUG();
++ }
++}
++
++void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
++ enum dma_data_direction dir)
++{
++ switch (dir) {
++ case DMA_TO_DEVICE:
++ return;
++ case DMA_FROM_DEVICE:
++ case DMA_BIDIRECTIONAL:
++ sbi_dma_sync(paddr, size, dir);
++ break;
++ default:
++ BUG();
++ }
++}
++
++pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
++ unsigned long size, pgprot_t vma_prot)
++{
++ if (!pfn_valid(pfn))
++ return pgprot_noncached(vma_prot);
++ else if (file->f_flags & O_SYNC)
++ return pgprot_writecombine(vma_prot);
++
++ return vma_prot;
++}
++EXPORT_SYMBOL(phys_mem_access_prot);
+--
+2.20.1
+
--- /dev/null
+From 23c539154e9c3ef77bfa17398121eaa3ef26eee5 Mon Sep 17 00:00:00 2001
+From: Guo Ren <guoren@linux.alibaba.com>
+Date: Sun, 6 Jun 2021 09:04:06 +0000
+Subject: [PATCH 067/124] riscv: cmo: Add vendor custom icache sync
+
+It's a draft version to show you how T-HEAD C9xx work with the
+icache sync (We use hardware broadcast mechanism, and our icache
+is VIPT):
+ - icache.i(v/p)a will broadcast all harts' icache invalidtion
+ - sync.is will broadcast all harts' pipeline flush and ensure all
+ broadcasts finished.
+
+This patch could improve the performance of OpenJDK on JIT and
+reduce flush_icache_all in linux.
+
+Epecially:
+static inline void set_pte_at(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep, pte_t pteval)
+{
+ if (pte_present(pteval) && pte_exec(pteval))
+ flush_icache_pte(pteval);
+
+ set_pte(ptep, pteval);
+}
+
+Different from sbi_dma_sync, it can't be hidden in SBI and we must
+set up a framework to hold all vendors' implementations in
+linux/arch/riscv.
+
+Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
+Signed-off-by: Liu Shaohua <liush@allwinnertech.com>
+Cc: Anup Patel <anup.patel@wdc.com>
+Cc: Atish Patra <atish.patra@wdc.com>
+Cc: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Chen-Yu Tsai <wens@csie.org>
+Cc: Drew Fustini <drew@beagleboard.org>
+Cc: Maxime Ripard <maxime@cerno.tech>
+Cc: Palmer Dabbelt <palmerdabbelt@google.com>
+Cc: Wei Fu <wefu@redhat.com>
+Cc: Wei Wu <lazyparser@gmail.com>
+---
+ arch/riscv/include/asm/cacheflush.h | 48 ++++++++++++++++++++++++++-
+ arch/riscv/kernel/vdso/flush_icache.S | 33 ++++++++++++++----
+ arch/riscv/mm/cacheflush.c | 3 +-
+ 3 files changed, 76 insertions(+), 8 deletions(-)
+
+diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h
+index 23ff70350992..2e2dba17084d 100644
+--- a/arch/riscv/include/asm/cacheflush.h
++++ b/arch/riscv/include/asm/cacheflush.h
+@@ -22,11 +22,57 @@ static inline void flush_dcache_page(struct page *page)
+ }
+ #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
+
++#define ICACHE_IPA_X5 ".long 0x0382800b"
++#define ICACHE_IVA_X5 ".long 0x0302800b"
++#define SYNC_IS ".long 0x01b0000b"
++
++static inline void flush_icache_range(unsigned long start, unsigned long end)
++{
++ register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1));
++
++ for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) {
++ __asm__ __volatile__ (
++ ICACHE_IVA_X5
++ :
++ : "r" (tmp)
++ : "memory");
++ }
++
++ __asm__ __volatile__(SYNC_IS);
++
++ return;
++}
++
++static inline void flush_icache_range_phy(unsigned long start, unsigned long end)
++{
++ register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1));
++
++ for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) {
++ __asm__ __volatile__ (
++ ICACHE_IPA_X5
++ :
++ : "r" (tmp)
++ : "memory");
++ }
++
++ __asm__ __volatile__(SYNC_IS);
++
++ return;
++}
++
++static inline void __flush_icache_page(struct page *page) {
++ unsigned long start = PFN_PHYS(page_to_pfn(page));
++
++ flush_icache_range_phy(start, start + PAGE_SIZE);
++
++ return;
++}
++
+ /*
+ * RISC-V doesn't have an instruction to flush parts of the instruction cache,
+ * so instead we just flush the whole thing.
+ */
+-#define flush_icache_range(start, end) flush_icache_all()
++#define flush_icache_range(start, end) flush_icache_range(start, end)
+ #define flush_icache_user_page(vma, pg, addr, len) \
+ flush_icache_mm(vma->vm_mm, 0)
+
+diff --git a/arch/riscv/kernel/vdso/flush_icache.S b/arch/riscv/kernel/vdso/flush_icache.S
+index 82f97d67c23e..efb2d2e8a768 100644
+--- a/arch/riscv/kernel/vdso/flush_icache.S
++++ b/arch/riscv/kernel/vdso/flush_icache.S
+@@ -5,18 +5,39 @@
+
+ #include <linux/linkage.h>
+ #include <asm/unistd.h>
++#include <asm/cache.h>
++
++/*
++ * icache.ipa rs1
++ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
++ * 0000001 11000 rs1 000 00000 0001011
++ *
++ * icache.iva rs1
++ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
++ * 0000001 10000 rs1 000 00000 0001011
++ *
++ * sync.is
++ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 |
++ * 0000000 11011 00000 000 00000 0001011
++ */
++#define ICACHE_IPA_X5 .long 0x0382800b
++#define ICACHE_IVA_X5 .long 0x0302800b
++#define SYNC_IS .long 0x01b0000b
+
+ .text
+ /* int __vdso_flush_icache(void *start, void *end, unsigned long flags); */
+ ENTRY(__vdso_flush_icache)
+ .cfi_startproc
+-#ifdef CONFIG_SMP
+- li a7, __NR_riscv_flush_icache
+- ecall
+-#else
+- fence.i
++ srli t0, a0, L1_CACHE_SHIFT
++ slli t0, t0, L1_CACHE_SHIFT
++ addi a1, a1, (L1_CACHE_BYTES - 1)
++ srli a1, a1, L1_CACHE_SHIFT
++ slli a1, a1, L1_CACHE_SHIFT
++1: ICACHE_IVA_X5
++ addi t0, t0, L1_CACHE_BYTES
++ bne t0, a1, 1b
++ SYNC_IS
+ li a0, 0
+-#endif
+ ret
+ .cfi_endproc
+ ENDPROC(__vdso_flush_icache)
+diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c
+index 89f81067e09e..9d2e5fa9d0e0 100644
+--- a/arch/riscv/mm/cacheflush.c
++++ b/arch/riscv/mm/cacheflush.c
+@@ -3,6 +3,7 @@
+ * Copyright (C) 2017 SiFive
+ */
+
++#include <linux/pfn.h>
+ #include <asm/cacheflush.h>
+
+ #ifdef CONFIG_SMP
+@@ -86,6 +87,6 @@ void flush_icache_pte(pte_t pte)
+ struct page *page = pte_page(pte);
+
+ if (!test_and_set_bit(PG_dcache_clean, &page->flags))
+- flush_icache_all();
++ __flush_icache_page(page);
+ }
+ #endif /* CONFIG_MMU */
+--
+2.20.1
+
--- /dev/null
+From 933ba1cb66d124454889c8c2b844fee2b0e0960b Mon Sep 17 00:00:00 2001
+From: Yangyu Chen <cyy@cyyself.name>
+Date: Tue, 26 Oct 2021 15:40:38 +0800
+Subject: [PATCH 068/124] fixed UB which lead to cache coherence bug
+
+---
+ arch/riscv/kernel/sbi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
+index 4daea631c078..77132e824954 100644
+--- a/arch/riscv/kernel/sbi.c
++++ b/arch/riscv/kernel/sbi.c
+@@ -560,7 +560,7 @@ void sbi_dma_sync(unsigned long start,
+ register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1);
+
+ for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES)
+- __asm__ __volatile__(".long 0x02b5000b");
++ __asm__ __volatile__(".long 0x02b5000b" : : "r"(i));
+
+ __asm__ __volatile__(".long 0x01b0000b");
+ #endif
+--
+2.20.1
+
--- /dev/null
+From c35eeb146ba1a615b686951a6a89794ee5682148 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 14:17:30 -0500
+Subject: [PATCH 069/124] Enable T-HEAD MMU extensions unconditionally
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/kernel/soc.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/arch/riscv/kernel/soc.c b/arch/riscv/kernel/soc.c
+index 05fa76467e69..8c9f33f921b4 100644
+--- a/arch/riscv/kernel/soc.c
++++ b/arch/riscv/kernel/soc.c
+@@ -44,6 +44,8 @@ void __init soc_setup_vm(void)
+
+ switch (vendor_id) {
+ case THEAD_VENDOR_ID:
++ // Do not rely on the bootloader...
++ default:
+ thead_init();
+ break;
+ }
+--
+2.20.1
+
--- /dev/null
+From 8255fd9e0ea6e7c0d621f401fc6fbc7fdfc73ab2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 15 Nov 2021 23:38:00 -0600
+Subject: [PATCH 070/124] riscv: Allow suspend
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/Kconfig | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index fb462acf769b..2d996d6f4265 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -194,6 +194,9 @@ config ARCH_WANT_GENERAL_HUGETLB
+ config ARCH_SUPPORTS_UPROBES
+ def_bool y
+
++config ARCH_SUSPEND_POSSIBLE
++ def_bool y
++
+ config STACKTRACE_SUPPORT
+ def_bool y
+
+--
+2.20.1
+
--- /dev/null
+From 751def916449fd13685e28c913032c51266b6d10 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 21:46:50 -0500
+Subject: [PATCH 071/124] riscv: cacheinfo: Remind myself to fix this
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/kernel/cacheinfo.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c
+index 90deabfe63ea..a1874a68a17e 100644
+--- a/arch/riscv/kernel/cacheinfo.c
++++ b/arch/riscv/kernel/cacheinfo.c
+@@ -94,6 +94,7 @@ static void fill_cacheinfo(struct cacheinfo **this_leaf,
+ {
+ unsigned int size, sets, line_size;
+
++ /* Buggy: may not init leaves, but num_leaves was set below. */
+ if (!of_property_read_u32(node, "cache-size", &size) &&
+ !of_property_read_u32(node, "cache-block-size", &line_size) &&
+ !of_property_read_u32(node, "cache-sets", &sets)) {
+--
+2.20.1
+
--- /dev/null
+From bab6179fa6b0f3f819968c4034c1517283eb3dc7 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:54:21 -0500
+Subject: [PATCH 072/124] clocksource: riscv: Prefer it over MMIO clocksources
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/clocksource/timer-riscv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
+index c51c5ed15aa7..a035bdf6fc58 100644
+--- a/drivers/clocksource/timer-riscv.c
++++ b/drivers/clocksource/timer-riscv.c
+@@ -54,7 +54,7 @@ static u64 notrace riscv_sched_clock(void)
+
+ static struct clocksource riscv_clocksource = {
+ .name = "riscv_clocksource",
+- .rating = 300,
++ .rating = 400,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .read = riscv_clocksource_rdtime,
+--
+2.20.1
+
--- /dev/null
+From 71f14616158d3a2bc1ffb726b05d4aa6bec0f459 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 15:02:27 -0500
+Subject: [PATCH 073/124] riscv: Enable cpufreq
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/Kconfig | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 2d996d6f4265..5f11f14f93a8 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -583,6 +583,8 @@ endmenu
+
+ menu "CPU Power Management"
+
++source "drivers/cpufreq/Kconfig"
++
+ source "drivers/cpuidle/Kconfig"
+
+ endmenu
+--
+2.20.1
+
--- /dev/null
+From 48836972991b23a270f5505e1ee42de7466ac9c1 Mon Sep 17 00:00:00 2001
+From: Shuosheng Huang <huangshuosheng@allwinnertech.com>
+Date: Tue, 8 Dec 2020 15:19:28 +0800
+Subject: [PATCH 074/124] cpufreq: sun50i: add efuse_xlate to get efuse
+ version.
+
+It's better to use efuse_xlate to extract the differentiated part
+regarding different SoC.
+
+Signed-off-by: Shuosheng Huang <huangshuosheng@allwinnertech.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/cpufreq/sun50i-cpufreq-nvmem.c | 84 ++++++++++++++++----------
+ 1 file changed, 52 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+index 2deed8d8773f..e29ada03873d 100644
+--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
++++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+@@ -19,24 +19,51 @@
+
+ #define MAX_NAME_LEN 7
+
+-#define NVMEM_MASK 0x7
+-#define NVMEM_SHIFT 5
++#define SUN50I_H6_NVMEM_MASK 0x7
++#define SUN50I_H6_NVMEM_SHIFT 5
++
++struct sunxi_cpufreq_soc_data {
++ int (*efuse_xlate)(struct nvmem_cell *speedbin_nvmem);
++};
+
+ static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;
+
++static int sun50i_h6_efuse_xlate(struct nvmem_cell *speedbin_nvmem)
++{
++ size_t len;
++ u32 *speedbin;
++ u32 efuse_value;
++
++ speedbin = nvmem_cell_read(speedbin_nvmem, &len);
++ if (IS_ERR(speedbin))
++ return PTR_ERR(speedbin);
++
++ efuse_value = (*speedbin >> SUN50I_H6_NVMEM_SHIFT) & SUN50I_H6_NVMEM_MASK;
++ kfree(speedbin);
++
++ /*
++ * We treat unexpected efuse values as if the SoC was from
++ * the slowest bin. Expected efuse values are 1-3, slowest
++ * to fastest.
++ */
++ if (efuse_value >= 1 && efuse_value <= 3)
++ return efuse_value - 1;
++ else
++ return 0;
++}
++
+ /**
+ * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value
+- * @versions: Set to the value parsed from efuse
++ * @soc_data: pointer to sunxi_cpufreq_soc_data context
+ *
+- * Returns 0 if success.
++ * Returns speed grade (OPP voltage index) if successful.
+ */
+-static int sun50i_cpufreq_get_efuse(u32 *versions)
++static int sun50i_cpufreq_get_efuse(const struct sunxi_cpufreq_soc_data *soc_data)
+ {
+ struct nvmem_cell *speedbin_nvmem;
+ struct device_node *np;
+ struct device *cpu_dev;
+- u32 *speedbin, efuse_value;
+- size_t len;
++ int speed;
+ int ret;
+
+ cpu_dev = get_cpu_device(0);
+@@ -63,43 +90,33 @@ static int sun50i_cpufreq_get_efuse(u32 *versions)
+ return PTR_ERR(speedbin_nvmem);
+ }
+
+- speedbin = nvmem_cell_read(speedbin_nvmem, &len);
++ speed = soc_data->efuse_xlate(speedbin_nvmem);
+ nvmem_cell_put(speedbin_nvmem);
+- if (IS_ERR(speedbin))
+- return PTR_ERR(speedbin);
+-
+- efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK;
+
+- /*
+- * We treat unexpected efuse values as if the SoC was from
+- * the slowest bin. Expected efuse values are 1-3, slowest
+- * to fastest.
+- */
+- if (efuse_value >= 1 && efuse_value <= 3)
+- *versions = efuse_value - 1;
+- else
+- *versions = 0;
+-
+- kfree(speedbin);
+- return 0;
++ return speed;
+ };
+
+ static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
+ {
++ const struct of_device_id *match;
+ struct opp_table **opp_tables;
+ char name[MAX_NAME_LEN];
+ unsigned int cpu;
+- u32 speed = 0;
++ int speed = 0;
+ int ret;
+
++ match = dev_get_platdata(&pdev->dev);
++ if (!match)
++ return -EINVAL;
++
+ opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables),
+ GFP_KERNEL);
+ if (!opp_tables)
+ return -ENOMEM;
+
+- ret = sun50i_cpufreq_get_efuse(&speed);
+- if (ret)
+- return ret;
++ speed = sun50i_cpufreq_get_efuse(match->data);
++ if (speed < 0)
++ return speed;
+
+ snprintf(name, MAX_NAME_LEN, "speed%d", speed);
+
+@@ -163,8 +180,12 @@ static struct platform_driver sun50i_cpufreq_driver = {
+ },
+ };
+
++static const struct sunxi_cpufreq_soc_data sun50i_h6_data = {
++ .efuse_xlate = sun50i_h6_efuse_xlate,
++};
++
+ static const struct of_device_id sun50i_cpufreq_match_list[] = {
+- { .compatible = "allwinner,sun50i-h6" },
++ { .compatible = "allwinner,sun50i-h6", .data = &sun50i_h6_data },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
+@@ -199,9 +220,8 @@ static int __init sun50i_cpufreq_init(void)
+ if (unlikely(ret < 0))
+ return ret;
+
+- sun50i_cpufreq_pdev =
+- platform_device_register_simple("sun50i-cpufreq-nvmem",
+- -1, NULL, 0);
++ sun50i_cpufreq_pdev = platform_device_register_data(NULL,
++ "sun50i-cpufreq-nvmem", -1, match, sizeof(*match));
+ ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev);
+ if (ret == 0)
+ return 0;
+--
+2.20.1
+
--- /dev/null
+From cd8725b4b8e7f00a06c5256162d499dff50ec3b8 Mon Sep 17 00:00:00 2001
+From: Shuosheng Huang <huangshuosheng@allwinnertech.com>
+Date: Tue, 8 Dec 2020 15:19:53 +0800
+Subject: [PATCH 075/124] cpufreq: sun50i: add A100 cpufreq support
+
+Add nvmem based cpufreq for Allwinner A100 SoC, which is similar to H6.
+
+Signed-off-by: Shuosheng Huang <huangshuosheng@allwinnertech.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/cpufreq/cpufreq-dt-platdev.c | 1 +
+ drivers/cpufreq/sun50i-cpufreq-nvmem.c | 31 ++++++++++++++++++++++++++
+ 2 files changed, 32 insertions(+)
+
+diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
+index ca1d103ec449..b85fe43d0257 100644
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
+@@ -101,6 +101,7 @@ static const struct of_device_id allowlist[] __initconst = {
+ * platforms using "operating-points-v2" property.
+ */
+ static const struct of_device_id blocklist[] __initconst = {
++ { .compatible = "allwinner,sun50i-a100", },
+ { .compatible = "allwinner,sun50i-h6", },
+
+ { .compatible = "arm,vexpress", },
+diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+index e29ada03873d..3534133c3c9c 100644
+--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
++++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+@@ -19,6 +19,9 @@
+
+ #define MAX_NAME_LEN 7
+
++#define SUN50I_A100_NVMEM_MASK 0xf
++#define SUN50I_A100_NVMEM_SHIFT 12
++
+ #define SUN50I_H6_NVMEM_MASK 0x7
+ #define SUN50I_H6_NVMEM_SHIFT 5
+
+@@ -28,6 +31,29 @@ struct sunxi_cpufreq_soc_data {
+
+ static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;
+
++static int sun50i_a100_efuse_xlate(struct nvmem_cell *speedbin_nvmem)
++{
++ size_t len;
++ u16 *speedbin;
++ u16 efuse_value;
++
++ speedbin = nvmem_cell_read(speedbin_nvmem, &len);
++ if (IS_ERR(speedbin))
++ return PTR_ERR(speedbin);
++
++ efuse_value = (*speedbin >> SUN50I_A100_NVMEM_SHIFT) & SUN50I_A100_NVMEM_MASK;
++ kfree(speedbin);
++
++ switch (efuse_value) {
++ case 0b100:
++ return 2;
++ case 0b010:
++ return 1;
++ default:
++ return 0;
++ }
++}
++
+ static int sun50i_h6_efuse_xlate(struct nvmem_cell *speedbin_nvmem)
+ {
+ size_t len;
+@@ -180,11 +206,16 @@ static struct platform_driver sun50i_cpufreq_driver = {
+ },
+ };
+
++static const struct sunxi_cpufreq_soc_data sun50i_a100_data = {
++ .efuse_xlate = sun50i_a100_efuse_xlate,
++};
++
+ static const struct sunxi_cpufreq_soc_data sun50i_h6_data = {
+ .efuse_xlate = sun50i_h6_efuse_xlate,
+ };
+
+ static const struct of_device_id sun50i_cpufreq_match_list[] = {
++ { .compatible = "allwinner,sun50i-a100", .data = &sun50i_a100_data },
+ { .compatible = "allwinner,sun50i-h6", .data = &sun50i_h6_data },
+ {}
+ };
+--
+2.20.1
+
--- /dev/null
+From d1645eeec1f5361067b04213ffc9ab5ae6ba773b Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 21:18:52 -0500
+Subject: [PATCH 076/124] cpufreq: sun50i: Move out of ARM-specific section
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/arm64/configs/defconfig | 2 +-
+ drivers/cpufreq/Kconfig | 12 ++++++++++++
+ drivers/cpufreq/Kconfig.arm | 12 ------------
+ drivers/cpufreq/Makefile | 2 +-
+ 4 files changed, 14 insertions(+), 14 deletions(-)
+
+diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
+index 545197bc0501..7a5a8bfd654c 100644
+--- a/arch/arm64/configs/defconfig
++++ b/arch/arm64/configs/defconfig
+@@ -90,7 +90,6 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+ CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+ CONFIG_CPUFREQ_DT=y
+ CONFIG_ACPI_CPPC_CPUFREQ=m
+-CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=m
+ CONFIG_ARM_ARMADA_37XX_CPUFREQ=y
+ CONFIG_ARM_SCPI_CPUFREQ=y
+ CONFIG_ARM_IMX_CPUFREQ_DT=m
+@@ -100,6 +99,7 @@ CONFIG_ARM_RASPBERRYPI_CPUFREQ=m
+ CONFIG_ARM_SCMI_CPUFREQ=y
+ CONFIG_ARM_TEGRA186_CPUFREQ=y
+ CONFIG_QORIQ_CPUFREQ=y
++CONFIG_SUN50I_CPUFREQ_NVMEM=m
+ CONFIG_ARM_SCMI_PROTOCOL=y
+ CONFIG_ARM_SCPI_PROTOCOL=y
+ CONFIG_RASPBERRYPI_FIRMWARE=y
+diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
+index c3038cdc6865..3f98b0603c0c 100644
+--- a/drivers/cpufreq/Kconfig
++++ b/drivers/cpufreq/Kconfig
+@@ -321,5 +321,17 @@ config QORIQ_CPUFREQ
+ This adds the CPUFreq driver support for Freescale QorIQ SoCs
+ which are capable of changing the CPU's frequency dynamically.
+
++config SUN50I_CPUFREQ_NVMEM
++ tristate "Allwinner nvmem based SUN50I CPUFreq driver"
++ depends on ARCH_SUNXI || COMPILE_TEST
++ depends on NVMEM_SUNXI_SID
++ select PM_OPP
++ help
++ This adds the nvmem based CPUFreq driver for Allwinner
++ sun20i/sun50i SoCs.
++
++ To compile this driver as a module, choose M here: the
++ module will be called sun50i-cpufreq-nvmem.
++
+ endif
+ endmenu
+diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
+index 954749afb5fe..6b0aca5e0e2b 100644
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -29,18 +29,6 @@ config ACPI_CPPC_CPUFREQ_FIE
+
+ If in doubt, say N.
+
+-config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM
+- tristate "Allwinner nvmem based SUN50I CPUFreq driver"
+- depends on ARCH_SUNXI
+- depends on NVMEM_SUNXI_SID
+- select PM_OPP
+- help
+- This adds the nvmem based CPUFreq driver for Allwinner
+- h6 SoC.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called sun50i-cpufreq-nvmem.
+-
+ config ARM_ARMADA_37XX_CPUFREQ
+ tristate "Armada 37xx CPUFreq support"
+ depends on ARCH_MVEBU && CPUFREQ_DT
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index 48ee5859030c..61c80e5bd02f 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -78,7 +78,6 @@ obj-$(CONFIG_ARM_SCMI_CPUFREQ) += scmi-cpufreq.o
+ obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
+ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
+ obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
+-obj-$(CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o
+ obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
+ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
+ obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
+@@ -108,3 +107,4 @@ obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o
+ obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o
+ obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o
+ obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o
++obj-$(CONFIG_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o
+--
+2.20.1
+
--- /dev/null
+From 2b483c0072cc28b59bafa7fe9c4250b4f32dd5e0 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 22:10:05 -0500
+Subject: [PATCH 077/124] cpufreq: sun50i: Add D1 cpufreq support
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/cpufreq/cpufreq-dt-platdev.c | 1 +
+ drivers/cpufreq/sun50i-cpufreq-nvmem.c | 10 ++++++++++
+ 2 files changed, 11 insertions(+)
+
+diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
+index b85fe43d0257..d5f8f01f098d 100644
+--- a/drivers/cpufreq/cpufreq-dt-platdev.c
++++ b/drivers/cpufreq/cpufreq-dt-platdev.c
+@@ -101,6 +101,7 @@ static const struct of_device_id allowlist[] __initconst = {
+ * platforms using "operating-points-v2" property.
+ */
+ static const struct of_device_id blocklist[] __initconst = {
++ { .compatible = "allwinner,sun20i-d1", },
+ { .compatible = "allwinner,sun50i-a100", },
+ { .compatible = "allwinner,sun50i-h6", },
+
+diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+index 3534133c3c9c..7eaf91f0e5de 100644
+--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
++++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+@@ -31,6 +31,11 @@ struct sunxi_cpufreq_soc_data {
+
+ static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev;
+
++static int sun20i_d1_efuse_xlate(struct nvmem_cell *speedbin_nvmem)
++{
++ return 0;
++}
++
+ static int sun50i_a100_efuse_xlate(struct nvmem_cell *speedbin_nvmem)
+ {
+ size_t len;
+@@ -206,6 +211,10 @@ static struct platform_driver sun50i_cpufreq_driver = {
+ },
+ };
+
++static const struct sunxi_cpufreq_soc_data sun20i_d1_data = {
++ .efuse_xlate = sun20i_d1_efuse_xlate,
++};
++
+ static const struct sunxi_cpufreq_soc_data sun50i_a100_data = {
+ .efuse_xlate = sun50i_a100_efuse_xlate,
+ };
+@@ -215,6 +224,7 @@ static const struct sunxi_cpufreq_soc_data sun50i_h6_data = {
+ };
+
+ static const struct of_device_id sun50i_cpufreq_match_list[] = {
++ { .compatible = "allwinner,sun20i-d1", .data = &sun20i_d1_data },
+ { .compatible = "allwinner,sun50i-a100", .data = &sun50i_a100_data },
+ { .compatible = "allwinner,sun50i-h6", .data = &sun50i_h6_data },
+ {}
+--
+2.20.1
+
--- /dev/null
+From e3547799347f60c1b177406849bddec867fad343 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 14:51:23 -0500
+Subject: [PATCH 078/124] dmaengine: sun6i: Changes from BSP
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/dma/sun6i-dma.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
+index 795cce445532..0387a06c0774 100644
+--- a/drivers/dma/sun6i-dma.c
++++ b/drivers/dma/sun6i-dma.c
+@@ -938,6 +938,7 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+ vchan_get_all_descriptors(&vchan->vc, &head);
+
+ if (pchan) {
++ writel(DMA_CHAN_PAUSE_PAUSE, pchan->base + DMA_CHAN_PAUSE);
+ writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE);
+ writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE);
+
+@@ -954,6 +955,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
+ return 0;
+ }
+
++static void sun6i_dma_synchronize(struct dma_chan *chan)
++{
++ struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
++
++ vchan_synchronize(&vchan->vc);
++}
++
+ static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+@@ -1357,6 +1365,7 @@ static int sun6i_dma_probe(struct platform_device *pdev)
+ sdc->slave.device_pause = sun6i_dma_pause;
+ sdc->slave.device_resume = sun6i_dma_resume;
+ sdc->slave.device_terminate_all = sun6i_dma_terminate_all;
++ sdc->slave.device_synchronize = sun6i_dma_synchronize;
+ sdc->slave.src_addr_widths = sdc->cfg->src_addr_widths;
+ sdc->slave.dst_addr_widths = sdc->cfg->dst_addr_widths;
+ sdc->slave.directions = BIT(DMA_DEV_TO_MEM) |
+@@ -1415,9 +1424,9 @@ static int sun6i_dma_probe(struct platform_device *pdev)
+ vchan_init(&vchan->vc, &sdc->slave);
+ }
+
+- ret = reset_control_deassert(sdc->rstc);
++ ret = reset_control_reset(sdc->rstc);
+ if (ret) {
+- dev_err(&pdev->dev, "Couldn't deassert the device from reset\n");
++ dev_err(&pdev->dev, "Couldn't reset the device\n");
+ goto err_chan_free;
+ }
+
+--
+2.20.1
+
--- /dev/null
+From c32597907fa4dfdbdf96424f930b6b288acc1ee0 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:21:00 -0500
+Subject: [PATCH 079/124] iommu/sun50i: Add support for D1 IOMMU
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/iommu/sun50i-iommu.c | 21 ++++++++++++++++++++-
+ 1 file changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c
+index 92997021e188..ba8341f21478 100644
+--- a/drivers/iommu/sun50i-iommu.c
++++ b/drivers/iommu/sun50i-iommu.c
+@@ -47,15 +47,20 @@
+ #define IOMMU_TLB_FLUSH_MACRO_TLB BIT(16)
+ #define IOMMU_TLB_FLUSH_MICRO_TLB(i) (BIT(i) & GENMASK(5, 0))
+
++#define IOMMU_TLB_IVLD_MODE_SEL_REG 0x084
++#define IOMMU_TLB_IVLD_START_ADDR_REG 0x088
++#define IOMMU_TLB_IVLD_END_ADDR_REG 0x08c
+ #define IOMMU_TLB_IVLD_ADDR_REG 0x090
+ #define IOMMU_TLB_IVLD_ADDR_MASK_REG 0x094
+ #define IOMMU_TLB_IVLD_ENABLE_REG 0x098
+ #define IOMMU_TLB_IVLD_ENABLE_ENABLE BIT(0)
+
+ #define IOMMU_PC_IVLD_ADDR_REG 0x0a0
++#define IOMMU_PC_IVLD_START_ADDR_REG 0x0a4
+ #define IOMMU_PC_IVLD_ENABLE_REG 0x0a8
+ #define IOMMU_PC_IVLD_ENABLE_ENABLE BIT(0)
+
++#define IOMMU_PC_IVLD_END_ADDR_REG 0x0ac
+ #define IOMMU_DM_AUT_CTRL_REG(d) (0x0b0 + ((d) / 2) * 4)
+ #define IOMMU_DM_AUT_CTRL_RD_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2)))
+ #define IOMMU_DM_AUT_CTRL_WR_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2) + 1))
+@@ -71,6 +76,19 @@
+ #define IOMMU_L1PG_INT_REG 0x0180
+ #define IOMMU_L2PG_INT_REG 0x0184
+
++#define IOMMU_VA_REG 0x0190
++#define IOMMU_VA_DATA_REG 0x0194
++#define IOMMU_VA_CONFIG_REG 0x0198
++#define IOMMU_PMU_ENABLE_REG 0x0200
++#define IOMMU_PMU_CLR_REG 0x0210
++#define IOMMU_PMU_ACCESS_LOW_REG(i) (0x230 + (i) * 16)
++#define IOMMU_PMU_ACCESS_HIGH_REG(i) (0x234 + (i) * 16)
++#define IOMMU_PMU_HIT_LOW_REG(i) (0x238 + (i) * 16)
++#define IOMMU_PMU_HIT_HIGH_REG(i) (0x23c + (i) * 16)
++#define IOMMU_PMU_TL_LOW_REG(i) (0x300 + (i) * 16)
++#define IOMMU_PMU_TL_HIGH_REG(i) (0x304 + (i) * 16)
++#define IOMMU_PMU_ML_REG(i) (0x308 + (i) * 16)
++
+ #define IOMMU_INT_INVALID_L2PG BIT(17)
+ #define IOMMU_INT_INVALID_L1PG BIT(16)
+ #define IOMMU_INT_MASTER_PERMISSION(m) BIT(m)
+@@ -945,7 +963,7 @@ static int sun50i_iommu_probe(struct platform_device *pdev)
+ goto err_free_group;
+ }
+
+- iommu->reset = devm_reset_control_get(&pdev->dev, NULL);
++ iommu->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(iommu->reset)) {
+ dev_err(&pdev->dev, "Couldn't get our reset line.\n");
+ ret = PTR_ERR(iommu->reset);
+@@ -986,6 +1004,7 @@ static int sun50i_iommu_probe(struct platform_device *pdev)
+ }
+
+ static const struct of_device_id sun50i_iommu_dt[] = {
++ { .compatible = "allwinner,sun20i-d1-iommu", },
+ { .compatible = "allwinner,sun50i-h6-iommu", },
+ { /* sentinel */ },
+ };
+--
+2.20.1
+
--- /dev/null
+From e18f604236129fd20868c53e5dc32f119e469be9 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 15 Nov 2021 23:37:02 -0600
+Subject: [PATCH 080/124] irqchip/sifive-plic: Add T-HEAD PLIC compatible
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/irqchip/irq-sifive-plic.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
+index cf74cfa82045..0397a3174aad 100644
+--- a/drivers/irqchip/irq-sifive-plic.c
++++ b/drivers/irqchip/irq-sifive-plic.c
+@@ -392,3 +392,4 @@ static int __init plic_init(struct device_node *node,
+
+ IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
+ IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
++IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init);
+--
+2.20.1
+
--- /dev/null
+From f0e764625b5bcfc260f74ede0a9d4ed497609211 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 17 May 2021 00:00:52 -0500
+Subject: [PATCH 081/124] mailbox: Add v2 mailbox
+
+---
+ .../mailbox/allwinner,sun20i-d1-msgbox.yaml | 70 +++++++++++++++++++
+ drivers/mailbox/Kconfig | 13 +++-
+ .../dt-bindings/mailbox/sun20i-d1-msgbox.h | 18 +++++
+ 3 files changed, 99 insertions(+), 2 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/mailbox/allwinner,sun20i-d1-msgbox.yaml
+ create mode 100644 include/dt-bindings/mailbox/sun20i-d1-msgbox.h
+
+diff --git a/Documentation/devicetree/bindings/mailbox/allwinner,sun20i-d1-msgbox.yaml b/Documentation/devicetree/bindings/mailbox/allwinner,sun20i-d1-msgbox.yaml
+new file mode 100644
+index 000000000000..d61237dbbd33
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mailbox/allwinner,sun20i-d1-msgbox.yaml
+@@ -0,0 +1,70 @@
++# SPDX-License-Identifier: GPL-2.0
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/mailbox/allwinner,sun20i-d1-msgbox.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Allwinner sunxi Message Box v2
++
++maintainers:
++ - Samuel Holland <samuel@sholland.org>
++
++properties:
++ compatible:
++ oneOf:
++ - const: allwinner,sun20i-d1-msgbox
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++ description: bus clock
++
++ resets:
++ maxItems: 1
++ description: bus reset
++
++ interrupts:
++ items:
++ - description: receive interrupt
++ - description: transmit interrupt
++
++ interrupt-names:
++ items:
++ - const: "rx"
++ - const: "tx"
++
++ '#mbox-cells':
++ const: 2
++ description: first cell is the user/channel number, second is direction
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - resets
++ - interrupts
++ - interrupt-names
++ - '#mbox-cells'
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/clock/sun20i-d1-ccu.h>
++ #include <dt-bindings/interrupt-controller/irq.h>
++ #include <dt-bindings/reset/sun20i-d1-ccu.h>
++
++ msgbox: mailbox@3003000 {
++ compatible = "allwinner,sun20i-d1-msgbox";
++ reg = <0x3003000 0x1000>;
++ clocks = <&ccu CLK_BUS_MSGBOX0>;
++ resets = <&ccu RST_BUS_MSGBOX0>;
++ interrupts = <101 IRQ_TYPE_LEVEL_HIGH>,
++ <102 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "rx", "tx";
++ #mbox-cells = <2>;
++ };
++
++...
+diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
+index c9fc06c7e685..75adbdfe19cc 100644
+--- a/drivers/mailbox/Kconfig
++++ b/drivers/mailbox/Kconfig
+@@ -247,13 +247,22 @@ config ZYNQMP_IPI_MBOX
+ registers to kick the other processor or enquire status.
+
+ config SUN6I_MSGBOX
+- tristate "Allwinner sun6i/sun8i/sun9i/sun50i Message Box"
++ tristate "Allwinner sun6i/sun8i/sun9i/sun50i ARISC Message Box"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ default ARCH_SUNXI
+ help
+ Mailbox implementation for the hardware message box present in
+ various Allwinner SoCs. This mailbox is used for communication
+- between the application CPUs and the power management coprocessor.
++ between the ARM CPUs and the ARISC power management coprocessor.
++
++config SUN8I_MSGBOX
++ tristate "Allwinner sun8i/sun20i/sun50i DSP/RISC-V Message Box"
++ depends on ARCH_SUNXI || COMPILE_TEST
++ default ARCH_SUNXI
++ help
++ Mailbox implementation for the hardware message box present in
++ various Allwinner SoCs. This mailbox is used for communication
++ between the ARM/RISC-V CPUs and the integrated DSP.
+
+ config SPRD_MBOX
+ tristate "Spreadtrum Mailbox"
+diff --git a/include/dt-bindings/mailbox/sun20i-d1-msgbox.h b/include/dt-bindings/mailbox/sun20i-d1-msgbox.h
+new file mode 100644
+index 000000000000..2f149f6d59f3
+--- /dev/null
++++ b/include/dt-bindings/mailbox/sun20i-d1-msgbox.h
+@@ -0,0 +1,18 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * This header provides constants for binding nvidia,tegra186-hsp.
++ */
++
++#ifndef _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_
++#define _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_
++
++/* First cell: channel (transmitting user) */
++#define MBOX_USER_CPUX 0
++#define MBOX_USER_DSP 1
++#define MBOX_USER_RISCV 2
++
++/* Second cell: direction (RX if phandle references local mailbox, else TX) */
++#define MBOX_RX 0
++#define MBOX_TX 1
++
++#endif /* _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_ */
+--
+2.20.1
+
--- /dev/null
+From 0d13a0d00a53d70b8a45437dadec46c42be7b56a Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:47:22 -0500
+Subject: [PATCH 082/124] media: cedrus: Add D1 variant
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/staging/media/sunxi/cedrus/cedrus.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
+index c0d005dafc6c..98c66431b232 100644
+--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
++++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
+@@ -527,6 +527,14 @@ static const struct cedrus_variant sun8i_r40_cedrus_variant = {
+ .mod_rate = 297000000,
+ };
+
++static const struct cedrus_variant sun20i_d1_cedrus_variant = {
++ .capabilities = CEDRUS_CAPABILITY_UNTILED |
++ CEDRUS_CAPABILITY_MPEG2_DEC |
++ CEDRUS_CAPABILITY_H264_DEC |
++ CEDRUS_CAPABILITY_H265_DEC,
++ .mod_rate = 432000000,
++};
++
+ static const struct cedrus_variant sun50i_a64_cedrus_variant = {
+ .capabilities = CEDRUS_CAPABILITY_UNTILED |
+ CEDRUS_CAPABILITY_MPEG2_DEC |
+@@ -583,6 +591,10 @@ static const struct of_device_id cedrus_dt_match[] = {
+ .compatible = "allwinner,sun8i-r40-video-engine",
+ .data = &sun8i_r40_cedrus_variant,
+ },
++ {
++ .compatible = "allwinner,sun20i-d1-video-engine",
++ .data = &sun20i_d1_cedrus_variant,
++ },
+ {
+ .compatible = "allwinner,sun50i-a64-video-engine",
+ .data = &sun50i_a64_cedrus_variant,
+--
+2.20.1
+
--- /dev/null
+From 7be2c5fed24014995642b24f14d414c4471b79e4 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:15:56 -0500
+Subject: [PATCH 083/124] mmc: sunxi-mmc: Correct the maximum transfer size
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/mmc/host/sunxi-mmc.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
+index 2702736a1c57..4e0d593dbbe7 100644
+--- a/drivers/mmc/host/sunxi-mmc.c
++++ b/drivers/mmc/host/sunxi-mmc.c
+@@ -214,6 +214,9 @@
+ #define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */
+ #define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */
+
++/* Buffer size must be a multiple of 4 bytes. */
++#define SDXC_IDMAC_DES1_ALIGN 4
++
+ #define SDXC_CLK_400K 0
+ #define SDXC_CLK_25M 1
+ #define SDXC_CLK_50M 2
+@@ -361,17 +364,16 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
+ {
+ struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
+ dma_addr_t next_desc = host->sg_dma;
+- int i, max_len = (1 << host->cfg->idma_des_size_bits);
++ int i;
+
+ for (i = 0; i < data->sg_len; i++) {
+ pdes[i].config = cpu_to_le32(SDXC_IDMAC_DES0_CH |
+ SDXC_IDMAC_DES0_OWN |
+ SDXC_IDMAC_DES0_DIC);
+
+- if (data->sg[i].length == max_len)
+- pdes[i].buf_size = 0; /* 0 == max_len */
+- else
+- pdes[i].buf_size = cpu_to_le32(data->sg[i].length);
++ pdes[i].buf_size =
++ cpu_to_le32(ALIGN(data->sg[i].length,
++ SDXC_IDMAC_DES1_ALIGN));
+
+ next_desc += sizeof(struct sunxi_idma_des);
+ pdes[i].buf_addr_ptr1 =
+@@ -1411,7 +1413,8 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
+ mmc->max_blk_count = 8192;
+ mmc->max_blk_size = 4096;
+ mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des);
+- mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits);
++ mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits) -
++ SDXC_IDMAC_DES1_ALIGN;
+ mmc->max_req_size = mmc->max_seg_size * mmc->max_segs;
+ /* 400kHz ~ 52MHz */
+ mmc->f_min = 400000;
+--
+2.20.1
+
--- /dev/null
+From 24c1d741a49938258a8664959e0dbb17e151cae3 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:16:30 -0500
+Subject: [PATCH 084/124] mmc: sunxi-mmc: Fix DMA descriptors above 32 bits
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/mmc/host/sunxi-mmc.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
+index 4e0d593dbbe7..d99aba9d7304 100644
+--- a/drivers/mmc/host/sunxi-mmc.c
++++ b/drivers/mmc/host/sunxi-mmc.c
+@@ -379,8 +379,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
+ pdes[i].buf_addr_ptr1 =
+ cpu_to_le32(sg_dma_address(&data->sg[i]) >>
+ host->cfg->idma_des_shift);
+- pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >>
+- host->cfg->idma_des_shift);
++ pdes[i].buf_addr_ptr2 =
++ cpu_to_le32(next_desc >>
++ host->cfg->idma_des_shift);
+ }
+
+ pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD);
+--
+2.20.1
+
--- /dev/null
+From 8d2fd59f8a638745eabd207d3db9fa02d2f9ba98 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:16:55 -0500
+Subject: [PATCH 085/124] mmc: sunxi-mmc: Add D1 MMC compatible
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/mmc/host/sunxi-mmc.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
+index d99aba9d7304..cde3e8dbc94b 100644
+--- a/drivers/mmc/host/sunxi-mmc.c
++++ b/drivers/mmc/host/sunxi-mmc.c
+@@ -1170,6 +1170,14 @@ static const struct sunxi_mmc_cfg sun9i_a80_cfg = {
+ .can_calibrate = false,
+ };
+
++static const struct sunxi_mmc_cfg sun20i_d1_cfg = {
++ .idma_des_size_bits = 13,
++ .idma_des_shift = 2,
++ .can_calibrate = true,
++ .mask_data0 = true,
++ .needs_new_timings = true,
++};
++
+ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+ .idma_des_size_bits = 16,
+ .clk_delays = NULL,
+@@ -1208,6 +1216,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = {
+ { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
+ { .compatible = "allwinner,sun8i-a83t-emmc", .data = &sun8i_a83t_emmc_cfg },
+ { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
++ { .compatible = "allwinner,sun20i-d1-mmc", .data = &sun20i_d1_cfg },
+ { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
+ { .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg },
+--
+2.20.1
+
--- /dev/null
+From 22fa62f3cad1d1a053760e32d2de7cf1209a4a91 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:40:49 -0500
+Subject: [PATCH 086/124] mmc: sunxi-mmc: Add more registers
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/mmc/host/sunxi-mmc.c | 55 ++++++++++++++++++++++++++++--------
+ 1 file changed, 43 insertions(+), 12 deletions(-)
+
+diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
+index cde3e8dbc94b..e6a21bcc3b47 100644
+--- a/drivers/mmc/host/sunxi-mmc.c
++++ b/drivers/mmc/host/sunxi-mmc.c
+@@ -54,25 +54,56 @@
+ #define SDXC_REG_MISTA (0x34) /* SMC Masked Interrupt Status Register */
+ #define SDXC_REG_RINTR (0x38) /* SMC Raw Interrupt Status Register */
+ #define SDXC_REG_STAS (0x3C) /* SMC Status Register */
+-#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Registe */
++#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Register */
+ #define SDXC_REG_FUNS (0x44) /* SMC Function Select Register */
+ #define SDXC_REG_CBCR (0x48) /* SMC CIU Byte Count Register */
+ #define SDXC_REG_BBCR (0x4C) /* SMC BIU Byte Count Register */
+ #define SDXC_REG_DBGC (0x50) /* SMC Debug Enable Register */
+-#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset for Register */
++#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset Register */
+ #define SDXC_REG_DMAC (0x80) /* SMC IDMAC Control Register */
+-#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Addre */
++#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Address */
+ #define SDXC_REG_IDST (0x88) /* SMC IDMAC Status Register */
+ #define SDXC_REG_IDIE (0x8C) /* SMC IDMAC Interrupt Enable Register */
+-#define SDXC_REG_CHDA (0x90)
+-#define SDXC_REG_CBDA (0x94)
++#define SDXC_REG_CHDA (0x90) /* Current Host Descriptor Address */
++#define SDXC_REG_CBDA (0x94) /* Current Buffer Descriptor Address */
++
++/* New registers introduced in A80 */
++#define SDXC_REG_A12A 0x058 /* Auto Command 12 Register */
++#define SDXC_REG_THLD 0x100 /* Card Threshold Control Register */
++#define SDXC_REG_DSBD 0x10C /* eMMC 4.5 DDR Start Bit Detection */
++
++/* New registers introduced in A83T */
++#define SDXC_REG_NTSR 0x05C /* New Timing Set Register */
++#define SDXC_REG_SDBG 0x060 /* New Timing Set Debug Register */
++
++/* New registers introduced in H3 */
++#define SDXC_REG_RES_CRC 0x110 /* CRC Response from Card/eMMC */
++#define SDXC_REG_D7_CRC 0x114 /* CRC Data 7 from Card/eMMC */
++#define SDXC_REG_D6_CRC 0x118 /* CRC Data 6 from Card/eMMC */
++#define SDXC_REG_D5_CRC 0x11C /* CRC Data 5 from Card/eMMC */
++#define SDXC_REG_D4_CRC 0x120 /* CRC Data 4 from Card/eMMC */
++#define SDXC_REG_D3_CRC 0x124 /* CRC Data 3 from Card/eMMC */
++#define SDXC_REG_D2_CRC 0x128 /* CRC Data 2 from Card/eMMC */
++#define SDXC_REG_D1_CRC 0x12C /* CRC Data 1 from Card/eMMC */
++#define SDXC_REG_D0_CRC 0x130 /* CRC Data 0 from Card/eMMC */
++#define SDXC_REG_CRC_STA 0x134 /* CRC Status from Write Operation */
+
+ /* New registers introduced in A64 */
+-#define SDXC_REG_A12A 0x058 /* SMC Auto Command 12 Register */
+-#define SDXC_REG_SD_NTSR 0x05C /* SMC New Timing Set Register */
++#define SDXC_REG_CSDC 0x054 /* CRC Status Detect Register */
+ #define SDXC_REG_DRV_DL 0x140 /* Drive Delay Control Register */
+-#define SDXC_REG_SAMP_DL_REG 0x144 /* SMC sample delay control */
+-#define SDXC_REG_DS_DL_REG 0x148 /* SMC data strobe delay control */
++#define SDXC_REG_SAMP_DL 0x144 /* Sample Delay Control Register */
++#define SDXC_REG_DS_DL 0x148 /* Data Strobe Delay Control Register */
++
++/* New registers introduced in H6 */
++#define SDXC_REG_EMCE 0x064 /* Embedded Encrypt/Decrypt Control */
++#define SDXC_REG_EMCE_DBG 0x068 /* Embedded Encrypt/Decrypt Debug */
++
++/* New registers introduced in H616 */
++#define SDXC_REG_EXT_CMD 0x138 /* Extended Command Register */
++#define SDXC_REG_EXT_RESP 0x13C /* Extended Response Register */
++
++/* New registers introduced in A100 */
++#define SDXC_REG_HS400_DL 0x14C /* HS400 Delay Control Register */
+
+ #define mmc_readl(host, reg) \
+ readl((host)->reg_base + SDXC_##reg)
+@@ -836,9 +867,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
+ */
+ if (host->use_new_timings) {
+ /* Don't touch the delay bits */
+- rval = mmc_readl(host, REG_SD_NTSR);
++ rval = mmc_readl(host, REG_NTSR);
+ rval |= SDXC_2X_TIMING_MODE;
+- mmc_writel(host, REG_SD_NTSR, rval);
++ mmc_writel(host, REG_NTSR, rval);
+ }
+
+ /* sunxi_mmc_clk_set_phase expects the actual card clock rate */
+@@ -846,7 +877,7 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
+ if (ret)
+ return ret;
+
+- ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG);
++ ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL);
+ if (ret)
+ return ret;
+
+--
+2.20.1
+
--- /dev/null
+From 0f84a1ffd2d5e74c17bf50642e137f5f356567df Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Tue, 15 Jun 2021 12:06:29 +0100
+Subject: [PATCH 087/124] phy: sun4i-usb: Rework HCI PHY (aka. "pmu_unk1")
+ handling
+
+As Icenowy pointed out, newer manuals (starting with H6) actually
+document the register block at offset 0x800 as "HCI controller and PHY
+interface", also describe the bits in our "PMU_UNK1" register.
+Let's put proper names to those "unknown" variables and symbols.
+
+While we are at it, generalise the existing code by allowing a bitmap
+of bits to clear and set, to cover newer SoCs: The A100 and H616 use a
+different bit for the SIDDQ control.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+---
+ drivers/phy/allwinner/phy-sun4i-usb.c | 30 ++++++++++++---------------
+ 1 file changed, 13 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
+index 788dd5cdbb7d..142f4cafdc78 100644
+--- a/drivers/phy/allwinner/phy-sun4i-usb.c
++++ b/drivers/phy/allwinner/phy-sun4i-usb.c
+@@ -43,7 +43,7 @@
+ #define REG_PHYCTL_A33 0x10
+ #define REG_PHY_OTGCTL 0x20
+
+-#define REG_PMU_UNK1 0x10
++#define REG_HCI_PHY_CTL 0x10
+
+ #define PHYCTL_DATA BIT(7)
+
+@@ -82,6 +82,7 @@
+ /* A83T specific control bits for PHY0 */
+ #define PHY_CTL_VBUSVLDEXT BIT(5)
+ #define PHY_CTL_SIDDQ BIT(3)
++#define PHY_CTL_H3_SIDDQ BIT(1)
+
+ /* A83T specific control bits for PHY2 HSIC */
+ #define SUNXI_EHCI_HS_FORCE BIT(20)
+@@ -115,9 +116,9 @@ struct sun4i_usb_phy_cfg {
+ int hsic_index;
+ enum sun4i_usb_phy_type type;
+ u32 disc_thresh;
++ u32 hci_phy_ctl_clear;
+ u8 phyctl_offset;
+ bool dedicated_clocks;
+- bool enable_pmu_unk1;
+ bool phy0_dual_route;
+ int missing_phys;
+ };
+@@ -288,6 +289,12 @@ static int sun4i_usb_phy_init(struct phy *_phy)
+ return ret;
+ }
+
++ if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
++ val = readl(phy->pmu + REG_HCI_PHY_CTL);
++ val &= ~data->cfg->hci_phy_ctl_clear;
++ writel(val, phy->pmu + REG_HCI_PHY_CTL);
++ }
++
+ if (data->cfg->type == sun8i_a83t_phy ||
+ data->cfg->type == sun50i_h6_phy) {
+ if (phy->index == 0) {
+@@ -297,11 +304,6 @@ static int sun4i_usb_phy_init(struct phy *_phy)
+ writel(val, data->base + data->cfg->phyctl_offset);
+ }
+ } else {
+- if (phy->pmu && data->cfg->enable_pmu_unk1) {
+- val = readl(phy->pmu + REG_PMU_UNK1);
+- writel(val & ~2, phy->pmu + REG_PMU_UNK1);
+- }
+-
+ /* Enable USB 45 Ohm resistor calibration */
+ if (phy->index == 0)
+ sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
+@@ -863,7 +865,6 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
+@@ -872,7 +873,6 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
+ .disc_thresh = 2,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
+@@ -881,7 +881,6 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
+@@ -890,7 +889,6 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
+ .disc_thresh = 2,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
+@@ -899,7 +897,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
+@@ -908,7 +905,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = false,
+ };
+
+ static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
+@@ -925,7 +921,7 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = true,
++ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+ };
+
+@@ -935,7 +931,7 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = true,
++ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+ };
+
+@@ -945,7 +941,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = true,
++ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+ };
+
+@@ -955,7 +951,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+- .enable_pmu_unk1 = true,
++ .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
+ .phy0_dual_route = true,
+ };
+
+--
+2.20.1
+
--- /dev/null
+From 7aff4d9c913586d7d4ffe33f88c17d8d4d8302bd Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 22:14:15 -0500
+Subject: [PATCH 088/124] phy: sun4i-usb: Remove disc_thresh where not
+ applicable
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/phy/allwinner/phy-sun4i-usb.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
+index 142f4cafdc78..6675dad17415 100644
+--- a/drivers/phy/allwinner/phy-sun4i-usb.c
++++ b/drivers/phy/allwinner/phy-sun4i-usb.c
+@@ -958,7 +958,6 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
+ static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
+ .num_phys = 4,
+ .type = sun50i_h6_phy,
+- .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+ .phy0_dual_route = true,
+--
+2.20.1
+
--- /dev/null
+From b3518c170a136e9c3ec605d89a944203324cee1b Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:45:25 -0500
+Subject: [PATCH 089/124] phy: sun4i-usb: Add D1 variant
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/phy/allwinner/phy-sun4i-usb.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
+index 6675dad17415..021335c91661 100644
+--- a/drivers/phy/allwinner/phy-sun4i-usb.c
++++ b/drivers/phy/allwinner/phy-sun4i-usb.c
+@@ -945,6 +945,15 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
+ .phy0_dual_route = true,
+ };
+
++static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = {
++ .num_phys = 2,
++ .type = sun50i_h6_phy,
++ .phyctl_offset = REG_PHYCTL_A33,
++ .dedicated_clocks = true,
++ .hci_phy_ctl_clear = PHY_CTL_SIDDQ,
++ .phy0_dual_route = true,
++};
++
+ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
+ .num_phys = 2,
+ .type = sun50i_a64_phy,
+@@ -975,8 +984,8 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
+ { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
+ { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
+ { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
+- { .compatible = "allwinner,sun50i-a64-usb-phy",
+- .data = &sun50i_a64_cfg},
++ { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg },
++ { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
+ { },
+ };
+--
+2.20.1
+
--- /dev/null
+From fa878b2c67b76258b85b00af41e46ec205720be0 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 19:56:23 -0500
+Subject: [PATCH 090/124] pinctrl: sunxi: Support new 2.5V I/O bias mode
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 11 +++++++++++
+ drivers/pinctrl/sunxi/pinctrl-sunxi.h | 7 +++++++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index 862c84efb718..8a9c0ee98d8e 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -654,6 +654,16 @@ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl,
+ reg &= ~IO_BIAS_MASK;
+ writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin));
+ return 0;
++ case BIAS_VOLTAGE_PIO_POW_MODE_CTL:
++ val = 1800000 < uV && uV <= 2500000 ? BIT(bank) : 0;
++
++ raw_spin_lock_irqsave(&pctl->lock, flags);
++ reg = readl(pctl->membase + PIO_POW_MOD_CTL_REG);
++ reg &= ~BIT(bank);
++ writel(reg | val, pctl->membase + PIO_POW_MOD_CTL_REG);
++ raw_spin_unlock_irqrestore(&pctl->lock, flags);
++
++ fallthrough;
+ case BIAS_VOLTAGE_PIO_POW_MODE_SEL:
+ val = uV <= 1800000 ? 1 : 0;
+
+@@ -662,6 +672,7 @@ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl,
+ reg &= ~(1 << bank);
+ writel(reg | val << bank, pctl->membase + PIO_POW_MOD_SEL_REG);
+ raw_spin_unlock_irqrestore(&pctl->lock, flags);
++
+ return 0;
+ default:
+ return -EINVAL;
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+index a32bb5bcb754..0f1aab58650c 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+@@ -98,6 +98,7 @@
+ #define PINCTRL_SUN8I_V3S BIT(10)
+
+ #define PIO_POW_MOD_SEL_REG 0x340
++#define PIO_POW_MOD_CTL_REG 0x344
+
+ enum sunxi_desc_bias_voltage {
+ BIAS_VOLTAGE_NONE,
+@@ -111,6 +112,12 @@ enum sunxi_desc_bias_voltage {
+ * register, as seen on H6 SoC, for example.
+ */
+ BIAS_VOLTAGE_PIO_POW_MODE_SEL,
++ /*
++ * Bias voltage is set through PIO_POW_MOD_SEL_REG
++ * and PIO_POW_MOD_CTL_REG register, as seen on
++ * A100 and D1 SoC, for example.
++ */
++ BIAS_VOLTAGE_PIO_POW_MODE_CTL,
+ };
+
+ struct sunxi_desc_function {
+--
+2.20.1
+
--- /dev/null
+From 0d32a8415787d2833c67d4710bba4eb264abd13c Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:06:19 -0500
+Subject: [PATCH 091/124] pinctrl: sunxi: Adapt for D1 register layout
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.h | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+index 0f1aab58650c..8ebba0ab5f69 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
+@@ -34,11 +34,11 @@
+
+ #define SUNXI_PIN_NAME_MAX_LEN 5
+
+-#define BANK_MEM_SIZE 0x24
++#define BANK_MEM_SIZE 0x30
+ #define MUX_REGS_OFFSET 0x0
+ #define DATA_REGS_OFFSET 0x10
+ #define DLEVEL_REGS_OFFSET 0x14
+-#define PULL_REGS_OFFSET 0x1c
++#define PULL_REGS_OFFSET 0x24
+
+ #define PINS_PER_BANK 32
+ #define MUX_PINS_PER_REG 8
+@@ -47,8 +47,8 @@
+ #define DATA_PINS_PER_REG 32
+ #define DATA_PINS_BITS 1
+ #define DATA_PINS_MASK 0x01
+-#define DLEVEL_PINS_PER_REG 16
+-#define DLEVEL_PINS_BITS 2
++#define DLEVEL_PINS_PER_REG 8
++#define DLEVEL_PINS_BITS 4
+ #define DLEVEL_PINS_MASK 0x03
+ #define PULL_PINS_PER_REG 16
+ #define PULL_PINS_BITS 2
+@@ -84,7 +84,7 @@
+ #define IO_BIAS_MASK GENMASK(3, 0)
+
+ #define SUN4I_FUNC_INPUT 0
+-#define SUN4I_FUNC_IRQ 6
++#define SUN4I_FUNC_IRQ 0xe
+
+ #define PINCTRL_SUN5I_A10S BIT(1)
+ #define PINCTRL_SUN5I_A13 BIT(2)
+--
+2.20.1
+
--- /dev/null
+From 80a49483f4354770415223ffdcb46ad5f4c3f73a Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:13:04 -0500
+Subject: [PATCH 092/124] pinctrl: sunxi: Add support for Allwinner D1 SoC
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/pinctrl/sunxi/Kconfig | 5 +
+ drivers/pinctrl/sunxi/Makefile | 1 +
+ drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c | 836 ++++++++++++++++++++++
+ 3 files changed, 842 insertions(+)
+ create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c
+
+diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
+index 33751a6a0757..a6ac1c1f2585 100644
+--- a/drivers/pinctrl/sunxi/Kconfig
++++ b/drivers/pinctrl/sunxi/Kconfig
+@@ -84,6 +84,11 @@ config PINCTRL_SUN9I_A80_R
+ depends on RESET_CONTROLLER
+ select PINCTRL_SUNXI
+
++config PINCTRL_SUN20I_D1
++ bool "Support for the Allwinner D1 PIO"
++ default RISCV && ARCH_SUNXI
++ select PINCTRL_SUNXI
++
+ config PINCTRL_SUN50I_A64
+ bool "Support for the Allwinner A64 PIO"
+ default ARM64 && ARCH_SUNXI
+diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
+index d3440c42b9d6..2ff5a55927ad 100644
+--- a/drivers/pinctrl/sunxi/Makefile
++++ b/drivers/pinctrl/sunxi/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_PINCTRL_SUN8I_A83T_R) += pinctrl-sun8i-a83t-r.o
+ obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o
+ obj-$(CONFIG_PINCTRL_SUN8I_H3_R) += pinctrl-sun8i-h3-r.o
+ obj-$(CONFIG_PINCTRL_SUN8I_V3S) += pinctrl-sun8i-v3s.o
++obj-$(CONFIG_PINCTRL_SUN20I_D1) += pinctrl-sun20i-d1.o
+ obj-$(CONFIG_PINCTRL_SUN50I_H5) += pinctrl-sun50i-h5.o
+ obj-$(CONFIG_PINCTRL_SUN50I_H6) += pinctrl-sun50i-h6.o
+ obj-$(CONFIG_PINCTRL_SUN50I_H6_R) += pinctrl-sun50i-h6-r.o
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c b/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c
+new file mode 100644
+index 000000000000..ed72ebc1e2cd
+--- /dev/null
++++ b/drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c
+@@ -0,0 +1,836 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Allwinner D1 SoC pinctrl driver.
++ *
++ * Copyright (c) 2020 wuyan@allwinnertech.com
++ * Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/pinctrl/pinctrl.h>
++
++#include "pinctrl-sunxi.h"
++
++/* PB:8pins,PC:8pins,PD:23pins,PE:18pins,PF:7pins, PG:16pins */
++static const struct sunxi_desc_pin d1_pins[] = {
++ /* PB */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "pwm"),
++ SUNXI_FUNCTION(0x3, "ir"), /* TX */
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x5, "spi1"), /* WP */
++ SUNXI_FUNCTION(0x6, "uart0"), /* TX */
++ SUNXI_FUNCTION(0x7, "uart2"), /* TX */
++ SUNXI_FUNCTION(0x8, "spdif"), /* OUT */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "pwm"),
++ SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT3 */
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN3 */
++ SUNXI_FUNCTION(0x6, "uart0"), /* RX */
++ SUNXI_FUNCTION(0x7, "uart2"), /* RX */
++ SUNXI_FUNCTION(0x8, "ir"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */
++ SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT2 */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */
++ SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN2 */
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D18 */
++ SUNXI_FUNCTION(0x7, "uart4"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */
++ SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT1 */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */
++ SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN0 */
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D19 */
++ SUNXI_FUNCTION(0x7, "uart4"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */
++ SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT0 */
++ SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */
++ SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN1 */
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D20 */
++ SUNXI_FUNCTION(0x7, "uart5"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */
++ SUNXI_FUNCTION(0x3, "i2s2"), /* BCLK */
++ SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D21 */
++ SUNXI_FUNCTION(0x7, "uart5"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */
++ SUNXI_FUNCTION(0x3, "i2s2"), /* LRCK */
++ SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D22 */
++ SUNXI_FUNCTION(0x7, "uart3"), /* TX */
++ SUNXI_FUNCTION(0x8, "bist0"), /* BIST_RESULT0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 6)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */
++ SUNXI_FUNCTION(0x3, "i2s2"), /* MCLK */
++ SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x5, "ir"), /* RX */
++ SUNXI_FUNCTION(0x6, "lcd0"), /* D23 */
++ SUNXI_FUNCTION(0x7, "uart3"), /* RX */
++ SUNXI_FUNCTION(0x8, "bist1"), /* BIST_RESULT1 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 7)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "dmic"), /* DATA3 */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x5, "spi1"), /* HOLD */
++ SUNXI_FUNCTION(0x6, "uart0"), /* TX */
++ SUNXI_FUNCTION(0x7, "uart1"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 8)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "dmic"), /* DATA2 */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x5, "spi1"), /* MISO */
++ SUNXI_FUNCTION(0x6, "uart0"), /* RX */
++ SUNXI_FUNCTION(0x7, "uart1"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 9)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 10),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "dmic"), /* DATA1 */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */
++ SUNXI_FUNCTION(0x5, "spi1"), /* MOSI */
++ SUNXI_FUNCTION(0x6, "clk"), /* FANOUT0 */
++ SUNXI_FUNCTION(0x7, "uart1"), /* RTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 10)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 11),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "dmic"), /* DATA0 */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */
++ SUNXI_FUNCTION(0x5, "spi1"), /* CLK */
++ SUNXI_FUNCTION(0x6, "clk"), /* FANOUT1 */
++ SUNXI_FUNCTION(0x7, "uart1"), /* CTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 11)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 12),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "dmic"), /* CLK */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "spdif"), /* IN */
++ SUNXI_FUNCTION(0x5, "spi1"), /* CS0 */
++ SUNXI_FUNCTION(0x6, "clk"), /* FANOUT2 */
++ SUNXI_FUNCTION(0x7, "ir"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 12)),
++ /* PC */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart2"), /* TX */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x4, "ledc"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart2"), /* RX */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* CLK */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* CLK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* CMD */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* D2 */
++ SUNXI_FUNCTION(0x4, "boot"), /* SEL0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* MISO */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* D1 */
++ SUNXI_FUNCTION(0x4, "boot"), /* SEL1 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* WP */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* D0 */
++ SUNXI_FUNCTION(0x4, "uart3"), /* TX */
++ SUNXI_FUNCTION(0x5, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x6, "pll"), /* DBG-CLK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 6)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spi0"), /* HOLD */
++ SUNXI_FUNCTION(0x3, "mmc2"), /* D3 */
++ SUNXI_FUNCTION(0x4, "uart3"), /* RX */
++ SUNXI_FUNCTION(0x5, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 7)),
++ /* PD */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V0P */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D0P */
++ SUNXI_FUNCTION(0x5, "i2c0"), /* SCK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V0N */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D0N */
++ SUNXI_FUNCTION(0x5, "uart2"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V1P */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D1P */
++ SUNXI_FUNCTION(0x5, "uart2"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V1N */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D1N */
++ SUNXI_FUNCTION(0x5, "uart2"), /* RTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V2P */
++ SUNXI_FUNCTION(0x4, "dsi"), /* CKP */
++ SUNXI_FUNCTION(0x5, "uart2"), /* CTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V2N */
++ SUNXI_FUNCTION(0x4, "dsi"), /* CKN */
++ SUNXI_FUNCTION(0x5, "uart5"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* CKP */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D2P */
++ SUNXI_FUNCTION(0x5, "uart5"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 6)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* CKN */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D2N */
++ SUNXI_FUNCTION(0x5, "uart4"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 7)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V3P */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D3P */
++ SUNXI_FUNCTION(0x5, "uart4"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 8)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */
++ SUNXI_FUNCTION(0x3, "lvds0"), /* V3N */
++ SUNXI_FUNCTION(0x4, "dsi"), /* D3N */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 9)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V0P */
++ SUNXI_FUNCTION(0x4, "spi1"), /* CS0 */
++ SUNXI_FUNCTION(0x5, "uart3"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 10)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V0N */
++ SUNXI_FUNCTION(0x4, "spi1"), /* CLK */
++ SUNXI_FUNCTION(0x5, "uart3"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 11)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V1P */
++ SUNXI_FUNCTION(0x4, "spi1"), /* MOSI */
++ SUNXI_FUNCTION(0x5, "i2c0"), /* SDA */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 12)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V1N */
++ SUNXI_FUNCTION(0x4, "spi1"), /* MISO */
++ SUNXI_FUNCTION(0x5, "uart3"), /* RTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 13)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V2P */
++ SUNXI_FUNCTION(0x4, "spi1"), /* HOLD */
++ SUNXI_FUNCTION(0x5, "uart3"), /* CTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 14)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V2N */
++ SUNXI_FUNCTION(0x4, "spi1"), /* WP */
++ SUNXI_FUNCTION(0x5, "ir"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 15)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* CKP */
++ SUNXI_FUNCTION(0x4, "dmic"), /* DATA3 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 16)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* CKN */
++ SUNXI_FUNCTION(0x4, "dmic"), /* DATA2 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 17)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V3P */
++ SUNXI_FUNCTION(0x4, "dmic"), /* DATA1 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 18)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* DE */
++ SUNXI_FUNCTION(0x3, "lvds1"), /* V3N */
++ SUNXI_FUNCTION(0x4, "dmic"), /* DATA0 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 19)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x4, "dmic"), /* CLK */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 20)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x4, "uart1"), /* TX */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 21)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "spdif"), /* OUT */
++ SUNXI_FUNCTION(0x3, "ir"), /* RX */
++ SUNXI_FUNCTION(0x4, "uart1"), /* RX */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 22)),
++ /* PE */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* HSYNC */
++ SUNXI_FUNCTION(0x3, "uart2"), /* RTS */
++ SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */
++ SUNXI_FUNCTION(0x5, "lcd0"), /* HSYNC */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXCTL/CRS_DV */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* VSYNC */
++ SUNXI_FUNCTION(0x3, "uart2"), /* CTS */
++ SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */
++ SUNXI_FUNCTION(0x5, "lcd0"), /* VSYNC */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXD0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* PCLK */
++ SUNXI_FUNCTION(0x3, "uart2"), /* TX */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */
++ SUNXI_FUNCTION(0x6, "uart0"), /* TX */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXD1 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "csi0"), /* MCLK */
++ SUNXI_FUNCTION(0x3, "uart2"), /* RX */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */
++ SUNXI_FUNCTION(0x6, "uart0"), /* RX */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXCK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D0 */
++ SUNXI_FUNCTION(0x3, "uart4"), /* TX */
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */
++ SUNXI_FUNCTION(0x6, "d_jtag"), /* MS */
++ SUNXI_FUNCTION(0x7, "r_jtag"), /* MS */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXD0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D1 */
++ SUNXI_FUNCTION(0x3, "uart4"), /* RX */
++ SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x5, "ledc"),
++ SUNXI_FUNCTION(0x6, "d_jtag"), /* D1 */
++ SUNXI_FUNCTION(0x7, "r_jtag"), /* D1 */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXD1 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D2 */
++ SUNXI_FUNCTION(0x3, "uart5"), /* TX */
++ SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x5, "spdif"), /* IN */
++ SUNXI_FUNCTION(0x6, "d_jtag"), /* D0 */
++ SUNXI_FUNCTION(0x7, "r_jtag"), /* D0 */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXCTL/TXEN */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 6)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D3 */
++ SUNXI_FUNCTION(0x3, "uart5"), /* RX */
++ SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x5, "spdif"), /* OUT */
++ SUNXI_FUNCTION(0x6, "d_jtag"), /* CK */
++ SUNXI_FUNCTION(0x7, "r_jtag"), /* CK */
++ SUNXI_FUNCTION(0x8, "emac"), /* CK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 7)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D4 */
++ SUNXI_FUNCTION(0x3, "uart1"), /* RTS */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "uart3"), /* TX */
++ SUNXI_FUNCTION(0x6, "jtag"), /* MS */
++ SUNXI_FUNCTION(0x8, "emac"), /* MDC */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 8)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D5 */
++ SUNXI_FUNCTION(0x3, "uart1"), /* CTS */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "uart3"), /* RX */
++ SUNXI_FUNCTION(0x6, "jtag"), /* D1 */
++ SUNXI_FUNCTION(0x8, "emac"), /* MDIO */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 9)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D6 */
++ SUNXI_FUNCTION(0x3, "uart1"), /* TX */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "ir"), /* RX */
++ SUNXI_FUNCTION(0x6, "jtag"), /* D0 */
++ SUNXI_FUNCTION(0x8, "emac"), /* EPHY-25M */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 10)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ncsi0"), /* D7 */
++ SUNXI_FUNCTION(0x3, "uart1"), /* RX */
++ SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT3 */
++ SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN3 */
++ SUNXI_FUNCTION(0x6, "jtag"), /* CK */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXD2 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 11)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x3, "ncsi0"), /* FIELD */
++ SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT2 */
++ SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN2 */
++ SUNXI_FUNCTION(0x8, "emac"), /* TXD3 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 12)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x3, "pwm"),
++ SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT0 */
++ SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN1 */
++ SUNXI_FUNCTION(0x6, "dmic"), /* DATA3 */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXD2 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 13)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c1"), /* SCK */
++ SUNXI_FUNCTION(0x3, "d_jtag"), /* MS */
++ SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT1 */
++ SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN0 */
++ SUNXI_FUNCTION(0x6, "dmic"), /* DATA2 */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXD3 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 14)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c1"), /* SDA */
++ SUNXI_FUNCTION(0x3, "d_jtag"), /* D1 */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "i2s0"), /* LRCK */
++ SUNXI_FUNCTION(0x6, "dmic"), /* DATA1 */
++ SUNXI_FUNCTION(0x8, "emac"), /* RXCK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 15)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x3, "d_jtag"), /* D0 */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "i2s0"), /* BCLK */
++ SUNXI_FUNCTION(0x6, "dmic"), /* DATA0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 16)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x3, "d_jtag"), /* CK */
++ SUNXI_FUNCTION(0x4, "ir"), /* TX */
++ SUNXI_FUNCTION(0x5, "i2s0"), /* MCLK */
++ SUNXI_FUNCTION(0x6, "dmic"), /* CLK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 17)),
++ /* PF */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */
++ SUNXI_FUNCTION(0x3, "jtag"), /* MS */
++ SUNXI_FUNCTION(0x4, "r_jtag"), /* MS */
++ SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT1 */
++ SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */
++ SUNXI_FUNCTION(0x3, "jtag"), /* DI */
++ SUNXI_FUNCTION(0x4, "r_jtag"), /* DI */
++ SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT0 */
++ SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN1 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */
++ SUNXI_FUNCTION(0x3, "uart0"), /* TX */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */
++ SUNXI_FUNCTION(0x5, "ledc"),
++ SUNXI_FUNCTION(0x6, "spdif"), /* IN */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */
++ SUNXI_FUNCTION(0x3, "jtag"), /* DO */
++ SUNXI_FUNCTION(0x4, "r_jtag"), /* DO */
++ SUNXI_FUNCTION(0x5, "i2s2"), /* BCLK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */
++ SUNXI_FUNCTION(0x3, "uart0"), /* RX */
++ SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION(0x6, "ir"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */
++ SUNXI_FUNCTION(0x3, "jtag"), /* CK */
++ SUNXI_FUNCTION(0x4, "r_jtag"), /* CK */
++ SUNXI_FUNCTION(0x5, "i2s2"), /* LRCK */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x3, "spdif"), /* OUT */
++ SUNXI_FUNCTION(0x4, "ir"), /* RX */
++ SUNXI_FUNCTION(0x5, "i2s2"), /* MCLK */
++ SUNXI_FUNCTION(0x6, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 6)),
++ /* PG */
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */
++ SUNXI_FUNCTION(0x3, "uart3"), /* TX */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXCTRL/CRS_DV */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 0)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */
++ SUNXI_FUNCTION(0x3, "uart3"), /* RX */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXD0 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 1)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */
++ SUNXI_FUNCTION(0x3, "uart3"), /* RTS */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXD1 */
++ SUNXI_FUNCTION(0x5, "uart4"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 2)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */
++ SUNXI_FUNCTION(0x3, "uart3"), /* CTS */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXCK */
++ SUNXI_FUNCTION(0x5, "uart4"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 3)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */
++ SUNXI_FUNCTION(0x3, "uart5"), /* TX */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXD0 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 4)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */
++ SUNXI_FUNCTION(0x3, "uart5"), /* RX */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXD1 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 5)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart1"), /* TX */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXD2 */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 6)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart1"), /* RX */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXD3 */
++ SUNXI_FUNCTION(0x5, "spdif"), /* IN */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 7)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
++ SUNXI_FUNCTION(0x3, "i2c1"), /* SCK */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXD2 */
++ SUNXI_FUNCTION(0x5, "uart3"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 8)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
++ SUNXI_FUNCTION(0x3, "i2c1"), /* SDA */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXD3 */
++ SUNXI_FUNCTION(0x5, "uart3"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 9)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "pwm"),
++ SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x4, "emac"), /* RXCK */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */
++ SUNXI_FUNCTION(0x6, "ir"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 10)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2s1"), /* MCLK */
++ SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x4, "emac"), /* EPHY-25M */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */
++ SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 11)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2s1"), /* LRCK */
++ SUNXI_FUNCTION(0x3, "i2c0"), /* SCK */
++ SUNXI_FUNCTION(0x4, "emac"), /* TXCTL/TXEN */
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */
++ SUNXI_FUNCTION(0x6, "pwm"),
++ SUNXI_FUNCTION(0x7, "uart1"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 12)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2s1"), /* BCLK */
++ SUNXI_FUNCTION(0x3, "i2c0"), /* SDA */
++ SUNXI_FUNCTION(0x4, "emac"), /* CLKIN/RXER */
++ SUNXI_FUNCTION(0x5, "pwm"),
++ SUNXI_FUNCTION(0x6, "ledc"),
++ SUNXI_FUNCTION(0x7, "uart1"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 13)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 14),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2s1_din"), /* DIN0 */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */
++ SUNXI_FUNCTION(0x4, "emac"), /* MDC */
++ SUNXI_FUNCTION(0x5, "i2s1_dout"), /* DOUT1 */
++ SUNXI_FUNCTION(0x6, "spi0"), /* WP */
++ SUNXI_FUNCTION(0x7, "uart1"), /* RTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 14)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 15),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "i2s1_dout"), /* DOUT0 */
++ SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */
++ SUNXI_FUNCTION(0x4, "emac"), /* MDIO */
++ SUNXI_FUNCTION(0x5, "i2s1_din"), /* DIN1 */
++ SUNXI_FUNCTION(0x6, "spi0"), /* HOLD */
++ SUNXI_FUNCTION(0x7, "uart1"), /* CTS */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 15)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 16),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "ir"), /* RX */
++ SUNXI_FUNCTION(0x3, "tcon"), /* TRIG0 */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */
++ SUNXI_FUNCTION(0x6, "spdif"), /* IN */
++ SUNXI_FUNCTION(0x7, "ledc"),
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 16)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 17),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart2"), /* TX */
++ SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */
++ SUNXI_FUNCTION(0x6, "ir"), /* TX */
++ SUNXI_FUNCTION(0x7, "uart0"), /* TX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 17)),
++ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 18),
++ SUNXI_FUNCTION(0x0, "gpio_in"),
++ SUNXI_FUNCTION(0x1, "gpio_out"),
++ SUNXI_FUNCTION(0x2, "uart2"), /* RX */
++ SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */
++ SUNXI_FUNCTION(0x4, "pwm"),
++ SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */
++ SUNXI_FUNCTION(0x6, "spdif"), /* OUT */
++ SUNXI_FUNCTION(0x7, "uart0"), /* RX */
++ SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 18)),
++};
++
++static const unsigned int d1_irq_bank_map[] = { 1, 2, 3, 4, 5, 6 };
++
++static const struct sunxi_pinctrl_desc d1_pinctrl_data = {
++ .pins = d1_pins,
++ .npins = ARRAY_SIZE(d1_pins),
++ .irq_banks = ARRAY_SIZE(d1_irq_bank_map),
++ .irq_bank_map = d1_irq_bank_map,
++ .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL,
++};
++
++static int d1_pinctrl_probe(struct platform_device *pdev)
++{
++ return sunxi_pinctrl_init(pdev, &d1_pinctrl_data);
++}
++
++static const struct of_device_id d1_pinctrl_match[] = {
++ { .compatible = "allwinner,sun20i-d1-pinctrl" },
++ {}
++};
++
++static struct platform_driver d1_pinctrl_driver = {
++ .probe = d1_pinctrl_probe,
++ .driver = {
++ .name = "sun20i-d1-pinctrl",
++ .of_match_table = d1_pinctrl_match,
++ },
++};
++builtin_platform_driver(d1_pinctrl_driver);
+--
+2.20.1
+
--- /dev/null
+From c19eb2d30c0af0add66346252b9f2c742aa02f47 Mon Sep 17 00:00:00 2001
+From: Ban Tao <fengzheng923@gmail.com>
+Date: Tue, 2 Mar 2021 20:40:23 +0800
+Subject: [PATCH 093/124] pwm: sun8i-v536: document device tree bindings
+
+This adds binding documentation for sun8i-v536 SoC PWM driver.
+
+Signed-off-by: Ban Tao <fengzheng923@gmail.com>
+---
+ .../bindings/pwm/pwm-sun8i-v536.txt | 24 +++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt
+
+diff --git a/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt b/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt
+new file mode 100644
+index 000000000000..ab3f4fe0560a
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt
+@@ -0,0 +1,24 @@
++Allwinner sun8i-v536 SoC PWM controller
++
++Required properties:
++ - compatible: should be "allwinner,<name>-pwm"
++ "allwinner,sun8i-v833-pwm"
++ "allwinner,sun8i-v536-pwm"
++ "allwinner,sun50i-r818-pwm"
++ "allwinner,sun50i-a133-pwm"
++ "allwinner,sun50i-r329-pwm"
++ - reg: physical base address and length of the controller's registers
++ - #pwm-cells: should be 3. See pwm.txt in this directory for a description of
++ the cells format.
++ - clocks: From common clock binding, handle to the parent clock.
++ - resets: From reset clock binding, handle to the parent clock.
++
++Example:
++
++ pwm: pwm@300a0000 {
++ compatible = "allwinner,sun50i-r818-pwm";
++ reg = <0x0300a000 0x3ff>;
++ clocks = <&ccu CLK_BUS_PWM>;
++ resets = <&ccu RST_BUS_PWM>;
++ #pwm-cells = <3>;
++ };
+--
+2.20.1
+
--- /dev/null
+From 042d00b1479f3f1ad314ac2f3f0041dc806b9c5b Mon Sep 17 00:00:00 2001
+From: Ban Tao <fengzheng923@gmail.com>
+Date: Tue, 2 Mar 2021 20:37:37 +0800
+Subject: [PATCH 094/124] pwm: sunxi: Add Allwinner SoC PWM controller driver
+
+The Allwinner R818, A133, R329, V536 and V833 has a new PWM controller
+IP compared to the older Allwinner SoCs.
+
+Signed-off-by: Ban Tao <fengzheng923@gmail.com>
+---
+ MAINTAINERS | 6 +
+ drivers/pwm/Kconfig | 11 +
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-sun8i-v536.c | 401 +++++++++++++++++++++++++++++++++++
+ 4 files changed, 419 insertions(+)
+ create mode 100644 drivers/pwm/pwm-sun8i-v536.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 8b4809d2c789..bba1b777dee0 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -779,6 +779,12 @@ L: linux-media@vger.kernel.org
+ S: Maintained
+ F: drivers/staging/media/sunxi/cedrus/
+
++ALLWINNER PWM DRIVER
++M: Ban Tao <fengzheng923@gmail.com>
++L: linux-pwm@vger.kernel.org
++S: Maintained
++F: drivers/pwm/pwm-sun8i-v536.c
++
+ ALPHA PORT
+ M: Richard Henderson <rth@twiddle.net>
+ M: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index aa29841bbb79..e31884525081 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -570,6 +570,17 @@ config PWM_SUN4I
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sun4i.
+
++config PWM_SUN8I_V536
++ tristate "Allwinner SUN8I_V536 PWM support"
++ depends on ARCH_SUNXI || COMPILE_TEST
++ depends on HAS_IOMEM && COMMON_CLK
++ help
++ Enhanced PWM framework driver for Allwinner R818, A133, R329,
++ V536 and V833 SoCs.
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-sun8i-v536.
++
+ config PWM_TEGRA
+ tristate "NVIDIA Tegra PWM support"
+ depends on ARCH_TEGRA || COMPILE_TEST
+diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
+index 708840b7fba8..2ab762e719ff 100644
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -53,6 +53,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
+ obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
+ obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
+ obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
++obj-$(CONFIG_PWM_SUN8I_V536) += pwm-sun8i-v536.o
+ obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
+ obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o
+ obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o
+diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c
+new file mode 100644
+index 000000000000..52101df6bd41
+--- /dev/null
++++ b/drivers/pwm/pwm-sun8i-v536.c
+@@ -0,0 +1,401 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Driver for Allwinner sun8i-v536 Pulse Width Modulation Controller
++ *
++ * Copyright (C) 2021 Ban Tao <fengzheng923@gmail.com>
++ *
++ *
++ * Limitations:
++ * - When PWM is disabled, the output is driven to inactive.
++ * - If the register is reconfigured while PWM is running,
++ * it does not complete the currently running period.
++ * - If the user input duty is beyond acceptible limits,
++ * -EINVAL is returned.
++ */
++
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/pwm.h>
++#include <linux/clk.h>
++#include <linux/reset.h>
++
++#define PWM_GET_CLK_OFFSET(chan) (0x20 + ((chan >> 1) * 0x4))
++#define PWM_CLK_APB_SCR BIT(7)
++#define PWM_DIV_M 0
++#define PWM_DIV_M_MASK GENMASK(3, PWM_DIV_M)
++
++#define PWM_CLK_REG 0x40
++#define PWM_CLK_GATING BIT(0)
++
++#define PWM_ENABLE_REG 0x80
++#define PWM_EN BIT(0)
++
++#define PWM_CTL_REG(chan) (0x100 + 0x20 * chan)
++#define PWM_ACT_STA BIT(8)
++#define PWM_PRESCAL_K 0
++#define PWM_PRESCAL_K_MASK GENMASK(7, PWM_PRESCAL_K)
++
++#define PWM_PERIOD_REG(chan) (0x104 + 0x20 * chan)
++#define PWM_ENTIRE_CYCLE 16
++#define PWM_ENTIRE_CYCLE_MASK GENMASK(31, PWM_ENTIRE_CYCLE)
++#define PWM_ACT_CYCLE 0
++#define PWM_ACT_CYCLE_MASK GENMASK(15, PWM_ACT_CYCLE)
++
++#define BIT_CH(bit, chan) ((bit) << (chan))
++#define SET_BITS(shift, mask, reg, val) \
++ (((reg) & ~mask) | (val << (shift)))
++
++#define PWM_OSC_CLK 24000000
++#define PWM_PRESCALER_MAX 256
++#define PWM_CLK_DIV_M__MAX 9
++#define PWM_ENTIRE_CYCLE_MAX 65536
++
++struct sun8i_pwm_data {
++ unsigned int npwm;
++};
++
++struct sun8i_pwm_chip {
++ struct pwm_chip chip;
++ struct clk *clk;
++ struct reset_control *rst_clk;
++ void __iomem *base;
++ const struct sun8i_pwm_data *data;
++};
++
++static inline struct sun8i_pwm_chip *to_sun8i_pwm_chip(struct pwm_chip *chip)
++{
++ return container_of(chip, struct sun8i_pwm_chip, chip);
++}
++
++static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *chip,
++ unsigned long offset)
++{
++ return readl(chip->base + offset);
++}
++
++static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *chip,
++ u32 val, unsigned long offset)
++{
++ writel(val, chip->base + offset);
++}
++
++static void sun8i_pwm_get_state(struct pwm_chip *chip,
++ struct pwm_device *pwm,
++ struct pwm_state *state)
++{
++ struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip);
++ u64 clk_rate;
++ u32 tmp, entire_cycles, active_cycles;
++ unsigned int prescaler, div_m;
++
++ tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ if (tmp & PWM_CLK_APB_SCR)
++ clk_rate = clk_get_rate(pc->clk);
++ else
++ clk_rate = PWM_OSC_CLK;
++
++ tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ div_m = 0x1 << (tmp & PWM_DIV_M_MASK);
++
++ tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm));
++ prescaler = (tmp & PWM_PRESCAL_K_MASK) + 1;
++
++ tmp = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm));
++ entire_cycles = (tmp >> PWM_ENTIRE_CYCLE) + 1;
++ active_cycles = (tmp & PWM_ACT_CYCLE_MASK);
++
++ /* (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns. */
++ state->period = DIV_ROUND_CLOSEST_ULL(entire_cycles * NSEC_PER_SEC,
++ clk_rate) * div_m * prescaler;
++ /* duty_ns / period_ns = active_cycles / entire_cycles. */
++ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(active_cycles * state->period,
++ entire_cycles);
++
++ /* parsing polarity */
++ tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm));
++ if (tmp & PWM_ACT_STA)
++ state->polarity = PWM_POLARITY_NORMAL;
++ else
++ state->polarity = PWM_POLARITY_INVERSED;
++
++ /* parsing enabled */
++ tmp = sun8i_pwm_readl(pc, PWM_ENABLE_REG);
++ if (tmp & BIT_CH(PWM_EN, pwm->hwpwm))
++ state->enabled = true;
++ else
++ state->enabled = false;
++
++ dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld polarity=%s enabled=%s.\n",
++ state->duty_cycle, state->period,
++ state->polarity ? "inversed":"normal",
++ state->enabled ? "true":"false");
++}
++
++static void sun8i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
++ enum pwm_polarity polarity)
++{
++ struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip);
++ u32 temp;
++
++ temp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm));
++
++ if (polarity == PWM_POLARITY_NORMAL)
++ temp |= PWM_ACT_STA;
++ else
++ temp &= ~PWM_ACT_STA;
++
++ sun8i_pwm_writel(pc, temp, PWM_CTL_REG(pwm->hwpwm));
++}
++
++static int sun8i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
++ const struct pwm_state *state)
++{
++ struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip);
++ unsigned long long c;
++ unsigned long entire_cycles, active_cycles;
++ unsigned int div_m, prescaler;
++ u64 duty_ns = state->duty_cycle, period_ns = state->period;
++ u32 config;
++ int ret = 0;
++
++ if (period_ns > 334) {
++ /* if freq < 3M, then select 24M clock */
++ c = PWM_OSC_CLK;
++ config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ config &= ~PWM_CLK_APB_SCR;
++ sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ } else {
++ /* if freq > 3M, then select APB as clock */
++ c = clk_get_rate(pc->clk);
++ config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ config |= PWM_CLK_APB_SCR;
++ sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ }
++
++ dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld c =%llu.\n",
++ duty_ns, period_ns, c);
++
++ /*
++ * (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns.
++ * So, entire_cycles = clk * period_ns / NSEC_PER_SEC / div_m / prescaler.
++ */
++ c = c * period_ns;
++ c = DIV_ROUND_CLOSEST_ULL(c, NSEC_PER_SEC);
++ for (div_m = 0; div_m < PWM_CLK_DIV_M__MAX; div_m++) {
++ for (prescaler = 0; prescaler < PWM_PRESCALER_MAX; prescaler++) {
++ /*
++ * actual prescaler = prescaler(reg value) + 1.
++ * actual div_m = 0x1 << div_m(reg value).
++ */
++ entire_cycles = ((unsigned long)c >> div_m)/(prescaler + 1);
++ if (entire_cycles <= PWM_ENTIRE_CYCLE_MAX)
++ goto calc_end;
++ }
++ }
++ ret = -EINVAL;
++ goto exit;
++
++calc_end:
++ /*
++ * duty_ns / period_ns = active_cycles / entire_cycles.
++ * So, active_cycles = entire_cycles * duty_ns / period_ns.
++ */
++ c = (unsigned long long)entire_cycles * duty_ns;
++ c = DIV_ROUND_CLOSEST_ULL(c, period_ns);
++ active_cycles = c;
++ if (entire_cycles == 0)
++ entire_cycles++;
++
++ /* config clk div_m*/
++ config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++ config = SET_BITS(PWM_DIV_M, PWM_DIV_M_MASK, config, div_m);
++ sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm));
++
++ /* config prescaler */
++ config = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm));
++ config = SET_BITS(PWM_PRESCAL_K, PWM_PRESCAL_K_MASK, config, prescaler);
++ sun8i_pwm_writel(pc, config, PWM_CTL_REG(pwm->hwpwm));
++
++ /* config active and period cycles */
++ config = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm));
++ config = SET_BITS(PWM_ACT_CYCLE, PWM_ACT_CYCLE_MASK, config, active_cycles);
++ config = SET_BITS(PWM_ENTIRE_CYCLE, PWM_ENTIRE_CYCLE_MASK,
++ config, (entire_cycles - 1));
++ sun8i_pwm_writel(pc, config, PWM_PERIOD_REG(pwm->hwpwm));
++
++ dev_dbg(chip->dev, "active_cycles=%lu entire_cycles=%lu prescaler=%u div_m=%u\n",
++ active_cycles, entire_cycles, prescaler, div_m);
++
++exit:
++ return ret;
++}
++
++static void sun8i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
++ bool enable)
++{
++ struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip);
++ u32 clk, pwm_en;
++
++ clk = sun8i_pwm_readl(pc, PWM_CLK_REG);
++ pwm_en = sun8i_pwm_readl(pc, PWM_ENABLE_REG);
++
++ if (enable) {
++ clk |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
++ sun8i_pwm_writel(pc, clk, PWM_CLK_REG);
++
++ pwm_en |= BIT_CH(PWM_EN, pwm->hwpwm);
++ sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG);
++ } else {
++ pwm_en &= ~BIT_CH(PWM_EN, pwm->hwpwm);
++ sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG);
++
++ clk &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
++ sun8i_pwm_writel(pc, clk, PWM_CLK_REG);
++ }
++}
++
++static int sun8i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++ const struct pwm_state *state)
++{
++ struct pwm_state curstate;
++ int ret;
++
++ pwm_get_state(pwm, &curstate);
++
++ ret = sun8i_pwm_config(chip, pwm, state);
++
++ if (state->polarity != curstate.polarity)
++ sun8i_pwm_set_polarity(chip, pwm, state->polarity);
++
++ if (state->enabled != curstate.enabled)
++ sun8i_pwm_enable(chip, pwm, state->enabled);
++
++ return ret;
++}
++
++static const struct pwm_ops sun8i_pwm_ops = {
++ .get_state = sun8i_pwm_get_state,
++ .apply = sun8i_pwm_apply,
++ .owner = THIS_MODULE,
++};
++
++static const struct sun8i_pwm_data sun8i_pwm_data_c9 = {
++ .npwm = 9,
++};
++
++static const struct sun8i_pwm_data sun50i_pwm_data_c16 = {
++ .npwm = 16,
++};
++
++static const struct of_device_id sun8i_pwm_dt_ids[] = {
++ {
++ .compatible = "allwinner,sun8i-v536-pwm",
++ .data = &sun8i_pwm_data_c9,
++ }, {
++ .compatible = "allwinner,sun50i-r818-pwm",
++ .data = &sun50i_pwm_data_c16,
++ }, {
++ /* sentinel */
++ },
++};
++MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids);
++
++static int sun8i_pwm_probe(struct platform_device *pdev)
++{
++ struct sun8i_pwm_chip *pc;
++ int ret;
++
++ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
++ if (!pc)
++ return dev_err_probe(&pdev->dev, -ENOMEM,
++ "memory allocation failed\n");
++
++ pc->data = of_device_get_match_data(&pdev->dev);
++ if (!pc->data)
++ return dev_err_probe(&pdev->dev, -ENODEV,
++ "can't get match data\n");
++
++ pc->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(pc->base))
++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->base),
++ "can't remap pwm resource\n");
++
++ pc->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(pc->clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
++ "get clock failed\n");
++
++ pc->rst_clk = devm_reset_control_get_exclusive(&pdev->dev, NULL);
++ if (IS_ERR(pc->rst_clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->rst_clk),
++ "get reset failed\n");
++
++ /* Deassert reset */
++ ret = reset_control_deassert(pc->rst_clk);
++ if (ret < 0)
++ return dev_err_probe(&pdev->dev, ret,
++ "cannot deassert reset control\n");
++
++ ret = clk_prepare_enable(pc->clk);
++ if (ret) {
++ dev_err(&pdev->dev, "cannot prepare and enable clk %pe\n",
++ ERR_PTR(ret));
++ goto err_clk;
++ }
++
++ pc->chip.dev = &pdev->dev;
++ pc->chip.ops = &sun8i_pwm_ops;
++ pc->chip.npwm = pc->data->npwm;
++ pc->chip.of_xlate = of_pwm_xlate_with_flags;
++ pc->chip.base = -1;
++ pc->chip.of_pwm_n_cells = 3;
++
++ ret = pwmchip_add(&pc->chip);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
++ goto err_pwm_add;
++ }
++
++ platform_set_drvdata(pdev, pc);
++
++ return 0;
++
++err_pwm_add:
++ clk_disable_unprepare(pc->clk);
++err_clk:
++ reset_control_assert(pc->rst_clk);
++
++ return ret;
++}
++
++static int sun8i_pwm_remove(struct platform_device *pdev)
++{
++ struct sun8i_pwm_chip *pc = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = pwmchip_remove(&pc->chip);
++ if (ret)
++ return ret;
++
++ clk_disable_unprepare(pc->clk);
++ reset_control_assert(pc->rst_clk);
++
++ return 0;
++}
++
++static struct platform_driver sun8i_pwm_driver = {
++ .driver = {
++ .name = "sun8i-pwm-v536",
++ .of_match_table = sun8i_pwm_dt_ids,
++ },
++ .probe = sun8i_pwm_probe,
++ .remove = sun8i_pwm_remove,
++};
++module_platform_driver(sun8i_pwm_driver);
++
++MODULE_ALIAS("platform:sun8i-v536-pwm");
++MODULE_AUTHOR("Ban Tao <fengzheng923@gmail.com>");
++MODULE_DESCRIPTION("Allwinner sun8i-v536 PWM driver");
++MODULE_LICENSE("GPL v2");
+--
+2.20.1
+
--- /dev/null
+From 526751701c01331fb898334df5f491a2b10bc9b1 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 11:05:20 -0500
+Subject: [PATCH 095/124] squash? pwm: sunxi: Add Allwinner SoC PWM controller
+ driver
+
+---
+ drivers/pwm/Kconfig | 4 ++--
+ drivers/pwm/pwm-sun8i-v536.c | 6 +-----
+ 2 files changed, 3 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index e31884525081..f956bb0a65bf 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -571,11 +571,11 @@ config PWM_SUN4I
+ will be called pwm-sun4i.
+
+ config PWM_SUN8I_V536
+- tristate "Allwinner SUN8I_V536 PWM support"
++ tristate "Allwinner SUN8I V536 enhanced PWM support"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on HAS_IOMEM && COMMON_CLK
+ help
+- Enhanced PWM framework driver for Allwinner R818, A133, R329,
++ Enhanced PWM framework driver for Allwinner A133, D1, R329, R818,
+ V536 and V833 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c
+index 52101df6bd41..ab302c6cb4ee 100644
+--- a/drivers/pwm/pwm-sun8i-v536.c
++++ b/drivers/pwm/pwm-sun8i-v536.c
+@@ -373,12 +373,8 @@ static int sun8i_pwm_probe(struct platform_device *pdev)
+ static int sun8i_pwm_remove(struct platform_device *pdev)
+ {
+ struct sun8i_pwm_chip *pc = platform_get_drvdata(pdev);
+- int ret;
+-
+- ret = pwmchip_remove(&pc->chip);
+- if (ret)
+- return ret;
+
++ pwmchip_remove(&pc->chip);
+ clk_disable_unprepare(pc->clk);
+ reset_control_assert(pc->rst_clk);
+
+--
+2.20.1
+
--- /dev/null
+From 75fe896da6096919effe9d00cfa21dbb0c6b367e Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:56:25 -0500
+Subject: [PATCH 096/124] pwm: sun8i-v536: Add support for the Allwinner D1
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/pwm/pwm-sun8i-v536.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c
+index ab302c6cb4ee..732f15c88b67 100644
+--- a/drivers/pwm/pwm-sun8i-v536.c
++++ b/drivers/pwm/pwm-sun8i-v536.c
+@@ -285,6 +285,10 @@ static const struct sun8i_pwm_data sun8i_pwm_data_c9 = {
+ .npwm = 9,
+ };
+
++static const struct sun8i_pwm_data sun20i_pwm_data_c8 = {
++ .npwm = 8,
++};
++
+ static const struct sun8i_pwm_data sun50i_pwm_data_c16 = {
+ .npwm = 16,
+ };
+@@ -293,6 +297,9 @@ static const struct of_device_id sun8i_pwm_dt_ids[] = {
+ {
+ .compatible = "allwinner,sun8i-v536-pwm",
+ .data = &sun8i_pwm_data_c9,
++ }, {
++ .compatible = "allwinner,sun20i-d1-pwm",
++ .data = &sun20i_pwm_data_c8,
+ }, {
+ .compatible = "allwinner,sun50i-r818-pwm",
+ .data = &sun50i_pwm_data_c16,
+--
+2.20.1
+
--- /dev/null
+From 52c9bd03a8e25ee0de97ade64ebfd8f0b40eaa2d Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 11 Jul 2021 10:54:55 -0500
+Subject: [PATCH 097/124] remoteproc: sun8i-dsp: Add a driver for the DSPs in
+ sunxi SoCs
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/remoteproc/Kconfig | 7 +
+ drivers/remoteproc/Makefile | 1 +
+ drivers/remoteproc/sun8i_dsp_rproc.c | 269 +++++++++++++++++++++++++++
+ 3 files changed, 277 insertions(+)
+ create mode 100644 drivers/remoteproc/sun8i_dsp_rproc.c
+
+diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
+index 9a6eedc3994a..3dbdf61be7ee 100644
+--- a/drivers/remoteproc/Kconfig
++++ b/drivers/remoteproc/Kconfig
+@@ -289,6 +289,13 @@ config STM32_RPROC
+
+ This can be either built-in or a loadable module.
+
++config SUN8I_DSP_REMOTEPROC
++ tristate "Allwinner sun8i DSP remoteproc support"
++ depends on ARCH_SUNXI || COMPILE_TEST
++ help
++ Say y here to support the DSP in some sun8i/sun20i/sun50i SoCs
++ via the remote processor framework.
++
+ config TI_K3_DSP_REMOTEPROC
+ tristate "TI K3 DSP remoteproc support"
+ depends on ARCH_K3
+diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
+index bb26c9e4ef9c..e2daa32065a4 100644
+--- a/drivers/remoteproc/Makefile
++++ b/drivers/remoteproc/Makefile
+@@ -33,5 +33,6 @@ qcom_wcnss_pil-y += qcom_wcnss_iris.o
+ obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o
+ obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o
+ obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o
++obj-$(CONFIG_SUN8I_DSP_REMOTEPROC) += sun8i_dsp_rproc.o
+ obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o
+ obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o
+diff --git a/drivers/remoteproc/sun8i_dsp_rproc.c b/drivers/remoteproc/sun8i_dsp_rproc.c
+new file mode 100644
+index 000000000000..0d7682a4788e
+--- /dev/null
++++ b/drivers/remoteproc/sun8i_dsp_rproc.c
+@@ -0,0 +1,269 @@
++// SPDX-License-Identifier: GPL-2.0
++//
++// Copyright (c) 2021 Samuel Holland <samuel@sholland.org>
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/mailbox_client.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/remoteproc.h>
++#include <linux/reset.h>
++#include <linux/soc/sunxi/sunxi_sram.h>
++
++#include "remoteproc_internal.h"
++
++#define SUN8I_DSP_RESET_VEC_REG 0x0000
++#define SUN8I_DSP_CTRL_REG0 0x0004
++#define SUN8I_DSP_CTRL_REG0_RUN_STALL BIT(0)
++#define SUN8I_DSP_CTRL_REG0_RESET_VEC_SEL BIT(1)
++#define SUN8I_DSP_CTRL_REG0_DSP_CLKEN BIT(2)
++#define SUN8I_DSP_CTRL_REG1 0x0008
++#define SUN8I_DSP_PRID_REG 0x000c
++#define SUN8I_DSP_PRID_REG_PRID_MASK (0xff << 0)
++#define SUN8I_DSP_STAT_REG 0x0010
++#define SUN8I_DSP_STAT_REG_PFAULT_INFO_VALID BIT(0)
++#define SUN8I_DSP_STAT_REG_PFAULT_ERROR BIT(1)
++#define SUN8I_DSP_STAT_REG_DOUBLE_EXCE_ERROR BIT(2)
++#define SUN8I_DSP_STAT_REG_XOCD_MODE BIT(3)
++#define SUN8I_DSP_STAT_REG_DEBUG_MODE BIT(4)
++#define SUN8I_DSP_STAT_REG_PWAIT_MODE BIT(5)
++#define SUN8I_DSP_STAT_REG_IRAM0_LOAD_STORE BIT(6)
++#define SUN8I_DSP_BIST_CTRL_REG 0x0014
++#define SUN8I_DSP_BIST_CTRL_REG_EN BIT(0)
++#define SUN8I_DSP_BIST_CTRL_REG_WDATA_PAT_MASK (0x7 << 1)
++#define SUN8I_DSP_BIST_CTRL_REG_ADDR_MODE_SEL BIT(4)
++#define SUN8I_DSP_BIST_CTRL_REG_REG_SEL_MASK (0x7 << 5)
++#define SUN8I_DSP_BIST_CTRL_REG_BUSY BIT(8)
++#define SUN8I_DSP_BIST_CTRL_REG_STOP BIT(9)
++#define SUN8I_DSP_BIST_CTRL_REG_ERR_CYC_MASK (0x3 << 10)
++#define SUN8I_DSP_BIST_CTRL_REG_ERR_PAT_MASK (0x7 << 12)
++#define SUN8I_DSP_BIST_CTRL_REG_ERR_STA BIT(15)
++#define SUN8I_DSP_BIST_CTRL_REG_SELECT_MASK (0xf << 16)
++#define SUN8I_DSP_JTRST_REG 0x001c
++#define SUN8I_DSP_VER_REG 0x0020
++#define SUN8I_DSP_VER_REG_MINOR_VER_MASK (0x1f << 0)
++#define SUN8I_DSP_VER_REG_MAJOR_VER_MASK (0x1f << 16)
++
++#define SUN8I_DSP_CLK_FREQ 400000000
++
++struct sun8i_dsp_rproc {
++ void __iomem *cfg_base;
++ struct clk *cfg_clk;
++ struct reset_control *cfg_reset;
++ struct reset_control *dbg_reset;
++ struct clk *dsp_clk;
++ struct reset_control *dsp_reset;
++ struct mbox_client client;
++ struct mbox_chan *rx_chan;
++ struct mbox_chan *tx_chan;
++};
++
++static int sun8i_dsp_rproc_start(struct rproc *rproc)
++{
++ struct sun8i_dsp_rproc *dsp = rproc->priv;
++ int ret;
++ u32 val;
++
++ ret = sunxi_sram_claim(rproc->dev.parent);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(dsp->cfg_clk);
++ if (ret)
++ goto err_release_sram;
++
++ ret = reset_control_deassert(dsp->cfg_reset);
++ if (ret)
++ goto err_gate_cfg;
++
++ ret = reset_control_deassert(dsp->dbg_reset);
++ if (ret)
++ goto err_reset_cfg;
++
++ writel(rproc->bootaddr, dsp->cfg_base + SUN8I_DSP_RESET_VEC_REG);
++
++ val = readl(dsp->cfg_base + SUN8I_DSP_CTRL_REG0);
++ val |= SUN8I_DSP_CTRL_REG0_RESET_VEC_SEL |
++ SUN8I_DSP_CTRL_REG0_RUN_STALL;
++ writel(val, dsp->cfg_base + SUN8I_DSP_CTRL_REG0);
++
++ ret = clk_prepare_enable(dsp->dsp_clk);
++ if (ret)
++ goto err_reset_dbg;
++
++ ret = reset_control_deassert(dsp->dsp_reset);
++ if (ret)
++ goto err_gate_dsp;
++
++ val &= ~SUN8I_DSP_CTRL_REG0_RUN_STALL;
++ writel(val, dsp->cfg_base + SUN8I_DSP_CTRL_REG0);
++
++ return 0;
++
++err_gate_dsp:
++ clk_disable_unprepare(dsp->dsp_clk);
++err_reset_dbg:
++ reset_control_assert(dsp->dbg_reset);
++err_reset_cfg:
++ reset_control_assert(dsp->cfg_reset);
++err_gate_cfg:
++ clk_disable_unprepare(dsp->cfg_clk);
++err_release_sram:
++ sunxi_sram_release(rproc->dev.parent);
++
++ return ret;
++}
++
++static int sun8i_dsp_rproc_stop(struct rproc *rproc)
++{
++ struct sun8i_dsp_rproc *dsp = rproc->priv;
++
++ reset_control_assert(dsp->dsp_reset);
++ clk_disable_unprepare(dsp->dsp_clk);
++ reset_control_assert(dsp->dbg_reset);
++ reset_control_assert(dsp->cfg_reset);
++ clk_disable_unprepare(dsp->cfg_clk);
++ sunxi_sram_release(rproc->dev.parent);
++
++ return 0;
++}
++
++static void sun8i_dsp_rproc_kick(struct rproc *rproc, int vqid)
++{
++ struct sun8i_dsp_rproc *dsp = rproc->priv;
++ long msg = vqid;
++ int ret;
++
++ ret = mbox_send_message(dsp->tx_chan, (void *)msg);
++ if (ret)
++ dev_warn(&rproc->dev, "Failed to kick: %d\n", ret);
++}
++
++static const struct rproc_ops sun8i_dsp_rproc_ops = {
++ .start = sun8i_dsp_rproc_start,
++ .stop = sun8i_dsp_rproc_stop,
++ .kick = sun8i_dsp_rproc_kick,
++};
++
++static void sun8i_dsp_rproc_mbox_rx_callback(struct mbox_client *client, void *msg)
++{
++ struct rproc *rproc = dev_get_drvdata(client->dev);
++
++ rproc_vq_interrupt(rproc, (long)msg);
++}
++
++static void sun8i_dsp_rproc_mbox_free(void *data)
++{
++ struct sun8i_dsp_rproc *dsp = data;
++
++ if (!IS_ERR_OR_NULL(dsp->tx_chan))
++ mbox_free_channel(dsp->tx_chan);
++ if (!IS_ERR_OR_NULL(dsp->rx_chan))
++ mbox_free_channel(dsp->rx_chan);
++}
++
++static int sun8i_dsp_rproc_probe(struct platform_device *pdev)
++{
++ struct device_node *np = pdev->dev.of_node;
++ struct device *dev = &pdev->dev;
++ struct sun8i_dsp_rproc *dsp;
++ const char *firmware_name;
++ struct rproc *rproc;
++ int i, ret;
++ u32 freq;
++
++ firmware_name = NULL;
++ of_property_read_string(np, "firmware-name", &firmware_name);
++ rproc = devm_rproc_alloc(dev, dev_name(dev), &sun8i_dsp_rproc_ops,
++ firmware_name, sizeof(struct sun8i_dsp_rproc));
++ if (!rproc)
++ return -ENOMEM;
++
++ dev_set_drvdata(dev, rproc);
++ dsp = rproc->priv;
++
++ i = of_property_match_string(np, "reg-names", "cfg");
++ if (i < 0)
++ return -EINVAL;
++
++ dsp->cfg_base = devm_platform_ioremap_resource(pdev, i);
++ if (IS_ERR(dsp->cfg_base))
++ return dev_err_probe(dev, PTR_ERR(dsp->cfg_base),
++ "Failed to map cfg\n");
++
++ dsp->cfg_clk = devm_clk_get(dev, "cfg");
++ if (IS_ERR(dsp->cfg_clk))
++ return dev_err_probe(dev, PTR_ERR(dsp->cfg_clk),
++ "Failed to get %s clock\n", "cfg");
++
++ dsp->cfg_reset = devm_reset_control_get_exclusive(dev, "cfg");
++ if (IS_ERR(dsp->cfg_reset))
++ return dev_err_probe(dev, PTR_ERR(dsp->cfg_reset),
++ "Failed to get %s reset\n", "cfg");
++
++ dsp->cfg_reset = devm_reset_control_get_exclusive(dev, "dbg");
++ if (IS_ERR(dsp->cfg_reset))
++ return dev_err_probe(dev, PTR_ERR(dsp->cfg_reset),
++ "Failed to get %s reset\n", "dbg");
++
++ dsp->dsp_clk = devm_clk_get(dev, "dsp");
++ if (IS_ERR(dsp->dsp_clk))
++ return dev_err_probe(dev, PTR_ERR(dsp->dsp_clk),
++ "Failed to get %s clock\n", "dsp");
++
++ freq = SUN8I_DSP_CLK_FREQ;
++ of_property_read_u32(np, "clock-frequency", &freq);
++ ret = clk_set_rate(dsp->dsp_clk, freq);
++ if (ret)
++ return dev_err_probe(dev, ret,
++ "Failed to set clock frequency\n");
++
++ dsp->dsp_reset = devm_reset_control_get_exclusive(dev, "dsp");
++ if (IS_ERR(dsp->dsp_reset))
++ return dev_err_probe(dev, PTR_ERR(dsp->dsp_reset),
++ "Failed to get %s reset\n", "dsp");
++
++ dsp->client.dev = dev;
++ dsp->client.rx_callback = sun8i_dsp_rproc_mbox_rx_callback;
++
++ ret = devm_add_action(dev, sun8i_dsp_rproc_mbox_free, dsp);
++ if (ret)
++ return ret;
++
++ dsp->rx_chan = mbox_request_channel_byname(&dsp->client, "rx");
++ if (IS_ERR(dsp->rx_chan))
++ return dev_err_probe(dev, PTR_ERR(dsp->rx_chan),
++ "Failed to request RX channel\n");
++
++ dsp->tx_chan = mbox_request_channel_byname(&dsp->client, "tx");
++ if (IS_ERR(dsp->tx_chan))
++ return dev_err_probe(dev, PTR_ERR(dsp->tx_chan),
++ "Failed to request TX channel\n");
++
++ ret = devm_rproc_add(dev, rproc);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to register rproc\n");
++
++ return 0;
++}
++
++static const struct of_device_id sun8i_dsp_rproc_of_match[] = {
++ { .compatible = "allwinner,sun20i-d1-dsp" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, sun8i_dsp_rproc_of_match);
++
++static struct platform_driver sun8i_dsp_rproc_driver = {
++ .probe = sun8i_dsp_rproc_probe,
++ .driver = {
++ .name = "sun8i-dsp-rproc",
++ .of_match_table = sun8i_dsp_rproc_of_match,
++ },
++};
++module_platform_driver(sun8i_dsp_rproc_driver);
++
++MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
++MODULE_DESCRIPTION("Allwinner sun8i DSP remoteproc driver");
++MODULE_LICENSE("GPL");
+--
+2.20.1
+
--- /dev/null
+From 6d3665651113e11ec7f8e6e24f471376064d23d2 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Fri, 26 Feb 2021 10:27:51 +0000
+Subject: [PATCH 098/124] rtc: sun6i: Add support for linear day storage
+
+Newer versions of the Allwinner RTC, as for instance found in the H616
+SoC, no longer store a broken-down day/month/year representation in the
+RTC_DAY_REG, but just a linear day number.
+The user manual does not give any indication about the expected epoch
+time of this day count, but the BSP kernel uses the UNIX epoch, which
+allows easy support due to existing conversion functions in the kernel.
+
+Allow tagging a compatible string with a flag, and use that to mark
+those new RTCs. Then convert between a UNIX day number (converted into
+seconds) and the broken-down day representation using mktime64() and
+time64_to_tm() in the set_time/get_time functions.
+
+That enables support for the RTC in those new chips.
+
+Reviewed-by: Andre Przywara <andre.przywara@arm.com>
+---
+ drivers/rtc/rtc-sun6i.c | 58 +++++++++++++++++++++++++++++------------
+ 1 file changed, 41 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index 4358cde143d4..1d2a72438bcf 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -134,12 +134,15 @@ struct sun6i_rtc_clk_data {
+ unsigned int has_auto_swt : 1;
+ };
+
++#define RTC_LINEAR_DAY BIT(0)
++
+ struct sun6i_rtc_dev {
+ struct rtc_device *rtc;
+ const struct sun6i_rtc_clk_data *data;
+ void __iomem *base;
+ int irq;
+ unsigned long alarm;
++ unsigned long flags;
+
+ struct clk_hw hw;
+ struct clk_hw *int_osc;
+@@ -455,17 +458,30 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+ rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time);
+ rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
+
+- rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
+- rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date);
+- rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
+-
+- rtc_tm->tm_mon -= 1;
+-
+- /*
+- * switch from (data_year->min)-relative offset to
+- * a (1900)-relative one
+- */
+- rtc_tm->tm_year += SUN6I_YEAR_OFF;
++ if (chip->flags & RTC_LINEAR_DAY) {
++ struct tm tm;
++
++ /*
++ * Newer chips store a linear day number, the manual
++ * does not mandate any epoch base. The BSP driver uses
++ * the UNIX epoch, let's just copy that, as it's the
++ * easiest anyway.
++ */
++ time64_to_tm((date & 0xffff) * 3600ULL * 24, 0, &tm);
++ rtc_tm->tm_mday = tm.tm_mday;
++ rtc_tm->tm_mon = tm.tm_mon;
++ rtc_tm->tm_year = tm.tm_year;
++ } else {
++ rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
++ rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date) - 1;
++ rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
++
++ /*
++ * switch from (data_year->min)-relative offset to
++ * a (1900)-relative one
++ */
++ rtc_tm->tm_year += SUN6I_YEAR_OFF;
++ }
+
+ return 0;
+ }
+@@ -555,15 +571,21 @@ static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+ u32 date = 0;
+ u32 time = 0;
+
+- rtc_tm->tm_year -= SUN6I_YEAR_OFF;
+ rtc_tm->tm_mon += 1;
+
+- date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+- SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
+- SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
++ if (chip->flags & RTC_LINEAR_DAY) {
++ date = mktime64(rtc_tm->tm_year + 1900, rtc_tm->tm_mon,
++ rtc_tm->tm_mday, 0, 0, 0) / (3600ULL * 24);
++ } else {
++ rtc_tm->tm_year -= SUN6I_YEAR_OFF;
++
++ date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
++ SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
++ SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
+
+- if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
+- date |= SUN6I_LEAP_SET_VALUE(1);
++ if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN))
++ date |= SUN6I_LEAP_SET_VALUE(1);
++ }
+
+ time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
+ SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
+@@ -677,6 +699,8 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, chip);
+
++ chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev);
++
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0)
+ return chip->irq;
+--
+2.20.1
+
--- /dev/null
+From a502296b25fd6244b196f49409aecdf5050304e5 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Wed, 21 Apr 2021 12:46:43 +0100
+Subject: [PATCH 099/124] rtc: sun6i: Add Allwinner H616 support
+
+The H616 RTC changes its day storage to the newly introduced linear day
+scheme, so pair the new compatible string with this feature flag.
+
+Signed-off-by: Andre Przywara <andre.przywara@arm.com>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/rtc/rtc-sun6i.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index 1d2a72438bcf..e067082df356 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -772,6 +772,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-v3-rtc" },
+ { .compatible = "allwinner,sun50i-h5-rtc" },
+ { .compatible = "allwinner,sun50i-h6-rtc" },
++ { .compatible = "allwinner,sun50i-h616-rtc",
++ .data = (void *)RTC_LINEAR_DAY },
+ { /* sentinel */ },
+ };
+ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
+--
+2.20.1
+
--- /dev/null
+From 121b5566f4642b44b396a3221b6a3cfdfdf46f7d Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 21:51:03 -0500
+Subject: [PATCH 100/124] rtc: sun6i: Add Allwinner D1 support
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/rtc/rtc-sun6i.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
+index e067082df356..c966dea4394e 100644
+--- a/drivers/rtc/rtc-sun6i.c
++++ b/drivers/rtc/rtc-sun6i.c
+@@ -774,6 +774,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
+ { .compatible = "allwinner,sun50i-h6-rtc" },
+ { .compatible = "allwinner,sun50i-h616-rtc",
+ .data = (void *)RTC_LINEAR_DAY },
++ { .compatible = "allwinner,sun20i-d1-rtc",
++ .data = (void *)RTC_LINEAR_DAY },
+ { /* sentinel */ },
+ };
+ MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
+--
+2.20.1
+
--- /dev/null
+From 233da064f08769f760c7ceaa708791ec7687f8c8 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 19 Jan 2020 15:52:21 -0600
+Subject: [PATCH 101/124] soc: sunxi: sram: Actually marked claimed regions as
+ claimed
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index a8f3876963a0..f3d3f9259df9 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -254,6 +254,7 @@ int sunxi_sram_claim(struct device *dev)
+ writel(val | ((device << sram_data->offset) & mask),
+ base + sram_data->reg);
+
++ sram_desc->claimed = true;
+ spin_unlock(&sram_lock);
+
+ return 0;
+--
+2.20.1
+
--- /dev/null
+From 08c2a7e1ffff8ff09f7159e8eeab246a6ee1220b Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 11 Jul 2021 10:56:17 -0500
+Subject: [PATCH 102/124] soc: sunxi: sram: Map SRAM back to CPU on release
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index f3d3f9259df9..bb8d8d44a621 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -265,17 +265,26 @@ int sunxi_sram_release(struct device *dev)
+ {
+ const struct sunxi_sram_data *sram_data;
+ struct sunxi_sram_desc *sram_desc;
++ unsigned int device;
++ u32 val, mask;
+
+ if (!dev || !dev->of_node)
+ return -EINVAL;
+
+- sram_data = sunxi_sram_of_parse(dev->of_node, NULL);
++ sram_data = sunxi_sram_of_parse(dev->of_node, &device);
+ if (IS_ERR(sram_data))
+ return -EINVAL;
+
+ sram_desc = to_sram_desc(sram_data);
+
+ spin_lock(&sram_lock);
++ mask = GENMASK(sram_data->offset + sram_data->width - 1,
++ sram_data->offset);
++ val = readl(base + sram_data->reg);
++ val &= ~mask;
++ writel(val | ((~device << sram_data->offset) & mask),
++ base + sram_data->reg);
++
+ sram_desc->claimed = false;
+ spin_unlock(&sram_lock);
+
+--
+2.20.1
+
--- /dev/null
+From 16e90c44a87be00a2727d745f8dfe1b29dd93300 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:47:08 -0500
+Subject: [PATCH 103/124] soc: sunxi: sram: Fix debugfs file leak
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index bb8d8d44a621..6fb8c6de8c91 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -106,6 +106,7 @@ static struct device *sram_dev;
+ static LIST_HEAD(claimed_sram);
+ static DEFINE_SPINLOCK(sram_lock);
+ static void __iomem *base;
++static struct dentry *debugfs_dir;
+
+ static int sunxi_sram_show(struct seq_file *s, void *data)
+ {
+@@ -341,7 +342,6 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = {
+
+ static int sunxi_sram_probe(struct platform_device *pdev)
+ {
+- struct dentry *d;
+ struct regmap *emac_clock;
+ const struct sunxi_sramc_variant *variant;
+
+@@ -357,11 +357,6 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+
+ of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+- d = debugfs_create_file("sram", S_IRUGO, NULL, NULL,
+- &sunxi_sram_fops);
+- if (!d)
+- return -ENOMEM;
+-
+ if (variant->num_emac_clocks > 0) {
+ emac_clock = devm_regmap_init_mmio(&pdev->dev, base,
+ &sunxi_sram_emac_clock_regmap);
+@@ -370,6 +365,17 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ return PTR_ERR(emac_clock);
+ }
+
++ debugfs_dir = debugfs_create_dir("sunxi-sram", NULL);
++ debugfs_create_file("sram", S_IRUGO, debugfs_dir, NULL,
++ &sunxi_sram_fops);
++
++ return 0;
++}
++
++static int sunxi_sram_remove(struct platform_device *pdev)
++{
++ debugfs_remove(debugfs_dir);
++
+ return 0;
+ }
+
+@@ -420,6 +426,7 @@ static struct platform_driver sunxi_sram_driver = {
+ .of_match_table = sunxi_sram_dt_match,
+ },
+ .probe = sunxi_sram_probe,
++ .remove = sunxi_sram_remove,
+ };
+ module_platform_driver(sunxi_sram_driver);
+
+--
+2.20.1
+
--- /dev/null
+From acd63d88d3461bacd505de88dc66c5ae2f313de2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:48:49 -0500
+Subject: [PATCH 104/124] soc: sunxi: sram: Use devm_of_platform_populate
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index 6fb8c6de8c91..17b89b5a8e6e 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -344,6 +344,7 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ {
+ struct regmap *emac_clock;
+ const struct sunxi_sramc_variant *variant;
++ int ret;
+
+ sram_dev = &pdev->dev;
+
+@@ -355,8 +356,6 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+- of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+-
+ if (variant->num_emac_clocks > 0) {
+ emac_clock = devm_regmap_init_mmio(&pdev->dev, base,
+ &sunxi_sram_emac_clock_regmap);
+@@ -365,6 +364,10 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ return PTR_ERR(emac_clock);
+ }
+
++ ret = devm_of_platform_populate(&pdev->dev);
++ if (ret)
++ return ret;
++
+ debugfs_dir = debugfs_create_dir("sunxi-sram", NULL);
+ debugfs_create_file("sram", S_IRUGO, debugfs_dir, NULL,
+ &sunxi_sram_fops);
+--
+2.20.1
+
--- /dev/null
+From 16cf89f5a45d680974614ec3847f3609e15de9ab Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:49:10 -0500
+Subject: [PATCH 105/124] soc: sunxi: sram: Add support for D1 LDOs
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 71 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 68 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index 17b89b5a8e6e..a82351220c04 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -18,9 +18,13 @@
+ #include <linux/of_device.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
++#include <linux/regulator/driver.h>
+
+ #include <linux/soc/sunxi/sunxi_sram.h>
+
++#define SUNXI_SRAM_EMAC_CLOCK_REG 0x30
++#define SUNXI_SYS_LDO_CTRL_REG 0x150
++
+ struct sunxi_sram_func {
+ char *func;
+ u8 val;
+@@ -293,7 +297,47 @@ int sunxi_sram_release(struct device *dev)
+ }
+ EXPORT_SYMBOL(sunxi_sram_release);
+
++static const struct regulator_ops sunxi_ldo_ops = {
++ .list_voltage = regulator_list_voltage_linear,
++ .map_voltage = regulator_map_voltage_linear,
++ .set_voltage_sel = regulator_set_voltage_sel_regmap,
++ .get_voltage_sel = regulator_get_voltage_sel_regmap,
++};
++
++static const struct regulator_desc sun20i_d1_ldos[] = {
++ {
++ .name = "ldoa",
++ .supply_name = "ldo-in",
++ .of_match = "ldoa",
++ .regulators_node = "regulators",
++ .ops = &sunxi_ldo_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++ .n_voltages = BIT(5),
++ .min_uV = 1600005, /* nominally 1.8 V */
++ .uV_step = 13333,
++ .vsel_reg = SUNXI_SYS_LDO_CTRL_REG,
++ .vsel_mask = GENMASK(7, 0),
++ },
++ {
++ .name = "ldob",
++ .supply_name = "ldo-in",
++ .of_match = "ldob",
++ .regulators_node = "regulators",
++ .ops = &sunxi_ldo_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++ .n_voltages = BIT(6),
++ .min_uV = 1166675, /* nominally 1.5 V */
++ .uV_step = 13333,
++ .vsel_reg = SUNXI_SYS_LDO_CTRL_REG,
++ .vsel_mask = GENMASK(15, 8),
++ },
++};
++
+ struct sunxi_sramc_variant {
++ const struct regulator_desc *ldos;
++ int num_ldos;
+ int num_emac_clocks;
+ };
+
+@@ -305,6 +349,12 @@ static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = {
+ .num_emac_clocks = 1,
+ };
+
++static const struct sunxi_sramc_variant sun20i_d1_sramc_variant = {
++ .ldos = sun20i_d1_ldos,
++ .num_ldos = ARRAY_SIZE(sun20i_d1_ldos),
++ .num_emac_clocks = 1,
++};
++
+ static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = {
+ .num_emac_clocks = 1,
+ };
+@@ -313,7 +363,6 @@ static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = {
+ .num_emac_clocks = 2,
+ };
+
+-#define SUNXI_SRAM_EMAC_CLOCK_REG 0x30
+ static bool sunxi_sram_regmap_accessible_reg(struct device *dev,
+ unsigned int reg)
+ {
+@@ -321,6 +370,8 @@ static bool sunxi_sram_regmap_accessible_reg(struct device *dev,
+
+ variant = of_device_get_match_data(dev);
+
++ if (reg == SUNXI_SYS_LDO_CTRL_REG)
++ return true;
+ if (reg < SUNXI_SRAM_EMAC_CLOCK_REG)
+ return false;
+ if (reg > SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4)
+@@ -334,7 +385,7 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = {
+ .val_bits = 32,
+ .reg_stride = 4,
+ /* last defined register */
+- .max_register = SUNXI_SRAM_EMAC_CLOCK_REG + 4,
++ .max_register = SUNXI_SYS_LDO_CTRL_REG,
+ /* other devices have no business accessing other registers */
+ .readable_reg = sunxi_sram_regmap_accessible_reg,
+ .writeable_reg = sunxi_sram_regmap_accessible_reg,
+@@ -356,7 +407,7 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+- if (variant->num_emac_clocks > 0) {
++ if (variant->num_ldos || variant->num_emac_clocks) {
+ emac_clock = devm_regmap_init_mmio(&pdev->dev, base,
+ &sunxi_sram_emac_clock_regmap);
+
+@@ -364,6 +415,20 @@ static int sunxi_sram_probe(struct platform_device *pdev)
+ return PTR_ERR(emac_clock);
+ }
+
++ if (variant->num_ldos) {
++ struct regulator_config config = { .dev = &pdev->dev };
++ struct regulator_dev *rdev;
++ int i;
++
++ for (i = 0; i < variant->num_ldos; ++i) {
++ const struct regulator_desc *desc = &variant->ldos[i];
++
++ rdev = devm_regulator_register(&pdev->dev, desc, &config);
++ if (IS_ERR(rdev))
++ return PTR_ERR(rdev);
++ }
++ }
++
+ ret = devm_of_platform_populate(&pdev->dev);
+ if (ret)
+ return ret;
+--
+2.20.1
+
--- /dev/null
+From 8278aabd7e8a85327973e770aafdffb7e767f32e Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:46:04 -0500
+Subject: [PATCH 106/124] soc: sunxi: sram: Fix debugfs for A64 SRAM C
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index a82351220c04..8b5356ef04be 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -82,8 +82,8 @@ static struct sunxi_sram_desc sun4i_a10_sram_d = {
+
+ static struct sunxi_sram_desc sun50i_a64_sram_c = {
+ .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1,
+- SUNXI_SRAM_MAP(0, 1, "cpu"),
+- SUNXI_SRAM_MAP(1, 0, "de2")),
++ SUNXI_SRAM_MAP(1, 0, "cpu"),
++ SUNXI_SRAM_MAP(0, 1, "de2")),
+ };
+
+ static const struct of_device_id sunxi_sram_dt_ids[] = {
+--
+2.20.1
+
--- /dev/null
+From d7dcb35564295be22f4cffb5cab335d6b75ad49d Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:46:19 -0500
+Subject: [PATCH 107/124] soc: sunxi: sram: Add D1 DSP SRAM
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index 8b5356ef04be..49d5ba9f74ba 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -80,6 +80,12 @@ static struct sunxi_sram_desc sun4i_a10_sram_d = {
+ SUNXI_SRAM_MAP(1, 1, "usb-otg")),
+ };
+
++static struct sunxi_sram_desc sun20i_d1_dsp_sram = {
++ .data = SUNXI_SRAM_DATA("DSP", 0x8, 0, 1,
++ SUNXI_SRAM_MAP(1, 0, "cpu"),
++ SUNXI_SRAM_MAP(0, 1, "dsp")),
++};
++
+ static struct sunxi_sram_desc sun50i_a64_sram_c = {
+ .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1,
+ SUNXI_SRAM_MAP(1, 0, "cpu"),
+@@ -99,6 +105,10 @@ static const struct of_device_id sunxi_sram_dt_ids[] = {
+ .compatible = "allwinner,sun4i-a10-sram-d",
+ .data = &sun4i_a10_sram_d.data,
+ },
++ {
++ .compatible = "allwinner,sun20i-d1-dsp-sram",
++ .data = &sun20i_d1_dsp_sram.data,
++ },
+ {
+ .compatible = "allwinner,sun50i-a64-sram-c",
+ .data = &sun50i_a64_sram_c.data,
+--
+2.20.1
+
--- /dev/null
+From a456c9424e9a3b092e2c8ad6e41b6f41c1a89b4f Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:46:45 -0500
+Subject: [PATCH 108/124] soc: sunxi: sram: Add D1 syscon variant
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/soc/sunxi/sunxi_sram.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c
+index 49d5ba9f74ba..3f6118998f4e 100644
+--- a/drivers/soc/sunxi/sunxi_sram.c
++++ b/drivers/soc/sunxi/sunxi_sram.c
+@@ -478,6 +478,10 @@ static const struct of_device_id sunxi_sram_dt_match[] = {
+ .compatible = "allwinner,sun8i-h3-system-control",
+ .data = &sun8i_h3_sramc_variant,
+ },
++ {
++ .compatible = "allwinner,sun20i-d1-system-control",
++ .data = &sun20i_d1_sramc_variant,
++ },
+ {
+ .compatible = "allwinner,sun50i-a64-sram-controller",
+ .data = &sun50i_a64_sramc_variant,
+--
+2.20.1
+
--- /dev/null
+From 548fbde3da6fc700ec040e78a763470f5b844865 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Fri, 16 Jul 2021 21:33:16 -0500
+Subject: [PATCH 109/124] spi: spi-sun6i: Use a struct for quirks
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/spi/spi-sun6i.c | 32 ++++++++++++++++++++++----------
+ 1 file changed, 22 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
+index 23ad052528db..125fd3052db5 100644
+--- a/drivers/spi/spi-sun6i.c
++++ b/drivers/spi/spi-sun6i.c
+@@ -85,7 +85,12 @@
+ #define SUN6I_TXDATA_REG 0x200
+ #define SUN6I_RXDATA_REG 0x300
+
++struct sun6i_spi_quirks {
++ unsigned long fifo_depth;
++};
++
+ struct sun6i_spi {
++ const struct sun6i_spi_quirks *quirks;
+ struct spi_master *master;
+ void __iomem *base_addr;
+ dma_addr_t dma_addr_rx;
+@@ -99,7 +104,6 @@ struct sun6i_spi {
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ int len;
+- unsigned long fifo_depth;
+ };
+
+ static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
+@@ -156,7 +160,7 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
+ u8 byte;
+
+ /* See how much data we can fit */
+- cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
++ cnt = sspi->quirks->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
+
+ len = min((int)cnt, sspi->len);
+
+@@ -289,14 +293,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
+ * the hardcoded value used in old generation of Allwinner
+ * SPI controller. (See spi-sun4i.c)
+ */
+- trig_level = sspi->fifo_depth / 4 * 3;
++ trig_level = sspi->quirks->fifo_depth / 4 * 3;
+ } else {
+ /*
+ * Setup FIFO DMA request trigger level
+ * We choose 1/2 of the full fifo depth, that value will
+ * be used as DMA burst length.
+ */
+- trig_level = sspi->fifo_depth / 2;
++ trig_level = sspi->quirks->fifo_depth / 2;
+
+ if (tfr->tx_buf)
+ reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
+@@ -410,9 +414,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
+ reg = SUN6I_INT_CTL_TC;
+
+ if (!use_dma) {
+- if (rx_len > sspi->fifo_depth)
++ if (rx_len > sspi->quirks->fifo_depth)
+ reg |= SUN6I_INT_CTL_RF_RDY;
+- if (tx_len > sspi->fifo_depth)
++ if (tx_len > sspi->quirks->fifo_depth)
+ reg |= SUN6I_INT_CTL_TF_ERQ;
+ }
+
+@@ -543,7 +547,7 @@ static bool sun6i_spi_can_dma(struct spi_master *master,
+ * the fifo length we can just fill the fifo and wait for a single
+ * irq, so don't bother setting up dma
+ */
+- return xfer->len > sspi->fifo_depth;
++ return xfer->len > sspi->quirks->fifo_depth;
+ }
+
+ static int sun6i_spi_probe(struct platform_device *pdev)
+@@ -582,7 +586,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
+ }
+
+ sspi->master = master;
+- sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev);
++ sspi->quirks = of_device_get_match_data(&pdev->dev);
+
+ master->max_speed_hz = 100 * 1000 * 1000;
+ master->min_speed_hz = 3 * 1000;
+@@ -696,9 +700,17 @@ static int sun6i_spi_remove(struct platform_device *pdev)
+ return 0;
+ }
+
++static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = {
++ .fifo_depth = SUN6I_FIFO_DEPTH,
++};
++
++static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = {
++ .fifo_depth = SUN8I_FIFO_DEPTH,
++};
++
+ static const struct of_device_id sun6i_spi_match[] = {
+- { .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH },
+- { .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH },
++ { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks },
++ { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, sun6i_spi_match);
+--
+2.20.1
+
--- /dev/null
+From da1ec11ed9fe9d7239506b84d06c48e31238e10e Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Fri, 16 Jul 2021 21:46:31 -0500
+Subject: [PATCH 110/124] spi: spi-sun6i: Add Allwinner R329 support
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/spi/spi-sun6i.c | 78 ++++++++++++++++++++++++++---------------
+ 1 file changed, 49 insertions(+), 29 deletions(-)
+
+diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
+index 125fd3052db5..77a9e0646f3f 100644
+--- a/drivers/spi/spi-sun6i.c
++++ b/drivers/spi/spi-sun6i.c
+@@ -30,6 +30,7 @@
+ #define SUN6I_GBL_CTL_REG 0x04
+ #define SUN6I_GBL_CTL_BUS_ENABLE BIT(0)
+ #define SUN6I_GBL_CTL_MASTER BIT(1)
++#define SUN6I_GBL_CTL_SAMPLE_MODE BIT(2)
+ #define SUN6I_GBL_CTL_TP BIT(7)
+ #define SUN6I_GBL_CTL_RST BIT(31)
+
+@@ -87,6 +88,8 @@
+
+ struct sun6i_spi_quirks {
+ unsigned long fifo_depth;
++ bool has_divider : 1;
++ bool has_new_sample_mode : 1;
+ };
+
+ struct sun6i_spi {
+@@ -351,38 +354,44 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
+ sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
+
+ /* Ensure that we have a parent clock fast enough */
+- mclk_rate = clk_get_rate(sspi->mclk);
+- if (mclk_rate < (2 * tfr->speed_hz)) {
+- clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
++ if (sspi->quirks->has_divider) {
+ mclk_rate = clk_get_rate(sspi->mclk);
+- }
++ if (mclk_rate < (2 * tfr->speed_hz)) {
++ clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
++ mclk_rate = clk_get_rate(sspi->mclk);
++ }
+
+- /*
+- * Setup clock divider.
+- *
+- * We have two choices there. Either we can use the clock
+- * divide rate 1, which is calculated thanks to this formula:
+- * SPI_CLK = MOD_CLK / (2 ^ cdr)
+- * Or we can use CDR2, which is calculated with the formula:
+- * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
+- * Wether we use the former or the latter is set through the
+- * DRS bit.
+- *
+- * First try CDR2, and if we can't reach the expected
+- * frequency, fall back to CDR1.
+- */
+- div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
+- div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
+- if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
+- reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
+- tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
++ /*
++ * Setup clock divider.
++ *
++ * We have two choices there. Either we can use the clock
++ * divide rate 1, which is calculated thanks to this formula:
++ * SPI_CLK = MOD_CLK / (2 ^ cdr)
++ * Or we can use CDR2, which is calculated with the formula:
++ * SPI_CLK = MOD_CLK / (2 * (cdr + 1))
++ * Wether we use the former or the latter is set through the
++ * DRS bit.
++ *
++ * First try CDR2, and if we can't reach the expected
++ * frequency, fall back to CDR1.
++ */
++ div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
++ div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
++ if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
++ reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
++ tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
++ } else {
++ div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
++ reg = SUN6I_CLK_CTL_CDR1(div);
++ tfr->effective_speed_hz = mclk_rate / (1 << div);
++ }
++ sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
+ } else {
+- div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
+- reg = SUN6I_CLK_CTL_CDR1(div);
+- tfr->effective_speed_hz = mclk_rate / (1 << div);
++ clk_set_rate(sspi->mclk, tfr->speed_hz);
++ mclk_rate = clk_get_rate(sspi->mclk);
++ tfr->effective_speed_hz = mclk_rate;
+ }
+
+- sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
+ /* Finally enable the bus - doing so before might raise SCK to HIGH */
+ reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
+ reg |= SUN6I_GBL_CTL_BUS_ENABLE;
+@@ -492,6 +501,7 @@ static int sun6i_spi_runtime_resume(struct device *dev)
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct sun6i_spi *sspi = spi_master_get_devdata(master);
+ int ret;
++ u32 reg;
+
+ ret = clk_prepare_enable(sspi->hclk);
+ if (ret) {
+@@ -511,8 +521,10 @@ static int sun6i_spi_runtime_resume(struct device *dev)
+ goto err2;
+ }
+
+- sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG,
+- SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP);
++ reg = SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP;
++ if (sspi->quirks->has_new_sample_mode)
++ reg |= SUN6I_GBL_CTL_SAMPLE_MODE;
++ sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg);
+
+ return 0;
+
+@@ -702,15 +714,23 @@ static int sun6i_spi_remove(struct platform_device *pdev)
+
+ static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = {
+ .fifo_depth = SUN6I_FIFO_DEPTH,
++ .has_divider = true,
+ };
+
+ static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = {
+ .fifo_depth = SUN8I_FIFO_DEPTH,
++ .has_divider = true,
++};
++
++static const struct sun6i_spi_quirks sun50i_r329_spi_quirks = {
++ .fifo_depth = SUN8I_FIFO_DEPTH,
++ .has_new_sample_mode = true,
+ };
+
+ static const struct of_device_id sun6i_spi_match[] = {
+ { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks },
+ { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks },
++ { .compatible = "allwinner,sun50i-r329-spi", .data = &sun50i_r329_spi_quirks },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, sun6i_spi_match);
+--
+2.20.1
+
--- /dev/null
+From f45398824024d393f28667e96ad8c4203add0770 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 17 Jul 2021 11:19:29 -0500
+Subject: [PATCH 111/124] spi: spi-sun6i: Dual/Quad RX Support
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/spi/spi-sun6i.c | 17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
+index 77a9e0646f3f..a80ccf0973a8 100644
+--- a/drivers/spi/spi-sun6i.c
++++ b/drivers/spi/spi-sun6i.c
+@@ -82,6 +82,8 @@
+ #define SUN6I_XMIT_CNT_REG 0x34
+
+ #define SUN6I_BURST_CTL_CNT_REG 0x38
++#define SUN6I_BURST_CTL_CNT_QUAD_EN BIT(29)
++#define SUN6I_BURST_CTL_CNT_DUAL_EN BIT(28)
+
+ #define SUN6I_TXDATA_REG 0x200
+ #define SUN6I_RXDATA_REG 0x300
+@@ -404,7 +406,17 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
+ /* Setup the counters */
+ sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len);
+ sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
+- sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
++
++ reg = tx_len;
++ switch (tfr->rx_nbits) {
++ case SPI_NBITS_QUAD:
++ reg |= SUN6I_BURST_CTL_CNT_QUAD_EN;
++ break;
++ case SPI_NBITS_DUAL:
++ reg |= SUN6I_BURST_CTL_CNT_DUAL_EN;
++ break;
++ }
++ sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, reg);
+
+ if (!use_dma) {
+ /* Fill the TX FIFO */
+@@ -606,7 +618,8 @@ static int sun6i_spi_probe(struct platform_device *pdev)
+ master->set_cs = sun6i_spi_set_cs;
+ master->transfer_one = sun6i_spi_transfer_one;
+ master->num_chipselect = 4;
+- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST
++ | SPI_RX_DUAL | SPI_RX_QUAD;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->dev.of_node = pdev->dev.of_node;
+ master->auto_runtime_pm = true;
+--
+2.20.1
+
--- /dev/null
+From 3b46883dc7c1f51aeca6b189121eef5f43560e82 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 20:25:05 -0500
+Subject: [PATCH 112/124] thermal/of: Remove duplicate null check
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/thermal/thermal_of.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
+index 6379f26a335f..3d1527ba003f 100644
+--- a/drivers/thermal/thermal_of.c
++++ b/drivers/thermal/thermal_of.c
+@@ -555,10 +555,6 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
+
+ tz = tzd->devdata;
+
+- /* no __thermal_zone, nothing to be done */
+- if (!tz)
+- return;
+-
+ /* stop temperature polling */
+ thermal_zone_device_disable(tzd);
+
+--
+2.20.1
+
--- /dev/null
+From 875adee436d43269aa11d98c0396dc298407975a Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:50:57 -0500
+Subject: [PATCH 113/124] ASoC: sun4i-i2s: Also set capture DMA width
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-i2s.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
+index 7047f71629ab..75075acc8658 100644
+--- a/sound/soc/sunxi/sun4i-i2s.c
++++ b/sound/soc/sunxi/sun4i-i2s.c
+@@ -631,6 +631,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
+ params_physical_width(params));
+ return -EINVAL;
+ }
++ i2s->capture_dma_data.addr_width = width;
+ i2s->playback_dma_data.addr_width = width;
+
+ sr = i2s->variant->get_sr(word_size);
+--
+2.20.1
+
--- /dev/null
+From 7794f47389db9147af69e4a7aa07902d25547827 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:52:47 -0500
+Subject: [PATCH 114/124] ASoC: sun4i-spdif: Add support for separate resets
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun4i-spdif.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index 7c6d596da84f..ecbe8e51b9a5 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -26,10 +26,11 @@
+ #include <sound/soc.h>
+
+ #define SUN4I_SPDIF_CTL (0x00)
++ #define SUN4I_SPDIF_CTL_RST_RX BIT(12)
+ #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */
+ #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2)
+ #define SUN4I_SPDIF_CTL_GEN BIT(1)
+- #define SUN4I_SPDIF_CTL_RESET BIT(0)
++ #define SUN4I_SPDIF_CTL_RST_TX BIT(0)
+
+ #define SUN4I_SPDIF_TXCFG (0x04)
+ #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31)
+@@ -193,7 +194,7 @@ static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
+ const struct sun4i_spdif_quirks *quirks = host->quirks;
+
+ /* soft reset SPDIF */
+- regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET);
++ regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RST_TX);
+
+ /* flush TX FIFO */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
+--
+2.20.1
+
--- /dev/null
+From bcf8b842b5cfeeccb52acade92c4b0318a9eaf68 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sat, 12 Jun 2021 23:42:48 -0500
+Subject: [PATCH 115/124] ASoC: sun20i-codec: New driver for D1 internal codec
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/Kconfig | 6 +
+ sound/soc/sunxi/Makefile | 1 +
+ sound/soc/sunxi/sun20i-codec.c | 939 +++++++++++++++++++++++++++++++++
+ 3 files changed, 946 insertions(+)
+ create mode 100644 sound/soc/sunxi/sun20i-codec.c
+
+diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
+index ddcaaa98d3cb..c4ef1f41b244 100644
+--- a/sound/soc/sunxi/Kconfig
++++ b/sound/soc/sunxi/Kconfig
+@@ -30,6 +30,12 @@ config SND_SUN8I_CODEC_ANALOG
+ Say Y or M if you want to add support for the analog controls for
+ the codec embedded in newer Allwinner SoCs.
+
++config SND_SUN20I_CODEC
++ tristate "Allwinner D1 (sun20i) Audio Codec"
++ depends on ARCH_SUNXI || COMPILE_TEST
++ help
++ Say Y or M to add support for the audio codec in Allwinner D1 SoC.
++
+ config SND_SUN50I_CODEC_ANALOG
+ tristate "Allwinner sun50i Codec Analog Controls Support"
+ depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST
+diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
+index a86be340a076..7bbe2526e16e 100644
+--- a/sound/soc/sunxi/Makefile
++++ b/sound/soc/sunxi/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+ obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
+ obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
+ obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
++obj-$(CONFIG_SND_SUN20I_CODEC) += sun20i-codec.o
+ obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o
+ obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
+ obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o
+diff --git a/sound/soc/sunxi/sun20i-codec.c b/sound/soc/sunxi/sun20i-codec.c
+new file mode 100644
+index 000000000000..f99e81f93a1b
+--- /dev/null
++++ b/sound/soc/sunxi/sun20i-codec.c
+@@ -0,0 +1,939 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/regulator/driver.h>
++#include <linux/reset.h>
++
++#include <sound/dmaengine_pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/simple_card_utils.h>
++#include <sound/soc.h>
++#include <sound/soc-dai.h>
++#include <sound/soc-dapm.h>
++#include <sound/tlv.h>
++
++#define SUN20I_CODEC_DAC_DPC 0x0000
++#define SUN20I_CODEC_DAC_DPC_EN_DA 31
++#define SUN20I_CODEC_DAC_DPC_HPF_EN 18
++#define SUN20I_CODEC_DAC_DPC_DVOL 12
++#define SUN20I_CODEC_DAC_VOL_CTRL 0x0004
++#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL 16
++#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L 8
++#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R 0
++#define SUN20I_CODEC_DAC_FIFOC 0x0010
++#define SUN20I_CODEC_DAC_FIFOC_FS 29
++#define SUN20I_CODEC_DAC_FIFOC_FIFO_MODE 24
++#define SUN20I_CODEC_DAC_FIFOC_DRQ_CLR_CNT 21
++#define SUN20I_CODEC_DAC_FIFOC_TRIG_LEVEL 8
++#define SUN20I_CODEC_DAC_FIFOC_MONO_EN 6
++#define SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS 5
++#define SUN20I_CODEC_DAC_FIFOC_DRQ_EN 4
++#define SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH 0
++#define SUN20I_CODEC_DAC_TXDATA 0x0020
++#define SUN20I_CODEC_DAC_DEBUG 0x0028
++#define SUN20I_CODEC_DAC_DEBUG_DA_SWP 6
++#define SUN20I_CODEC_DAC_ADDA_LOOP_MODE 0
++
++#define SUN20I_CODEC_ADC_FIFOC 0x0030
++#define SUN20I_CODEC_ADC_FIFOC_FS 29
++#define SUN20I_CODEC_ADC_FIFOC_EN_AD 28
++#define SUN20I_CODEC_ADC_FIFOC_FIFO_MODE 24
++#define SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS 16
++#define SUN20I_CODEC_ADC_FIFOC_TRIG_LEVEL 4
++#define SUN20I_CODEC_ADC_FIFOC_DRQ_EN 3
++#define SUN20I_CODEC_ADC_FIFOC_FIFO_FLUSH 0
++#define SUN20I_CODEC_ADC_VOL_CTRL 0x0034
++#define SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL 16
++#define SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL 8
++#define SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL 0
++#define SUN20I_CODEC_ADC_RXDATA 0x0040
++#define SUN20I_CODEC_ADC_DEBUG 0x004c
++#define SUN20I_CODEC_ADC_DEBUG_AD_SWP1 24
++#define SUN20I_CODEC_ADC_DIG_CTRL 0x0050
++#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN 16
++#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN 0
++
++#define SUN20I_CODEC_DAC_DAP_CTRL 0x00f0
++#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_EN 31
++#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_DRC_EN 29
++#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_HPF_EN 28
++
++#define SUN20I_CODEC_ADC_DAP_CTRL 0x00f8
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_EN 31
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_DRC_EN 29
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_HPF_EN 28
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_EN 27
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_DRC_EN 25
++#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_HPF_EN 24
++
++#define SUN20I_CODEC_ADC1 0x0300
++#define SUN20I_CODEC_ADC1_ADC1_EN 31
++#define SUN20I_CODEC_ADC1_MICIN1_PGA_EN 30
++#define SUN20I_CODEC_ADC1_ADC1_DITHER_EN 29
++#define SUN20I_CODEC_ADC1_MICIN1_SIN_EN 28
++#define SUN20I_CODEC_ADC1_FMINL_EN 27
++#define SUN20I_CODEC_ADC1_FMINL_GAIN 26
++#define SUN20I_CODEC_ADC1_DITHER_LEVEL 24
++#define SUN20I_CODEC_ADC1_LINEINL_EN 23
++#define SUN20I_CODEC_ADC1_LINEINL_GAIN 22
++#define SUN20I_CODEC_ADC1_ADC1_PGA_GAIN 8
++#define SUN20I_CODEC_ADC2 0x0304
++#define SUN20I_CODEC_ADC2_ADC2_EN 31
++#define SUN20I_CODEC_ADC2_MICIN2_PGA_EN 30
++#define SUN20I_CODEC_ADC2_ADC2_DITHER_EN 29
++#define SUN20I_CODEC_ADC2_MICIN2_SIN_EN 28
++#define SUN20I_CODEC_ADC2_FMINR_EN 27
++#define SUN20I_CODEC_ADC2_FMINR_GAIN 26
++#define SUN20I_CODEC_ADC2_DITHER_LEVEL 24
++#define SUN20I_CODEC_ADC2_LINEINR_EN 23
++#define SUN20I_CODEC_ADC2_LINEINR_GAIN 22
++#define SUN20I_CODEC_ADC2_ADC2_PGA_GAIN 8
++#define SUN20I_CODEC_ADC3 0x0308
++#define SUN20I_CODEC_ADC3_ADC3_EN 31
++#define SUN20I_CODEC_ADC3_MICIN3_PGA_EN 30
++#define SUN20I_CODEC_ADC3_ADC3_DITHER_EN 29
++#define SUN20I_CODEC_ADC3_MICIN3_SIN_EN 28
++#define SUN20I_CODEC_ADC3_DITHER_LEVEL 24
++#define SUN20I_CODEC_ADC3_ADC3_PGA_GAIN 8
++
++#define SUN20I_CODEC_DAC 0x0310
++#define SUN20I_CODEC_DAC_DACL_EN 15
++#define SUN20I_CODEC_DAC_DACR_EN 14
++#define SUN20I_CODEC_DAC_LINEOUTL_EN 13
++#define SUN20I_CODEC_DAC_LMUTE 12
++#define SUN20I_CODEC_DAC_LINEOUTR_EN 11
++#define SUN20I_CODEC_DAC_RMUTE 10
++#define SUN20I_CODEC_DAC_LINEOUTL_DIFFEN 6
++#define SUN20I_CODEC_DAC_LINEOUTR_DIFFEN 5
++#define SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL 0
++
++#define SUN20I_CODEC_MICBIAS 0x0318
++#define SUN20I_CODEC_MICBIAS_SELDETADCFS 28
++#define SUN20I_CODEC_MICBIAS_SELDETADCDB 26
++#define SUN20I_CODEC_MICBIAS_SELDETADCBF 24
++#define SUN20I_CODEC_MICBIAS_JACKDETEN 23
++#define SUN20I_CODEC_MICBIAS_SELDETADCDY 21
++#define SUN20I_CODEC_MICBIAS_MICADCEN 20
++#define SUN20I_CODEC_MICBIAS_POPFREE 19
++#define SUN20I_CODEC_MICBIAS_DET_MODE 18
++#define SUN20I_CODEC_MICBIAS_AUTOPLEN 17
++#define SUN20I_CODEC_MICBIAS_MICDETPL 16
++#define SUN20I_CODEC_MICBIAS_HMICBIASEN 15
++#define SUN20I_CODEC_MICBIAS_HMICBIASSEL 13
++#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_EN 12
++#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_CLK 10
++#define SUN20I_CODEC_MICBIAS_MMICBIASEN 7
++#define SUN20I_CODEC_MICBIAS_MMICBIASSEL 5
++#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_EN 4
++#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_CLK 2
++
++/* TODO */
++#define SUN20I_CODEC_RAMP 0x031c
++#define SUN20I_CODEC_RAMP_HP_PULL_OUT_EN 15
++
++#define SUN20I_CODEC_HMIC_CTRL 0x0328
++#define SUN20I_CODEC_HMIC_CTRL_SAMPLE_SELECT 21
++#define SUN20I_CODEC_HMIC_CTRL_MDATA_THRESHOLD 16
++#define SUN20I_CODEC_HMIC_CTRL_SF 14
++#define SUN20I_CODEC_HMIC_CTRL_M 10
++#define SUN20I_CODEC_HMIC_CTRL_N 6
++#define SUN20I_CODEC_HMIC_CTRL_THRESH_DEBOUNCE 3
++#define SUN20I_CODEC_HMIC_CTRL_JACK_OUT_IRQ_EN 2
++#define SUN20I_CODEC_HMIC_CTRL_JACK_IN_IRQ_EN 1
++#define SUN20I_CODEC_HMIC_CTRL_MIC_DET_IRQ_EN 0
++#define SUN20I_CODEC_HMIC_STS 0x032c
++#define SUN20I_CODEC_HMIC_STS_MDATA_DISCARD 13
++#define SUN20I_CODEC_HMIC_STS_HMIC_DATA 8
++#define SUN20I_CODEC_HMIC_STS_JACK_OUT_IRQ 4
++#define SUN20I_CODEC_HMIC_STS_JACK_IN_IRQ 3
++#define SUN20I_CODEC_HMIC_STS_MIC_DET_IRQ 0
++
++#define SUN20I_CODEC_HP2 0x0340
++#define SUN20I_CODEC_HP2_HPFB_BUF_EN 31
++#define SUN20I_CODEC_HP2_HEADPHONE_GAIN 28
++#define SUN20I_CODEC_HP2_HPFB_RES 26
++#define SUN20I_CODEC_HP2_HP_DRVEN 21
++#define SUN20I_CODEC_HP2_HP_DRVOUTEN 20
++#define SUN20I_CODEC_HP2_RSWITCH 19
++#define SUN20I_CODEC_HP2_RAMPEN 18
++#define SUN20I_CODEC_HP2_HPFB_IN_EN 17
++#define SUN20I_CODEC_HP2_RAMP_FINAL_CONTROL 16
++#define SUN20I_CODEC_HP2_RAMP_OUT_EN 15
++#define SUN20I_CODEC_HP2_RAMP_FINAL_STATE_RES 13
++
++/* Not affected by codec bus clock/reset */
++#define SUN20I_CODEC_POWER 0x0348
++#define SUN20I_CODEC_POWER_ALDO_EN_MASK BIT(31)
++#define SUN20I_CODEC_POWER_HPLDO_EN_MASK BIT(30)
++#define SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK GENMASK(14, 12)
++#define SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK GENMASK(10, 8)
++
++#define SUN20I_CODEC_ADC_CUR 0x034c
++
++#define SUN20I_CODEC_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|\
++ SNDRV_PCM_FMTBIT_S20_LE|\
++ SNDRV_PCM_FMTBIT_S32_LE)
++
++#define DRIVER_NAME "sun20i-codec"
++#define PREFIX "allwinner,"
++
++/* snd_soc_register_card() takes over drvdata, so the card must be first! */
++struct sun20i_codec {
++ struct snd_soc_card card;
++ struct snd_soc_dai_link dai_link;
++ struct snd_soc_dai_link_component dlcs[3];
++ struct snd_dmaengine_dai_dma_data dma_data[2];
++
++ struct clk *bus_clk;
++ struct clk *adc_clk;
++ struct clk *dac_clk;
++ struct reset_control *reset;
++};
++
++static int sun20i_codec_dai_probe(struct snd_soc_dai *dai)
++{
++ struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai);
++
++ snd_soc_dai_init_dma_data(dai,
++ &codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
++ &codec->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
++
++ return 0;
++}
++
++static struct clk *sun20i_codec_get_clk(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai);
++
++ return substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
++ codec->adc_clk : codec->dac_clk;
++}
++
++static const unsigned int sun20i_codec_rates[] = {
++ 7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000,
++ 29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000,
++};
++
++static const struct snd_pcm_hw_constraint_list sun20i_codec_rate_lists[] = {
++ [SNDRV_PCM_STREAM_PLAYBACK] = {
++ .list = sun20i_codec_rates,
++ .count = ARRAY_SIZE(sun20i_codec_rates),
++ },
++ [SNDRV_PCM_STREAM_CAPTURE] = {
++ .list = sun20i_codec_rates,
++ .count = ARRAY_SIZE(sun20i_codec_rates) - 4, /* max 48 kHz */
++ },
++};
++
++static int sun20i_codec_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ const struct snd_pcm_hw_constraint_list *list;
++ int ret;
++
++ list = &sun20i_codec_rate_lists[substream->stream];
++ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
++ SNDRV_PCM_HW_PARAM_RATE, list);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(sun20i_codec_get_clk(substream, dai));
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static void sun20i_codec_shutdown(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ clk_disable_unprepare(sun20i_codec_get_clk(substream, dai));
++}
++
++static unsigned int sun20i_codec_get_clk_rate(unsigned int sample_rate)
++{
++ return (sample_rate % 4000) ? 22579200 : 24576000;
++}
++
++static const unsigned short sun20i_codec_divisors[] = {
++ 512, 1024, 2048, 128,
++ 768, 1536, 3072, 256,
++};
++
++static int sun20i_codec_get_fs(unsigned int clk_rate, unsigned int sample_rate)
++{
++ unsigned int divisor = clk_rate / sample_rate;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(sun20i_codec_divisors); ++i)
++ if (sun20i_codec_divisors[i] == divisor)
++ return i;
++
++ return -EINVAL;
++}
++
++static int sun20i_codec_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai);
++ struct snd_soc_component *component = dai->component;
++ unsigned int channels = params_channels(params);
++ unsigned int sample_bits = params_width(params);
++ unsigned int sample_rate = params_rate(params);
++ unsigned int clk_rate = sun20i_codec_get_clk_rate(sample_rate);
++ enum dma_slave_buswidth dma_width;
++ unsigned int reg;
++ int ret, val;
++
++ switch (params_physical_width(params)) {
++ case 16:
++ dma_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
++ break;
++ case 32:
++ dma_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
++ break;
++ default:
++ dev_err(dai->dev, "Unsupported physical sample width: %d\n",
++ params_physical_width(params));
++ return -EINVAL;
++ }
++ codec->dma_data[substream->stream].addr_width = dma_width;
++
++ ret = clk_set_rate(sun20i_codec_get_clk(substream, dai),
++ sun20i_codec_get_clk_rate(sample_rate));
++ if (ret)
++ return ret;
++
++ reg = substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
++ SUN20I_CODEC_ADC_FIFOC : SUN20I_CODEC_DAC_FIFOC;
++
++ val = sun20i_codec_get_fs(clk_rate, sample_rate);
++ if (val < 0)
++ return val;
++ snd_soc_component_update_bits(component, reg,
++ 0x7 << SUN20I_CODEC_DAC_FIFOC_FS,
++ val << SUN20I_CODEC_DAC_FIFOC_FS);
++
++ /* Data is at MSB for full 4-byte samples, otherwise at LSB. */
++ val = sample_bits != 32;
++ snd_soc_component_update_bits(component, reg,
++ 0x1 << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE,
++ val << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE);
++
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++ val = sample_bits > 16;
++ snd_soc_component_update_bits(component, reg,
++ 0x1 << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS,
++ val << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS);
++
++ val = BIT(channels) - 1;
++ snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL,
++ 0xf << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN,
++ val << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN);
++ } else {
++ val = sample_bits > 16;
++ snd_soc_component_update_bits(component, reg,
++ 0x1 << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS,
++ val << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS);
++
++ val = channels == 1;
++ snd_soc_component_update_bits(component, reg,
++ 0x1 << SUN20I_CODEC_DAC_FIFOC_MONO_EN,
++ val << SUN20I_CODEC_DAC_FIFOC_MONO_EN);
++ }
++
++ return 0;
++}
++
++static int sun20i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_component *component = dai->component;
++ unsigned int reg, mask;
++
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
++ reg = SUN20I_CODEC_ADC_FIFOC;
++ mask = BIT(SUN20I_CODEC_ADC_FIFOC_DRQ_EN);
++ } else {
++ reg = SUN20I_CODEC_DAC_FIFOC;
++ mask = BIT(SUN20I_CODEC_DAC_FIFOC_DRQ_EN);
++ }
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ mask |= BIT(SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH);
++ snd_soc_component_update_bits(component, reg, mask, mask);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ snd_soc_component_update_bits(component, reg, mask, 0);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops sun20i_codec_dai_ops = {
++ .startup = sun20i_codec_startup,
++ .shutdown = sun20i_codec_shutdown,
++ .hw_params = sun20i_codec_hw_params,
++ .trigger = sun20i_codec_trigger,
++};
++
++static struct snd_soc_dai_driver sun20i_codec_dai = {
++ .name = DRIVER_NAME,
++ .probe = sun20i_codec_dai_probe,
++ .ops = &sun20i_codec_dai_ops,
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 3, /* ??? */
++ .rates = SNDRV_PCM_RATE_CONTINUOUS,
++ .formats = SUN20I_CODEC_PCM_FORMATS,
++ .sig_bits = 20,
++ },
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = SNDRV_PCM_RATE_CONTINUOUS,
++ .formats = SUN20I_CODEC_PCM_FORMATS,
++ .sig_bits = 20,
++ },
++};
++
++static const DECLARE_TLV_DB_SCALE(sun20i_codec_boost_vol_scale, 0, 600, 0);
++static const DECLARE_TLV_DB_SCALE(sun20i_codec_digital_vol_scale, -12000, 75, 1);
++static const DECLARE_TLV_DB_SCALE(sun20i_codec_headphone_vol_scale, -4200, 600, 0);
++/* FIXME */
++static const DECLARE_TLV_DB_SCALE(sun20i_codec_line_out_vol_scale, -4650, 150, 1);
++/* FIXME */
++static const DECLARE_TLV_DB_SCALE(sun20i_codec_pga_vol_scale, 500, 100, 0);
++
++static const char *const sun20i_codec_line_out_mode_enum_text[] = {
++ "Single-Ended", "Differential"
++};
++
++static const SOC_ENUM_DOUBLE_DECL(sun20i_codec_line_out_mode_enum,
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_LINEOUTL_DIFFEN,
++ SUN20I_CODEC_DAC_LINEOUTR_DIFFEN,
++ sun20i_codec_line_out_mode_enum_text);
++
++static const struct snd_kcontrol_new sun20i_codec_controls[] = {
++ /* Digital Controls */
++ SOC_DOUBLE_TLV("DAC Playback Volume",
++ SUN20I_CODEC_DAC_VOL_CTRL,
++ SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L,
++ SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R,
++ 0xc0, 0, sun20i_codec_digital_vol_scale),
++ SOC_SINGLE_TLV("ADC3 Capture Volume",
++ SUN20I_CODEC_ADC_VOL_CTRL,
++ SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL,
++ 0xc0, 0, sun20i_codec_digital_vol_scale),
++ SOC_SINGLE_TLV("ADC2 Capture Volume",
++ SUN20I_CODEC_ADC_VOL_CTRL,
++ SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL,
++ 0xc0, 0, sun20i_codec_digital_vol_scale),
++ SOC_SINGLE_TLV("ADC1 Capture Volume",
++ SUN20I_CODEC_ADC_VOL_CTRL,
++ SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL,
++ 0xc0, 0, sun20i_codec_digital_vol_scale),
++
++ /* Analog Controls */
++ SOC_DOUBLE_R_TLV("FM Capture Volume",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC1_FMINL_GAIN,
++ 0x1, 0, sun20i_codec_boost_vol_scale),
++ SOC_DOUBLE_R_TLV("Line In Capture Volume",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC1_LINEINL_GAIN,
++ 0x1, 0, sun20i_codec_boost_vol_scale),
++ SOC_ENUM("Line Out Mode Playback Enum",
++ sun20i_codec_line_out_mode_enum),
++ SOC_SINGLE_TLV("Line Out Playback Volume",
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL,
++ 0x1f, 0, sun20i_codec_line_out_vol_scale),
++ SOC_SINGLE_TLV("Headphone Playback Volume",
++ SUN20I_CODEC_HP2,
++ SUN20I_CODEC_HP2_HEADPHONE_GAIN,
++ 0x7, 1, sun20i_codec_headphone_vol_scale),
++};
++
++static const struct snd_kcontrol_new sun20i_codec_line_out_switch =
++ SOC_DAPM_DOUBLE("Line Out Playback Switch",
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_LMUTE,
++ SUN20I_CODEC_DAC_RMUTE, 1, 1);
++
++static const struct snd_kcontrol_new sun20i_codec_hp_switch =
++ SOC_DAPM_SINGLE("Headphone Playback Switch",
++ SUN20I_CODEC_HP2,
++ SUN20I_CODEC_HP2_HP_DRVOUTEN, 1, 0);
++
++static const struct snd_kcontrol_new sun20i_codec_adc12_mixer_controls[] = {
++ /* ADC1 Only */
++ SOC_DAPM_SINGLE("Mic1 Capture Switch",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC1_MICIN1_SIN_EN, 1, 0),
++ /* Shared */
++ SOC_DAPM_DOUBLE_R("FM Capture Switch",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC1_FMINL_EN, 1, 0),
++ /* Shared */
++ SOC_DAPM_DOUBLE_R("Line In Capture Switch",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC1_LINEINL_EN, 1, 0),
++ /* ADC2 Only */
++ SOC_DAPM_SINGLE("Mic2 Capture Switch",
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC2_MICIN2_SIN_EN, 1, 0),
++};
++
++static const struct snd_kcontrol_new sun20i_codec_adc3_mixer_controls[] = {
++ SOC_DAPM_SINGLE("Mic3 Capture Switch",
++ SUN20I_CODEC_ADC3,
++ SUN20I_CODEC_ADC3_MICIN3_SIN_EN, 1, 0),
++};
++
++static const struct snd_kcontrol_new sun20i_codec_mic1_volume =
++ SOC_DAPM_SINGLE_TLV("Capture Volume",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC1_ADC1_PGA_GAIN,
++ 0x1f, 0, sun20i_codec_pga_vol_scale);
++
++static const struct snd_kcontrol_new sun20i_codec_mic2_volume =
++ SOC_DAPM_SINGLE_TLV("Capture Volume",
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC2_ADC2_PGA_GAIN,
++ 0x1f, 0, sun20i_codec_pga_vol_scale);
++
++static const struct snd_kcontrol_new sun20i_codec_mic3_volume =
++ SOC_DAPM_SINGLE_TLV("Capture Volume",
++ SUN20I_CODEC_ADC3,
++ SUN20I_CODEC_ADC3_ADC3_PGA_GAIN,
++ 0x1f, 0, sun20i_codec_pga_vol_scale);
++
++static const struct snd_soc_dapm_widget sun20i_codec_widgets[] = {
++ /* Playback */
++ SND_SOC_DAPM_OUTPUT("LINEOUTL"),
++ SND_SOC_DAPM_OUTPUT("LINEOUTR"),
++
++ SND_SOC_DAPM_SWITCH("LINEOUTL Switch",
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_LINEOUTL_EN, 0,
++ &sun20i_codec_line_out_switch),
++ SND_SOC_DAPM_SWITCH("LINEOUTR Switch",
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_LINEOUTR_EN, 0,
++ &sun20i_codec_line_out_switch),
++
++ SND_SOC_DAPM_OUTPUT("HPOUTL"),
++ SND_SOC_DAPM_OUTPUT("HPOUTR"),
++
++ SND_SOC_DAPM_SWITCH("HPOUTL Switch",
++ SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch),
++ SND_SOC_DAPM_SWITCH("HPOUTR Switch",
++ SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch),
++ SND_SOC_DAPM_SUPPLY("Headphone Driver",
++ SUN20I_CODEC_HP2,
++ SUN20I_CODEC_HP2_HP_DRVEN, 0, NULL, 0),
++
++ SND_SOC_DAPM_DAC("DACL", NULL,
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_DACL_EN, 0),
++ SND_SOC_DAPM_DAC("DACR", NULL,
++ SUN20I_CODEC_DAC,
++ SUN20I_CODEC_DAC_DACR_EN, 0),
++ SND_SOC_DAPM_SUPPLY("DAC",
++ SUN20I_CODEC_DAC_DPC,
++ SUN20I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),
++
++ SND_SOC_DAPM_AIF_IN("DACL FIFO", "Playback", 0,
++ SND_SOC_NOPM, 0, 0),
++ SND_SOC_DAPM_AIF_IN("DACR FIFO", "Playback", 1,
++ SND_SOC_NOPM, 0, 0),
++
++ /* Capture */
++ SND_SOC_DAPM_AIF_OUT("ADC1 FIFO", "Capture", 0,
++ SND_SOC_NOPM, 0, 0),
++ SND_SOC_DAPM_AIF_OUT("ADC2 FIFO", "Capture", 1,
++ SND_SOC_NOPM, 0, 0),
++ SND_SOC_DAPM_AIF_OUT("ADC3 FIFO", "Capture", 2,
++ SND_SOC_NOPM, 0, 0),
++
++ SND_SOC_DAPM_ADC("ADC1", NULL,
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC1_ADC1_EN, 0),
++ SND_SOC_DAPM_ADC("ADC2", NULL,
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC2_ADC2_EN, 0),
++ SND_SOC_DAPM_ADC("ADC3", NULL,
++ SUN20I_CODEC_ADC3,
++ SUN20I_CODEC_ADC3_ADC3_EN, 0),
++ SND_SOC_DAPM_SUPPLY("ADC",
++ SUN20I_CODEC_ADC_FIFOC,
++ SUN20I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),
++
++ SND_SOC_DAPM_MIXER_NAMED_CTL("ADC1 Mixer", SND_SOC_NOPM, 0, 0,
++ sun20i_codec_adc12_mixer_controls, 3),
++ SND_SOC_DAPM_MIXER_NAMED_CTL("ADC2 Mixer", SND_SOC_NOPM, 0, 0,
++ sun20i_codec_adc12_mixer_controls + 1, 3),
++ SND_SOC_DAPM_MIXER_NAMED_CTL("ADC3 Mixer", SND_SOC_NOPM, 0, 0,
++ sun20i_codec_adc3_mixer_controls,
++ ARRAY_SIZE(sun20i_codec_adc3_mixer_controls)),
++
++ SND_SOC_DAPM_PGA("Mic1",
++ SUN20I_CODEC_ADC1,
++ SUN20I_CODEC_ADC1_MICIN1_PGA_EN, 0,
++ &sun20i_codec_mic1_volume, 1),
++ SND_SOC_DAPM_PGA("Mic2",
++ SUN20I_CODEC_ADC2,
++ SUN20I_CODEC_ADC2_MICIN2_PGA_EN, 0,
++ &sun20i_codec_mic2_volume, 1),
++ SND_SOC_DAPM_PGA("Mic3",
++ SUN20I_CODEC_ADC3,
++ SUN20I_CODEC_ADC3_MICIN3_PGA_EN, 0,
++ &sun20i_codec_mic3_volume, 1),
++
++ SND_SOC_DAPM_INPUT("MICIN1"),
++ SND_SOC_DAPM_INPUT("MICIN2"),
++ SND_SOC_DAPM_INPUT("MICIN3"),
++
++ SND_SOC_DAPM_INPUT("FMINL"),
++ SND_SOC_DAPM_INPUT("FMINR"),
++
++ SND_SOC_DAPM_INPUT("LINEINL"),
++ SND_SOC_DAPM_INPUT("LINEINR"),
++
++ SND_SOC_DAPM_SUPPLY("HBIAS",
++ SUN20I_CODEC_MICBIAS,
++ SUN20I_CODEC_MICBIAS_HMICBIASEN, 0, NULL, 0),
++ SND_SOC_DAPM_SUPPLY("MBIAS",
++ SUN20I_CODEC_MICBIAS,
++ SUN20I_CODEC_MICBIAS_MMICBIASEN, 0, NULL, 0),
++
++ SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0),
++ SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0),
++ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd33", 0, 0),
++};
++
++static const struct snd_soc_dapm_route sun20i_codec_routes[] = {
++ /* Playback */
++ { "LINEOUTL", NULL, "LINEOUTL Switch" },
++ { "LINEOUTR", NULL, "LINEOUTR Switch" },
++
++ { "LINEOUTL Switch", "Line Out Playback Switch", "DACL" },
++ { "LINEOUTR Switch", "Line Out Playback Switch", "DACR" },
++
++ { "HPOUTL", NULL, "HPOUTL Switch" },
++ { "HPOUTR", NULL, "HPOUTR Switch" },
++
++ { "HPOUTL Switch", "Headphone Playback Switch", "DACL" },
++ { "HPOUTR Switch", "Headphone Playback Switch", "DACR" },
++ { "HPOUTL Switch", NULL, "Headphone Driver" },
++ { "HPOUTR Switch", NULL, "Headphone Driver" },
++ { "Headphone Driver", NULL, "hpvcc" },
++
++ { "DACL", NULL, "DACL FIFO" },
++ { "DACR", NULL, "DACR FIFO" },
++ { "DACL", NULL, "DAC" },
++ { "DACR", NULL, "DAC" },
++ { "DACL", NULL, "avcc" },
++ { "DACR", NULL, "avcc" },
++
++ /* Capture */
++ { "ADC1 FIFO", NULL, "ADC1" },
++ { "ADC2 FIFO", NULL, "ADC2" },
++ { "ADC3 FIFO", NULL, "ADC3" },
++
++ { "ADC1", NULL, "ADC1 Mixer" },
++ { "ADC2", NULL, "ADC2 Mixer" },
++ { "ADC3", NULL, "ADC3 Mixer" },
++ { "ADC1", NULL, "ADC" },
++ { "ADC2", NULL, "ADC" },
++ { "ADC3", NULL, "ADC" },
++ { "ADC1", NULL, "avcc" },
++ { "ADC2", NULL, "avcc" },
++ { "ADC3", NULL, "avcc" },
++
++ { "ADC1 Mixer", "Mic1 Capture Switch", "Mic1" },
++ { "ADC2 Mixer", "Mic2 Capture Switch", "Mic2" },
++ { "ADC3 Mixer", "Mic3 Capture Switch", "Mic3" },
++ { "ADC1 Mixer", "FM Capture Switch", "FMINL" },
++ { "ADC2 Mixer", "FM Capture Switch", "FMINR" },
++ { "ADC1 Mixer", "Line In Capture Switch", "LINEINL" },
++ { "ADC2 Mixer", "Line In Capture Switch", "LINEINR" },
++
++ { "Mic1", NULL, "MICIN1" },
++ { "Mic2", NULL, "MICIN2" },
++ { "Mic3", NULL, "MICIN3" },
++
++ { "HBIAS", NULL, "vdd33" },
++ { "MBIAS", NULL, "vdd33" },
++};
++
++static int sun20i_codec_component_probe(struct snd_soc_component *component)
++{
++ struct sun20i_codec *codec = snd_soc_component_get_drvdata(component);
++ int ret;
++
++ ret = reset_control_deassert(codec->reset);
++ if (ret)
++ return ret;
++
++ ret = clk_prepare_enable(codec->bus_clk);
++ if (ret)
++ goto err_assert_reset;
++
++ /* Enable digital volume control. */
++ snd_soc_component_update_bits(component, SUN20I_CODEC_DAC_VOL_CTRL,
++ 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL,
++ 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL);
++ snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL,
++ 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN,
++ 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN);
++
++ return 0;
++
++err_assert_reset:
++ reset_control_assert(codec->reset);
++
++ return ret;
++}
++
++static void sun20i_codec_component_remove(struct snd_soc_component *component)
++{
++ struct sun20i_codec *codec = snd_soc_component_get_drvdata(component);
++
++ clk_disable_unprepare(codec->bus_clk);
++ reset_control_assert(codec->reset);
++}
++
++static const struct snd_soc_component_driver sun20i_codec_component = {
++ .controls = sun20i_codec_controls,
++ .num_controls = ARRAY_SIZE(sun20i_codec_controls),
++ .dapm_widgets = sun20i_codec_widgets,
++ .num_dapm_widgets = ARRAY_SIZE(sun20i_codec_widgets),
++ .dapm_routes = sun20i_codec_routes,
++ .num_dapm_routes = ARRAY_SIZE(sun20i_codec_routes),
++ .probe = sun20i_codec_component_probe,
++ .remove = sun20i_codec_component_remove,
++};
++
++static int sun20i_codec_init_card(struct device *dev,
++ struct sun20i_codec *codec)
++{
++ struct snd_soc_dai_link *dai_link = &codec->dai_link;
++ struct snd_soc_card *card = &codec->card;
++ int ret;
++
++ codec->dlcs[0].of_node = dev->of_node;
++ codec->dlcs[0].dai_name = DRIVER_NAME;
++ codec->dlcs[1].name = "snd-soc-dummy";
++ codec->dlcs[1].dai_name = "snd-soc-dummy-dai";
++ codec->dlcs[2].of_node = dev->of_node;
++
++ dai_link->name = DRIVER_NAME;
++ dai_link->stream_name = DRIVER_NAME;
++ dai_link->cpus = &codec->dlcs[0];
++ dai_link->num_cpus = 1;
++ dai_link->codecs = &codec->dlcs[1];
++ dai_link->num_codecs = 1;
++ dai_link->platforms = &codec->dlcs[2];
++ dai_link->num_platforms = 1;
++
++ card->name = DRIVER_NAME;
++ card->dev = dev;
++ card->owner = THIS_MODULE;
++ card->dai_link = dai_link;
++ card->num_links = 1;
++ card->fully_routed = true;
++
++ ret = snd_soc_of_parse_audio_simple_widgets(card, PREFIX "widgets");
++ if (ret)
++ return ret;
++
++ ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing");
++ if (ret)
++ return ret;
++
++ ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs");
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static const struct regmap_config sun20i_codec_regmap_config = {
++ .reg_bits = 32,
++ .reg_stride = 4,
++ .val_bits = 32,
++ .max_register = SUN20I_CODEC_ADC_CUR,
++};
++
++static const struct regulator_ops sun20i_codec_ldo_ops = {
++ .list_voltage = regulator_list_voltage_linear,
++ .map_voltage = regulator_map_voltage_linear,
++ .set_voltage_sel = regulator_set_voltage_sel_regmap,
++ .get_voltage_sel = regulator_get_voltage_sel_regmap,
++ .enable = regulator_enable_regmap,
++ .disable = regulator_disable_regmap,
++ .is_enabled = regulator_is_enabled_regmap,
++};
++
++static const struct regulator_desc sun20i_codec_ldos[] = {
++ {
++ .name = "aldo",
++ .supply_name = "vdd33",
++ .of_match = "aldo",
++ .regulators_node = "regulators",
++ .ops = &sun20i_codec_ldo_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++ .n_voltages = BIT(3),
++ .min_uV = 1650000,
++ .uV_step = 50000,
++ .vsel_reg = SUN20I_CODEC_POWER,
++ .vsel_mask = SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK,
++ .enable_reg = SUN20I_CODEC_POWER,
++ .enable_mask = SUN20I_CODEC_POWER_ALDO_EN_MASK,
++ },
++ {
++ .name = "hpldo",
++ .supply_name = "hpldoin",
++ .of_match = "hpldo",
++ .regulators_node = "regulators",
++ .ops = &sun20i_codec_ldo_ops,
++ .type = REGULATOR_VOLTAGE,
++ .owner = THIS_MODULE,
++ .n_voltages = BIT(3),
++ .min_uV = 1650000,
++ .uV_step = 50000,
++ .vsel_reg = SUN20I_CODEC_POWER,
++ .vsel_mask = SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK,
++ .enable_reg = SUN20I_CODEC_POWER,
++ .enable_mask = SUN20I_CODEC_POWER_HPLDO_EN_MASK,
++ },
++};
++
++static int sun20i_codec_probe(struct platform_device *pdev)
++{
++ struct regulator_config config = { .dev = &pdev->dev };
++ struct device *dev = &pdev->dev;
++ struct sun20i_codec *codec;
++ struct regulator_dev *rdev;
++ struct regmap *regmap;
++ struct resource *res;
++ void __iomem *base;
++ int i, ret;
++
++ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
++ if (!codec)
++ return -ENOMEM;
++
++ dev_set_drvdata(dev, codec);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(base))
++ return dev_err_probe(dev, PTR_ERR(base),
++ "Failed to map registers\n");
++
++ regmap = devm_regmap_init_mmio(dev, base,
++ &sun20i_codec_regmap_config);
++ if (IS_ERR(regmap))
++ return dev_err_probe(dev, PTR_ERR(regmap),
++ "Failed to create regmap\n");
++
++ codec->bus_clk = devm_clk_get(dev, "bus");
++ if (IS_ERR(codec->bus_clk))
++ return dev_err_probe(dev, PTR_ERR(codec->bus_clk),
++ "Failed to get bus clock\n");
++
++ codec->adc_clk = devm_clk_get(dev, "adc");
++ if (IS_ERR(codec->adc_clk))
++ return dev_err_probe(dev, PTR_ERR(codec->adc_clk),
++ "Failed to get ADC clock\n");
++
++ codec->dac_clk = devm_clk_get(dev, "dac");
++ if (IS_ERR(codec->dac_clk))
++ return dev_err_probe(dev, PTR_ERR(codec->dac_clk),
++ "Failed to get DAC clock\n");
++
++ codec->reset = devm_reset_control_get_exclusive(dev, NULL);
++ if (IS_ERR(codec->reset))
++ return dev_err_probe(dev, PTR_ERR(codec->reset),
++ "Failed to get reset\n");
++
++ for (i = 0; i < ARRAY_SIZE(sun20i_codec_ldos); ++i) {
++ const struct regulator_desc *desc = &sun20i_codec_ldos[i];
++
++ rdev = devm_regulator_register(dev, desc, &config);
++ if (IS_ERR(rdev))
++ return PTR_ERR(rdev);
++ }
++
++ ret = devm_snd_soc_register_component(dev, &sun20i_codec_component,
++ &sun20i_codec_dai, 1);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to register component\n");
++
++ codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
++ res->start + SUN20I_CODEC_DAC_TXDATA;
++ codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 8;
++ codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
++ res->start + SUN20I_CODEC_ADC_RXDATA;
++ codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 8;
++
++ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to register PCM\n");
++
++ ret = sun20i_codec_init_card(dev, codec);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to initialize card\n");
++
++ ret = devm_snd_soc_register_card(dev, &codec->card);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to register card\n");
++
++ return 0;
++}
++
++static const struct of_device_id sun20i_codec_of_match[] = {
++ { .compatible = "allwinner,sun20i-d1-audio-codec" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, sun20i_codec_of_match);
++
++static struct platform_driver sun20i_codec_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .of_match_table = sun20i_codec_of_match,
++ },
++ .probe = sun20i_codec_probe,
++};
++module_platform_driver(sun20i_codec_driver);
++
++MODULE_DESCRIPTION("Allwinner D1 (sun20i) codec driver");
++MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:sun20i-codec");
+--
+2.20.1
+
--- /dev/null
+From fa221fe84aced5348277f372b327b4362b2123f8 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Wed, 23 Jun 2021 21:18:47 -0500
+Subject: [PATCH 116/124] ASoC: sun20i-codec: What is this ramp thing?
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ sound/soc/sunxi/sun20i-codec.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/sound/soc/sunxi/sun20i-codec.c b/sound/soc/sunxi/sun20i-codec.c
+index f99e81f93a1b..18551f5ef14e 100644
+--- a/sound/soc/sunxi/sun20i-codec.c
++++ b/sound/soc/sunxi/sun20i-codec.c
+@@ -711,6 +711,10 @@ static int sun20i_codec_component_probe(struct snd_soc_component *component)
+ 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN,
+ 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN);
+
++ /* Maaagic... */
++ snd_soc_component_update_bits(component, SUN20I_CODEC_RAMP,
++ BIT(1) | BIT(0), BIT(0));
++
+ return 0;
+
+ err_assert_reset:
+--
+2.20.1
+
--- /dev/null
+From 837e54cc6396950ad0ca852aff40a3c7c9e03fe1 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 15 Nov 2021 00:48:47 -0600
+Subject: [PATCH 117/124] Disable broken ARCH_SUNXI drivers
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/irqchip/Makefile | 6 +++---
+ drivers/soc/sunxi/Kconfig | 2 +-
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
+index ba1097e01991..1b9e412577e8 100644
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -23,9 +23,9 @@ obj-$(CONFIG_OMPIC) += irq-ompic.o
+ obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o
+ obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
+ obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
+-obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
+-obj-$(CONFIG_ARCH_SUNXI) += irq-sun6i-r.o
+-obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
++#obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
++#obj-$(CONFIG_ARCH_SUNXI) += irq-sun6i-r.o
++#obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
+ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
+ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
+ obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o
+diff --git a/drivers/soc/sunxi/Kconfig b/drivers/soc/sunxi/Kconfig
+index 1fef0e711056..7762020ac56d 100644
+--- a/drivers/soc/sunxi/Kconfig
++++ b/drivers/soc/sunxi/Kconfig
+@@ -5,7 +5,7 @@
+
+ config SUNXI_MBUS
+ bool
+- default ARCH_SUNXI
++ default ARCH_SUNXI && !RISCV
+ help
+ Say y to enable the fixups needed to support the Allwinner
+ MBUS DMA quirks.
+--
+2.20.1
+
--- /dev/null
+From 9e923b083e3c46b02c67a00ea9c973d66fcb5fa2 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:17:45 -0500
+Subject: [PATCH 118/124] riscv: Add Allwinner D1 SoC support
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/Kconfig.socs | 9 +++++++++
+ arch/riscv/boot/dts/Makefile | 1 +
+ arch/riscv/boot/dts/allwinner/Makefile | 1 +
+ 3 files changed, 11 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/allwinner/Makefile
+
+diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
+index 56bafc3dad4c..48c0812e2f8a 100644
+--- a/arch/riscv/Kconfig.socs
++++ b/arch/riscv/Kconfig.socs
+@@ -1,5 +1,14 @@
+ menu "SoC selection"
+
++config ARCH_SUNXI
++ bool "Allwinner sunXi SoCs"
++ select SIFIVE_PLIC
++ select SUN4I_TIMER
++ select SUN5I_HSTIMER
++ select SUN20I_INTC
++ help
++ This enables support for Allwinner sunXi SoC platforms.
++
+ config SOC_MICROCHIP_POLARFIRE
+ bool "Microchip PolarFire SoCs"
+ select MCHP_CLK_MPFS
+diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile
+index fe996b88319e..05b36b76eaac 100644
+--- a/arch/riscv/boot/dts/Makefile
++++ b/arch/riscv/boot/dts/Makefile
+@@ -1,4 +1,5 @@
+ # SPDX-License-Identifier: GPL-2.0
++subdir-y += allwinner
+ subdir-y += sifive
+ subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan
+ subdir-y += microchip
+diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile
+new file mode 100644
+index 000000000000..f66554cd5c45
+--- /dev/null
++++ b/arch/riscv/boot/dts/allwinner/Makefile
+@@ -0,0 +1 @@
++# SPDX-License-Identifier: GPL-2.0
+--
+2.20.1
+
--- /dev/null
+From df2ef356052fd89dda0d44e374dc423be773a6a1 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:18:17 -0500
+Subject: [PATCH 119/124] riscv: Add Allwinner D1 SoC device tree
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 1159 ++++++++++++++++++
+ 1 file changed, 1159 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi
+
+diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi
+new file mode 100644
+index 000000000000..af0fdadc0d14
+--- /dev/null
++++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi
+@@ -0,0 +1,1159 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++
++#include <dt-bindings/clock/sun20i-d1-ccu.h>
++#include <dt-bindings/clock/sun20i-d1-r-ccu.h>
++#include <dt-bindings/clock/sun6i-rtc.h>
++#include <dt-bindings/clock/sun8i-de2.h>
++#include <dt-bindings/clock/sun8i-tcon-top.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/mailbox/sun20i-d1-msgbox.h>
++#include <dt-bindings/reset/sun20i-d1-ccu.h>
++#include <dt-bindings/reset/sun20i-d1-r-ccu.h>
++#include <dt-bindings/reset/sun8i-de2.h>
++#include <dt-bindings/thermal/thermal.h>
++
++/ {
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ // FIXME: no riscv architecture support for cpufreq
++ cpu_opp_table: cpu-opp-table {
++ compatible = "allwinner,sun20i-d1-operating-points",
++ "allwinner,sun50i-h6-operating-points";
++ nvmem-cells = <&cpu_speed_grade>;
++
++ opp-1080000000 {
++ // FIXME: this is probably wrong now.
++ clock-latency-ns = <244144>; /* 8 32k periods */
++ opp-hz = /bits/ 64 <1008000000>;
++
++ // FIXME: derive a real voltage range.
++ opp-microvolt-speed0 = <1100000>;
++ };
++ };
++
++ cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ timebase-frequency = <24000000>;
++
++ cpu0: cpu@0 {
++ // FIXME: is this the right compatible?
++ compatible = "thead,c906", "riscv";
++ device_type = "cpu";
++ reg = <0>;
++ clocks = <&ccu CLK_RISCV>;
++ clock-frequency = <24000000>;
++ #cooling-cells = <2>;
++ d-cache-block-size = <64>;
++ d-cache-sets = <256>;
++ d-cache-size = <32768>;
++ i-cache-block-size = <64>;
++ i-cache-sets = <128>;
++ i-cache-size = <32768>;
++ mmu-type = "riscv,sv39";
++ operating-points-v2 = <&cpu_opp_table>;
++ riscv,isa = "rv64imafdc";
++
++ cpu0_intc: interrupt-controller {
++ compatible = "riscv,cpu-intc";
++ #address-cells = <0>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++ };
++ };
++
++ osc24M: osc24M_clk {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <24000000>;
++ clock-output-names = "osc24M";
++ };
++
++ // FIXME: depends on what T-HEAD tries to upstream.
++ pmu {
++ compatible = "thead,c900-pmu";
++ };
++
++ thermal-zones {
++ cpu-thermal {
++ polling-delay = <0>;
++ polling-delay-passive = <0>;
++ thermal-sensors = <&ths 0>;
++
++ trips {
++ cpu_target: cpu-target {
++ hysteresis = <3000>;
++ temperature = <85000>;
++ type = "passive";
++ };
++
++ cpu-crit {
++ hysteresis = <0>;
++ temperature = <110000>;
++ type = "critical";
++ };
++ };
++
++ cooling-maps {
++ map0 {
++ trip = <&cpu_target>;
++ cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
++ };
++ };
++ };
++ };
++
++ soc {
++ compatible = "simple-bus";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++ interrupt-parent = <&intc>;
++
++ // TODO: write a binding and driver.
++ dsp: dsp@1700000 {
++ compatible = "allwinner,sun20i-d1-dsp";
++ reg = <0x1700000 0x400>;
++ reg-names = "cfg";
++ clocks = <&ccu CLK_BUS_DSP_CFG>,
++ <&ccu CLK_DSP>;
++ clock-names = "cfg", "dsp";
++ resets = <&ccu RST_BUS_DSP_CFG>,
++ <&ccu RST_BUS_DSP_DBG>,
++ <&ccu RST_DSP>;
++ allwinner,sram = <&dsp_sram 1>;
++ interrupts = <136 IRQ_TYPE_LEVEL_HIGH>,
++ <137 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "dee", "pfe";
++ // FIXME: this will be different for R528 (CPUX).
++ mboxes = <&riscv_msgbox MBOX_USER_DSP MBOX_RX>,
++ <&dsp_msgbox MBOX_USER_RISCV MBOX_TX>;
++ mbox-names = "rx", "tx";
++ };
++
++ dsp_wdt: watchdog@1700400 {
++ compatible = "allwinner,sun20i-d1-wdt";
++ reg = <0x1700400 0x20>;
++ clocks = <&osc24M>;
++ interrupts = <138 IRQ_TYPE_LEVEL_HIGH>;
++ status = "reserved";
++ };
++
++ // TODO: write a binding and driver.
++ dsp_msgbox: mailbox@1701000 {
++ compatible = "allwinner,sun20i-d1-msgbox";
++ reg = <0x1701000 0x1000>;
++ clocks = <&ccu CLK_BUS_MSGBOX1>;
++ resets = <&ccu RST_BUS_MSGBOX1>;
++ interrupts = <139 IRQ_TYPE_LEVEL_HIGH>,
++ <140 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "rx", "tx";
++ #mbox-cells = <2>;
++ };
++
++ ve: video-codec@1c0e000 {
++ compatible = "allwinner,sun20i-d1-video-engine";
++ reg = <0x1c0e000 0x2000>;
++ clocks = <&ccu CLK_BUS_VE>,
++ <&ccu CLK_VE>,
++ <&ccu CLK_MBUS_VE>;
++ clock-names = "ahb", "mod", "ram";
++ resets = <&ccu RST_BUS_VE>;
++ allwinner,sram = <&ve_sram 1>;
++ interconnects = <&mbus 4>;
++ interconnect-names = "dma-mem";
++ interrupts = <82 IRQ_TYPE_LEVEL_HIGH>;
++ iommus = <&iommu 0>;
++ };
++
++ gpio: pinctrl@2000000 {
++ compatible = "allwinner,sun20i-d1-pinctrl";
++ #address-cells = <0>;
++ reg = <0x2000000 0x800>;
++ clocks = <&ccu CLK_APB0>,
++ <&osc24M>,
++ <&rtc CLK_OSC32K>;
++ clock-names = "apb", "hosc", "losc";
++ gpio-controller;
++ #gpio-cells = <3>;
++ interrupts = <85 IRQ_TYPE_LEVEL_HIGH>,
++ <87 IRQ_TYPE_LEVEL_HIGH>,
++ <89 IRQ_TYPE_LEVEL_HIGH>,
++ <91 IRQ_TYPE_LEVEL_HIGH>,
++ <93 IRQ_TYPE_LEVEL_HIGH>,
++ <95 IRQ_TYPE_LEVEL_HIGH>;
++ // FIXME: not in binding, should we add these?
++ interrupt-names = "pb", "pc", "pd", "pe", "pf", "pg";
++ interrupt-controller;
++ #interrupt-cells = <3>;
++
++ /omit-if-no-ref/
++ i2c0_pb10_pins: i2c0-pb10-pins {
++ pins = "PB10", "PB11";
++ function = "i2c0";
++ };
++
++ /omit-if-no-ref/
++ i2c2_pb0_pins: i2c2-pb0-pins {
++ pins = "PB0", "PB1";
++ function = "i2c2";
++ };
++
++ /omit-if-no-ref/
++ i2c3_pb6_pins: i2c3-pb6-pins {
++ pins = "PB6", "PB7";
++ function = "i2c3";
++ };
++
++ /omit-if-no-ref/
++ mmc0_pins: mmc0-pins {
++ pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
++ function = "mmc0";
++ };
++
++ /omit-if-no-ref/
++ mmc1_pins: mmc1-pins {
++ pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5";
++ function = "mmc1";
++ };
++
++ /omit-if-no-ref/
++ mmc2_pins: mmc2-pins {
++ pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7";
++ function = "mmc2";
++ };
++
++ /omit-if-no-ref/
++ rgmii_pe_pins: rgmii-pe-pins {
++ pins = "PE0", "PE1", "PE2", "PE3", "PE4",
++ "PE5", "PE6", "PE7", "PE8", "PE9",
++ "PE11", "PE12", "PE13", "PE14", "PE15";
++ function = "emac";
++ };
++
++ /omit-if-no-ref/
++ spi0_pins: spi0-pins {
++ pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7";
++ function = "spi0";
++ };
++
++ /omit-if-no-ref/
++ spi1_pb_pins: spi1-pb-pins {
++ pins = "PB0", "PB8", "PB9", "PB10", "PB11", "PB12";
++ function = "spi1";
++ };
++
++ /omit-if-no-ref/
++ spi1_pd_pins: spi1-pd-pins {
++ pins = "PD10", "PD11", "PD12", "PD13", "PD14", "PD15";
++ function = "spi1";
++ };
++
++ /omit-if-no-ref/
++ uart0_pb8_pins: uart0-pb8-pins {
++ pins = "PB8", "PB9";
++ function = "uart0";
++ };
++
++ /omit-if-no-ref/
++ uart1_pg6_pins: uart1-pg6-pins {
++ pins = "PG6", "PG7";
++ function = "uart1";
++ };
++
++ /omit-if-no-ref/
++ uart1_pg8_rts_cts_pins: uart1-pg8-rts-cts-pins {
++ pins = "PG8", "PG9";
++ function = "uart1";
++ };
++ };
++
++ pwm: pwm@2000c00 {
++ compatible = "allwinner,sun20i-d1-pwm";
++ reg = <0x2000c00 0x400>;
++ clocks = <&ccu CLK_BUS_PWM>, <&osc24M>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_PWM>;
++ interrupts = <34 IRQ_TYPE_LEVEL_HIGH>;
++ #pwm-cells = <3>;
++ status = "disabled";
++ };
++
++ ccu: clock-controller@2001000 {
++ compatible = "allwinner,sun20i-d1-ccu";
++ reg = <0x2001000 0x1000>;
++ clocks = <&osc24M>,
++ <&rtc CLK_OSC32K>,
++ <&rtc CLK_IOSC>;
++ clock-names = "hosc", "losc", "iosc";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ // TODO: write a binding and driver.
++ ir_tx: irled@2003000 {
++ compatible = "allwinner,sun20i-d1-ir-tx";
++ reg = <0x2003000 0x400>;
++ clocks = <&ccu CLK_BUS_IR_TX>,
++ <&osc24M>,
++ <&ccu CLK_IR_TX>;
++ clock-names = "bus", "pclk", "mclk";
++ resets = <&ccu RST_BUS_IR_TX>;
++ dmas = <&dma 13>;
++ dma-names = "tx";
++ interrupts = <35 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ ledc: led-controller@2008000 {
++ compatible = "allwinner,sun20i-d1-ledc",
++ "allwinner,sun50i-r329-ledc";
++ reg = <0x2008000 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_LEDC>, <&ccu CLK_LEDC>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_LEDC>;
++ dmas = <&dma 42>;
++ dma-names = "tx";
++ interrupts = <36 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a binding and driver.
++ gpadc: adc@2009000 {
++ compatible = "allwinner,sun20i-d1-gpadc";
++ reg = <0x2009000 0x400>;
++ clocks = <&ccu CLK_BUS_GPADC>;
++ resets = <&ccu RST_BUS_GPADC>;
++ dmas = <&dma 12>;
++ dma-names = "rx";
++ interrupts = <73 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ ths: temperature-sensor@2009400 {
++ compatible = "allwinner,sun20i-d1-ths";
++ reg = <0x2009400 0x400>;
++ clocks = <&ccu CLK_BUS_THS>, <&osc24M>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_THS>;
++ interrupts = <74 IRQ_TYPE_LEVEL_HIGH>;
++ nvmem-cells = <&ths_calib>;
++ nvmem-cell-names = "calibration";
++ #thermal-sensor-cells = <0>;
++ };
++
++ lradc: keys@2009800 {
++ compatible = "allwinner,sun20i-d1-lradc",
++ "allwinner,sun50i-r329-lradc";
++ reg = <0x2009800 0x400>;
++ clocks = <&ccu CLK_BUS_LRADC>;
++ resets = <&ccu RST_BUS_LRADC>;
++ interrupts = <77 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a binding and driver.
++ tpadc: touchscreen@2009c00 {
++ compatible = "allwinner,sun20i-d1-ts";
++ reg = <0x2009c00 0x400>;
++ clocks = <&ccu CLK_BUS_TPADC>, <&ccu CLK_TPADC>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_TPADC>;
++ dmas = <&dma 13>;
++ dma-names = "rx";
++ interrupts = <78 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // FIXME: this driver probably needs updates.
++ iommu: iommu@2010000 {
++ compatible = "allwinner,sun20i-d1-iommu";
++ reg = <0x2010000 0x10000>;
++ clocks = <&ccu CLK_BUS_IOMMU>;
++ interrupts = <80 IRQ_TYPE_LEVEL_HIGH>;
++ #iommu-cells = <1>;
++ };
++
++ codec: audio-codec@2030000 {
++ compatible = "allwinner,sun20i-d1-audio-codec";
++ reg = <0x2030000 0x1000>;
++ clocks = <&ccu CLK_BUS_AUDIO>,
++ <&ccu CLK_AUDIO_ADC>,
++ <&ccu CLK_AUDIO_DAC>,
++ <&osc24M>,
++ <&rtc CLK_OSC32K>;
++ clock-names = "bus", "adc", "dac", "hosc", "losc";
++ resets = <&ccu RST_BUS_AUDIO>;
++ dmas = <&dma 7>, <&dma 7>;
++ dma-names = "rx", "tx";
++ interrupts = <41 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++
++ regulators {
++ reg_aldo: aldo {
++ regulator-name = "aldo";
++ };
++
++ reg_hpldo: hpldo {
++ regulator-name = "hpldo";
++ };
++ };
++
++ };
++
++ // TODO: try the posted driver.
++ dmic: dmic@2031000 {
++ compatible = "allwinner,sun20i-d1-dmic";
++ reg = <0x2031000 0x400>;
++ clocks = <&ccu CLK_BUS_DMIC>,
++ <&ccu CLK_DMIC>;
++ clock-names = "bus", "mod";
++ resets = <&ccu RST_BUS_DMIC>;
++ dmas = <&dma 8>;
++ dma-names = "rx";
++ interrupts = <40 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ i2s0: i2s@2032000 {
++ compatible = "allwinner,sun20i-d1-i2s";
++ reg = <0x2032000 0x1000>;
++ clocks = <&ccu CLK_BUS_I2S0>,
++ <&ccu CLK_I2S0>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S0>;
++ dmas = <&dma 3>, <&dma 3>;
++ dma-names = "rx", "tx";
++ interrupts = <42 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ i2s1: i2s@2033000 {
++ compatible = "allwinner,sun20i-d1-i2s";
++ reg = <0x2033000 0x1000>;
++ clocks = <&ccu CLK_BUS_I2S1>,
++ <&ccu CLK_I2S1>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S1>;
++ dmas = <&dma 4>, <&dma 4>;
++ dma-names = "rx", "tx";
++ interrupts = <43 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ // TODO: how to integrate ASRC? same or separate node?
++ i2s2: i2s@2034000 {
++ compatible = "allwinner,sun20i-d1-i2s";
++ reg = <0x2034000 0x1000>;
++ clocks = <&ccu CLK_BUS_I2S2>,
++ <&ccu CLK_I2S2>;
++ clock-names = "apb", "mod";
++ resets = <&ccu RST_BUS_I2S2>;
++ dmas = <&dma 5>, <&dma 5>;
++ dma-names = "rx", "tx";
++ interrupts = <44 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ // TODO: add receive functionality
++ spdif: spdif@2036000 {
++ compatible = "allwinner,sun20i-d1-spdif";
++ reg = <0x2036000 0x400>;
++ clocks = <&ccu CLK_BUS_SPDIF>,
++ <&ccu CLK_SPDIF_RX>,
++ <&ccu CLK_SPDIF_TX>;
++ clock-names = "apb", "rx", "tx";
++ resets = <&ccu RST_BUS_SPDIF>;
++ dmas = <&dma 2>, <&dma 2>;
++ dma-names = "rx", "tx";
++ interrupts = <39 IRQ_TYPE_LEVEL_HIGH>;
++ #sound-dai-cells = <0>;
++ status = "disabled";
++ };
++
++ timer: timer@2050000 {
++ compatible = "allwinner,sun20i-d1-timer",
++ "allwinner,sun8i-a23-timer";
++ reg = <0x2050000 0xa0>;
++ clocks = <&osc24M>;
++ interrupts = <75 IRQ_TYPE_LEVEL_HIGH>,
++ <76 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ wdt: watchdog@20500a0 {
++ compatible = "allwinner,sun20i-d1-wdt-reset",
++ "allwinner,sun20i-d1-wdt";
++ reg = <0x20500a0 0x20>;
++ clocks = <&osc24M>;
++ interrupts = <79 IRQ_TYPE_LEVEL_HIGH>;
++ status = "reserved";
++ };
++
++ // TODO: write a driver.
++ uart0: serial@2500000 {
++ compatible = "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2500000 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART0>;
++ resets = <&ccu RST_BUS_UART0>;
++ dmas = <&dma 14>, <&dma 14>;
++ dma-names = "rx", "tx";
++ fifo-size = <64>;
++ interrupts = <18 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a driver, add IDMA?
++ uart1: serial@2500400 {
++ compatible = "allwinner,sun20i-d1-uart1",
++ "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2500400 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART1>;
++ resets = <&ccu RST_BUS_UART1>;
++ dmas = <&dma 15>, <&dma 15>;
++ dma-names = "rx", "tx";
++ fifo-size = <256>;
++ interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a driver.
++ uart2: serial@2500800 {
++ compatible = "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2500800 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART2>;
++ resets = <&ccu RST_BUS_UART2>;
++ dmas = <&dma 16>, <&dma 16>;
++ dma-names = "rx", "tx";
++ fifo-size = <256>;
++ interrupts = <20 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a driver.
++ uart3: serial@2500c00 {
++ compatible = "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2500c00 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART3>;
++ resets = <&ccu RST_BUS_UART3>;
++ dmas = <&dma 17>, <&dma 17>;
++ dma-names = "rx", "tx";
++ fifo-size = <256>;
++ interrupts = <21 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a driver.
++ uart4: serial@2501000 {
++ compatible = "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2501000 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART4>;
++ resets = <&ccu RST_BUS_UART4>;
++ dmas = <&dma 18>, <&dma 18>;
++ dma-names = "rx", "tx";
++ fifo-size = <256>;
++ interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ // TODO: write a driver.
++ uart5: serial@2501400 {
++ compatible = "allwinner,sun20i-d1-uart",
++ "snps,dw-apb-uart";
++ reg = <0x2501400 0x400>;
++ reg-io-width = <4>;
++ reg-shift = <2>;
++ clocks = <&ccu CLK_BUS_UART5>;
++ resets = <&ccu RST_BUS_UART5>;
++ dmas = <&dma 19>, <&dma 19>;
++ dma-names = "rx", "tx";
++ fifo-size = <256>;
++ interrupts = <23 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i2c0: i2c@2502000 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502000 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_I2C0>;
++ resets = <&ccu RST_BUS_I2C0>;
++ dmas = <&dma 43>, <&dma 43>;
++ dma-names = "rx", "tx";
++ interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i2c1: i2c@2502400 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502400 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_I2C1>;
++ resets = <&ccu RST_BUS_I2C1>;
++ dmas = <&dma 44>, <&dma 44>;
++ dma-names = "rx", "tx";
++ interrupts = <26 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i2c2: i2c@2502800 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502800 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_I2C2>;
++ resets = <&ccu RST_BUS_I2C2>;
++ dmas = <&dma 45>, <&dma 45>;
++ dma-names = "rx", "tx";
++ interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ i2c3: i2c@2502c00 {
++ compatible = "allwinner,sun20i-d1-i2c",
++ "allwinner,sun6i-a31-i2c";
++ reg = <0x2502c00 0x400>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_I2C3>;
++ resets = <&ccu RST_BUS_I2C3>;
++ dmas = <&dma 46>, <&dma 46>;
++ dma-names = "rx", "tx";
++ interrupts = <28 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ syscon: syscon@3000000 {
++ compatible = "allwinner,sun20i-d1-system-control";
++ reg = <0x3000000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges;
++
++ regulators {
++ reg_ldoa: ldoa {
++ regulator-name = "ldoa";
++ };
++
++ reg_ldob: ldob {
++ regulator-name = "ldob";
++ };
++ };
++
++ sram@400000 {
++ compatible = "mmio-sram";
++ reg = <0x400000 0x20000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0 0x400000 0x20000>;
++
++ /*
++ * This can be further divided into DSP IRAM,
++ * DSP DRAM0, and DSP DRAM1, but the mapping
++ * of all three is controlled by a single bit.
++ */
++ dsp_sram: sram-section@0 {
++ compatible = "allwinner,sun20i-d1-dsp-sram";
++ reg = <0 0x20000>;
++ };
++ };
++
++ // FIXME: Address is not verified. It is copied from A64/H6.
++ sram@1d00000 {
++ compatible = "mmio-sram";
++ reg = <0x1d00000 0x40000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ ranges = <0 0x1d00000 0x40000>;
++
++ ve_sram: sram-section@0 {
++ compatible = "allwinner,sun20i-d1-sram-c1",
++ "allwinner,sun4i-a10-sram-c1";
++ reg = <0 0x40000>;
++ };
++ };
++ };
++
++ dma: dma-controller@3002000 {
++ compatible = "allwinner,sun20i-d1-dma";
++ reg = <0x3002000 0x1000>;
++ clocks = <&ccu CLK_BUS_DMA>, <&ccu CLK_MBUS_DMA>;
++ clock-names = "bus", "mbus";
++ resets = <&ccu RST_BUS_DMA>;
++ #dma-cells = <1>;
++ dma-channels = <16>;
++ dma-requests = <48>;
++ interrupts = <66 IRQ_TYPE_LEVEL_HIGH>,
++ <142 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ msgbox: mailbox@3003000 {
++ compatible = "allwinner,sun20i-d1-msgbox";
++ reg = <0x3003000 0x1000>;
++ clocks = <&ccu CLK_BUS_MSGBOX0>;
++ resets = <&ccu RST_BUS_MSGBOX0>;
++ interrupts = <101 IRQ_TYPE_LEVEL_HIGH>,
++ <102 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "rx", "tx";
++ #mbox-cells = <2>;
++ };
++
++ hwspinlock: hwlock@3005000 {
++ compatible = "allwinner,sun20i-d1-hwspinlock",
++ "allwinner,sun6i-a31-hwspinlock";
++ reg = <0x3005000 0x1000>;
++ clocks = <&ccu CLK_BUS_SPINLOCK>;
++ resets = <&ccu RST_BUS_SPINLOCK>;
++ interrupts = <70 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ sid: efuse@3006000 {
++ compatible = "allwinner,sun20i-d1-sid",
++ "allwinner,sun50i-a64-sid";
++ reg = <0x3006000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ cpu_speed_grade: cpu-speed-grade@0 {
++ reg = <0x0 0x2>;
++ };
++
++ ths_calib: ths-calib@14 {
++ reg = <0x14 0x4>;
++ };
++ };
++
++ // TODO: write a binding and driver.
++ hstimer: timer@3008000 {
++ compatible = "allwinner,sun20i-d1-hstimer",
++ "allwinner,sun50i-h6-hstimer";
++ reg = <0x3008000 0x1000>;
++ clocks = <&ccu CLK_BUS_HSTIMER>;
++ resets = <&ccu RST_BUS_HSTIMER>;
++ interrupts = <71 IRQ_TYPE_LEVEL_HIGH>,
++ <72 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ crypto: crypto@3040000 {
++ compatible = "allwinner,sun20i-d1-crypto";
++ reg = <0x3040000 0x800>;
++ clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>, <&ccu CLK_MBUS_CE>;
++ clock-names = "bus", "mod", "ram";
++ resets = <&ccu RST_BUS_CE>;
++ interrupts = <68 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // TODO: write a binding and driver.
++ mbus: dram-controller@3102000 {
++ compatible = "allwinner,sun20i-d1-mbus";
++ reg = <0x3102000 0x200000>;
++ #address-cells = <1>;
++ #size-cells = <1>;
++ clocks = <&ccu CLK_BUS_DRAM>,
++ <&ccu CLK_DRAM>,
++ <&ccu CLK_MBUS>;
++ clock-names = "bus", "dram", "mbus";
++ dma-ranges = <0 0x40000000 0x80000000>;
++ #interconnect-cells = <1>;
++ interrupts = <59 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ mmc0: mmc@4020000 {
++ compatible = "allwinner,sun20i-d1-mmc";
++ reg = <0x4020000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC0>;
++ reset-names = "ahb";
++ cap-sd-highspeed;
++ interrupts = <56 IRQ_TYPE_LEVEL_HIGH>;
++ max-frequency = <150000000>;
++ no-mmc;
++ status = "disabled";
++ };
++
++ mmc1: mmc@4021000 {
++ compatible = "allwinner,sun20i-d1-mmc";
++ reg = <0x4021000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC1>;
++ reset-names = "ahb";
++ cap-sd-highspeed;
++ interrupts = <57 IRQ_TYPE_LEVEL_HIGH>;
++ max-frequency = <150000000>;
++ no-mmc;
++ status = "disabled";
++ };
++
++ mmc2: mmc@4022000 {
++ compatible = "allwinner,sun20i-d1-emmc";
++ reg = <0x4022000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
++ clock-names = "ahb", "mmc";
++ resets = <&ccu RST_BUS_MMC2>;
++ reset-names = "ahb";
++ cap-mmc-highspeed;
++ interrupts = <58 IRQ_TYPE_LEVEL_HIGH>;
++ max-frequency = <150000000>;
++ mmc-ddr-1_8v;
++ mmc-ddr-3_3v;
++ no-sd;
++ no-sdio;
++ status = "disabled";
++ };
++
++ spi0: spi@4025000 {
++ compatible = "allwinner,sun20i-d1-spi",
++ "allwinner,sun50i-r329-spi";
++ reg = <0x4025000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
++ clock-names = "ahb", "mod";
++ resets = <&ccu RST_BUS_SPI0>;
++ dmas = <&dma 22>, <&dma 22>;
++ dma-names = "rx", "tx";
++ interrupts = <31 IRQ_TYPE_LEVEL_HIGH>;
++ num-cs = <1>;
++ status = "disabled";
++ };
++
++ spi1: spi@4026000 {
++ compatible = "allwinner,sun20i-d1-spi-dbi",
++ "allwinner,sun50i-r329-spi-dbi",
++ "allwinner,sun50i-r329-spi";
++ reg = <0x4026000 0x1000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>;
++ clock-names = "ahb", "mod";
++ resets = <&ccu RST_BUS_SPI1>;
++ dmas = <&dma 23>, <&dma 23>;
++ dma-names = "rx", "tx";
++ interrupts = <32 IRQ_TYPE_LEVEL_HIGH>;
++ num-cs = <1>;
++ status = "disabled";
++ };
++
++ usb_otg: usb@4100000 {
++ compatible = "allwinner,sun20i-d1-musb",
++ "allwinner,sun8i-a33-musb";
++ reg = <0x4100000 0x400>;
++ clocks = <&ccu CLK_BUS_OTG>;
++ resets = <&ccu RST_BUS_OTG>;
++ dmas = <&dma 30>, <&dma 30>,
++ <&dma 31>, <&dma 31>,
++ <&dma 32>, <&dma 32>,
++ <&dma 33>, <&dma 33>,
++ <&dma 34>, <&dma 34>;
++ dma-names = "ep1_rx", "ep1_tx",
++ "ep2_rx", "ep2_tx",
++ "ep3_rx", "ep3_tx",
++ "ep4_rx", "ep4_tx",
++ "ep5_rx", "ep5_tx";
++ extcon = <&usbphy 0>;
++ interrupts = <45 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "mc";
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ usbphy: phy@4100400 {
++ compatible = "allwinner,sun20i-d1-usb-phy";
++ reg = <0x4100400 0x100>,
++ <0x4101800 0x100>,
++ <0x4200800 0x100>;
++ reg-names = "phy_ctrl",
++ "pmu0",
++ "pmu1";
++ clocks = <&osc24M>,
++ <&osc24M>;
++ clock-names = "usb0_phy",
++ "usb1_phy";
++ resets = <&ccu RST_USB_PHY0>,
++ <&ccu RST_USB_PHY1>;
++ reset-names = "usb0_reset",
++ "usb1_reset";
++ #phy-cells = <1>;
++ status = "disabled";
++ };
++
++ ehci0: usb@4101000 {
++ compatible = "allwinner,sun20i-d1-ehci",
++ "generic-ehci";
++ reg = <0x4101000 0x100>;
++ clocks = <&ccu CLK_BUS_OHCI0>,
++ <&ccu CLK_BUS_EHCI0>,
++ <&ccu CLK_USB_OHCI0>;
++ resets = <&ccu RST_BUS_OHCI0>,
++ <&ccu RST_BUS_EHCI0>;
++ interrupts = <46 IRQ_TYPE_LEVEL_HIGH>;
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ohci0: usb@4101400 {
++ compatible = "allwinner,sun20i-d1-ohci",
++ "generic-ohci";
++ reg = <0x4101400 0x100>;
++ clocks = <&ccu CLK_BUS_OHCI0>,
++ <&ccu CLK_USB_OHCI0>;
++ resets = <&ccu RST_BUS_OHCI0>;
++ interrupts = <47 IRQ_TYPE_LEVEL_HIGH>;
++ phys = <&usbphy 0>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ehci1: usb@4200000 {
++ compatible = "allwinner,sun20i-d1-ehci",
++ "generic-ehci";
++ reg = <0x4200000 0x100>;
++ clocks = <&ccu CLK_BUS_OHCI1>,
++ <&ccu CLK_BUS_EHCI1>,
++ <&ccu CLK_USB_OHCI1>;
++ resets = <&ccu RST_BUS_OHCI1>,
++ <&ccu RST_BUS_EHCI1>;
++ interrupts = <49 IRQ_TYPE_LEVEL_HIGH>;
++ phys = <&usbphy 1>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ ohci1: usb@4200400 {
++ compatible = "allwinner,sun20i-d1-ohci",
++ "generic-ohci";
++ reg = <0x4200400 0x100>;
++ clocks = <&ccu CLK_BUS_OHCI1>,
++ <&ccu CLK_USB_OHCI1>;
++ resets = <&ccu RST_BUS_OHCI1>;
++ interrupts = <50 IRQ_TYPE_LEVEL_HIGH>;
++ phys = <&usbphy 1>;
++ phy-names = "usb";
++ status = "disabled";
++ };
++
++ emac: ethernet@4500000 {
++ compatible = "allwinner,sun20i-d1-emac",
++ "allwinner,sun50i-a64-emac";
++ reg = <0x4500000 0x10000>;
++ clocks = <&ccu CLK_BUS_EMAC>;
++ clock-names = "stmmaceth";
++ resets = <&ccu RST_BUS_EMAC>;
++ reset-names = "stmmaceth";
++ interrupts = <62 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "macirq";
++ syscon = <&syscon>;
++ status = "disabled";
++
++ mdio: mdio {
++ compatible = "snps,dwmac-mdio";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++ };
++
++ de: display-engine@5000000 {
++ reg = <0x5000000 0x400000>;
++ interrupts = <103 IRQ_TYPE_LEVEL_HIGH>;
++ interconnects = <&mbus 11>;
++ interconnect-names = "dma-mem";
++ iommus = <&iommu 2>;
++ };
++
++ deinterlace: deinterlace@5400000 {
++ reg = <0x5400000 0x10000>;
++ interconnects = <&mbus 10>;
++ interconnect-names = "dma-mem";
++ interrupts = <104 IRQ_TYPE_LEVEL_HIGH>;
++ iommus = <&iommu 4>;
++ };
++
++ g2d: g2d@5410000 {
++ reg = <0x5410000 0x40000>;
++ interconnects = <&mbus 9>;
++ interconnect-names = "dma-mem";
++ interrupts = <105 IRQ_TYPE_LEVEL_HIGH>;
++ iommus = <&iommu 3>;
++ };
++
++ dsi: dsi@5450000 {
++ reg = <0x5450000 0x2000>;
++ interrupts = <108 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ tcon_top: tcon-top@5460000 {
++ reg = <0x5460000 0x1000>;
++ };
++
++ tcon_lcd: lcd-controller@5461000 {
++ reg = <0x5461000 0x1000>;
++ interrupts = <106 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ tcon_tv: lcd-controller@5470000 {
++ reg = <0x5470000 0x1000>;
++ interrupts = <107 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ hdmi: hdmi@5500000 {
++ reg = <0x5500000 0x100000>;
++ interrupts = <109 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ tve_top: video-codec@5600000 {
++ reg = <0x5600000 0x4000>;
++ };
++
++ tve0: video-codec@5604000 {
++ reg = <0x5604000 0x4000>;
++ interrupts = <110 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ csi: csi@5800000 {
++ reg = <0x5800000 0x400000>;
++ interrupts = <111 IRQ_TYPE_LEVEL_HIGH>,
++ <112 IRQ_TYPE_LEVEL_HIGH>,
++ <116 IRQ_TYPE_LEVEL_HIGH>,
++ <122 IRQ_TYPE_LEVEL_HIGH>;
++ interconnects = <&mbus 7>;
++ interconnect-names = "dma-mem";
++ iommus = <&iommu 1>;
++ };
++
++ tvd_top: video-codec@5c00000 {
++ reg = <0x5c00000 0x1000>;
++ interconnects = <&mbus 6>;
++ interconnect-names = "dma-mem";
++ };
++
++ tvd0: video-codec@5c01000 {
++ reg = <0x5c01000 0x1000>;
++ interrupts = <123 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ intc: interrupt-controller@6010000 {
++ compatible = "allwinner,sun20i-d1-intc";
++ reg = <0x6010000 0x100>;
++ #address-cells = <0>;
++ clocks = <&ccu CLK_BUS_RISCV_CFG>;
++ resets = <&ccu RST_BUS_RISCV_CFG>;
++ interrupt-parent = <&plic>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ };
++
++ riscv_wdt: watchdog@6011000 {
++ compatible = "allwinner,sun20i-d1-wdt";
++ reg = <0x6011000 0x20>;
++ clocks = <&osc24M>;
++ interrupts = <147 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ riscv_msgbox: mailbox@601f000 {
++ compatible = "allwinner,sun20i-d1-msgbox";
++ reg = <0x601f000 0x1000>;
++ clocks = <&ccu CLK_BUS_MSGBOX2>;
++ resets = <&ccu RST_BUS_MSGBOX2>;
++ interrupts = <144 IRQ_TYPE_LEVEL_HIGH>,
++ <145 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "rx", "tx";
++ #mbox-cells = <2>;
++ };
++
++ r_ccu: clock-controller@7010000 {
++ compatible = "allwinner,sun20i-d1-r-ccu";
++ reg = <0x7010000 0x400>;
++ clocks = <&osc24M>,
++ <&rtc CLK_OSC32K>,
++ <&rtc CLK_IOSC>,
++ <&ccu CLK_PLL_PERIPH0_DIV3>;
++ clock-names = "hosc", "losc", "iosc", "pll-periph";
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ interrupts = <64 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ r_ir_rx: ir@7040000 {
++ compatible = "allwinner,sun20i-d1-ir",
++ "allwinner,sun6i-a31-ir";
++ reg = <0x7040000 0x400>;
++ clocks = <&r_ccu CLK_BUS_R_IR_RX>, <&r_ccu CLK_R_IR_RX>;
++ clock-names = "apb", "ir";
++ resets = <&r_ccu RST_BUS_R_IR_RX>;
++ interrupts = <167 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // TODO: audit all blocks for hidden use of CLK_DCXO24M
++ rtc: rtc@7090000 {
++ compatible = "allwinner,sun20i-d1-rtc",
++ "allwinner,sun50i-r329-rtc";
++ reg = <0x7090000 0x400>;
++ clocks = <&r_ccu CLK_BUS_R_RTC>,
++ <&osc24M>,
++ <&r_ccu CLK_R_AHB>;
++ clock-names = "bus", "hosc", "ahb";
++ #clock-cells = <1>;
++ interrupts = <160 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ plic: interrupt-controller@10000000 {
++ compatible = "allwinner,sun20i-d1-plic",
++ "thead,c900-plic";
++ reg = <0x10000000 0x4000000>;
++ #address-cells = <0>;
++ interrupts-extended = <&cpu0_intc 11>,
++ <&cpu0_intc 9>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ riscv,ndev = <176>;
++ };
++
++ clint: clint@14000000 {
++ compatible = "allwinner,sun20i-d1-clint",
++ "sifive,clint0";
++ reg = <0x14000000 0xc000>;
++ reg-io-width = <4>;
++ interrupts-extended = <&cpu0_intc 3>,
++ <&cpu0_intc 7>;
++ };
++ };
++};
+--
+2.20.1
+
--- /dev/null
+From 5ff65d483cc6aa18f21900a72ab3e78eb2308031 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 16 May 2021 14:18:46 -0500
+Subject: [PATCH 120/124] riscv: Add D1 Nezha board device tree
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/boot/dts/allwinner/Makefile | 1 +
+ .../boot/dts/allwinner/sun20i-d1-nezha.dts | 377 ++++++++++++++++++
+ 2 files changed, 378 insertions(+)
+ create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts
+
+diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile
+index f66554cd5c45..b0a15e8c8d82 100644
+--- a/arch/riscv/boot/dts/allwinner/Makefile
++++ b/arch/riscv/boot/dts/allwinner/Makefile
+@@ -1 +1,2 @@
+ # SPDX-License-Identifier: GPL-2.0
++dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha.dtb
+diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts
+new file mode 100644
+index 000000000000..cab31cb2b346
+--- /dev/null
++++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts
+@@ -0,0 +1,377 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++
++/dts-v1/;
++
++#include "sun20i-d1.dtsi"
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/input/input.h>
++#include <dt-bindings/leds/common.h>
++#include <dt-bindings/pwm/pwm.h>
++
++/ {
++ model = "Allwinner D1 NeZha";
++ compatible = "allwinner,d1-nezha", "allwinner,sun20i-d1";
++
++ aliases {
++ ethernet0 = &emac;
++ mmc0 = &mmc0;
++ mmc1 = &mmc1;
++ mmc2 = &mmc2;
++ serial0 = &uart0;
++ spi0 = &spi0;
++ };
++
++ memory@40000000 {
++ device_type = "memory";
++ reg = <0x40000000 0x20000000>;
++ };
++
++ chosen {
++ stdout-path = "serial0:115200n8";
++ };
++
++ hdmi_connector: connector {
++ compatible = "hdmi-connector";
++ type = "a";
++
++ port {
++ hdmi_con_in: endpoint {
++ // FIXME: remote-endpoint = <&hdmi_out_con>;
++ };
++ };
++ };
++
++ reg_usbvbus: usbvbus {
++ compatible = "regulator-fixed";
++ regulator-name = "usbvbus";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ gpio = <&gpio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */
++ enable-active-high;
++ vin-supply = <®_vcc>;
++ };
++
++ reg_vcc: vcc {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ };
++
++ reg_vcc_3v3: vcc-3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ vin-supply = <®_vcc>;
++ };
++
++ reg_vdd_cpu: vdd-cpu {
++ compatible = "pwm-regulator";
++ pwms = <&pwm 0 50000 0>;
++ pwm-supply = <®_vcc>;
++ regulator-name = "vdd-cpu";
++ regulator-min-microvolt = <810000>;
++ regulator-max-microvolt = <1160000>;
++ };
++
++ wifi_pwrseq: wifi-pwrseq {
++ compatible = "mmc-pwrseq-simple";
++ reset-gpios = <&gpio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */
++ };
++};
++
++&codec {
++ allwinner,routing = "Headphone Jack", "HPOUTL",
++ "Headphone Jack", "HPOUTR",
++ "LINEINL", "HPOUTL",
++ "LINEINR", "HPOUTR",
++ "MICIN3", "Headset Microphone",
++ "Headset Microphone", "HBIAS";
++ allwinner,widgets = "Microphone", "Headset Microphone",
++ "Headphone", "Headphone Jack";
++ avcc-supply = <®_aldo>;
++ hpvcc-supply = <®_hpldo>;
++ vdd33-supply = <®_vcc_3v3>;
++ status = "okay";
++};
++
++&cpu0 {
++ cpu-supply = <®_vdd_cpu>;
++};
++
++&ehci0 {
++ status = "okay";
++};
++
++&ehci1 {
++ status = "okay";
++};
++
++&emac {
++ pinctrl-0 = <&rgmii_pe_pins>;
++ pinctrl-names = "default";
++ phy-handle = <&ext_rgmii_phy>;
++ phy-mode = "rgmii-id";
++ phy-supply = <®_vcc_3v3>;
++ status = "okay";
++};
++
++&gpio {
++ vcc-pa-supply = <®_vcc_3v3>;
++ vcc-pb-supply = <®_vcc_3v3>;
++ vcc-pc-supply = <®_vcc_3v3>;
++ vcc-pd-supply = <®_vcc_3v3>;
++ vcc-pe-supply = <®_vcc_3v3>;
++ vcc-pf-supply = <®_vcc_3v3>;
++ vcc-pg-supply = <®_vcc_3v3>;
++
++ i2s2_pb_pins: i2s2-pb-pins {
++ pins = "PB5", "PB6", "PB7";
++ function = "i2s2";
++ };
++
++ i2s2_pb3_din_pin: i2s2-pb3-din-pin {
++ pins = "PB3";
++ function = "i2s2_din";
++ };
++
++ i2s2_pb4_dout_pin: i2s2-pb4-dout-pin {
++ pins = "PB4";
++ function = "i2s2_dout";
++ };
++
++ ledc_pc0_pin: ledc-pc0-pin {
++ pins = "PC0";
++ function = "ledc";
++ };
++
++ pwm0_pd16_pin: pwm0-pd16-pin {
++ pins = "PD16";
++ function = "pwm";
++ };
++
++ pwm2_pd18_pin: pwm2-pd18-pin {
++ pins = "PD18";
++ function = "pwm";
++ };
++
++ pwm7_pd22_pin: pwm7-pd22-pin {
++ pins = "PD22";
++ function = "pwm";
++ };
++
++ spdif_pd22_pin: spdif-pd22-pin {
++ pins = "PD22";
++ function = "spdif";
++ };
++};
++
++&i2c0 {
++ pinctrl-0 = <&i2c0_pb10_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&i2c2 {
++ pinctrl-0 = <&i2c2_pb0_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++
++ pcf8574a: gpio@38 {
++ compatible = "nxp,pcf8574a";
++ #address-cells = <0>;
++ reg = <0x38>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupts-extended = <&gpio 1 2 IRQ_TYPE_LEVEL_LOW>; /* PB2 */
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ };
++};
++
++&i2s2 {
++ pinctrl-0 = <&i2s2_pb_pins>, <&i2s2_pb3_din_pin>, <&i2s2_pb4_dout_pin>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&ledc {
++ pinctrl-0 = <&ledc_pc0_pin>;
++ pinctrl-names = "default";
++ status = "okay";
++
++ led@0 {
++ reg = <0x0>;
++ color = <LED_COLOR_ID_RGB>;
++ function = LED_FUNCTION_INDICATOR;
++ };
++};
++
++&lradc {
++ vref-supply = <®_aldo>;
++ wakeup-source;
++ status = "okay";
++
++ button-160 {
++ label = "OK";
++ linux,code = <KEY_OK>;
++ channel = <0>;
++ voltage = <160000>;
++ };
++};
++
++&mdio {
++ ext_rgmii_phy: ethernet-phy@1 {
++ compatible = "ethernet-phy-ieee802.3-c22";
++ reg = <1>;
++ };
++};
++
++&mmc0 {
++ bus-width = <4>;
++ cd-gpios = <&gpio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */
++ disable-wp;
++ vmmc-supply = <®_vcc_3v3>;
++ vqmmc-supply = <®_vcc_3v3>;
++ pinctrl-0 = <&mmc0_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&mmc1 {
++ bus-width = <4>;
++ mmc-pwrseq = <&wifi_pwrseq>;
++ non-removable;
++ vmmc-supply = <®_vcc_3v3>;
++ vqmmc-supply = <®_vcc_3v3>;
++ pinctrl-0 = <&mmc1_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++
++ xr829: wifi@1 {
++ reg = <1>;
++ host-wake-gpios = <&gpio 6 10 GPIO_ACTIVE_LOW>; /* PG10 */
++ };
++};
++
++&ohci0 {
++ status = "okay";
++};
++
++&ohci1 {
++ status = "okay";
++};
++
++&pwm {
++ pinctrl-0 = <&pwm0_pd16_pin>, <&pwm2_pd18_pin>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++®_aldo {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ vdd33-supply = <®_vcc_3v3>;
++};
++
++®_hpldo {
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ hpldoin-supply = <®_vcc_3v3>;
++};
++
++®_ldoa {
++ regulator-always-on;
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ ldo-in-supply = <®_vcc_3v3>;
++};
++
++&spdif {
++ pinctrl-0 = <&spdif_pd22_pin>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&spi0 {
++ pinctrl-0 = <&spi0_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++
++ flash@0 {
++ compatible = "spi-nand";
++ reg = <0>;
++
++ partitions {
++ compatible = "fixed-partitions";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ partition@0 {
++ label = "boot0";
++ reg = <0x00000000 0x00100000>;
++ };
++
++ partition@100000 {
++ label = "uboot";
++ reg = <0x00100000 0x00300000>;
++ };
++
++ partition@400000 {
++ label = "secure_storage";
++ reg = <0x00400000 0x00100000>;
++ };
++
++ partition@500000 {
++ label = "sys";
++ reg = <0x00500000 0x0fb00000>;
++ };
++ };
++ };
++};
++
++&spi1 {
++ pinctrl-0 = <&spi1_pd_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&ths {
++ vref-supply = <®_aldo>;
++};
++
++&uart0 {
++ pinctrl-0 = <&uart0_pb8_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&uart1 {
++ pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++
++ bluetooth {
++ compatible = "xradio,xr829-bt";
++ device-wakeup-gpios = <&gpio 6 16 GPIO_ACTIVE_LOW>; /* PG16 */
++ interrupts-extended = <&gpio 6 17 IRQ_TYPE_LEVEL_LOW>; /* PG17 */
++ interrupt-names = "wakeup";
++ reset-gpios = <&gpio 6 18 GPIO_ACTIVE_LOW>; /* PG18 */
++ };
++};
++
++&usb_otg {
++ dr_mode = "otg";
++ status = "okay";
++};
++
++&usbphy {
++ usb0_id_det-gpios = <&gpio 3 21 GPIO_ACTIVE_LOW>; /* PD21 */
++ usb0_vbus_det-gpios = <&gpio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */
++ usb0_vbus-supply = <®_usbvbus>;
++ usb1_vbus-supply = <®_vcc>;
++ status = "okay";
++};
+--
+2.20.1
+
--- /dev/null
+From bbac293092123341e0eaa3905508e2264f6b1cec Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 13 Jun 2021 23:54:42 -0500
+Subject: [PATCH 121/124] misc changes
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/Kconfig | 2 +-
+ arch/riscv/mm/physaddr.c | 2 +-
+ drivers/mmc/host/sunxi-mmc.c | 15 +++++++++++----
+ drivers/of/irq.c | 7 ++++---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 8 ++++++++
+ include/linux/dmaengine.h | 2 +-
+ sound/soc/soc-core.c | 3 +++
+ sound/soc/soc-dapm.c | 2 +-
+ sound/soc/sunxi/Makefile | 4 ++--
+ sound/soc/sunxi/sun4i-i2s.c | 6 ------
+ sound/soc/sunxi/sun4i-spdif.c | 11 +++++++++--
+ 11 files changed, 41 insertions(+), 21 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 5f11f14f93a8..b942ba3e8eae 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -261,7 +261,7 @@ config ARCH_RV64I
+ select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL
+ select HAVE_FUNCTION_GRAPH_TRACER
+ select HAVE_FUNCTION_TRACER if !XIP_KERNEL
+- select SWIOTLB if MMU
++ # select SWIOTLB if MMU
+
+ endchoice
+
+diff --git a/arch/riscv/mm/physaddr.c b/arch/riscv/mm/physaddr.c
+index e7fd0c253c7b..917d9868bb90 100644
+--- a/arch/riscv/mm/physaddr.c
++++ b/arch/riscv/mm/physaddr.c
+@@ -14,7 +14,7 @@ phys_addr_t __virt_to_phys(unsigned long x)
+ * Boundary checking aginst the kernel linear mapping space.
+ */
+ WARN(y >= KERN_VIRT_SIZE,
+- "virt_to_phys used for non-linear address: %pK (%pS)\n",
++ "virt_to_phys used for non-linear address: %p (%pS)\n",
+ (void *)x, (void *)x);
+
+ return __va_to_pa_nodebug(x);
+diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
+index e6a21bcc3b47..09a85de3e5f2 100644
+--- a/drivers/mmc/host/sunxi-mmc.c
++++ b/drivers/mmc/host/sunxi-mmc.c
+@@ -749,6 +749,17 @@ static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
+ */
+ writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off);
+
++#if 0
++ writel(SDXC_CAL_START, host->reg_base + reg_off);
++
++ unsigned long expire = jiffies + msecs_to_jiffies(250);
++ u32 rval;
++
++ do {
++ rval = readl(host->reg_base + reg_off);
++ } while (time_before(jiffies, expire) && !(rval & SDXC_CAL_DONE));
++#endif
++
+ return 0;
+ }
+
+@@ -1211,7 +1222,6 @@ static const struct sunxi_mmc_cfg sun20i_d1_cfg = {
+
+ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+ .idma_des_size_bits = 16,
+- .clk_delays = NULL,
+ .can_calibrate = true,
+ .mask_data0 = true,
+ .needs_new_timings = true,
+@@ -1219,7 +1229,6 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
+
+ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+ .idma_des_size_bits = 13,
+- .clk_delays = NULL,
+ .can_calibrate = true,
+ .needs_new_timings = true,
+ };
+@@ -1227,7 +1236,6 @@ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+ static const struct sunxi_mmc_cfg sun50i_a100_cfg = {
+ .idma_des_size_bits = 16,
+ .idma_des_shift = 2,
+- .clk_delays = NULL,
+ .can_calibrate = true,
+ .mask_data0 = true,
+ .needs_new_timings = true,
+@@ -1236,7 +1244,6 @@ static const struct sunxi_mmc_cfg sun50i_a100_cfg = {
+ static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = {
+ .idma_des_size_bits = 13,
+ .idma_des_shift = 2,
+- .clk_delays = NULL,
+ .can_calibrate = true,
+ .needs_new_timings = true,
+ };
+diff --git a/drivers/of/irq.c b/drivers/of/irq.c
+index d84d27fb2203..33878ffb9a49 100644
+--- a/drivers/of/irq.c
++++ b/drivers/of/irq.c
+@@ -511,6 +511,8 @@ void __init of_irq_init(const struct of_device_id *matches)
+ desc->interrupt_parent = of_irq_find_parent(np);
+ if (desc->interrupt_parent == np)
+ desc->interrupt_parent = NULL;
++ pr_notice("of_irq_init: found %pOF with parent %pOF\n",
++ desc->dev, desc->interrupt_parent);
+ list_add_tail(&desc->list, &intc_desc_list);
+ }
+
+@@ -535,9 +537,8 @@ void __init of_irq_init(const struct of_device_id *matches)
+
+ of_node_set_flag(desc->dev, OF_POPULATED);
+
+- pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
+- desc->dev,
+- desc->dev, desc->interrupt_parent);
++ pr_notice("of_irq_init: init %pOF with parent %pOF\n",
++ desc->dev, desc->interrupt_parent);
+ ret = desc->irq_init_cb(desc->dev,
+ desc->interrupt_parent);
+ if (ret) {
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index 8a9c0ee98d8e..d5814eaac183 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -10,6 +10,7 @@
+ * warranty of any kind, whether express or implied.
+ */
+
++#define DEBUG
+ #include <linux/io.h>
+ #include <linux/clk.h>
+ #include <linux/gpio/driver.h>
+@@ -1026,8 +1027,15 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d)
+ u32 status_reg = sunxi_irq_status_reg(pctl->desc, d->hwirq);
+ u8 status_idx = sunxi_irq_status_offset(d->hwirq);
+
++ u32 old = readl(pctl->membase + status_reg);
++
+ /* Clear the IRQ */
+ writel(1 << status_idx, pctl->membase + status_reg);
++
++ u32 new = readl(pctl->membase + status_reg);
++
++ pr_err("acked %ld in 0x%08x, was 0x%08x, now 0x%08x\n",
++ d->hwirq, status_reg, old, new);
+ }
+
+ static void sunxi_pinctrl_irq_mask(struct irq_data *d)
+diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
+index e5c2c9e71bf1..1a9072556c01 100644
+--- a/include/linux/dmaengine.h
++++ b/include/linux/dmaengine.h
+@@ -394,7 +394,7 @@ enum dma_slave_buswidth {
+ * should be read (RX), if the source is memory this argument is
+ * ignored.
+ * @dst_addr: this is the physical address where DMA slave data
+- * should be written (TX), if the source is memory this argument
++ * should be written (TX), if the destination is memory this argument
+ * is ignored.
+ * @src_addr_width: this is the width in bytes of the source (RX)
+ * register where DMA data shall be read. If the source
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index 80ca260595fd..21ec90b1901a 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -17,6 +17,7 @@
+ // o Add more codecs and platforms to ensure good API coverage.
+ // o Support TDM on PCM and I2S
+
++#define DEBUG
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+ #include <linux/init.h>
+@@ -1042,6 +1043,8 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
+ if (!snd_soc_is_matching_component(platform, component))
+ continue;
+
++ dev_warn(card->dev, "ASoC: Adding component %s for platform %pOF\n",
++ component->name, platform->of_node);
+ snd_soc_rtd_add_component(rtd, component);
+ }
+ }
+diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
+index 59d07648a7e7..2c2ce6682eeb 100644
+--- a/sound/soc/soc-dapm.c
++++ b/sound/soc/soc-dapm.c
+@@ -2339,7 +2339,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
+ * right channel.
+ *
+ * A stereo control is signified by a valid 'rconnect'
+- * value, either 0 for unconnected, or >= 0 for connected.
++ * value, either 0 for unconnected, or > 0 for connected.
+ * This is chosen instead of using snd_soc_volsw_is_stereo,
+ * so that the behavior of snd_soc_dapm_mixer_update_power
+ * doesn't change even when the kcontrol passed in is
+diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
+index 7bbe2526e16e..4392e0e49dd0 100644
+--- a/sound/soc/sunxi/Makefile
++++ b/sound/soc/sunxi/Makefile
+@@ -2,8 +2,8 @@
+ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+ obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
+ obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
++obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o
++obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
+ obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o
+ obj-$(CONFIG_SND_SUN20I_CODEC) += sun20i-codec.o
+ obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o
+-obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o
+-obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o
+diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
+index 75075acc8658..7f7d45d2827c 100644
+--- a/sound/soc/sunxi/sun4i-i2s.c
++++ b/sound/soc/sunxi/sun4i-i2s.c
+@@ -38,10 +38,6 @@
+ #define SUN4I_I2S_FMT0_BCLK_POLARITY_MASK BIT(6)
+ #define SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 6)
+ #define SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 6)
+-#define SUN4I_I2S_FMT0_SR_MASK GENMASK(5, 4)
+-#define SUN4I_I2S_FMT0_SR(sr) ((sr) << 4)
+-#define SUN4I_I2S_FMT0_WSS_MASK GENMASK(3, 2)
+-#define SUN4I_I2S_FMT0_WSS(wss) ((wss) << 2)
+ #define SUN4I_I2S_FMT0_FMT_MASK GENMASK(1, 0)
+ #define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0)
+ #define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0)
+@@ -71,8 +67,6 @@
+ #define SUN4I_I2S_INT_STA_REG 0x20
+
+ #define SUN4I_I2S_CLK_DIV_REG 0x24
+-#define SUN4I_I2S_CLK_DIV_MCLK_EN BIT(7)
+-#define SUN4I_I2S_CLK_DIV_BCLK_MASK GENMASK(6, 4)
+ #define SUN4I_I2S_CLK_DIV_BCLK(bclk) ((bclk) << 4)
+ #define SUN4I_I2S_CLK_DIV_MCLK_MASK GENMASK(3, 0)
+ #define SUN4I_I2S_CLK_DIV_MCLK(mclk) ((mclk) << 0)
+diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
+index ecbe8e51b9a5..e2a8aaaf53dd 100644
+--- a/sound/soc/sunxi/sun4i-spdif.c
++++ b/sound/soc/sunxi/sun4i-spdif.c
+@@ -53,6 +53,8 @@
+
+ #define SUN4I_SPDIF_TXFIFO (0x0C)
+
++#define SUN8I_SPDIF_ISTA (0x0C)
++
+ #define SUN4I_SPDIF_RXFIFO (0x10)
+
+ #define SUN4I_SPDIF_FCTL (0x14)
+@@ -81,9 +83,14 @@
+
+ #define SUN4I_SPDIF_FSTA (0x18)
+ #define SUN4I_SPDIF_FSTA_TXE BIT(14)
+- #define SUN4I_SPDIF_FSTA_TXECNTSHT (8)
++ #define SUN4I_SPDIF_FSTA_TXE_CNT (8)
+ #define SUN4I_SPDIF_FSTA_RXA BIT(6)
+- #define SUN4I_SPDIF_FSTA_RXACNTSHT (0)
++ #define SUN4I_SPDIF_FSTA_RXA_CNT (0)
++
++ #define SUN8I_SPDIF_FSTA_TXE BIT(31)
++ #define SUN8I_SPDIF_FSTA_TXE_CNT (16)
++ #define SUN8I_SPDIF_FSTA_RXA BIT(15)
++ #define SUN8I_SPDIF_FSTA_RXA_CNT (0)
+
+ #define SUN4I_SPDIF_INT (0x1C)
+ #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18)
+--
+2.20.1
+
--- /dev/null
+From b63fd7efd835bb7a1603ba821c6cb9db14a6fb06 Mon Sep 17 00:00:00 2001
+From: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+Date: Tue, 12 Oct 2021 10:41:58 +0200
+Subject: [PATCH 122/124] pinctrl: sunxi: sunxi_pinctrl_irq_ack avoid build
+ warning
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Avoid a build warning
+
+drivers/pinctrl/sunxi/pinctrl-sunxi.c:
+In function ‘sunxi_pinctrl_irq_ack’:
+drivers/pinctrl/sunxi/pinctrl-sunxi.c:1035:2: warning:
+ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
+ 1035 | u32 new = readl(pctl->membase + status_reg);
+ | ^~~
+
+Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+---
+ drivers/pinctrl/sunxi/pinctrl-sunxi.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+index d5814eaac183..5f3410f92e4a 100644
+--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
++++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+@@ -1026,13 +1026,14 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d)
+ struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
+ u32 status_reg = sunxi_irq_status_reg(pctl->desc, d->hwirq);
+ u8 status_idx = sunxi_irq_status_offset(d->hwirq);
++ u32 new, old;
+
+- u32 old = readl(pctl->membase + status_reg);
++ old = readl(pctl->membase + status_reg);
+
+ /* Clear the IRQ */
+ writel(1 << status_idx, pctl->membase + status_reg);
+
+- u32 new = readl(pctl->membase + status_reg);
++ new = readl(pctl->membase + status_reg);
+
+ pr_err("acked %ld in 0x%08x, was 0x%08x, now 0x%08x\n",
+ d->hwirq, status_reg, old, new);
+--
+2.20.1
+
--- /dev/null
+From 4423494ac9e059965dfb62d3080d3947a0691770 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 8 Aug 2021 21:27:43 -0500
+Subject: [PATCH 123/124] Add a defconfig for the Nezha
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ arch/riscv/configs/nezha_defconfig | 236 +++++++++++++++++++++++++++++
+ 1 file changed, 236 insertions(+)
+ create mode 100644 arch/riscv/configs/nezha_defconfig
+
+diff --git a/arch/riscv/configs/nezha_defconfig b/arch/riscv/configs/nezha_defconfig
+new file mode 100644
+index 000000000000..f846fe210744
+--- /dev/null
++++ b/arch/riscv/configs/nezha_defconfig
+@@ -0,0 +1,236 @@
++CONFIG_DEFAULT_HOSTNAME="nezha"
++# CONFIG_SWAP is not set
++# CONFIG_CROSS_MEMORY_ATTACH is not set
++CONFIG_NO_HZ_IDLE=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_PREEMPT=y
++CONFIG_EXPERT=y
++# CONFIG_SYSFS_SYSCALL is not set
++# CONFIG_VM_EVENT_COUNTERS is not set
++# CONFIG_SLUB_DEBUG is not set
++# CONFIG_COMPAT_BRK is not set
++# CONFIG_SLAB_MERGE_DEFAULT is not set
++CONFIG_ARCH_SUNXI=y
++# CONFIG_RISCV_ERRATA_ALTERNATIVE is not set
++# CONFIG_RISCV_ISA_C is not set
++# CONFIG_EFI is not set
++CONFIG_PM_AUTOSLEEP=y
++CONFIG_PM_WAKELOCKS=y
++CONFIG_PM_DEBUG=y
++CONFIG_PM_ADVANCED_DEBUG=y
++CONFIG_PM_TEST_SUSPEND=y
++CONFIG_JUMP_LABEL=y
++# CONFIG_SECCOMP is not set
++# CONFIG_STACKPROTECTOR is not set
++# CONFIG_GCC_PLUGINS is not set
++# CONFIG_MQ_IOSCHED_DEADLINE is not set
++# CONFIG_MQ_IOSCHED_KYBER is not set
++# CONFIG_COREDUMP is not set
++CONFIG_NET=y
++CONFIG_PACKET=y
++CONFIG_UNIX=y
++CONFIG_INET=y
++# CONFIG_INET_DIAG is not set
++# CONFIG_IPV6_SIT is not set
++# CONFIG_WIRELESS is not set
++# CONFIG_ETHTOOL_NETLINK is not set
++CONFIG_DEVTMPFS=y
++CONFIG_DEVTMPFS_MOUNT=y
++# CONFIG_ALLOW_DEV_COREDUMP is not set
++CONFIG_MTD=y
++CONFIG_MTD_BLOCK=y
++CONFIG_MTD_PARTITIONED_MASTER=y
++CONFIG_MTD_SPI_NAND=y
++CONFIG_MTD_UBI=y
++CONFIG_MTD_UBI_FASTMAP=y
++CONFIG_MTD_UBI_BLOCK=y
++# CONFIG_BLK_DEV is not set
++CONFIG_SCSI=y
++# CONFIG_SCSI_PROC_FS is not set
++CONFIG_BLK_DEV_SD=y
++CONFIG_CHR_DEV_SG=y
++# CONFIG_BLK_DEV_BSG is not set
++CONFIG_SCSI_SCAN_ASYNC=y
++# CONFIG_SCSI_LOWLEVEL is not set
++CONFIG_NETDEVICES=y
++# CONFIG_NET_CORE is not set
++# CONFIG_NET_VENDOR_ALACRITECH is not set
++# CONFIG_NET_VENDOR_AMAZON is not set
++# CONFIG_NET_VENDOR_AQUANTIA is not set
++# CONFIG_NET_VENDOR_ARC is not set
++# CONFIG_NET_VENDOR_BROADCOM is not set
++# CONFIG_NET_VENDOR_CADENCE is not set
++# CONFIG_NET_VENDOR_CAVIUM is not set
++# CONFIG_NET_VENDOR_CORTINA is not set
++# CONFIG_NET_VENDOR_EZCHIP is not set
++# CONFIG_NET_VENDOR_GOOGLE is not set
++# CONFIG_NET_VENDOR_HUAWEI is not set
++# CONFIG_NET_VENDOR_INTEL is not set
++# CONFIG_NET_VENDOR_MICROSOFT is not set
++# CONFIG_NET_VENDOR_MARVELL is not set
++# CONFIG_NET_VENDOR_MELLANOX is not set
++# CONFIG_NET_VENDOR_MICREL is not set
++# CONFIG_NET_VENDOR_MICROCHIP is not set
++# CONFIG_NET_VENDOR_MICROSEMI is not set
++# CONFIG_NET_VENDOR_NATSEMI is not set
++# CONFIG_NET_VENDOR_NETRONOME is not set
++# CONFIG_NET_VENDOR_NI is not set
++# CONFIG_NET_VENDOR_PENSANDO is not set
++# CONFIG_NET_VENDOR_QUALCOMM is not set
++# CONFIG_NET_VENDOR_RENESAS is not set
++# CONFIG_NET_VENDOR_ROCKER is not set
++# CONFIG_NET_VENDOR_SAMSUNG is not set
++# CONFIG_NET_VENDOR_SEEQ is not set
++# CONFIG_NET_VENDOR_SOLARFLARE is not set
++# CONFIG_NET_VENDOR_SOCIONEXT is not set
++CONFIG_STMMAC_ETH=y
++# CONFIG_DWMAC_GENERIC is not set
++# CONFIG_NET_VENDOR_SYNOPSYS is not set
++# CONFIG_NET_VENDOR_VIA is not set
++# CONFIG_NET_VENDOR_WIZNET is not set
++# CONFIG_NET_VENDOR_XILINX is not set
++CONFIG_REALTEK_PHY=y
++# CONFIG_USB_NET_DRIVERS is not set
++# CONFIG_WLAN is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_KEYBOARD_ATKBD is not set
++CONFIG_KEYBOARD_GPIO=y
++CONFIG_KEYBOARD_SUN4I_LRADC=y
++# CONFIG_INPUT_MOUSE is not set
++# CONFIG_SERIO is not set
++# CONFIG_VT is not set
++# CONFIG_LEGACY_PTYS is not set
++# CONFIG_LDISC_AUTOLOAD is not set
++CONFIG_SERIAL_8250=y
++# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
++CONFIG_SERIAL_8250_CONSOLE=y
++CONFIG_SERIAL_8250_NR_UARTS=6
++CONFIG_SERIAL_8250_RUNTIME_UARTS=6
++CONFIG_SERIAL_8250_DW=y
++CONFIG_SERIAL_OF_PLATFORM=y
++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y
++CONFIG_HVC_RISCV_SBI=y
++CONFIG_I2C=y
++# CONFIG_I2C_COMPAT is not set
++CONFIG_I2C_CHARDEV=y
++# CONFIG_I2C_HELPER_AUTO is not set
++CONFIG_I2C_MV64XXX=y
++CONFIG_SPI=y
++CONFIG_SPI_SUN6I=y
++CONFIG_SPI_SPIDEV=y
++# CONFIG_PTP_1588_CLOCK is not set
++CONFIG_PINCTRL=y
++CONFIG_GPIO_SYSFS=y
++CONFIG_GPIO_PCF857X=y
++CONFIG_POWER_SUPPLY=y
++CONFIG_THERMAL=y
++CONFIG_THERMAL_STATISTICS=y
++CONFIG_THERMAL_WRITABLE_TRIPS=y
++CONFIG_THERMAL_GOV_BANG_BANG=y
++CONFIG_THERMAL_GOV_USER_SPACE=y
++CONFIG_CPU_THERMAL=y
++CONFIG_THERMAL_EMULATION=y
++CONFIG_SUN8I_THERMAL=y
++CONFIG_WATCHDOG=y
++CONFIG_WATCHDOG_SYSFS=y
++CONFIG_SUNXI_WATCHDOG=y
++CONFIG_REGULATOR=y
++CONFIG_REGULATOR_FIXED_VOLTAGE=y
++CONFIG_REGULATOR_PWM=y
++CONFIG_SOUND=y
++CONFIG_SND=y
++CONFIG_SND_HRTIMER=y
++CONFIG_SND_DYNAMIC_MINORS=y
++# CONFIG_SND_SUPPORT_OLD_API is not set
++# CONFIG_SND_DRIVERS is not set
++# CONFIG_SND_SPI is not set
++CONFIG_SND_USB_AUDIO=y
++CONFIG_SND_SOC=y
++CONFIG_SND_SUN20I_CODEC=y
++CONFIG_SND_SUN4I_I2S=y
++CONFIG_SND_SUN4I_SPDIF=y
++# CONFIG_USB_HID is not set
++CONFIG_USB=y
++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
++CONFIG_USB_DYNAMIC_MINORS=y
++CONFIG_USB_OTG=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_EHCI_HCD_PLATFORM=y
++CONFIG_USB_OHCI_HCD=y
++CONFIG_USB_OHCI_HCD_PLATFORM=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_UAS=y
++CONFIG_USB_MUSB_HDRC=y
++CONFIG_USB_MUSB_SUNXI=y
++CONFIG_USB_SERIAL=y
++CONFIG_USB_SERIAL_CH341=y
++CONFIG_NOP_USB_XCEIV=y
++CONFIG_USB_GADGET=y
++CONFIG_USB_ETH=y
++# CONFIG_USB_ETH_RNDIS is not set
++CONFIG_USB_ETH_EEM=y
++CONFIG_MMC=y
++CONFIG_MMC_SUNXI=y
++CONFIG_NEW_LEDS=y
++CONFIG_LEDS_CLASS=y
++CONFIG_LEDS_CLASS_MULTICOLOR=y
++CONFIG_LEDS_SUN50I_R329=y
++CONFIG_LEDS_TRIGGERS=y
++CONFIG_LEDS_TRIGGER_TIMER=y
++CONFIG_LEDS_TRIGGER_ONESHOT=y
++CONFIG_LEDS_TRIGGER_MTD=y
++CONFIG_LEDS_TRIGGER_HEARTBEAT=y
++CONFIG_LEDS_TRIGGER_CPU=y
++CONFIG_LEDS_TRIGGER_ACTIVITY=y
++CONFIG_LEDS_TRIGGER_PANIC=y
++CONFIG_LEDS_TRIGGER_NETDEV=y
++CONFIG_RTC_CLASS=y
++CONFIG_DMADEVICES=y
++CONFIG_DMA_SUN6I=y
++# CONFIG_VIRTIO_MENU is not set
++# CONFIG_VHOST_MENU is not set
++CONFIG_SUN8I_DE2_CCU=y
++CONFIG_HWSPINLOCK=y
++CONFIG_HWSPINLOCK_SUN6I=y
++CONFIG_MAILBOX=y
++CONFIG_SUN50I_IOMMU=y
++CONFIG_REMOTEPROC=y
++CONFIG_REMOTEPROC_CDEV=y
++CONFIG_SUN8I_DSP_REMOTEPROC=y
++CONFIG_RPMSG_CHAR=y
++CONFIG_RPMSG_VIRTIO=y
++CONFIG_PWM=y
++CONFIG_PWM_SUN8I_V536=y
++CONFIG_PHY_SUN4I_USB=y
++CONFIG_NVMEM_SUNXI_SID=y
++CONFIG_EXT4_FS=y
++# CONFIG_DNOTIFY is not set
++CONFIG_FANOTIFY=y
++CONFIG_TMPFS=y
++CONFIG_TMPFS_XATTR=y
++CONFIG_TMPFS_INODE64=y
++# CONFIG_MISC_FILESYSTEMS is not set
++# CONFIG_NETWORK_FILESYSTEMS is not set
++CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_CRYPTO_USER_API_SKCIPHER=y
++CONFIG_CRYPTO_USER_API_RNG=y
++CONFIG_CRYPTO_USER_API_AEAD=y
++# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set
++CONFIG_CRYPTO_DEV_SUN8I_CE=y
++CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y
++CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y
++CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y
++CONFIG_PRINTK_TIME=y
++# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
++# CONFIG_FRAME_POINTER is not set
++CONFIG_VMLINUX_MAP=y
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_MAGIC_SYSRQ_SERIAL is not set
++CONFIG_DEBUG_FS=y
++# CONFIG_DEBUG_MISC is not set
++# CONFIG_SCHED_DEBUG is not set
++CONFIG_DEBUG_ATOMIC_SLEEP=y
++CONFIG_STACKTRACE=y
++# CONFIG_RCU_TRACE is not set
++# CONFIG_FTRACE is not set
++# CONFIG_RUNTIME_TESTING_MENU is not set
+--
+2.20.1
+
--- /dev/null
+From 533f0a9a782e90ba8b30aa4deed0146f3133610e Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Mon, 31 May 2021 21:49:39 -0500
+Subject: [PATCH 124/124] watchdog: sunxi_wdt: Add support for D1
+
+D1 adds a key field to the "CFG" and "MODE" registers, that must be set
+to change the other bits. Add logic to set the key when updating those
+registers.
+
+Acked-by: Maxime Ripard <maxime@cerno.tech>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ drivers/watchdog/sunxi_wdt.c | 20 +++++++++++++++++++-
+ 1 file changed, 19 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
+index b50757882a98..6cf82922d3fb 100644
+--- a/drivers/watchdog/sunxi_wdt.c
++++ b/drivers/watchdog/sunxi_wdt.c
+@@ -48,6 +48,7 @@ struct sunxi_wdt_reg {
+ u8 wdt_timeout_shift;
+ u8 wdt_reset_mask;
+ u8 wdt_reset_val;
++ u32 wdt_key_val;
+ };
+
+ struct sunxi_wdt_dev {
+@@ -91,12 +92,14 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
+ val = readl(wdt_base + regs->wdt_cfg);
+ val &= ~(regs->wdt_reset_mask);
+ val |= regs->wdt_reset_val;
++ val |= regs->wdt_key_val;
+ writel(val, wdt_base + regs->wdt_cfg);
+
+ /* Set lowest timeout and enable watchdog */
+ val = readl(wdt_base + regs->wdt_mode);
+ val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
+ val |= WDT_MODE_EN;
++ val |= regs->wdt_key_val;
+ writel(val, wdt_base + regs->wdt_mode);
+
+ /*
+@@ -109,6 +112,7 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
+ mdelay(5);
+ val = readl(wdt_base + regs->wdt_mode);
+ val |= WDT_MODE_EN;
++ val |= regs->wdt_key_val;
+ writel(val, wdt_base + regs->wdt_mode);
+ }
+ return 0;
+@@ -141,6 +145,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
+ reg = readl(wdt_base + regs->wdt_mode);
+ reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
+ reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
++ reg |= regs->wdt_key_val;
+ writel(reg, wdt_base + regs->wdt_mode);
+
+ sunxi_wdt_ping(wdt_dev);
+@@ -154,7 +159,7 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
+ void __iomem *wdt_base = sunxi_wdt->wdt_base;
+ const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
+
+- writel(0, wdt_base + regs->wdt_mode);
++ writel(regs->wdt_key_val, wdt_base + regs->wdt_mode);
+
+ return 0;
+ }
+@@ -176,11 +181,13 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
+ reg = readl(wdt_base + regs->wdt_cfg);
+ reg &= ~(regs->wdt_reset_mask);
+ reg |= regs->wdt_reset_val;
++ reg |= regs->wdt_key_val;
+ writel(reg, wdt_base + regs->wdt_cfg);
+
+ /* Enable watchdog */
+ reg = readl(wdt_base + regs->wdt_mode);
+ reg |= WDT_MODE_EN;
++ reg |= regs->wdt_key_val;
+ writel(reg, wdt_base + regs->wdt_mode);
+
+ return 0;
+@@ -220,9 +227,20 @@ static const struct sunxi_wdt_reg sun6i_wdt_reg = {
+ .wdt_reset_val = 0x01,
+ };
+
++static const struct sunxi_wdt_reg sun20i_wdt_reg = {
++ .wdt_ctrl = 0x10,
++ .wdt_cfg = 0x14,
++ .wdt_mode = 0x18,
++ .wdt_timeout_shift = 4,
++ .wdt_reset_mask = 0x03,
++ .wdt_reset_val = 0x01,
++ .wdt_key_val = 0x16aa0000,
++};
++
+ static const struct of_device_id sunxi_wdt_dt_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg },
+ { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg },
++ { .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg },
+ { /* sentinel */ }
+ };
+ MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
+--
+2.20.1
+
--- /dev/null
+From aa7c2a299b211120e4fcdc66afe130cd6cda571d Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Sun, 6 Jun 2021 10:52:16 -0500
+Subject: [PATCH 060/136] dt-bindings: clk: Add compatibles for D1 CCUs
+
+The D1 has a CCU and a R_CCU (PRCM CCU) like most other sunxi SoCs, with
+3 and 4 clock inputs, respectively. Add the compatibles for them.
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ .../clock/allwinner,sun4i-a10-ccu.yaml | 4 +
+ include/dt-bindings/clock/sun20i-d1-ccu.h | 156 ++++++++++++++++++
+ include/dt-bindings/clock/sun20i-d1-r-ccu.h | 19 +++
+ include/dt-bindings/reset/sun20i-d1-ccu.h | 77 +++++++++
+ include/dt-bindings/reset/sun20i-d1-r-ccu.h | 16 ++
+ 5 files changed, 272 insertions(+)
+ create mode 100644 include/dt-bindings/clock/sun20i-d1-ccu.h
+ create mode 100644 include/dt-bindings/clock/sun20i-d1-r-ccu.h
+ create mode 100644 include/dt-bindings/reset/sun20i-d1-ccu.h
+ create mode 100644 include/dt-bindings/reset/sun20i-d1-r-ccu.h
+
+--- a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml
++++ b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml
+@@ -34,6 +34,8 @@ properties:
+ - allwinner,sun8i-v3-ccu
+ - allwinner,sun8i-v3s-ccu
+ - allwinner,sun9i-a80-ccu
++ - allwinner,sun20i-d1-ccu
++ - allwinner,sun20i-d1-r-ccu
+ - allwinner,sun50i-a64-ccu
+ - allwinner,sun50i-a64-r-ccu
+ - allwinner,sun50i-a100-ccu
+@@ -79,6 +81,7 @@ if:
+ enum:
+ - allwinner,sun8i-a83t-r-ccu
+ - allwinner,sun8i-h3-r-ccu
++ - allwinner,sun20i-d1-r-ccu
+ - allwinner,sun50i-a64-r-ccu
+ - allwinner,sun50i-a100-r-ccu
+ - allwinner,sun50i-h6-r-ccu
+@@ -99,6 +102,7 @@ else:
+ properties:
+ compatible:
+ enum:
++ - allwinner,sun20i-d1-ccu
+ - allwinner,sun50i-a100-ccu
+ - allwinner,sun50i-h6-ccu
+ - allwinner,sun50i-h616-ccu
+--- /dev/null
++++ b/include/dt-bindings/clock/sun20i-d1-ccu.h
+@@ -0,0 +1,156 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++/*
++ * Copyright (C) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_
++#define _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_
++
++#define CLK_PLL_CPUX 0
++#define CLK_PLL_DDR0 1
++#define CLK_PLL_PERIPH0_4X 2
++#define CLK_PLL_PERIPH0_2X 3
++#define CLK_PLL_PERIPH0_800M 4
++#define CLK_PLL_PERIPH0 5
++#define CLK_PLL_PERIPH0_DIV3 6
++#define CLK_PLL_VIDEO0_4X 7
++#define CLK_PLL_VIDEO0_2X 8
++#define CLK_PLL_VIDEO0 9
++#define CLK_PLL_VIDEO1_4X 10
++#define CLK_PLL_VIDEO1_2X 11
++#define CLK_PLL_VIDEO1 12
++#define CLK_PLL_VE 13
++#define CLK_PLL_AUDIO0_4X 14
++#define CLK_PLL_AUDIO0_2X 15
++#define CLK_PLL_AUDIO0 16
++#define CLK_PLL_AUDIO1 17
++#define CLK_PLL_AUDIO1_DIV2 18
++#define CLK_PLL_AUDIO1_DIV5 19
++#define CLK_CPUX 20
++#define CLK_CPUX_AXI 21
++#define CLK_CPUX_APB 22
++#define CLK_PSI_AHB 23
++#define CLK_APB0 24
++#define CLK_APB1 25
++#define CLK_MBUS 26
++#define CLK_DE 27
++#define CLK_BUS_DE 28
++#define CLK_DI 29
++#define CLK_BUS_DI 30
++#define CLK_G2D 31
++#define CLK_BUS_G2D 32
++#define CLK_CE 33
++#define CLK_BUS_CE 34
++#define CLK_VE 35
++#define CLK_BUS_VE 36
++#define CLK_BUS_DMA 37
++#define CLK_BUS_MSGBOX0 38
++#define CLK_BUS_MSGBOX1 39
++#define CLK_BUS_MSGBOX2 40
++#define CLK_BUS_SPINLOCK 41
++#define CLK_BUS_HSTIMER 42
++#define CLK_AVS 43
++#define CLK_BUS_DBG 44
++#define CLK_BUS_PWM 45
++#define CLK_BUS_IOMMU 46
++#define CLK_DRAM 47
++#define CLK_MBUS_DMA 48
++#define CLK_MBUS_VE 49
++#define CLK_MBUS_CE 50
++#define CLK_MBUS_TVIN 51
++#define CLK_MBUS_CSI 52
++#define CLK_MBUS_G2D 53
++#define CLK_MBUS_RISCV 54
++#define CLK_BUS_DRAM 55
++#define CLK_MMC0 56
++#define CLK_MMC1 57
++#define CLK_MMC2 58
++#define CLK_BUS_MMC0 59
++#define CLK_BUS_MMC1 60
++#define CLK_BUS_MMC2 61
++#define CLK_BUS_UART0 62
++#define CLK_BUS_UART1 63
++#define CLK_BUS_UART2 64
++#define CLK_BUS_UART3 65
++#define CLK_BUS_UART4 66
++#define CLK_BUS_UART5 67
++#define CLK_BUS_I2C0 68
++#define CLK_BUS_I2C1 69
++#define CLK_BUS_I2C2 70
++#define CLK_BUS_I2C3 71
++#define CLK_SPI0 72
++#define CLK_SPI1 73
++#define CLK_BUS_SPI0 74
++#define CLK_BUS_SPI1 75
++#define CLK_EMAC_25M 76
++#define CLK_BUS_EMAC 77
++#define CLK_IR_TX 78
++#define CLK_BUS_IR_TX 79
++#define CLK_BUS_GPADC 80
++#define CLK_BUS_THS 81
++#define CLK_I2S0 82
++#define CLK_I2S1 83
++#define CLK_I2S2 84
++#define CLK_I2S2_ASRC 85
++#define CLK_BUS_I2S0 86
++#define CLK_BUS_I2S1 87
++#define CLK_BUS_I2S2 88
++#define CLK_SPDIF_TX 89
++#define CLK_SPDIF_RX 90
++#define CLK_BUS_SPDIF 91
++#define CLK_DMIC 92
++#define CLK_BUS_DMIC 93
++#define CLK_AUDIO_DAC 94
++#define CLK_AUDIO_ADC 95
++#define CLK_BUS_AUDIO 96
++#define CLK_USB_OHCI0 97
++#define CLK_USB_OHCI1 98
++#define CLK_BUS_OHCI0 99
++#define CLK_BUS_OHCI1 100
++#define CLK_BUS_EHCI0 101
++#define CLK_BUS_EHCI1 102
++#define CLK_BUS_OTG 103
++#define CLK_BUS_LRADC 104
++#define CLK_BUS_DPSS_TOP 105
++#define CLK_HDMI_24M 106
++#define CLK_HDMI_CEC_32K 107
++#define CLK_HDMI_CEC 108
++#define CLK_BUS_HDMI 109
++#define CLK_MIPI_DSI 110
++#define CLK_BUS_MIPI_DSI 111
++#define CLK_TCON_LCD0 112
++#define CLK_BUS_TCON_LCD0 113
++#define CLK_TCON_TV 114
++#define CLK_BUS_TCON_TV 115
++#define CLK_TVE 116
++#define CLK_BUS_TVE_TOP 117
++#define CLK_BUS_TVE 118
++#define CLK_TVD 119
++#define CLK_BUS_TVD_TOP 120
++#define CLK_BUS_TVD 121
++#define CLK_LEDC 122
++#define CLK_BUS_LEDC 123
++#define CLK_CSI_TOP 124
++#define CLK_CSI_MCLK 125
++#define CLK_BUS_CSI 126
++#define CLK_TPADC 127
++#define CLK_BUS_TPADC 128
++#define CLK_BUS_TZMA 129
++#define CLK_DSP 130
++#define CLK_BUS_DSP_CFG 131
++#define CLK_RISCV 132
++#define CLK_RISCV_AXI 133
++#define CLK_BUS_RISCV_CFG 134
++#define CLK_FANOUT_24M 135
++#define CLK_FANOUT_12M 136
++#define CLK_FANOUT_16M 137
++#define CLK_FANOUT_25M 138
++#define CLK_FANOUT_32K 139
++#define CLK_FANOUT_27M 140
++#define CLK_FANOUT_PCLK 141
++#define CLK_FANOUT0 142
++#define CLK_FANOUT1 143
++#define CLK_FANOUT2 144
++
++#endif /* _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_ */
+--- /dev/null
++++ b/include/dt-bindings/clock/sun20i-d1-r-ccu.h
+@@ -0,0 +1,19 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++/*
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_
++#define _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_
++
++#define CLK_R_AHB 0
++
++#define CLK_BUS_R_TIMER 2
++#define CLK_BUS_R_TWD 3
++#define CLK_BUS_R_PPU 4
++#define CLK_R_IR_RX 5
++#define CLK_BUS_R_IR_RX 6
++#define CLK_BUS_R_RTC 7
++#define CLK_BUS_R_CPUCFG 8
++
++#endif /* _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_ */
+--- /dev/null
++++ b/include/dt-bindings/reset/sun20i-d1-ccu.h
+@@ -0,0 +1,77 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++/*
++ * Copyright (c) 2020 huangzhenwei@allwinnertech.com
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_RST_SUN20I_D1_CCU_H_
++#define _DT_BINDINGS_RST_SUN20I_D1_CCU_H_
++
++#define RST_MBUS 0
++#define RST_BUS_DE 1
++#define RST_BUS_DI 2
++#define RST_BUS_G2D 3
++#define RST_BUS_CE 4
++#define RST_BUS_VE 5
++#define RST_BUS_DMA 6
++#define RST_BUS_MSGBOX0 7
++#define RST_BUS_MSGBOX1 8
++#define RST_BUS_MSGBOX2 9
++#define RST_BUS_SPINLOCK 10
++#define RST_BUS_HSTIMER 11
++#define RST_BUS_DBG 12
++#define RST_BUS_PWM 13
++#define RST_BUS_DRAM 14
++#define RST_BUS_MMC0 15
++#define RST_BUS_MMC1 16
++#define RST_BUS_MMC2 17
++#define RST_BUS_UART0 18
++#define RST_BUS_UART1 19
++#define RST_BUS_UART2 20
++#define RST_BUS_UART3 21
++#define RST_BUS_UART4 22
++#define RST_BUS_UART5 23
++#define RST_BUS_I2C0 24
++#define RST_BUS_I2C1 25
++#define RST_BUS_I2C2 26
++#define RST_BUS_I2C3 27
++#define RST_BUS_SPI0 28
++#define RST_BUS_SPI1 29
++#define RST_BUS_EMAC 30
++#define RST_BUS_IR_TX 31
++#define RST_BUS_GPADC 32
++#define RST_BUS_THS 33
++#define RST_BUS_I2S0 34
++#define RST_BUS_I2S1 35
++#define RST_BUS_I2S2 36
++#define RST_BUS_SPDIF 37
++#define RST_BUS_DMIC 38
++#define RST_BUS_AUDIO 39
++#define RST_USB_PHY0 40
++#define RST_USB_PHY1 41
++#define RST_BUS_OHCI0 42
++#define RST_BUS_OHCI1 43
++#define RST_BUS_EHCI0 44
++#define RST_BUS_EHCI1 45
++#define RST_BUS_OTG 46
++#define RST_BUS_LRADC 47
++#define RST_BUS_DPSS_TOP 48
++#define RST_BUS_HDMI_SUB 49
++#define RST_BUS_HDMI_MAIN 50
++#define RST_BUS_MIPI_DSI 51
++#define RST_BUS_TCON_LCD0 52
++#define RST_BUS_TCON_TV 53
++#define RST_BUS_LVDS0 54
++#define RST_BUS_TVE 55
++#define RST_BUS_TVE_TOP 56
++#define RST_BUS_TVD 57
++#define RST_BUS_TVD_TOP 58
++#define RST_BUS_LEDC 59
++#define RST_BUS_CSI 60
++#define RST_BUS_TPADC 61
++#define RST_DSP 62
++#define RST_BUS_DSP_CFG 63
++#define RST_BUS_DSP_DBG 64
++#define RST_BUS_RISCV_CFG 65
++
++#endif /* _DT_BINDINGS_RST_SUN20I_D1_CCU_H_ */
+--- /dev/null
++++ b/include/dt-bindings/reset/sun20i-d1-r-ccu.h
+@@ -0,0 +1,16 @@
++// SPDX-License-Identifier: (GPL-2.0+ or MIT)
++/*
++ * Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
++ */
++
++#ifndef _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_
++#define _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_
++
++#define RST_BUS_R_TIMER 0
++#define RST_BUS_R_TWD 1
++#define RST_BUS_R_PPU 2
++#define RST_BUS_R_IR_RX 3
++#define RST_BUS_R_RTC 4
++#define RST_BUS_R_CPUCFG 5
++
++#endif /* _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_ */
--- /dev/null
+From 80ee04e4cc1fcdcd46c2a67e841fae1532f2ed69 Mon Sep 17 00:00:00 2001
+From: Samuel Holland <samuel@sholland.org>
+Date: Tue, 31 Aug 2021 23:37:55 -0500
+Subject: [PATCH 041/136] dt-bindings: rtc: sun6i: Add H616, R329, and D1
+ support
+
+These new RTC variants all have a single alarm, like the R40 variant.
+
+For the new SoCs, start requiring a complete list of input clocks. The
+H616 has three required clocks. The R329 also has three required clocks
+(but one is different), plus an optional crystal oscillator input. The
+D1 RTC is identical to the one in the R329.
+
+And since these new SoCs will have a well-defined output clock order as
+well, they do not need the clock-output-names property.
+
+Series-changes: 2
+ - Properly update the DT binding clocks and clock-names properties.
+
+Series-changes: 3
+ - Add/fix several maxItems attributes for clocks and clock-items
+
+Signed-off-by: Samuel Holland <samuel@sholland.org>
+---
+ .../bindings/rtc/allwinner,sun6i-a31-rtc.yaml | 76 ++++++++++++++++++-
+ include/dt-bindings/clock/sun6i-rtc.h | 10 +++
+ 2 files changed, 83 insertions(+), 3 deletions(-)
+ create mode 100644 include/dt-bindings/clock/sun6i-rtc.h
+
+--- /dev/null
++++ b/include/dt-bindings/clock/sun6i-rtc.h
+@@ -0,0 +1,10 @@
++/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */
++
++#ifndef _DT_BINDINGS_CLK_SUN6I_RTC_H_
++#define _DT_BINDINGS_CLK_SUN6I_RTC_H_
++
++#define CLK_OSC32K 0
++#define CLK_OSC32K_FANOUT 1
++#define CLK_IOSC 2
++
++#endif /* _DT_BINDINGS_CLK_SUN6I_RTC_H_ */