summaryrefslogtreecommitdiff
path: root/drivers/reset
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/reset')
-rw-r--r--drivers/reset/Kconfig401
-rw-r--r--drivers/reset/Makefile53
-rw-r--r--drivers/reset/amlogic/Kconfig27
-rw-r--r--drivers/reset/amlogic/Makefile4
-rw-r--r--drivers/reset/amlogic/reset-meson-audio-arb.c193
-rw-r--r--drivers/reset/amlogic/reset-meson-aux.c81
-rw-r--r--drivers/reset/amlogic/reset-meson-common.c142
-rw-r--r--drivers/reset/amlogic/reset-meson.c105
-rw-r--r--drivers/reset/amlogic/reset-meson.h28
-rw-r--r--drivers/reset/core.c1366
-rw-r--r--drivers/reset/hisilicon/Kconfig14
-rw-r--r--drivers/reset/hisilicon/Makefile3
-rw-r--r--drivers/reset/hisilicon/hi6220_reset.c223
-rw-r--r--drivers/reset/hisilicon/reset-hi3660.c127
-rw-r--r--drivers/reset/reset-a10sr.c128
-rw-r--r--drivers/reset/reset-aspeed.c253
-rw-r--r--drivers/reset/reset-ath79.c134
-rw-r--r--drivers/reset/reset-axs10x.c82
-rw-r--r--drivers/reset/reset-bcm6345.c134
-rw-r--r--drivers/reset/reset-berlin.c111
-rw-r--r--drivers/reset/reset-brcmstb-rescal.c105
-rw-r--r--drivers/reset/reset-brcmstb.c126
-rw-r--r--drivers/reset/reset-eic7700.c429
-rw-r--r--drivers/reset/reset-eyeq.c581
-rw-r--r--drivers/reset/reset-gpio.c120
-rw-r--r--drivers/reset/reset-hsdk.c135
-rw-r--r--drivers/reset/reset-imx-scu.c101
-rw-r--r--drivers/reset/reset-imx7.c407
-rw-r--r--drivers/reset/reset-imx8mp-audiomix.c162
-rw-r--r--drivers/reset/reset-intel-gw.c261
-rw-r--r--drivers/reset/reset-k210.c131
-rw-r--r--drivers/reset/reset-k230.c371
-rw-r--r--drivers/reset/reset-lantiq.c208
-rw-r--r--drivers/reset/reset-lpc18xx.c203
-rw-r--r--drivers/reset/reset-ma35d1.c235
-rw-r--r--drivers/reset/reset-microchip-sparx5.c227
-rw-r--r--drivers/reset/reset-mpfs.c207
-rw-r--r--drivers/reset/reset-npcm.c501
-rw-r--r--drivers/reset/reset-pistachio.c136
-rw-r--r--drivers/reset/reset-qcom-aoss.c132
-rw-r--r--drivers/reset/reset-qcom-pdc.c161
-rw-r--r--drivers/reset/reset-raspberrypi.c122
-rw-r--r--drivers/reset/reset-rzg2l-usbphy-ctrl.c278
-rw-r--r--drivers/reset/reset-rzv2h-usb2phy.c236
-rw-r--r--drivers/reset/reset-scmi.c132
-rw-r--r--drivers/reset/reset-simple.c207
-rw-r--r--drivers/reset/reset-socfpga.c120
-rw-r--r--drivers/reset/reset-spacemit.c304
-rw-r--r--drivers/reset/reset-sunplus.c211
-rw-r--r--drivers/reset/reset-sunxi.c83
-rw-r--r--drivers/reset/reset-th1520.c983
-rw-r--r--drivers/reset/reset-ti-sci.c259
-rw-r--r--drivers/reset/reset-ti-syscon.c228
-rw-r--r--drivers/reset/reset-tn48m.c128
-rw-r--r--drivers/reset/reset-tps380x.c126
-rw-r--r--drivers/reset/reset-uniphier-glue.c164
-rw-r--r--drivers/reset/reset-uniphier.c523
-rw-r--r--drivers/reset/reset-zynq.c133
-rw-r--r--drivers/reset/reset-zynqmp.c145
-rw-r--r--drivers/reset/starfive/Kconfig21
-rw-r--r--drivers/reset/starfive/Makefile5
-rw-r--r--drivers/reset/starfive/reset-starfive-jh7100.c74
-rw-r--r--drivers/reset/starfive/reset-starfive-jh7110.c103
-rw-r--r--drivers/reset/starfive/reset-starfive-jh71x0.c134
-rw-r--r--drivers/reset/starfive/reset-starfive-jh71x0.h14
-rw-r--r--drivers/reset/sti/Kconfig7
-rw-r--r--drivers/reset/sti/Makefile2
-rw-r--r--drivers/reset/sti/reset-stih407.c159
-rw-r--r--drivers/reset/sti/reset-syscfg.c197
-rw-r--r--drivers/reset/sti/reset-syscfg.h65
-rw-r--r--drivers/reset/tegra/Kconfig4
-rw-r--r--drivers/reset/tegra/Makefile2
-rw-r--r--drivers/reset/tegra/reset-bpmp.c75
73 files changed, 13679 insertions, 113 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c9d04f797862..6e5d6deffa7d 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config ARCH_HAS_RESET_CONTROLLER
bool
@@ -11,3 +12,403 @@ menuconfig RESET_CONTROLLER
via GPIOs or SoC-internal reset controller modules.
If unsure, say no.
+
+if RESET_CONTROLLER
+
+config RESET_A10SR
+ tristate "Altera Arria10 System Resource Reset"
+ depends on MFD_ALTERA_A10SR || COMPILE_TEST
+ help
+ This option enables support for the external reset functions for
+ peripheral PHYs on the Altera Arria10 System Resource Chip.
+
+config RESET_ASPEED
+ tristate "ASPEED Reset Driver"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This enables the reset controller driver for AST2700.
+
+config RESET_ATH79
+ bool "AR71xx Reset Driver" if COMPILE_TEST
+ default ATH79
+ help
+ This enables the ATH79 reset controller driver that supports the
+ AR71xx SoC reset controller.
+
+config RESET_AXS10X
+ bool "AXS10x Reset Driver" if COMPILE_TEST
+ default ARC_PLAT_AXS10X
+ help
+ This enables the reset controller driver for AXS10x.
+
+config RESET_BCM6345
+ bool "BCM6345 Reset Controller"
+ depends on BMIPS_GENERIC || COMPILE_TEST
+ default BMIPS_GENERIC
+ help
+ This enables the reset controller driver for BCM6345 SoCs.
+
+config RESET_BERLIN
+ tristate "Berlin Reset Driver"
+ depends on ARCH_BERLIN || COMPILE_TEST
+ default m if ARCH_BERLIN
+ help
+ This enables the reset controller driver for Marvell Berlin SoCs.
+
+config RESET_BRCMSTB
+ tristate "Broadcom STB reset controller"
+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ default ARCH_BRCMSTB || ARCH_BCM2835
+ help
+ This enables the reset controller driver for Broadcom STB SoCs using
+ a SUN_TOP_CTRL_SW_INIT style controller.
+
+config RESET_BRCMSTB_RESCAL
+ tristate "Broadcom STB RESCAL reset controller"
+ depends on HAS_IOMEM
+ depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ default ARCH_BRCMSTB || ARCH_BCM2835
+ help
+ This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on
+ BCM7216 or the BCM2712.
+
+config RESET_EIC7700
+ bool "Reset controller driver for ESWIN SoCs"
+ depends on ARCH_ESWIN || COMPILE_TEST
+ default ARCH_ESWIN
+ help
+ This enables the reset controller driver for ESWIN SoCs. This driver is
+ specific to ESWIN SoCs and should only be enabled if using such hardware.
+ The driver supports eic7700 series chips and provides functionality for
+ asserting and deasserting resets on the chip.
+
+config RESET_EYEQ
+ bool "Mobileye EyeQ reset controller"
+ depends on MACH_EYEQ5 || MACH_EYEQ6H || COMPILE_TEST
+ select AUXILIARY_BUS
+ default MACH_EYEQ5 || MACH_EYEQ6H
+ help
+ This enables the Mobileye EyeQ reset controller, used in EyeQ5, EyeQ6L
+ and EyeQ6H SoCs.
+
+ It has one or more domains, with a varying number of resets in each.
+ Registers are located in a shared register region called OLB. EyeQ6H
+ has multiple reset instances.
+
+config RESET_GPIO
+ tristate "GPIO reset controller"
+ depends on GPIOLIB
+ select AUXILIARY_BUS
+ help
+ This enables a generic reset controller for resets attached via
+ GPIOs. Typically for OF platforms this driver expects "reset-gpios"
+ property.
+
+ If compiled as module, it will be called reset-gpio.
+
+config RESET_HSDK
+ bool "Synopsys HSDK Reset Driver"
+ depends on HAS_IOMEM
+ depends on ARC_SOC_HSDK || COMPILE_TEST
+ help
+ This enables the reset controller driver for HSDK board.
+
+config RESET_IMX_SCU
+ tristate "i.MX8Q Reset Driver"
+ depends on IMX_SCU && HAVE_ARM_SMCCC
+ depends on (ARM64 && ARCH_MXC) || COMPILE_TEST
+ help
+ This enables the reset controller driver for i.MX8QM/i.MX8QXP
+
+config RESET_IMX7
+ tristate "i.MX7/8 Reset Driver"
+ depends on HAS_IOMEM
+ depends on SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST
+ default y if SOC_IMX7D
+ select MFD_SYSCON
+ help
+ This enables the reset controller driver for i.MX7 SoCs.
+
+config RESET_IMX8MP_AUDIOMIX
+ tristate "i.MX8MP AudioMix Reset Driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ select AUXILIARY_BUS
+ default CLK_IMX8MP
+ help
+ This enables the reset controller driver for i.MX8MP AudioMix
+
+config RESET_INTEL_GW
+ bool "Intel Reset Controller Driver"
+ depends on X86 || COMPILE_TEST
+ depends on OF && HAS_IOMEM
+ select REGMAP_MMIO
+ help
+ This enables the reset controller driver for Intel Gateway SoCs.
+ Say Y to control the reset signals provided by reset controller.
+ Otherwise, say N.
+
+config RESET_K210
+ bool "Reset controller driver for Canaan Kendryte K210 SoC"
+ depends on (SOC_CANAAN_K210 || COMPILE_TEST) && OF
+ select MFD_SYSCON
+ default SOC_CANAAN_K210
+ help
+ Support for the Canaan Kendryte K210 RISC-V SoC reset controller.
+ Say Y if you want to control reset signals provided by this
+ controller.
+
+config RESET_K230
+ tristate "Reset controller driver for Canaan Kendryte K230 SoC"
+ depends on ARCH_CANAAN || COMPILE_TEST
+ depends on OF
+ help
+ Support for the Canaan Kendryte K230 RISC-V SoC reset controller.
+ Say Y if you want to control reset signals provided by this
+ controller.
+
+config RESET_LANTIQ
+ bool "Lantiq XWAY Reset Driver" if COMPILE_TEST
+ default SOC_TYPE_XWAY
+ help
+ This enables the reset controller driver for Lantiq / Intel XWAY SoCs.
+
+config RESET_LPC18XX
+ bool "LPC18xx/43xx Reset Driver" if COMPILE_TEST
+ default ARCH_LPC18XX
+ help
+ This enables the reset controller driver for NXP LPC18xx/43xx SoCs.
+
+config RESET_MCHP_SPARX5
+ tristate "Microchip Sparx5 reset driver"
+ depends on ARCH_SPARX5 || ARCH_LAN969X || SOC_LAN966 || MCHP_LAN966X_PCI || COMPILE_TEST
+ default y if SPARX5_SWITCH
+ select MFD_SYSCON
+ help
+ This driver supports switch core reset for the Microchip Sparx5 SoC.
+
+config RESET_NPCM
+ bool "NPCM BMC Reset Driver" if COMPILE_TEST
+ default ARCH_NPCM
+ select AUXILIARY_BUS
+ help
+ This enables the reset controller driver for Nuvoton NPCM
+ BMC SoCs.
+
+config RESET_NUVOTON_MA35D1
+ bool "Nuvoton MA35D1 Reset Driver"
+ depends on ARCH_MA35 || COMPILE_TEST
+ default ARCH_MA35
+ help
+ This enables the reset controller driver for Nuvoton MA35D1 SoC.
+
+config RESET_PISTACHIO
+ bool "Pistachio Reset Driver"
+ depends on MIPS || COMPILE_TEST
+ help
+ This enables the reset driver for ImgTec Pistachio SoCs.
+
+config RESET_POLARFIRE_SOC
+ bool "Microchip PolarFire SoC (MPFS) Reset Driver"
+ depends on MCHP_CLK_MPFS
+ depends on MFD_SYSCON
+ select AUXILIARY_BUS
+ default MCHP_CLK_MPFS
+ help
+ This driver supports peripheral reset for the Microchip PolarFire SoC
+
+config RESET_QCOM_AOSS
+ tristate "Qcom AOSS Reset Driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ This enables the AOSS (always on subsystem) reset driver
+ for Qualcomm SDM845 SoCs. Say Y if you want to control
+ reset signals provided by AOSS for Modem, Venus, ADSP,
+ GPU, Camera, Wireless, Display subsystem. Otherwise, say N.
+
+config RESET_QCOM_PDC
+ tristate "Qualcomm PDC Reset Driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ help
+ This enables the PDC (Power Domain Controller) reset driver
+ for Qualcomm Technologies Inc SDM845 SoCs. Say Y if you want
+ to control reset signals provided by PDC for Modem, Compute,
+ Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS.
+
+config RESET_RASPBERRYPI
+ tristate "Raspberry Pi 4 Firmware Reset Driver"
+ depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST)
+ default USB_XHCI_PCI
+ help
+ Raspberry Pi 4's co-processor controls some of the board's HW
+ initialization process, but it's up to Linux to trigger it when
+ relevant. This driver provides a reset controller capable of
+ interfacing with RPi4's co-processor and model these firmware
+ initialization routines as reset lines.
+
+config RESET_RZG2L_USBPHY_CTRL
+ tristate "Renesas RZ/G2L USBPHY control driver"
+ depends on ARCH_RZG2L || COMPILE_TEST
+ select MFD_SYSCON
+ help
+ Support for USBPHY Control found on RZ/G2L family. It mainly
+ controls reset and power down of the USB/PHY.
+
+config RESET_RZV2H_USB2PHY
+ tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ help
+ Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC
+ (and similar SoCs).
+
+config RESET_SCMI
+ tristate "Reset driver controlled via ARM SCMI interface"
+ depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+ default ARM_SCMI_PROTOCOL
+ help
+ This driver provides support for reset signal/domains that are
+ controlled by firmware that implements the SCMI interface.
+
+ This driver uses SCMI Message Protocol to interact with the
+ firmware controlling all the reset signals.
+
+config RESET_SIMPLE
+ bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT
+ default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_SOPHGO || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC
+ depends on HAS_IOMEM
+ help
+ This enables a simple reset controller driver for reset lines that
+ that can be asserted and deasserted by toggling bits in a contiguous,
+ exclusive register space.
+
+ Currently this driver supports:
+ - Altera SoCFPGAs
+ - ASPEED BMC SoCs
+ - Bitmain BM1880 SoC
+ - Realtek SoCs
+ - RCC reset controller in STM32 MCUs
+ - Allwinner SoCs
+ - SiFive FU740 SoCs
+ - Sophgo SoCs
+
+config RESET_SOCFPGA
+ bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA)
+ default ARM && ARCH_INTEL_SOCFPGA
+ select RESET_SIMPLE
+ help
+ This enables the reset driver for the SoCFPGA ARMv7 platforms. This
+ driver gets initialized early during platform init calls.
+
+config RESET_SPACEMIT
+ tristate "SpacemiT reset driver"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ select AUXILIARY_BUS
+ default ARCH_SPACEMIT
+ help
+ This enables the reset controller driver for SpacemiT SoCs,
+ including the K1.
+
+config RESET_SUNPLUS
+ bool "Sunplus SoCs Reset Driver" if COMPILE_TEST
+ default ARCH_SUNPLUS
+ help
+ This enables the reset driver support for Sunplus SoCs.
+ The reset lines that can be asserted and deasserted by toggling bits
+ in a contiguous, exclusive register space. The register is HIWORD_MASKED,
+ which means each register holds 16 reset lines.
+
+config RESET_SUNXI
+ bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
+ default ARCH_SUNXI
+ select RESET_SIMPLE
+ help
+ This enables the reset driver for Allwinner SoCs.
+
+config RESET_TH1520
+ tristate "T-HEAD TH1520 reset controller"
+ depends on ARCH_THEAD || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This driver provides support for the T-HEAD TH1520 SoC reset controller,
+ which manages hardware reset lines for SoC components such as the GPU.
+ Enable this option if you need to control hardware resets on TH1520-based
+ systems.
+
+config RESET_TI_SCI
+ tristate "TI System Control Interface (TI-SCI) reset driver"
+ depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n)
+ help
+ This enables the reset driver support over TI System Control Interface
+ available on some new TI's SoCs. If you wish to use reset resources
+ managed by the TI System Controller, say Y here. Otherwise, say N.
+
+config RESET_TI_SYSCON
+ tristate "TI SYSCON Reset Driver"
+ depends on HAS_IOMEM
+ select MFD_SYSCON
+ help
+ This enables the reset driver support for TI devices with
+ memory-mapped reset registers as part of a syscon device node. If
+ you wish to use the reset framework for such memory-mapped devices,
+ say Y here. Otherwise, say N.
+
+config RESET_TI_TPS380X
+ tristate "TI TPS380x Reset Driver"
+ select GPIOLIB
+ help
+ This enables the reset driver support for TI TPS380x devices. If
+ you wish to use the reset framework for such devices, say Y here.
+ Otherwise, say N.
+
+config RESET_TN48M_CPLD
+ tristate "Delta Networks TN48M switch CPLD reset controller"
+ depends on MFD_TN48M_CPLD || COMPILE_TEST
+ default MFD_TN48M_CPLD
+ help
+ This enables the reset controller driver for the Delta TN48M CPLD.
+ It provides reset signals for Armada 7040 and 385 SoC-s, Alleycat 3X
+ switch MAC-s, Alaska OOB ethernet PHY, Quad Alaska ethernet PHY-s and
+ Microchip PD69200 PoE PSE controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called reset-tn48m.
+
+config RESET_UNIPHIER
+ tristate "Reset controller driver for UniPhier SoCs"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+ depends on OF && MFD_SYSCON
+ default ARCH_UNIPHIER
+ help
+ Support for reset controllers on UniPhier SoCs.
+ Say Y if you want to control reset signals provided by System Control
+ block, Media I/O block, Peripheral Block.
+
+config RESET_UNIPHIER_GLUE
+ tristate "Reset driver in glue layer for UniPhier SoCs"
+ depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+ default ARCH_UNIPHIER
+ select RESET_SIMPLE
+ help
+ Support for peripheral core reset included in its own glue layer
+ on UniPhier SoCs. Say Y if you want to control reset signals
+ provided by the glue layer.
+
+config RESET_ZYNQ
+ bool "ZYNQ Reset Driver" if COMPILE_TEST
+ default ARCH_ZYNQ
+ help
+ This enables the reset controller driver for Xilinx Zynq SoCs.
+
+config RESET_ZYNQMP
+ bool "ZYNQMP Reset Driver" if COMPILE_TEST
+ default ARCH_ZYNQMP
+ help
+ This enables the reset controller driver for Xilinx ZynqMP SoCs.
+
+source "drivers/reset/amlogic/Kconfig"
+source "drivers/reset/starfive/Kconfig"
+source "drivers/reset/sti/Kconfig"
+source "drivers/reset/hisilicon/Kconfig"
+source "drivers/reset/tegra/Kconfig"
+
+endif
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 1e2d83f2b995..9c3e484dfd81 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1 +1,52 @@
-obj-$(CONFIG_RESET_CONTROLLER) += core.o
+# SPDX-License-Identifier: GPL-2.0
+obj-y += core.o
+obj-y += amlogic/
+obj-y += hisilicon/
+obj-y += starfive/
+obj-y += sti/
+obj-y += tegra/
+obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o
+obj-$(CONFIG_RESET_ASPEED) += reset-aspeed.o
+obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
+obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o
+obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
+obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
+obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
+obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
+obj-$(CONFIG_RESET_EIC7700) += reset-eic7700.o
+obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o
+obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
+obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
+obj-$(CONFIG_RESET_IMX_SCU) += reset-imx-scu.o
+obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_IMX8MP_AUDIOMIX) += reset-imx8mp-audiomix.o
+obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o
+obj-$(CONFIG_RESET_K210) += reset-k210.o
+obj-$(CONFIG_RESET_K230) += reset-k230.o
+obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
+obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
+obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
+obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
+obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o
+obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
+obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
+obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
+obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
+obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
+obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
+obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o
+obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
+obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
+obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+obj-$(CONFIG_RESET_SPACEMIT) += reset-spacemit.o
+obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
+obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
+obj-$(CONFIG_RESET_TH1520) += reset-th1520.o
+obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
+obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
+obj-$(CONFIG_RESET_TI_TPS380X) += reset-tps380x.o
+obj-$(CONFIG_RESET_TN48M_CPLD) += reset-tn48m.o
+obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
+obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
+obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
+obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o
diff --git a/drivers/reset/amlogic/Kconfig b/drivers/reset/amlogic/Kconfig
new file mode 100644
index 000000000000..3bee9fd60269
--- /dev/null
+++ b/drivers/reset/amlogic/Kconfig
@@ -0,0 +1,27 @@
+config RESET_MESON_COMMON
+ tristate
+ select REGMAP
+
+config RESET_MESON
+ tristate "Meson Reset Driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ default ARCH_MESON
+ select REGMAP_MMIO
+ select RESET_MESON_COMMON
+ help
+ This enables the reset driver for Amlogic SoCs.
+
+config RESET_MESON_AUX
+ tristate "Meson Reset Auxiliary Driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ select AUXILIARY_BUS
+ select RESET_MESON_COMMON
+ help
+ This enables the reset auxiliary driver for Amlogic SoCs.
+
+config RESET_MESON_AUDIO_ARB
+ tristate "Meson Audio Memory Arbiter Reset Driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ help
+ This enables the reset driver for Audio Memory Arbiter of
+ Amlogic's A113 based SoCs
diff --git a/drivers/reset/amlogic/Makefile b/drivers/reset/amlogic/Makefile
new file mode 100644
index 000000000000..ca99a691282c
--- /dev/null
+++ b/drivers/reset/amlogic/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_RESET_MESON) += reset-meson.o
+obj-$(CONFIG_RESET_MESON_AUX) += reset-meson-aux.o
+obj-$(CONFIG_RESET_MESON_COMMON) += reset-meson-common.o
+obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
diff --git a/drivers/reset/amlogic/reset-meson-audio-arb.c b/drivers/reset/amlogic/reset-meson-audio-arb.c
new file mode 100644
index 000000000000..6ec253976bed
--- /dev/null
+++ b/drivers/reset/amlogic/reset-meson-audio-arb.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+// Copyright (c) 2018 BayLibre, SAS.
+// Author: Jerome Brunet <jbrunet@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h>
+
+struct meson_audio_arb_data {
+ struct reset_controller_dev rstc;
+ void __iomem *regs;
+ struct clk *clk;
+ const unsigned int *reset_bits;
+ spinlock_t lock;
+};
+
+struct meson_audio_arb_match_data {
+ const unsigned int *reset_bits;
+ unsigned int reset_num;
+};
+
+#define ARB_GENERAL_BIT 31
+
+static const unsigned int axg_audio_arb_reset_bits[] = {
+ [AXG_ARB_TODDR_A] = 0,
+ [AXG_ARB_TODDR_B] = 1,
+ [AXG_ARB_TODDR_C] = 2,
+ [AXG_ARB_FRDDR_A] = 4,
+ [AXG_ARB_FRDDR_B] = 5,
+ [AXG_ARB_FRDDR_C] = 6,
+};
+
+static const struct meson_audio_arb_match_data axg_audio_arb_match = {
+ .reset_bits = axg_audio_arb_reset_bits,
+ .reset_num = ARRAY_SIZE(axg_audio_arb_reset_bits),
+};
+
+static const unsigned int sm1_audio_arb_reset_bits[] = {
+ [AXG_ARB_TODDR_A] = 0,
+ [AXG_ARB_TODDR_B] = 1,
+ [AXG_ARB_TODDR_C] = 2,
+ [AXG_ARB_FRDDR_A] = 4,
+ [AXG_ARB_FRDDR_B] = 5,
+ [AXG_ARB_FRDDR_C] = 6,
+ [AXG_ARB_TODDR_D] = 3,
+ [AXG_ARB_FRDDR_D] = 7,
+};
+
+static const struct meson_audio_arb_match_data sm1_audio_arb_match = {
+ .reset_bits = sm1_audio_arb_reset_bits,
+ .reset_num = ARRAY_SIZE(sm1_audio_arb_reset_bits),
+};
+
+static int meson_audio_arb_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ u32 val;
+ struct meson_audio_arb_data *arb =
+ container_of(rcdev, struct meson_audio_arb_data, rstc);
+
+ spin_lock(&arb->lock);
+ val = readl(arb->regs);
+
+ if (assert)
+ val &= ~BIT(arb->reset_bits[id]);
+ else
+ val |= BIT(arb->reset_bits[id]);
+
+ writel(val, arb->regs);
+ spin_unlock(&arb->lock);
+
+ return 0;
+}
+
+static int meson_audio_arb_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ u32 val;
+ struct meson_audio_arb_data *arb =
+ container_of(rcdev, struct meson_audio_arb_data, rstc);
+
+ val = readl(arb->regs);
+
+ return !(val & BIT(arb->reset_bits[id]));
+}
+
+static int meson_audio_arb_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_audio_arb_update(rcdev, id, true);
+}
+
+static int meson_audio_arb_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_audio_arb_update(rcdev, id, false);
+}
+
+static const struct reset_control_ops meson_audio_arb_rstc_ops = {
+ .assert = meson_audio_arb_assert,
+ .deassert = meson_audio_arb_deassert,
+ .status = meson_audio_arb_status,
+};
+
+static const struct of_device_id meson_audio_arb_of_match[] = {
+ {
+ .compatible = "amlogic,meson-axg-audio-arb",
+ .data = &axg_audio_arb_match,
+ }, {
+ .compatible = "amlogic,meson-sm1-audio-arb",
+ .data = &sm1_audio_arb_match,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match);
+
+static void meson_audio_arb_remove(struct platform_device *pdev)
+{
+ struct meson_audio_arb_data *arb = platform_get_drvdata(pdev);
+
+ /* Disable all access */
+ spin_lock(&arb->lock);
+ writel(0, arb->regs);
+ spin_unlock(&arb->lock);
+}
+
+static int meson_audio_arb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct meson_audio_arb_match_data *data;
+ struct meson_audio_arb_data *arb;
+ int ret;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -EINVAL;
+
+ arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
+ if (!arb)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, arb);
+
+ arb->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(arb->clk))
+ return dev_err_probe(dev, PTR_ERR(arb->clk), "failed to get clock\n");
+
+ arb->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(arb->regs))
+ return PTR_ERR(arb->regs);
+
+ spin_lock_init(&arb->lock);
+ arb->reset_bits = data->reset_bits;
+ arb->rstc.nr_resets = data->reset_num;
+ arb->rstc.ops = &meson_audio_arb_rstc_ops;
+ arb->rstc.of_node = dev->of_node;
+ arb->rstc.owner = THIS_MODULE;
+
+ /*
+ * Enable general :
+ * In the initial state, all memory interfaces are disabled
+ * and the general bit is on
+ */
+ writel(BIT(ARB_GENERAL_BIT), arb->regs);
+
+ /* Register reset controller */
+ ret = devm_reset_controller_register(dev, &arb->rstc);
+ if (ret) {
+ dev_err(dev, "failed to register arb reset controller\n");
+ meson_audio_arb_remove(pdev);
+ }
+
+ return ret;
+}
+
+static struct platform_driver meson_audio_arb_pdrv = {
+ .probe = meson_audio_arb_probe,
+ .remove = meson_audio_arb_remove,
+ .driver = {
+ .name = "meson-audio-arb-reset",
+ .of_match_table = meson_audio_arb_of_match,
+ },
+};
+module_platform_driver(meson_audio_arb_pdrv);
+
+MODULE_DESCRIPTION("Amlogic A113 Audio Memory Arbiter");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/amlogic/reset-meson-aux.c b/drivers/reset/amlogic/reset-meson-aux.c
new file mode 100644
index 000000000000..33c06013439e
--- /dev/null
+++ b/drivers/reset/amlogic/reset-meson-aux.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Amlogic Meson Reset Auxiliary driver
+ *
+ * Copyright (c) 2024 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include "reset-meson.h"
+
+static const struct meson_reset_param meson_a1_audio_param = {
+ .reset_ops = &meson_reset_toggle_ops,
+ .reset_num = 32,
+ .level_offset = 0x28,
+};
+
+static const struct meson_reset_param meson_a1_audio_vad_param = {
+ .reset_ops = &meson_reset_toggle_ops,
+ .reset_num = 6,
+ .level_offset = 0x8,
+};
+
+static const struct meson_reset_param meson_g12a_audio_param = {
+ .reset_ops = &meson_reset_toggle_ops,
+ .reset_num = 26,
+ .level_offset = 0x24,
+};
+
+static const struct meson_reset_param meson_sm1_audio_param = {
+ .reset_ops = &meson_reset_toggle_ops,
+ .reset_num = 39,
+ .level_offset = 0x28,
+};
+
+static const struct auxiliary_device_id meson_reset_aux_ids[] = {
+ {
+ .name = "a1-audio-clkc.rst-a1",
+ .driver_data = (kernel_ulong_t)&meson_a1_audio_param,
+ }, {
+ .name = "a1-audio-clkc.rst-a1-vad",
+ .driver_data = (kernel_ulong_t)&meson_a1_audio_vad_param,
+ }, {
+ .name = "axg-audio-clkc.rst-g12a",
+ .driver_data = (kernel_ulong_t)&meson_g12a_audio_param,
+ }, {
+ .name = "axg-audio-clkc.rst-sm1",
+ .driver_data = (kernel_ulong_t)&meson_sm1_audio_param,
+ }, {}
+};
+MODULE_DEVICE_TABLE(auxiliary, meson_reset_aux_ids);
+
+static int meson_reset_aux_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ const struct meson_reset_param *param =
+ (const struct meson_reset_param *)(id->driver_data);
+ struct regmap *map;
+
+ map = dev_get_regmap(adev->dev.parent, NULL);
+ if (!map)
+ return -EINVAL;
+
+ return meson_reset_controller_register(&adev->dev, map, param);
+}
+
+static struct auxiliary_driver meson_reset_aux_driver = {
+ .probe = meson_reset_aux_probe,
+ .id_table = meson_reset_aux_ids,
+};
+module_auxiliary_driver(meson_reset_aux_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson Reset Auxiliary driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("MESON_RESET");
diff --git a/drivers/reset/amlogic/reset-meson-common.c b/drivers/reset/amlogic/reset-meson-common.c
new file mode 100644
index 000000000000..a90e0ecaaadf
--- /dev/null
+++ b/drivers/reset/amlogic/reset-meson-common.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Amlogic Meson Reset core functions
+ *
+ * Copyright (c) 2016-2024 BayLibre, SAS.
+ * Authors: Neil Armstrong <narmstrong@baylibre.com>
+ * Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include "reset-meson.h"
+
+struct meson_reset {
+ const struct meson_reset_param *param;
+ struct reset_controller_dev rcdev;
+ struct regmap *map;
+};
+
+static void meson_reset_offset_and_bit(struct meson_reset *data,
+ unsigned long id,
+ unsigned int *offset,
+ unsigned int *bit)
+{
+ unsigned int stride = regmap_get_reg_stride(data->map);
+
+ *offset = (id / (stride * BITS_PER_BYTE)) * stride;
+ *bit = id % (stride * BITS_PER_BYTE);
+}
+
+static int meson_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct meson_reset *data =
+ container_of(rcdev, struct meson_reset, rcdev);
+ unsigned int offset, bit;
+
+ meson_reset_offset_and_bit(data, id, &offset, &bit);
+ offset += data->param->reset_offset;
+
+ return regmap_write(data->map, offset, BIT(bit));
+}
+
+static int meson_reset_level(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct meson_reset *data =
+ container_of(rcdev, struct meson_reset, rcdev);
+ unsigned int offset, bit;
+
+ meson_reset_offset_and_bit(data, id, &offset, &bit);
+ offset += data->param->level_offset;
+ assert ^= data->param->level_low_reset;
+
+ return regmap_update_bits(data->map, offset,
+ BIT(bit), assert ? BIT(bit) : 0);
+}
+
+static int meson_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct meson_reset *data =
+ container_of(rcdev, struct meson_reset, rcdev);
+ unsigned int val, offset, bit;
+
+ meson_reset_offset_and_bit(data, id, &offset, &bit);
+ offset += data->param->level_offset;
+
+ regmap_read(data->map, offset, &val);
+ val = !!(BIT(bit) & val);
+
+ return val ^ data->param->level_low_reset;
+}
+
+static int meson_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_reset_level(rcdev, id, true);
+}
+
+static int meson_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_reset_level(rcdev, id, false);
+}
+
+static int meson_reset_level_toggle(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = meson_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ return meson_reset_deassert(rcdev, id);
+}
+
+const struct reset_control_ops meson_reset_ops = {
+ .reset = meson_reset_reset,
+ .assert = meson_reset_assert,
+ .deassert = meson_reset_deassert,
+ .status = meson_reset_status,
+};
+EXPORT_SYMBOL_NS_GPL(meson_reset_ops, "MESON_RESET");
+
+const struct reset_control_ops meson_reset_toggle_ops = {
+ .reset = meson_reset_level_toggle,
+ .assert = meson_reset_assert,
+ .deassert = meson_reset_deassert,
+ .status = meson_reset_status,
+};
+EXPORT_SYMBOL_NS_GPL(meson_reset_toggle_ops, "MESON_RESET");
+
+int meson_reset_controller_register(struct device *dev, struct regmap *map,
+ const struct meson_reset_param *param)
+{
+ struct meson_reset *data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->param = param;
+ data->map = map;
+ data->rcdev.owner = dev->driver->owner;
+ data->rcdev.nr_resets = param->reset_num;
+ data->rcdev.ops = data->param->reset_ops;
+ data->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+EXPORT_SYMBOL_NS_GPL(meson_reset_controller_register, "MESON_RESET");
+
+MODULE_DESCRIPTION("Amlogic Meson Reset Core function");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("MESON_RESET");
diff --git a/drivers/reset/amlogic/reset-meson.c b/drivers/reset/amlogic/reset-meson.c
new file mode 100644
index 000000000000..84610365a823
--- /dev/null
+++ b/drivers/reset/amlogic/reset-meson.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Amlogic Meson Reset Controller driver
+ *
+ * Copyright (c) 2016-2024 BayLibre, SAS.
+ * Authors: Neil Armstrong <narmstrong@baylibre.com>
+ * Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include "reset-meson.h"
+
+static const struct meson_reset_param meson8b_param = {
+ .reset_ops = &meson_reset_ops,
+ .reset_num = 256,
+ .reset_offset = 0x0,
+ .level_offset = 0x7c,
+ .level_low_reset = true,
+};
+
+static const struct meson_reset_param meson_a1_param = {
+ .reset_ops = &meson_reset_ops,
+ .reset_num = 96,
+ .reset_offset = 0x0,
+ .level_offset = 0x40,
+ .level_low_reset = true,
+};
+
+static const struct meson_reset_param meson_s4_param = {
+ .reset_ops = &meson_reset_ops,
+ .reset_num = 192,
+ .reset_offset = 0x0,
+ .level_offset = 0x40,
+ .level_low_reset = true,
+};
+
+static const struct meson_reset_param t7_param = {
+ .reset_num = 224,
+ .reset_offset = 0x0,
+ .level_offset = 0x40,
+ .level_low_reset = true,
+};
+
+static const struct of_device_id meson_reset_dt_ids[] = {
+ { .compatible = "amlogic,meson8b-reset", .data = &meson8b_param},
+ { .compatible = "amlogic,meson-gxbb-reset", .data = &meson8b_param},
+ { .compatible = "amlogic,meson-axg-reset", .data = &meson8b_param},
+ { .compatible = "amlogic,meson-a1-reset", .data = &meson_a1_param},
+ { .compatible = "amlogic,meson-s4-reset", .data = &meson_s4_param},
+ { .compatible = "amlogic,c3-reset", .data = &meson_s4_param},
+ { .compatible = "amlogic,t7-reset", .data = &t7_param},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int meson_reset_probe(struct platform_device *pdev)
+{
+ const struct meson_reset_param *param;
+ struct device *dev = &pdev->dev;
+ struct regmap *map;
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ param = device_get_match_data(dev);
+ if (!param)
+ return -ENODEV;
+
+ map = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(map))
+ return dev_err_probe(dev, PTR_ERR(map),
+ "can't init regmap mmio region\n");
+
+ return meson_reset_controller_register(dev, map, param);
+}
+
+static struct platform_driver meson_reset_driver = {
+ .probe = meson_reset_probe,
+ .driver = {
+ .name = "meson_reset",
+ .of_match_table = meson_reset_dt_ids,
+ },
+};
+module_platform_driver(meson_reset_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("MESON_RESET");
diff --git a/drivers/reset/amlogic/reset-meson.h b/drivers/reset/amlogic/reset-meson.h
new file mode 100644
index 000000000000..2051e126dc3a
--- /dev/null
+++ b/drivers/reset/amlogic/reset-meson.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/*
+ * Copyright (c) 2024 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ */
+
+#ifndef __MESON_RESET_H
+#define __MESON_RESET_H
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+struct meson_reset_param {
+ const struct reset_control_ops *reset_ops;
+ unsigned int reset_num;
+ unsigned int reset_offset;
+ unsigned int level_offset;
+ bool level_low_reset;
+};
+
+int meson_reset_controller_register(struct device *dev, struct regmap *map,
+ const struct meson_reset_param *param);
+
+extern const struct reset_control_ops meson_reset_ops;
+extern const struct reset_control_ops meson_reset_toggle_ops;
+
+#endif /* __MESON_RESET_H */
diff --git a/drivers/reset/core.c b/drivers/reset/core.c
index d1b6089a0ef8..0135dd0ae204 100644
--- a/drivers/reset/core.c
+++ b/drivers/reset/core.c
@@ -1,60 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Reset Controller framework
*
* Copyright 2013 Philipp Zabel, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
+
+#include <linux/acpi.h>
+#include <linux/atomic.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio/property.h>
+#include <linux/idr.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
-static DEFINE_MUTEX(reset_controller_list_mutex);
+static DEFINE_MUTEX(reset_list_mutex);
static LIST_HEAD(reset_controller_list);
+/* Protects reset_gpio_lookup_list */
+static DEFINE_MUTEX(reset_gpio_lookup_mutex);
+static LIST_HEAD(reset_gpio_lookup_list);
+static DEFINE_IDA(reset_gpio_ida);
+
/**
* struct reset_control - a reset control
* @rcdev: a pointer to the reset controller device
* this reset control belongs to
+ * @list: list entry for the rcdev's reset controller list
* @id: ID of the reset controller in the reset
* controller device
+ * @refcnt: Number of gets of this reset_control
+ * @acquired: Only one reset_control may be acquired for a given rcdev and id.
+ * @shared: Is this a shared (1), or an exclusive (0) reset_control?
+ * @array: Is this an array of reset controls (1)?
+ * @deassert_count: Number of times this reset line has been deasserted
+ * @triggered_count: Number of times this reset line has been reset. Currently
+ * only used for shared resets, which means that the value
+ * will be either 0 or 1.
*/
struct reset_control {
struct reset_controller_dev *rcdev;
- struct device *dev;
+ struct list_head list;
unsigned int id;
+ struct kref refcnt;
+ bool acquired;
+ bool shared;
+ bool array;
+ atomic_t deassert_count;
+ atomic_t triggered_count;
+};
+
+/**
+ * struct reset_control_array - an array of reset controls
+ * @base: reset control for compatibility with reset control API functions
+ * @num_rstcs: number of reset controls
+ * @rstc: array of reset controls
+ */
+struct reset_control_array {
+ struct reset_control base;
+ unsigned int num_rstcs;
+ struct reset_control *rstc[] __counted_by(num_rstcs);
+};
+
+/**
+ * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices
+ * @of_args: phandle to the reset controller with all the args like GPIO number
+ * @swnode: Software node containing the reference to the GPIO provider
+ * @list: list entry for the reset_gpio_lookup_list
+ */
+struct reset_gpio_lookup {
+ struct of_phandle_args of_args;
+ struct fwnode_handle *swnode;
+ struct list_head list;
};
+static const char *rcdev_name(struct reset_controller_dev *rcdev)
+{
+ if (rcdev->dev)
+ return dev_name(rcdev->dev);
+
+ if (rcdev->of_node)
+ return rcdev->of_node->full_name;
+
+ if (rcdev->of_args)
+ return rcdev->of_args->np->full_name;
+
+ return NULL;
+}
+
/**
* of_reset_simple_xlate - translate reset_spec to the reset line number
* @rcdev: a pointer to the reset controller device
* @reset_spec: reset line specifier as found in the device tree
- * @flags: a flags pointer to fill in (optional)
*
- * This simple translation function should be used for reset controllers
- * with 1:1 mapping, where reset lines can be indexed by number without gaps.
+ * This static translation function is used by default if of_xlate in
+ * :c:type:`reset_controller_dev` is not set. It is useful for all reset
+ * controllers with 1:1 mapping, where reset lines can be indexed by number
+ * without gaps.
*/
-int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
- const struct of_phandle_args *reset_spec)
+static int of_reset_simple_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
{
- if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
- return -EINVAL;
-
if (reset_spec->args[0] >= rcdev->nr_resets)
return -EINVAL;
return reset_spec->args[0];
}
-EXPORT_SYMBOL_GPL(of_reset_simple_xlate);
/**
* reset_controller_register - register a reset controller device
@@ -62,14 +123,19 @@ EXPORT_SYMBOL_GPL(of_reset_simple_xlate);
*/
int reset_controller_register(struct reset_controller_dev *rcdev)
{
+ if (rcdev->of_node && rcdev->of_args)
+ return -EINVAL;
+
if (!rcdev->of_xlate) {
rcdev->of_reset_n_cells = 1;
rcdev->of_xlate = of_reset_simple_xlate;
}
- mutex_lock(&reset_controller_list_mutex);
+ INIT_LIST_HEAD(&rcdev->reset_control_head);
+
+ mutex_lock(&reset_list_mutex);
list_add(&rcdev->list, &reset_controller_list);
- mutex_unlock(&reset_controller_list_mutex);
+ mutex_unlock(&reset_list_mutex);
return 0;
}
@@ -81,210 +147,1160 @@ EXPORT_SYMBOL_GPL(reset_controller_register);
*/
void reset_controller_unregister(struct reset_controller_dev *rcdev)
{
- mutex_lock(&reset_controller_list_mutex);
+ mutex_lock(&reset_list_mutex);
list_del(&rcdev->list);
- mutex_unlock(&reset_controller_list_mutex);
+ mutex_unlock(&reset_list_mutex);
}
EXPORT_SYMBOL_GPL(reset_controller_unregister);
+static void devm_reset_controller_release(struct device *dev, void *res)
+{
+ reset_controller_unregister(*(struct reset_controller_dev **)res);
+}
+
+/**
+ * devm_reset_controller_register - resource managed reset_controller_register()
+ * @dev: device that is registering this reset controller
+ * @rcdev: a pointer to the initialized reset controller device
+ *
+ * Managed reset_controller_register(). For reset controllers registered by
+ * this function, reset_controller_unregister() is automatically called on
+ * driver detach. See reset_controller_register() for more information.
+ */
+int devm_reset_controller_register(struct device *dev,
+ struct reset_controller_dev *rcdev)
+{
+ struct reset_controller_dev **rcdevp;
+ int ret;
+
+ rcdevp = devres_alloc(devm_reset_controller_release, sizeof(*rcdevp),
+ GFP_KERNEL);
+ if (!rcdevp)
+ return -ENOMEM;
+
+ ret = reset_controller_register(rcdev);
+ if (ret) {
+ devres_free(rcdevp);
+ return ret;
+ }
+
+ *rcdevp = rcdev;
+ devres_add(dev, rcdevp);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_reset_controller_register);
+
+static inline struct reset_control_array *
+rstc_to_array(struct reset_control *rstc) {
+ return container_of(rstc, struct reset_control_array, base);
+}
+
+static int reset_control_array_reset(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_reset(resets->rstc[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int reset_control_array_rearm(struct reset_control_array *resets)
+{
+ struct reset_control *rstc;
+ int i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ rstc = resets->rstc[i];
+
+ if (!rstc)
+ continue;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (rstc->shared) {
+ if (WARN_ON(atomic_read(&rstc->deassert_count) != 0))
+ return -EINVAL;
+ } else {
+ if (!rstc->acquired)
+ return -EPERM;
+ }
+ }
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ rstc = resets->rstc[i];
+
+ if (rstc && rstc->shared)
+ WARN_ON(atomic_dec_return(&rstc->triggered_count) < 0);
+ }
+
+ return 0;
+}
+
+static int reset_control_array_assert(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_assert(resets->rstc[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_deassert(resets->rstc[i]);
+ return ret;
+}
+
+static int reset_control_array_deassert(struct reset_control_array *resets)
+{
+ int ret, i;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ ret = reset_control_deassert(resets->rstc[i]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_assert(resets->rstc[i]);
+ return ret;
+}
+
+static int reset_control_array_acquire(struct reset_control_array *resets)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < resets->num_rstcs; i++) {
+ err = reset_control_acquire(resets->rstc[i]);
+ if (err < 0)
+ goto release;
+ }
+
+ return 0;
+
+release:
+ while (i--)
+ reset_control_release(resets->rstc[i]);
+
+ return err;
+}
+
+static void reset_control_array_release(struct reset_control_array *resets)
+{
+ unsigned int i;
+
+ for (i = 0; i < resets->num_rstcs; i++)
+ reset_control_release(resets->rstc[i]);
+}
+
+static inline bool reset_control_is_array(struct reset_control *rstc)
+{
+ return rstc->array;
+}
+
/**
* reset_control_reset - reset the controlled device
* @rstc: reset controller
+ *
+ * On a shared reset line the actual reset pulse is only triggered once for the
+ * lifetime of the reset_control instance: for all but the first caller this is
+ * a no-op.
+ * Consumers must not use reset_control_(de)assert on shared reset lines when
+ * reset_control_reset has been used.
+ *
+ * If rstc is NULL it is an optional reset and the function will just
+ * return 0.
*/
int reset_control_reset(struct reset_control *rstc)
{
- if (rstc->rcdev->ops->reset)
- return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
+ int ret;
+
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (reset_control_is_array(rstc))
+ return reset_control_array_reset(rstc_to_array(rstc));
+
+ if (!rstc->rcdev->ops->reset)
+ return -ENOTSUPP;
+
+ if (rstc->shared) {
+ if (WARN_ON(atomic_read(&rstc->deassert_count) != 0))
+ return -EINVAL;
+
+ if (atomic_inc_return(&rstc->triggered_count) != 1)
+ return 0;
+ } else {
+ if (!rstc->acquired)
+ return -EPERM;
+ }
+
+ ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
+ if (rstc->shared && ret)
+ atomic_dec(&rstc->triggered_count);
- return -ENOSYS;
+ return ret;
}
EXPORT_SYMBOL_GPL(reset_control_reset);
/**
+ * reset_control_bulk_reset - reset the controlled devices in order
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ *
+ * Issue a reset on all provided reset controls, in order.
+ *
+ * See also: reset_control_reset()
+ */
+int reset_control_bulk_reset(int num_rstcs,
+ struct reset_control_bulk_data *rstcs)
+{
+ int ret, i;
+
+ for (i = 0; i < num_rstcs; i++) {
+ ret = reset_control_reset(rstcs[i].rstc);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_reset);
+
+/**
+ * reset_control_rearm - allow shared reset line to be re-triggered"
+ * @rstc: reset controller
+ *
+ * On a shared reset line the actual reset pulse is only triggered once for the
+ * lifetime of the reset_control instance, except if this call is used.
+ *
+ * Calls to this function must be balanced with calls to reset_control_reset,
+ * a warning is thrown in case triggered_count ever dips below 0.
+ *
+ * Consumers must not use reset_control_(de)assert on shared reset lines when
+ * reset_control_reset or reset_control_rearm have been used.
+ *
+ * If rstc is NULL the function will just return 0.
+ */
+int reset_control_rearm(struct reset_control *rstc)
+{
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (reset_control_is_array(rstc))
+ return reset_control_array_rearm(rstc_to_array(rstc));
+
+ if (rstc->shared) {
+ if (WARN_ON(atomic_read(&rstc->deassert_count) != 0))
+ return -EINVAL;
+
+ WARN_ON(atomic_dec_return(&rstc->triggered_count) < 0);
+ } else {
+ if (!rstc->acquired)
+ return -EPERM;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(reset_control_rearm);
+
+/**
* reset_control_assert - asserts the reset line
* @rstc: reset controller
+ *
+ * Calling this on an exclusive reset controller guarantees that the reset
+ * will be asserted. When called on a shared reset controller the line may
+ * still be deasserted, as long as other users keep it so.
+ *
+ * For shared reset controls a driver cannot expect the hw's registers and
+ * internal state to be reset, but must be prepared for this to happen.
+ * Consumers must not use reset_control_reset on shared reset lines when
+ * reset_control_(de)assert has been used.
+ *
+ * If rstc is NULL it is an optional reset and the function will just
+ * return 0.
*/
int reset_control_assert(struct reset_control *rstc)
{
- if (rstc->rcdev->ops->assert)
- return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (reset_control_is_array(rstc))
+ return reset_control_array_assert(rstc_to_array(rstc));
+
+ if (rstc->shared) {
+ if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
+ return -EINVAL;
+
+ if (WARN_ON(atomic_read(&rstc->deassert_count) == 0))
+ return -EINVAL;
+
+ if (atomic_dec_return(&rstc->deassert_count) != 0)
+ return 0;
+
+ /*
+ * Shared reset controls allow the reset line to be in any state
+ * after this call, so doing nothing is a valid option.
+ */
+ if (!rstc->rcdev->ops->assert)
+ return 0;
+ } else {
+ /*
+ * If the reset controller does not implement .assert(), there
+ * is no way to guarantee that the reset line is asserted after
+ * this call.
+ */
+ if (!rstc->rcdev->ops->assert)
+ return -ENOTSUPP;
+
+ if (!rstc->acquired) {
+ WARN(1, "reset %s (ID: %u) is not acquired\n",
+ rcdev_name(rstc->rcdev), rstc->id);
+ return -EPERM;
+ }
+ }
- return -ENOSYS;
+ return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
}
EXPORT_SYMBOL_GPL(reset_control_assert);
/**
+ * reset_control_bulk_assert - asserts the reset lines in order
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ *
+ * Assert the reset lines for all provided reset controls, in order.
+ * If an assertion fails, already asserted resets are deasserted again.
+ *
+ * See also: reset_control_assert()
+ */
+int reset_control_bulk_assert(int num_rstcs,
+ struct reset_control_bulk_data *rstcs)
+{
+ int ret, i;
+
+ for (i = 0; i < num_rstcs; i++) {
+ ret = reset_control_assert(rstcs[i].rstc);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_deassert(rstcs[i].rstc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_assert);
+
+/**
* reset_control_deassert - deasserts the reset line
* @rstc: reset controller
+ *
+ * After calling this function, the reset is guaranteed to be deasserted.
+ * Consumers must not use reset_control_reset on shared reset lines when
+ * reset_control_(de)assert has been used.
+ *
+ * If rstc is NULL it is an optional reset and the function will just
+ * return 0.
*/
int reset_control_deassert(struct reset_control *rstc)
{
- if (rstc->rcdev->ops->deassert)
- return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
+ if (!rstc)
+ return 0;
- return -ENOSYS;
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (reset_control_is_array(rstc))
+ return reset_control_array_deassert(rstc_to_array(rstc));
+
+ if (rstc->shared) {
+ if (WARN_ON(atomic_read(&rstc->triggered_count) != 0))
+ return -EINVAL;
+
+ if (atomic_inc_return(&rstc->deassert_count) != 1)
+ return 0;
+ } else {
+ if (!rstc->acquired) {
+ WARN(1, "reset %s (ID: %u) is not acquired\n",
+ rcdev_name(rstc->rcdev), rstc->id);
+ return -EPERM;
+ }
+ }
+
+ /*
+ * If the reset controller does not implement .deassert(), we assume
+ * that it handles self-deasserting reset lines via .reset(). In that
+ * case, the reset lines are deasserted by default. If that is not the
+ * case, the reset controller driver should implement .deassert() and
+ * return -ENOTSUPP.
+ */
+ if (!rstc->rcdev->ops->deassert)
+ return 0;
+
+ return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
}
EXPORT_SYMBOL_GPL(reset_control_deassert);
/**
- * reset_control_get - Lookup and obtain a reference to a reset controller.
- * @dev: device to be reset by the controller
- * @id: reset line name
+ * reset_control_bulk_deassert - deasserts the reset lines in reverse order
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ *
+ * Deassert the reset lines for all provided reset controls, in reverse order.
+ * If a deassertion fails, already deasserted resets are asserted again.
+ *
+ * See also: reset_control_deassert()
+ */
+int reset_control_bulk_deassert(int num_rstcs,
+ struct reset_control_bulk_data *rstcs)
+{
+ int ret, i;
+
+ for (i = num_rstcs - 1; i >= 0; i--) {
+ ret = reset_control_deassert(rstcs[i].rstc);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i < num_rstcs)
+ reset_control_assert(rstcs[i++].rstc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_deassert);
+
+/**
+ * reset_control_status - returns a negative errno if not supported, a
+ * positive value if the reset line is asserted, or zero if the reset
+ * line is not asserted or if the desc is NULL (optional reset).
+ * @rstc: reset controller
+ */
+int reset_control_status(struct reset_control *rstc)
+{
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc))
+ return -EINVAL;
+
+ if (rstc->rcdev->ops->status)
+ return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(reset_control_status);
+
+/**
+ * reset_control_acquire() - acquires a reset control for exclusive use
+ * @rstc: reset control
*
- * Returns a struct reset_control or IS_ERR() condition containing errno.
+ * This is used to explicitly acquire a reset control for exclusive use. Note
+ * that exclusive resets are requested as acquired by default. In order for a
+ * second consumer to be able to control the reset, the first consumer has to
+ * release it first. Typically the easiest way to achieve this is to call the
+ * reset_control_get_exclusive_released() to obtain an instance of the reset
+ * control. Such reset controls are not acquired by default.
*
- * Use of id names is optional.
+ * Consumers implementing shared access to an exclusive reset need to follow
+ * a specific protocol in order to work together. Before consumers can change
+ * a reset they must acquire exclusive access using reset_control_acquire().
+ * After they are done operating the reset, they must release exclusive access
+ * with a call to reset_control_release(). Consumers are not granted exclusive
+ * access to the reset as long as another consumer hasn't released a reset.
+ *
+ * See also: reset_control_release()
*/
-struct reset_control *reset_control_get(struct device *dev, const char *id)
+int reset_control_acquire(struct reset_control *rstc)
{
- struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
- struct reset_controller_dev *r, *rcdev;
+ struct reset_control *rc;
+
+ if (!rstc)
+ return 0;
+
+ if (WARN_ON(IS_ERR(rstc)))
+ return -EINVAL;
+
+ if (reset_control_is_array(rstc))
+ return reset_control_array_acquire(rstc_to_array(rstc));
+
+ mutex_lock(&reset_list_mutex);
+
+ if (rstc->acquired) {
+ mutex_unlock(&reset_list_mutex);
+ return 0;
+ }
+
+ list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) {
+ if (rstc != rc && rstc->id == rc->id) {
+ if (rc->acquired) {
+ mutex_unlock(&reset_list_mutex);
+ return -EBUSY;
+ }
+ }
+ }
+
+ rstc->acquired = true;
+
+ mutex_unlock(&reset_list_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(reset_control_acquire);
+
+/**
+ * reset_control_bulk_acquire - acquires reset controls for exclusive use
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ *
+ * This is used to explicitly acquire reset controls requested with
+ * reset_control_bulk_get_exclusive_release() for temporary exclusive use.
+ *
+ * See also: reset_control_acquire(), reset_control_bulk_release()
+ */
+int reset_control_bulk_acquire(int num_rstcs,
+ struct reset_control_bulk_data *rstcs)
+{
+ int ret, i;
+
+ for (i = 0; i < num_rstcs; i++) {
+ ret = reset_control_acquire(rstcs[i].rstc);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (i--)
+ reset_control_release(rstcs[i].rstc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_acquire);
+
+/**
+ * reset_control_release() - releases exclusive access to a reset control
+ * @rstc: reset control
+ *
+ * Releases exclusive access right to a reset control previously obtained by a
+ * call to reset_control_acquire(). Until a consumer calls this function, no
+ * other consumers will be granted exclusive access.
+ *
+ * See also: reset_control_acquire()
+ */
+void reset_control_release(struct reset_control *rstc)
+{
+ if (!rstc || WARN_ON(IS_ERR(rstc)))
+ return;
+
+ if (reset_control_is_array(rstc))
+ reset_control_array_release(rstc_to_array(rstc));
+ else
+ rstc->acquired = false;
+}
+EXPORT_SYMBOL_GPL(reset_control_release);
+
+/**
+ * reset_control_bulk_release() - releases exclusive access to reset controls
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ *
+ * Releases exclusive access right to reset controls previously obtained by a
+ * call to reset_control_bulk_acquire().
+ *
+ * See also: reset_control_release(), reset_control_bulk_acquire()
+ */
+void reset_control_bulk_release(int num_rstcs,
+ struct reset_control_bulk_data *rstcs)
+{
+ int i;
+
+ for (i = 0; i < num_rstcs; i++)
+ reset_control_release(rstcs[i].rstc);
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_release);
+
+static struct reset_control *
+__reset_control_get_internal(struct reset_controller_dev *rcdev,
+ unsigned int index, enum reset_control_flags flags)
+{
+ bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
+ bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
+ struct reset_control *rstc;
+
+ lockdep_assert_held(&reset_list_mutex);
+
+ /* Expect callers to filter out OPTIONAL and DEASSERTED bits */
+ if (WARN_ON(flags & ~(RESET_CONTROL_FLAGS_BIT_SHARED |
+ RESET_CONTROL_FLAGS_BIT_ACQUIRED)))
+ return ERR_PTR(-EINVAL);
+
+ list_for_each_entry(rstc, &rcdev->reset_control_head, list) {
+ if (rstc->id == index) {
+ /*
+ * Allow creating a secondary exclusive reset_control
+ * that is initially not acquired for an already
+ * controlled reset line.
+ */
+ if (!rstc->shared && !shared && !acquired)
+ break;
+
+ if (WARN_ON(!rstc->shared || !shared))
+ return ERR_PTR(-EBUSY);
+
+ kref_get(&rstc->refcnt);
+ return rstc;
+ }
+ }
+
+ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
+ if (!rstc)
+ return ERR_PTR(-ENOMEM);
+
+ if (!try_module_get(rcdev->owner)) {
+ kfree(rstc);
+ return ERR_PTR(-ENODEV);
+ }
+
+ rstc->rcdev = rcdev;
+ list_add(&rstc->list, &rcdev->reset_control_head);
+ rstc->id = index;
+ kref_init(&rstc->refcnt);
+ rstc->acquired = acquired;
+ rstc->shared = shared;
+ get_device(rcdev->dev);
+
+ return rstc;
+}
+
+static void __reset_control_release(struct kref *kref)
+{
+ struct reset_control *rstc = container_of(kref, struct reset_control,
+ refcnt);
+
+ lockdep_assert_held(&reset_list_mutex);
+
+ module_put(rstc->rcdev->owner);
+
+ list_del(&rstc->list);
+ put_device(rstc->rcdev->dev);
+ kfree(rstc);
+}
+
+static void __reset_control_put_internal(struct reset_control *rstc)
+{
+ lockdep_assert_held(&reset_list_mutex);
+
+ if (IS_ERR_OR_NULL(rstc))
+ return;
+
+ kref_put(&rstc->refcnt, __reset_control_release);
+}
+
+static void reset_gpio_aux_device_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ kfree(adev);
+}
+
+static int reset_add_gpio_aux_device(struct device *parent,
+ struct fwnode_handle *swnode,
+ int id, void *pdata)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return -ENOMEM;
+
+ adev->id = id;
+ adev->name = "gpio";
+ adev->dev.parent = parent;
+ adev->dev.platform_data = pdata;
+ adev->dev.release = reset_gpio_aux_device_release;
+ device_set_node(&adev->dev, swnode);
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ret;
+ }
+
+ ret = __auxiliary_device_add(adev, "reset");
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ kfree(adev);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * @args: phandle to the GPIO provider with all the args like GPIO number
+ */
+static int __reset_add_reset_gpio_device(const struct of_phandle_args *args)
+{
+ struct property_entry properties[2] = { };
+ unsigned int offset, of_flags, lflags;
+ struct reset_gpio_lookup *rgpio_dev;
+ struct device *parent;
+ int id, ret;
+
+ /*
+ * Currently only #gpio-cells=2 is supported with the meaning of:
+ * args[0]: GPIO number
+ * args[1]: GPIO flags
+ * TODO: Handle other cases.
+ */
+ if (args->args_count != 2)
+ return -ENOENT;
+
+ /*
+ * Registering reset-gpio device might cause immediate
+ * bind, resulting in its probe() registering new reset controller thus
+ * taking reset_list_mutex lock via reset_controller_register().
+ */
+ lockdep_assert_not_held(&reset_list_mutex);
+
+ offset = args->args[0];
+ of_flags = args->args[1];
+
+ /*
+ * Later we map GPIO flags between OF and Linux, however not all
+ * constants from include/dt-bindings/gpio/gpio.h and
+ * include/linux/gpio/machine.h match each other.
+ *
+ * FIXME: Find a better way of translating OF flags to GPIO lookup
+ * flags.
+ */
+ if (of_flags > GPIO_ACTIVE_LOW) {
+ pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n",
+ of_flags, offset);
+ return -EINVAL;
+ }
+
+ struct gpio_device *gdev __free(gpio_device_put) =
+ gpio_device_find_by_fwnode(of_fwnode_handle(args->np));
+ if (!gdev)
+ return -EPROBE_DEFER;
+
+ guard(mutex)(&reset_gpio_lookup_mutex);
+
+ list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) {
+ if (args->np == rgpio_dev->of_args.np) {
+ if (of_phandle_args_equal(args, &rgpio_dev->of_args))
+ return 0; /* Already on the list, done */
+ }
+ }
+
+ lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW);
+ parent = gpio_device_to_device(gdev);
+ properties[0] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags);
+
+ id = ida_alloc(&reset_gpio_ida, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ /* Not freed on success, because it is persisent subsystem data. */
+ rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL);
+ if (!rgpio_dev) {
+ ret = -ENOMEM;
+ goto err_ida_free;
+ }
+
+ rgpio_dev->of_args = *args;
+ /*
+ * We keep the device_node reference, but of_args.np is put at the end
+ * of __of_reset_control_get(), so get it one more time.
+ * Hold reference as long as rgpio_dev memory is valid.
+ */
+ of_node_get(rgpio_dev->of_args.np);
+
+ rgpio_dev->swnode = fwnode_create_software_node(properties, NULL);
+ if (IS_ERR(rgpio_dev->swnode)) {
+ ret = PTR_ERR(rgpio_dev->swnode);
+ goto err_put_of_node;
+ }
+
+ ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id,
+ &rgpio_dev->of_args);
+ if (ret)
+ goto err_del_swnode;
+
+ list_add(&rgpio_dev->list, &reset_gpio_lookup_list);
+
+ return 0;
+
+err_del_swnode:
+ fwnode_remove_software_node(rgpio_dev->swnode);
+err_put_of_node:
+ of_node_put(rgpio_dev->of_args.np);
+ kfree(rgpio_dev);
+err_ida_free:
+ ida_free(&reset_gpio_ida, id);
+
+ return ret;
+}
+
+static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args,
+ bool gpio_fallback)
+{
+ struct reset_controller_dev *rcdev;
+
+ lockdep_assert_held(&reset_list_mutex);
+
+ list_for_each_entry(rcdev, &reset_controller_list, list) {
+ if (gpio_fallback) {
+ if (rcdev->of_args && of_phandle_args_equal(args,
+ rcdev->of_args))
+ return rcdev;
+ } else {
+ if (args->np == rcdev->of_node)
+ return rcdev;
+ }
+ }
+
+ return NULL;
+}
+
+struct reset_control *
+__of_reset_control_get(struct device_node *node, const char *id, int index,
+ enum reset_control_flags flags)
+{
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+ bool gpio_fallback = false;
+ struct reset_control *rstc;
+ struct reset_controller_dev *rcdev;
struct of_phandle_args args;
- int index = 0;
int rstc_id;
int ret;
- if (!dev)
+ if (!node)
return ERR_PTR(-EINVAL);
- if (id)
- index = of_property_match_string(dev->of_node,
+ if (id) {
+ index = of_property_match_string(node,
"reset-names", id);
- ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells",
+ if (index == -EILSEQ)
+ return ERR_PTR(index);
+ if (index < 0)
+ return optional ? NULL : ERR_PTR(-ENOENT);
+ }
+
+ ret = of_parse_phandle_with_args(node, "resets", "#reset-cells",
index, &args);
- if (ret)
+ if (ret == -EINVAL)
return ERR_PTR(ret);
+ if (ret) {
+ if (!IS_ENABLED(CONFIG_RESET_GPIO))
+ return optional ? NULL : ERR_PTR(ret);
- mutex_lock(&reset_controller_list_mutex);
- rcdev = NULL;
- list_for_each_entry(r, &reset_controller_list, list) {
- if (args.np == r->of_node) {
- rcdev = r;
- break;
+ /*
+ * There can be only one reset-gpio for regular devices, so
+ * don't bother with the "reset-gpios" phandle index.
+ */
+ ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells",
+ 0, &args);
+ if (ret)
+ return optional ? NULL : ERR_PTR(ret);
+
+ gpio_fallback = true;
+
+ ret = __reset_add_reset_gpio_device(&args);
+ if (ret) {
+ rstc = ERR_PTR(ret);
+ goto out_put;
}
}
- of_node_put(args.np);
+ mutex_lock(&reset_list_mutex);
+ rcdev = __reset_find_rcdev(&args, gpio_fallback);
if (!rcdev) {
- mutex_unlock(&reset_controller_list_mutex);
- return ERR_PTR(-ENODEV);
+ rstc = ERR_PTR(-EPROBE_DEFER);
+ goto out_unlock;
+ }
+
+ if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) {
+ rstc = ERR_PTR(-EINVAL);
+ goto out_unlock;
}
rstc_id = rcdev->of_xlate(rcdev, &args);
if (rstc_id < 0) {
- mutex_unlock(&reset_controller_list_mutex);
- return ERR_PTR(rstc_id);
+ rstc = ERR_PTR(rstc_id);
+ goto out_unlock;
}
- try_module_get(rcdev->owner);
- mutex_unlock(&reset_controller_list_mutex);
+ flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL;
- rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
- if (!rstc) {
- module_put(rcdev->owner);
- return ERR_PTR(-ENOMEM);
- }
+ /* reset_list_mutex also protects the rcdev's reset_control list */
+ rstc = __reset_control_get_internal(rcdev, rstc_id, flags);
- rstc->dev = dev;
- rstc->rcdev = rcdev;
- rstc->id = rstc_id;
+out_unlock:
+ mutex_unlock(&reset_list_mutex);
+out_put:
+ of_node_put(args.np);
return rstc;
}
-EXPORT_SYMBOL_GPL(reset_control_get);
+EXPORT_SYMBOL_GPL(__of_reset_control_get);
+
+struct reset_control *__reset_control_get(struct device *dev, const char *id,
+ int index, enum reset_control_flags flags)
+{
+ bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED;
+ bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED;
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+
+ if (WARN_ON(shared && acquired))
+ return ERR_PTR(-EINVAL);
+
+ if (dev->of_node)
+ return __of_reset_control_get(dev->of_node, id, index, flags);
+
+ return optional ? NULL : ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(__reset_control_get);
+
+int __reset_control_bulk_get(struct device *dev, int num_rstcs,
+ struct reset_control_bulk_data *rstcs,
+ enum reset_control_flags flags)
+{
+ int ret, i;
+
+ for (i = 0; i < num_rstcs; i++) {
+ rstcs[i].rstc = __reset_control_get(dev, rstcs[i].id, 0, flags);
+ if (IS_ERR(rstcs[i].rstc)) {
+ ret = PTR_ERR(rstcs[i].rstc);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ mutex_lock(&reset_list_mutex);
+ while (i--)
+ __reset_control_put_internal(rstcs[i].rstc);
+ mutex_unlock(&reset_list_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__reset_control_bulk_get);
+
+static void reset_control_array_put(struct reset_control_array *resets)
+{
+ int i;
+
+ mutex_lock(&reset_list_mutex);
+ for (i = 0; i < resets->num_rstcs; i++)
+ __reset_control_put_internal(resets->rstc[i]);
+ mutex_unlock(&reset_list_mutex);
+ kfree(resets);
+}
/**
* reset_control_put - free the reset controller
* @rstc: reset controller
*/
-
void reset_control_put(struct reset_control *rstc)
{
- if (IS_ERR(rstc))
+ if (IS_ERR_OR_NULL(rstc))
return;
- module_put(rstc->rcdev->owner);
- kfree(rstc);
+ if (reset_control_is_array(rstc)) {
+ reset_control_array_put(rstc_to_array(rstc));
+ return;
+ }
+
+ mutex_lock(&reset_list_mutex);
+ __reset_control_put_internal(rstc);
+ mutex_unlock(&reset_list_mutex);
}
EXPORT_SYMBOL_GPL(reset_control_put);
+/**
+ * reset_control_bulk_put - free the reset controllers
+ * @num_rstcs: number of entries in rstcs array
+ * @rstcs: array of struct reset_control_bulk_data with reset controls set
+ */
+void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs)
+{
+ mutex_lock(&reset_list_mutex);
+ while (num_rstcs--)
+ __reset_control_put_internal(rstcs[num_rstcs].rstc);
+ mutex_unlock(&reset_list_mutex);
+}
+EXPORT_SYMBOL_GPL(reset_control_bulk_put);
+
static void devm_reset_control_release(struct device *dev, void *res)
{
reset_control_put(*(struct reset_control **)res);
}
-/**
- * devm_reset_control_get - resource managed reset_control_get()
- * @dev: device to be reset by the controller
- * @id: reset line name
- *
- * Managed reset_control_get(). For reset controllers returned from this
- * function, reset_control_put() is called automatically on driver detach.
- * See reset_control_get() for more information.
- */
-struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
+static void devm_reset_control_release_deasserted(struct device *dev, void *res)
+{
+ struct reset_control *rstc = *(struct reset_control **)res;
+
+ reset_control_assert(rstc);
+ reset_control_put(rstc);
+}
+
+struct reset_control *
+__devm_reset_control_get(struct device *dev, const char *id, int index,
+ enum reset_control_flags flags)
{
struct reset_control **ptr, *rstc;
+ bool deasserted = flags & RESET_CONTROL_FLAGS_BIT_DEASSERTED;
- ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr),
+ ptr = devres_alloc(deasserted ? devm_reset_control_release_deasserted :
+ devm_reset_control_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
- rstc = reset_control_get(dev, id);
- if (!IS_ERR(rstc)) {
- *ptr = rstc;
- devres_add(dev, ptr);
- } else {
+ flags &= ~RESET_CONTROL_FLAGS_BIT_DEASSERTED;
+
+ rstc = __reset_control_get(dev, id, index, flags);
+ if (IS_ERR_OR_NULL(rstc)) {
devres_free(ptr);
+ return rstc;
+ }
+
+ if (deasserted) {
+ int ret;
+
+ ret = reset_control_deassert(rstc);
+ if (ret) {
+ reset_control_put(rstc);
+ devres_free(ptr);
+ return ERR_PTR(ret);
+ }
}
+ *ptr = rstc;
+ devres_add(dev, ptr);
+
return rstc;
}
-EXPORT_SYMBOL_GPL(devm_reset_control_get);
+EXPORT_SYMBOL_GPL(__devm_reset_control_get);
+
+struct reset_control_bulk_devres {
+ int num_rstcs;
+ struct reset_control_bulk_data *rstcs;
+};
-static int devm_reset_control_match(struct device *dev, void *res, void *data)
+static void devm_reset_control_bulk_release(struct device *dev, void *res)
{
- struct reset_control **rstc = res;
- if (WARN_ON(!rstc || !*rstc))
- return 0;
- return *rstc == data;
+ struct reset_control_bulk_devres *devres = res;
+
+ reset_control_bulk_put(devres->num_rstcs, devres->rstcs);
}
-/**
- * devm_reset_control_put - resource managed reset_control_put()
- * @rstc: reset controller to free
- *
- * Deallocate a reset control allocated withd devm_reset_control_get().
- * This function will not need to be called normally, as devres will take
- * care of freeing the resource.
- */
-void devm_reset_control_put(struct reset_control *rstc)
+static void devm_reset_control_bulk_release_deasserted(struct device *dev, void *res)
{
+ struct reset_control_bulk_devres *devres = res;
+
+ reset_control_bulk_assert(devres->num_rstcs, devres->rstcs);
+ reset_control_bulk_put(devres->num_rstcs, devres->rstcs);
+}
+
+int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs,
+ struct reset_control_bulk_data *rstcs,
+ enum reset_control_flags flags)
+{
+ struct reset_control_bulk_devres *ptr;
+ bool deasserted = flags & RESET_CONTROL_FLAGS_BIT_DEASSERTED;
int ret;
- ret = devres_release(rstc->dev, devm_reset_control_release,
- devm_reset_control_match, rstc);
- if (ret)
- WARN_ON(ret);
+ ptr = devres_alloc(deasserted ? devm_reset_control_bulk_release_deasserted :
+ devm_reset_control_bulk_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ flags &= ~RESET_CONTROL_FLAGS_BIT_DEASSERTED;
+
+ ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, flags);
+ if (ret < 0) {
+ devres_free(ptr);
+ return ret;
+ }
+
+ if (deasserted) {
+ ret = reset_control_bulk_deassert(num_rstcs, rstcs);
+ if (ret) {
+ reset_control_bulk_put(num_rstcs, rstcs);
+ devres_free(ptr);
+ return ret;
+ }
+ }
+
+ ptr->num_rstcs = num_rstcs;
+ ptr->rstcs = rstcs;
+ devres_add(dev, ptr);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(devm_reset_control_put);
+EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get);
/**
- * device_reset - find reset controller associated with the device
- * and perform reset
+ * __device_reset - find reset controller associated with the device
+ * and perform reset
* @dev: device to be reset by the controller
+ * @optional: whether it is optional to reset the device
*
- * Convenience wrapper for reset_control_get() and reset_control_reset().
+ * Convenience wrapper for __reset_control_get() and reset_control_reset().
* This is useful for the common case of devices with single, dedicated reset
- * lines.
+ * lines. _RST firmware method will be called for devices with ACPI.
*/
-int device_reset(struct device *dev)
+int __device_reset(struct device *dev, bool optional)
{
+ enum reset_control_flags flags;
struct reset_control *rstc;
int ret;
- rstc = reset_control_get(dev, NULL);
+#ifdef CONFIG_ACPI
+ acpi_handle handle = ACPI_HANDLE(dev);
+
+ if (handle) {
+ if (!acpi_has_method(handle, "_RST"))
+ return optional ? 0 : -ENOENT;
+ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_RST", NULL,
+ NULL)))
+ return -EIO;
+ }
+#endif
+
+ flags = optional ? RESET_CONTROL_OPTIONAL_EXCLUSIVE : RESET_CONTROL_EXCLUSIVE;
+ rstc = __reset_control_get(dev, NULL, 0, flags);
if (IS_ERR(rstc))
return PTR_ERR(rstc);
@@ -294,4 +1310,130 @@ int device_reset(struct device *dev)
return ret;
}
-EXPORT_SYMBOL_GPL(device_reset);
+EXPORT_SYMBOL_GPL(__device_reset);
+
+/*
+ * APIs to manage an array of reset controls.
+ */
+
+/**
+ * of_reset_control_get_count - Count number of resets available with a device
+ *
+ * @node: device node that contains 'resets'.
+ *
+ * Returns positive reset count on success, or error number on failure and
+ * on count being zero.
+ */
+static int of_reset_control_get_count(struct device_node *node)
+{
+ int count;
+
+ if (!node)
+ return -EINVAL;
+
+ count = of_count_phandle_with_args(node, "resets", "#reset-cells");
+ if (count == 0)
+ count = -ENOENT;
+
+ return count;
+}
+
+/**
+ * of_reset_control_array_get - Get a list of reset controls using
+ * device node.
+ *
+ * @np: device node for the device that requests the reset controls array
+ * @flags: whether reset controls are shared, optional, acquired
+ *
+ * Returns pointer to allocated reset_control on success or error on failure
+ */
+struct reset_control *
+of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags)
+{
+ bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL;
+ struct reset_control_array *resets;
+ struct reset_control *rstc;
+ int num, i;
+
+ num = of_reset_control_get_count(np);
+ if (num < 0)
+ return optional ? NULL : ERR_PTR(num);
+
+ resets = kzalloc(struct_size(resets, rstc, num), GFP_KERNEL);
+ if (!resets)
+ return ERR_PTR(-ENOMEM);
+ resets->num_rstcs = num;
+
+ for (i = 0; i < num; i++) {
+ rstc = __of_reset_control_get(np, NULL, i, flags);
+ if (IS_ERR(rstc))
+ goto err_rst;
+ resets->rstc[i] = rstc;
+ }
+ resets->base.array = true;
+
+ return &resets->base;
+
+err_rst:
+ mutex_lock(&reset_list_mutex);
+ while (--i >= 0)
+ __reset_control_put_internal(resets->rstc[i]);
+ mutex_unlock(&reset_list_mutex);
+
+ kfree(resets);
+
+ return rstc;
+}
+EXPORT_SYMBOL_GPL(of_reset_control_array_get);
+
+/**
+ * devm_reset_control_array_get - Resource managed reset control array get
+ *
+ * @dev: device that requests the list of reset controls
+ * @flags: whether reset controls are shared, optional, acquired
+ *
+ * The reset control array APIs are intended for a list of resets
+ * that just have to be asserted or deasserted, without any
+ * requirements on the order.
+ *
+ * Returns pointer to allocated reset_control on success or error on failure
+ */
+struct reset_control *
+devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags)
+{
+ struct reset_control **ptr, *rstc;
+
+ ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ rstc = of_reset_control_array_get(dev->of_node, flags);
+ if (IS_ERR_OR_NULL(rstc)) {
+ devres_free(ptr);
+ return rstc;
+ }
+
+ *ptr = rstc;
+ devres_add(dev, ptr);
+
+ return rstc;
+}
+EXPORT_SYMBOL_GPL(devm_reset_control_array_get);
+
+/**
+ * reset_control_get_count - Count number of resets available with a device
+ *
+ * @dev: device for which to return the number of resets
+ *
+ * Returns positive reset count on success, or error number on failure and
+ * on count being zero.
+ */
+int reset_control_get_count(struct device *dev)
+{
+ if (dev->of_node)
+ return of_reset_control_get_count(dev->of_node);
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(reset_control_get_count);
diff --git a/drivers/reset/hisilicon/Kconfig b/drivers/reset/hisilicon/Kconfig
new file mode 100644
index 000000000000..945ef7a12c36
--- /dev/null
+++ b/drivers/reset/hisilicon/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config COMMON_RESET_HI3660
+ tristate "Hi3660 Reset Driver"
+ depends on ARCH_HISI || COMPILE_TEST
+ default ARCH_HISI
+ help
+ Build the Hisilicon Hi3660 reset driver.
+
+config COMMON_RESET_HI6220
+ tristate "Hi6220 Reset Driver"
+ depends on ARCH_HISI || COMPILE_TEST
+ default ARCH_HISI
+ help
+ Build the Hisilicon Hi6220 reset driver.
diff --git a/drivers/reset/hisilicon/Makefile b/drivers/reset/hisilicon/Makefile
new file mode 100644
index 000000000000..cf86d1308f9c
--- /dev/null
+++ b/drivers/reset/hisilicon/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_COMMON_RESET_HI6220) += hi6220_reset.o
+obj-$(CONFIG_COMMON_RESET_HI3660) += reset-hi3660.o
diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c
new file mode 100644
index 000000000000..65aa5ff5ed82
--- /dev/null
+++ b/drivers/reset/hisilicon/hi6220_reset.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hisilicon Hi6220 reset controller driver
+ *
+ * Copyright (c) 2016 Linaro Limited.
+ * Copyright (c) 2015-2016 HiSilicon Limited.
+ *
+ * Author: Feng Chen <puck.chen@hisilicon.com>
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset-controller.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+
+#define PERIPH_ASSERT_OFFSET 0x300
+#define PERIPH_DEASSERT_OFFSET 0x304
+#define PERIPH_MAX_INDEX 0x509
+
+#define SC_MEDIA_RSTEN 0x052C
+#define SC_MEDIA_RSTDIS 0x0530
+#define MEDIA_MAX_INDEX 8
+
+#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev)
+
+enum hi6220_reset_ctrl_type {
+ PERIPHERAL,
+ MEDIA,
+ AO,
+};
+
+struct hi6220_reset_data {
+ struct reset_controller_dev rc_dev;
+ struct regmap *regmap;
+};
+
+static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+ u32 bank = idx >> 8;
+ u32 offset = idx & 0xff;
+ u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10;
+
+ return regmap_write(regmap, reg, BIT(offset));
+}
+
+static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+ u32 bank = idx >> 8;
+ u32 offset = idx & 0xff;
+ u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10;
+
+ return regmap_write(regmap, reg, BIT(offset));
+}
+
+static const struct reset_control_ops hi6220_peripheral_reset_ops = {
+ .assert = hi6220_peripheral_assert,
+ .deassert = hi6220_peripheral_deassert,
+};
+
+static int hi6220_media_assert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+
+ return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx));
+}
+
+static int hi6220_media_deassert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+
+ return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx));
+}
+
+static const struct reset_control_ops hi6220_media_reset_ops = {
+ .assert = hi6220_media_assert,
+ .deassert = hi6220_media_deassert,
+};
+
+#define AO_SCTRL_SC_PW_CLKEN0 0x800
+#define AO_SCTRL_SC_PW_CLKDIS0 0x804
+
+#define AO_SCTRL_SC_PW_RSTEN0 0x810
+#define AO_SCTRL_SC_PW_RSTDIS0 0x814
+
+#define AO_SCTRL_SC_PW_ISOEN0 0x820
+#define AO_SCTRL_SC_PW_ISODIS0 0x824
+#define AO_MAX_INDEX 12
+
+static int hi6220_ao_assert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+ int ret;
+
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTEN0, BIT(idx));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISOEN0, BIT(idx));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKDIS0, BIT(idx));
+ return ret;
+}
+
+static int hi6220_ao_deassert(struct reset_controller_dev *rc_dev,
+ unsigned long idx)
+{
+ struct hi6220_reset_data *data = to_reset_data(rc_dev);
+ struct regmap *regmap = data->regmap;
+ int ret;
+
+ /*
+ * It was suggested to disable isolation before enabling
+ * the clocks and deasserting reset, to avoid glitches.
+ * But this order is preserved to keep it matching the
+ * vendor code.
+ */
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTDIS0, BIT(idx));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISODIS0, BIT(idx));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKEN0, BIT(idx));
+ return ret;
+}
+
+static const struct reset_control_ops hi6220_ao_reset_ops = {
+ .assert = hi6220_ao_assert,
+ .deassert = hi6220_ao_deassert,
+};
+
+static int hi6220_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ enum hi6220_reset_ctrl_type type;
+ struct hi6220_reset_data *data;
+ struct regmap *regmap;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ type = (uintptr_t)of_device_get_match_data(dev);
+
+ regmap = syscon_node_to_regmap(np);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to get reset controller regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ data->regmap = regmap;
+ data->rc_dev.of_node = np;
+ if (type == MEDIA) {
+ data->rc_dev.ops = &hi6220_media_reset_ops;
+ data->rc_dev.nr_resets = MEDIA_MAX_INDEX;
+ } else if (type == PERIPHERAL) {
+ data->rc_dev.ops = &hi6220_peripheral_reset_ops;
+ data->rc_dev.nr_resets = PERIPH_MAX_INDEX;
+ } else {
+ data->rc_dev.ops = &hi6220_ao_reset_ops;
+ data->rc_dev.nr_resets = AO_MAX_INDEX;
+ }
+
+ return reset_controller_register(&data->rc_dev);
+}
+
+static const struct of_device_id hi6220_reset_match[] = {
+ {
+ .compatible = "hisilicon,hi6220-sysctrl",
+ .data = (void *)PERIPHERAL,
+ },
+ {
+ .compatible = "hisilicon,hi6220-mediactrl",
+ .data = (void *)MEDIA,
+ },
+ {
+ .compatible = "hisilicon,hi6220-aoctrl",
+ .data = (void *)AO,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, hi6220_reset_match);
+
+static struct platform_driver hi6220_reset_driver = {
+ .probe = hi6220_reset_probe,
+ .driver = {
+ .name = "reset-hi6220",
+ .of_match_table = hi6220_reset_match,
+ },
+};
+
+static int __init hi6220_reset_init(void)
+{
+ return platform_driver_register(&hi6220_reset_driver);
+}
+
+postcore_initcall(hi6220_reset_init);
+
+MODULE_DESCRIPTION("Hisilicon Hi6220 reset controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/hisilicon/reset-hi3660.c b/drivers/reset/hisilicon/reset-hi3660.c
new file mode 100644
index 000000000000..1beb275275ad
--- /dev/null
+++ b/drivers/reset/hisilicon/reset-hi3660.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2016-2017 Linaro Ltd.
+ * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
+ */
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+struct hi3660_reset_controller {
+ struct reset_controller_dev rst;
+ struct regmap *map;
+};
+
+#define to_hi3660_reset_controller(_rst) \
+ container_of(_rst, struct hi3660_reset_controller, rst)
+
+static int hi3660_reset_program_hw(struct reset_controller_dev *rcdev,
+ unsigned long idx, bool assert)
+{
+ struct hi3660_reset_controller *rc = to_hi3660_reset_controller(rcdev);
+ unsigned int offset = idx >> 8;
+ unsigned int mask = BIT(idx & 0x1f);
+
+ if (assert)
+ return regmap_write(rc->map, offset, mask);
+ else
+ return regmap_write(rc->map, offset + 4, mask);
+}
+
+static int hi3660_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return hi3660_reset_program_hw(rcdev, idx, true);
+}
+
+static int hi3660_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return hi3660_reset_program_hw(rcdev, idx, false);
+}
+
+static int hi3660_reset_dev(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ int err;
+
+ err = hi3660_reset_assert(rcdev, idx);
+ if (err)
+ return err;
+
+ return hi3660_reset_deassert(rcdev, idx);
+}
+
+static const struct reset_control_ops hi3660_reset_ops = {
+ .reset = hi3660_reset_dev,
+ .assert = hi3660_reset_assert,
+ .deassert = hi3660_reset_deassert,
+};
+
+static int hi3660_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned int offset, bit;
+
+ offset = reset_spec->args[0];
+ bit = reset_spec->args[1];
+
+ return (offset << 8) | bit;
+}
+
+static int hi3660_reset_probe(struct platform_device *pdev)
+{
+ struct hi3660_reset_controller *rc;
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+
+ rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ rc->map = syscon_regmap_lookup_by_phandle(np, "hisilicon,rst-syscon");
+ if (rc->map == ERR_PTR(-ENODEV)) {
+ /* fall back to the deprecated compatible */
+ rc->map = syscon_regmap_lookup_by_phandle(np,
+ "hisi,rst-syscon");
+ }
+ if (IS_ERR(rc->map)) {
+ return dev_err_probe(dev, PTR_ERR(rc->map),
+ "failed to get hisilicon,rst-syscon\n");
+ }
+
+ rc->rst.ops = &hi3660_reset_ops,
+ rc->rst.of_node = np;
+ rc->rst.of_reset_n_cells = 2;
+ rc->rst.of_xlate = hi3660_reset_xlate;
+
+ return reset_controller_register(&rc->rst);
+}
+
+static const struct of_device_id hi3660_reset_match[] = {
+ { .compatible = "hisilicon,hi3660-reset", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hi3660_reset_match);
+
+static struct platform_driver hi3660_reset_driver = {
+ .probe = hi3660_reset_probe,
+ .driver = {
+ .name = "hi3660-reset",
+ .of_match_table = hi3660_reset_match,
+ },
+};
+
+static int __init hi3660_reset_init(void)
+{
+ return platform_driver_register(&hi3660_reset_driver);
+}
+arch_initcall(hi3660_reset_init);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hi3660-reset");
+MODULE_DESCRIPTION("HiSilicon Hi3660 Reset Driver");
diff --git a/drivers/reset/reset-a10sr.c b/drivers/reset/reset-a10sr.c
new file mode 100644
index 000000000000..99b3bc8382f3
--- /dev/null
+++ b/drivers/reset/reset-a10sr.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright Intel Corporation (C) 2017. All Rights Reserved
+ *
+ * Reset driver for Altera Arria10 MAX5 System Resource Chip
+ *
+ * Adapted from reset-socfpga.c
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/altera-a10sr.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/altr,rst-mgr-a10sr.h>
+
+struct a10sr_reset {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static inline struct a10sr_reset *to_a10sr_rst(struct reset_controller_dev *rc)
+{
+ return container_of(rc, struct a10sr_reset, rcdev);
+}
+
+static inline int a10sr_reset_shift(unsigned long id)
+{
+ switch (id) {
+ case A10SR_RESET_ENET_HPS:
+ return 1;
+ case A10SR_RESET_PCIE:
+ case A10SR_RESET_FILE:
+ case A10SR_RESET_BQSPI:
+ case A10SR_RESET_USB:
+ return id + 11;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int a10sr_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct a10sr_reset *a10r = to_a10sr_rst(rcdev);
+ int offset = a10sr_reset_shift(id);
+ u8 mask = ALTR_A10SR_REG_BIT_MASK(offset);
+ int index = ALTR_A10SR_HPS_RST_REG + ALTR_A10SR_REG_OFFSET(offset);
+
+ return regmap_update_bits(a10r->regmap, index, mask, assert ? 0 : mask);
+}
+
+static int a10sr_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return a10sr_reset_update(rcdev, id, true);
+}
+
+static int a10sr_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return a10sr_reset_update(rcdev, id, false);
+}
+
+static int a10sr_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+ struct a10sr_reset *a10r = to_a10sr_rst(rcdev);
+ int offset = a10sr_reset_shift(id);
+ u8 mask = ALTR_A10SR_REG_BIT_MASK(offset);
+ int index = ALTR_A10SR_HPS_RST_REG + ALTR_A10SR_REG_OFFSET(offset);
+ unsigned int value;
+
+ ret = regmap_read(a10r->regmap, index, &value);
+ if (ret < 0)
+ return ret;
+
+ return !!(value & mask);
+}
+
+static const struct reset_control_ops a10sr_reset_ops = {
+ .assert = a10sr_reset_assert,
+ .deassert = a10sr_reset_deassert,
+ .status = a10sr_reset_status,
+};
+
+static int a10sr_reset_probe(struct platform_device *pdev)
+{
+ struct altr_a10sr *a10sr = dev_get_drvdata(pdev->dev.parent);
+ struct a10sr_reset *a10r;
+
+ a10r = devm_kzalloc(&pdev->dev, sizeof(struct a10sr_reset),
+ GFP_KERNEL);
+ if (!a10r)
+ return -ENOMEM;
+
+ a10r->rcdev.owner = THIS_MODULE;
+ a10r->rcdev.nr_resets = A10SR_RESET_NUM;
+ a10r->rcdev.ops = &a10sr_reset_ops;
+ a10r->rcdev.of_node = pdev->dev.of_node;
+ a10r->regmap = a10sr->regmap;
+
+ platform_set_drvdata(pdev, a10r);
+
+ return devm_reset_controller_register(&pdev->dev, &a10r->rcdev);
+}
+
+static const struct of_device_id a10sr_reset_of_match[] = {
+ { .compatible = "altr,a10sr-reset" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, a10sr_reset_of_match);
+
+static struct platform_driver a10sr_reset_driver = {
+ .probe = a10sr_reset_probe,
+ .driver = {
+ .name = "altr_a10sr_reset",
+ .of_match_table = a10sr_reset_of_match,
+ },
+};
+module_platform_driver(a10sr_reset_driver);
+
+MODULE_AUTHOR("Thor Thayer <thor.thayer@linux.intel.com>");
+MODULE_DESCRIPTION("Altera Arria10 System Resource Reset Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c
new file mode 100644
index 000000000000..dd2f860a69d7
--- /dev/null
+++ b/drivers/reset/reset-aspeed.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2024 ASPEED Technology Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/reset/aspeed,ast2700-scu.h>
+
+#define SCU0_RESET_CTRL1 0x200
+#define SCU0_RESET_CTRL2 0x220
+#define SCU1_RESET_CTRL1 0x200
+#define SCU1_RESET_CTRL2 0x220
+#define SCU1_PCIE3_CTRL 0x908
+
+struct ast2700_reset_signal {
+ bool dedicated_clr; /* dedicated reset clr offset */
+ u32 offset, bit;
+};
+
+struct aspeed_reset_info {
+ unsigned int nr_resets;
+ const struct ast2700_reset_signal *signal;
+};
+
+struct aspeed_reset {
+ struct reset_controller_dev rcdev;
+ struct aspeed_reset_info *info;
+ spinlock_t lock; /* Protect read-modify-write cycle */
+ void __iomem *base;
+};
+
+static const struct ast2700_reset_signal ast2700_reset0_signals[] = {
+ [SCU0_RESET_SDRAM] = { true, SCU0_RESET_CTRL1, BIT(0) },
+ [SCU0_RESET_DDRPHY] = { true, SCU0_RESET_CTRL1, BIT(1) },
+ [SCU0_RESET_RSA] = { true, SCU0_RESET_CTRL1, BIT(2) },
+ [SCU0_RESET_SHA3] = { true, SCU0_RESET_CTRL1, BIT(3) },
+ [SCU0_RESET_HACE] = { true, SCU0_RESET_CTRL1, BIT(4) },
+ [SCU0_RESET_SOC] = { true, SCU0_RESET_CTRL1, BIT(5) },
+ [SCU0_RESET_VIDEO] = { true, SCU0_RESET_CTRL1, BIT(6) },
+ [SCU0_RESET_2D] = { true, SCU0_RESET_CTRL1, BIT(7) },
+ [SCU0_RESET_PCIS] = { true, SCU0_RESET_CTRL1, BIT(8) },
+ [SCU0_RESET_RVAS0] = { true, SCU0_RESET_CTRL1, BIT(9) },
+ [SCU0_RESET_RVAS1] = { true, SCU0_RESET_CTRL1, BIT(10) },
+ [SCU0_RESET_SM3] = { true, SCU0_RESET_CTRL1, BIT(11) },
+ [SCU0_RESET_SM4] = { true, SCU0_RESET_CTRL1, BIT(12) },
+ [SCU0_RESET_CRT0] = { true, SCU0_RESET_CTRL1, BIT(13) },
+ [SCU0_RESET_ECC] = { true, SCU0_RESET_CTRL1, BIT(14) },
+ [SCU0_RESET_DP_PCI] = { true, SCU0_RESET_CTRL1, BIT(15) },
+ [SCU0_RESET_UFS] = { true, SCU0_RESET_CTRL1, BIT(16) },
+ [SCU0_RESET_EMMC] = { true, SCU0_RESET_CTRL1, BIT(17) },
+ [SCU0_RESET_PCIE1RST] = { true, SCU0_RESET_CTRL1, BIT(18) },
+ [SCU0_RESET_PCIE1RSTOE] = { true, SCU0_RESET_CTRL1, BIT(19) },
+ [SCU0_RESET_PCIE0RST] = { true, SCU0_RESET_CTRL1, BIT(20) },
+ [SCU0_RESET_PCIE0RSTOE] = { true, SCU0_RESET_CTRL1, BIT(21) },
+ [SCU0_RESET_JTAG] = { true, SCU0_RESET_CTRL1, BIT(22) },
+ [SCU0_RESET_MCTP0] = { true, SCU0_RESET_CTRL1, BIT(23) },
+ [SCU0_RESET_MCTP1] = { true, SCU0_RESET_CTRL1, BIT(24) },
+ [SCU0_RESET_XDMA0] = { true, SCU0_RESET_CTRL1, BIT(25) },
+ [SCU0_RESET_XDMA1] = { true, SCU0_RESET_CTRL1, BIT(26) },
+ [SCU0_RESET_H2X1] = { true, SCU0_RESET_CTRL1, BIT(27) },
+ [SCU0_RESET_DP] = { true, SCU0_RESET_CTRL1, BIT(28) },
+ [SCU0_RESET_DP_MCU] = { true, SCU0_RESET_CTRL1, BIT(29) },
+ [SCU0_RESET_SSP] = { true, SCU0_RESET_CTRL1, BIT(30) },
+ [SCU0_RESET_H2X0] = { true, SCU0_RESET_CTRL1, BIT(31) },
+ [SCU0_RESET_PORTA_VHUB] = { true, SCU0_RESET_CTRL2, BIT(0) },
+ [SCU0_RESET_PORTA_PHY3] = { true, SCU0_RESET_CTRL2, BIT(1) },
+ [SCU0_RESET_PORTA_XHCI] = { true, SCU0_RESET_CTRL2, BIT(2) },
+ [SCU0_RESET_PORTB_VHUB] = { true, SCU0_RESET_CTRL2, BIT(3) },
+ [SCU0_RESET_PORTB_PHY3] = { true, SCU0_RESET_CTRL2, BIT(4) },
+ [SCU0_RESET_PORTB_XHCI] = { true, SCU0_RESET_CTRL2, BIT(5) },
+ [SCU0_RESET_PORTA_VHUB_EHCI] = { true, SCU0_RESET_CTRL2, BIT(6) },
+ [SCU0_RESET_PORTB_VHUB_EHCI] = { true, SCU0_RESET_CTRL2, BIT(7) },
+ [SCU0_RESET_UHCI] = { true, SCU0_RESET_CTRL2, BIT(8) },
+ [SCU0_RESET_TSP] = { true, SCU0_RESET_CTRL2, BIT(9) },
+ [SCU0_RESET_E2M0] = { true, SCU0_RESET_CTRL2, BIT(10) },
+ [SCU0_RESET_E2M1] = { true, SCU0_RESET_CTRL2, BIT(11) },
+ [SCU0_RESET_VLINK] = { true, SCU0_RESET_CTRL2, BIT(12) },
+};
+
+static const struct ast2700_reset_signal ast2700_reset1_signals[] = {
+ [SCU1_RESET_LPC0] = { true, SCU1_RESET_CTRL1, BIT(0) },
+ [SCU1_RESET_LPC1] = { true, SCU1_RESET_CTRL1, BIT(1) },
+ [SCU1_RESET_MII] = { true, SCU1_RESET_CTRL1, BIT(2) },
+ [SCU1_RESET_PECI] = { true, SCU1_RESET_CTRL1, BIT(3) },
+ [SCU1_RESET_PWM] = { true, SCU1_RESET_CTRL1, BIT(4) },
+ [SCU1_RESET_MAC0] = { true, SCU1_RESET_CTRL1, BIT(5) },
+ [SCU1_RESET_MAC1] = { true, SCU1_RESET_CTRL1, BIT(6) },
+ [SCU1_RESET_MAC2] = { true, SCU1_RESET_CTRL1, BIT(7) },
+ [SCU1_RESET_ADC] = { true, SCU1_RESET_CTRL1, BIT(8) },
+ [SCU1_RESET_SD] = { true, SCU1_RESET_CTRL1, BIT(9) },
+ [SCU1_RESET_ESPI0] = { true, SCU1_RESET_CTRL1, BIT(10) },
+ [SCU1_RESET_ESPI1] = { true, SCU1_RESET_CTRL1, BIT(11) },
+ [SCU1_RESET_JTAG1] = { true, SCU1_RESET_CTRL1, BIT(12) },
+ [SCU1_RESET_SPI0] = { true, SCU1_RESET_CTRL1, BIT(13) },
+ [SCU1_RESET_SPI1] = { true, SCU1_RESET_CTRL1, BIT(14) },
+ [SCU1_RESET_SPI2] = { true, SCU1_RESET_CTRL1, BIT(15) },
+ [SCU1_RESET_I3C0] = { true, SCU1_RESET_CTRL1, BIT(16) },
+ [SCU1_RESET_I3C1] = { true, SCU1_RESET_CTRL1, BIT(17) },
+ [SCU1_RESET_I3C2] = { true, SCU1_RESET_CTRL1, BIT(18) },
+ [SCU1_RESET_I3C3] = { true, SCU1_RESET_CTRL1, BIT(19) },
+ [SCU1_RESET_I3C4] = { true, SCU1_RESET_CTRL1, BIT(20) },
+ [SCU1_RESET_I3C5] = { true, SCU1_RESET_CTRL1, BIT(21) },
+ [SCU1_RESET_I3C6] = { true, SCU1_RESET_CTRL1, BIT(22) },
+ [SCU1_RESET_I3C7] = { true, SCU1_RESET_CTRL1, BIT(23) },
+ [SCU1_RESET_I3C8] = { true, SCU1_RESET_CTRL1, BIT(24) },
+ [SCU1_RESET_I3C9] = { true, SCU1_RESET_CTRL1, BIT(25) },
+ [SCU1_RESET_I3C10] = { true, SCU1_RESET_CTRL1, BIT(26) },
+ [SCU1_RESET_I3C11] = { true, SCU1_RESET_CTRL1, BIT(27) },
+ [SCU1_RESET_I3C12] = { true, SCU1_RESET_CTRL1, BIT(28) },
+ [SCU1_RESET_I3C13] = { true, SCU1_RESET_CTRL1, BIT(29) },
+ [SCU1_RESET_I3C14] = { true, SCU1_RESET_CTRL1, BIT(30) },
+ [SCU1_RESET_I3C15] = { true, SCU1_RESET_CTRL1, BIT(31) },
+ [SCU1_RESET_MCU0] = { true, SCU1_RESET_CTRL2, BIT(0) },
+ [SCU1_RESET_MCU1] = { true, SCU1_RESET_CTRL2, BIT(1) },
+ [SCU1_RESET_H2A_SPI1] = { true, SCU1_RESET_CTRL2, BIT(2) },
+ [SCU1_RESET_H2A_SPI2] = { true, SCU1_RESET_CTRL2, BIT(3) },
+ [SCU1_RESET_UART0] = { true, SCU1_RESET_CTRL2, BIT(4) },
+ [SCU1_RESET_UART1] = { true, SCU1_RESET_CTRL2, BIT(5) },
+ [SCU1_RESET_UART2] = { true, SCU1_RESET_CTRL2, BIT(6) },
+ [SCU1_RESET_UART3] = { true, SCU1_RESET_CTRL2, BIT(7) },
+ [SCU1_RESET_I2C_FILTER] = { true, SCU1_RESET_CTRL2, BIT(8) },
+ [SCU1_RESET_CALIPTRA] = { true, SCU1_RESET_CTRL2, BIT(9) },
+ [SCU1_RESET_XDMA] = { true, SCU1_RESET_CTRL2, BIT(10) },
+ [SCU1_RESET_FSI] = { true, SCU1_RESET_CTRL2, BIT(12) },
+ [SCU1_RESET_CAN] = { true, SCU1_RESET_CTRL2, BIT(13) },
+ [SCU1_RESET_MCTP] = { true, SCU1_RESET_CTRL2, BIT(14) },
+ [SCU1_RESET_I2C] = { true, SCU1_RESET_CTRL2, BIT(15) },
+ [SCU1_RESET_UART6] = { true, SCU1_RESET_CTRL2, BIT(16) },
+ [SCU1_RESET_UART7] = { true, SCU1_RESET_CTRL2, BIT(17) },
+ [SCU1_RESET_UART8] = { true, SCU1_RESET_CTRL2, BIT(18) },
+ [SCU1_RESET_UART9] = { true, SCU1_RESET_CTRL2, BIT(19) },
+ [SCU1_RESET_LTPI0] = { true, SCU1_RESET_CTRL2, BIT(20) },
+ [SCU1_RESET_VGAL] = { true, SCU1_RESET_CTRL2, BIT(21) },
+ [SCU1_RESET_LTPI1] = { true, SCU1_RESET_CTRL2, BIT(22) },
+ [SCU1_RESET_ACE] = { true, SCU1_RESET_CTRL2, BIT(23) },
+ [SCU1_RESET_E2M] = { true, SCU1_RESET_CTRL2, BIT(24) },
+ [SCU1_RESET_UHCI] = { true, SCU1_RESET_CTRL2, BIT(25) },
+ [SCU1_RESET_PORTC_USB2UART] = { true, SCU1_RESET_CTRL2, BIT(26) },
+ [SCU1_RESET_PORTC_VHUB_EHCI] = { true, SCU1_RESET_CTRL2, BIT(27) },
+ [SCU1_RESET_PORTD_USB2UART] = { true, SCU1_RESET_CTRL2, BIT(28) },
+ [SCU1_RESET_PORTD_VHUB_EHCI] = { true, SCU1_RESET_CTRL2, BIT(29) },
+ [SCU1_RESET_H2X] = { true, SCU1_RESET_CTRL2, BIT(30) },
+ [SCU1_RESET_I3CDMA] = { true, SCU1_RESET_CTRL2, BIT(31) },
+ [SCU1_RESET_PCIE2RST] = { false, SCU1_PCIE3_CTRL, BIT(0) },
+};
+
+static inline struct aspeed_reset *to_aspeed_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct aspeed_reset, rcdev);
+}
+
+static int aspeed_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct aspeed_reset *rc = to_aspeed_reset(rcdev);
+ void __iomem *reg_offset = rc->base + rc->info->signal[id].offset;
+
+ if (rc->info->signal[id].dedicated_clr) {
+ writel(rc->info->signal[id].bit, reg_offset);
+ } else {
+ guard(spinlock_irqsave)(&rc->lock);
+ writel(readl(reg_offset) & ~rc->info->signal[id].bit, reg_offset);
+ }
+
+ return 0;
+}
+
+static int aspeed_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct aspeed_reset *rc = to_aspeed_reset(rcdev);
+ void __iomem *reg_offset = rc->base + rc->info->signal[id].offset;
+
+ if (rc->info->signal[id].dedicated_clr) {
+ writel(rc->info->signal[id].bit, reg_offset + 0x04);
+ } else {
+ guard(spinlock_irqsave)(&rc->lock);
+ writel(readl(reg_offset) | rc->info->signal[id].bit, reg_offset);
+ }
+
+ return 0;
+}
+
+static int aspeed_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct aspeed_reset *rc = to_aspeed_reset(rcdev);
+ void __iomem *reg_offset = rc->base + rc->info->signal[id].offset;
+
+ return (readl(reg_offset) & rc->info->signal[id].bit) ? 1 : 0;
+}
+
+static const struct reset_control_ops aspeed_reset_ops = {
+ .assert = aspeed_reset_assert,
+ .deassert = aspeed_reset_deassert,
+ .status = aspeed_reset_status,
+};
+
+static int aspeed_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct aspeed_reset *reset;
+ struct device *dev = &adev->dev;
+
+ reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL);
+ if (!reset)
+ return -ENOMEM;
+
+ spin_lock_init(&reset->lock);
+
+ reset->info = (struct aspeed_reset_info *)id->driver_data;
+ reset->rcdev.owner = THIS_MODULE;
+ reset->rcdev.nr_resets = reset->info->nr_resets;
+ reset->rcdev.ops = &aspeed_reset_ops;
+ reset->rcdev.of_node = dev->parent->of_node;
+ reset->rcdev.dev = dev;
+ reset->rcdev.of_reset_n_cells = 1;
+ reset->base = (void __iomem *)adev->dev.platform_data;
+
+ return devm_reset_controller_register(dev, &reset->rcdev);
+}
+
+static const struct aspeed_reset_info ast2700_reset0_info = {
+ .nr_resets = ARRAY_SIZE(ast2700_reset0_signals),
+ .signal = ast2700_reset0_signals,
+};
+
+static const struct aspeed_reset_info ast2700_reset1_info = {
+ .nr_resets = ARRAY_SIZE(ast2700_reset1_signals),
+ .signal = ast2700_reset1_signals,
+};
+
+static const struct auxiliary_device_id aspeed_reset_ids[] = {
+ { .name = "clk_ast2700.reset0", .driver_data = (kernel_ulong_t)&ast2700_reset0_info },
+ { .name = "clk_ast2700.reset1", .driver_data = (kernel_ulong_t)&ast2700_reset1_info },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, aspeed_reset_ids);
+
+static struct auxiliary_driver aspeed_reset_driver = {
+ .probe = aspeed_reset_probe,
+ .id_table = aspeed_reset_ids,
+};
+
+module_auxiliary_driver(aspeed_reset_driver);
+
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_DESCRIPTION("ASPEED SoC Reset Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c
new file mode 100644
index 000000000000..b5d620132052
--- /dev/null
+++ b/drivers/reset/reset-ath79.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AR71xx Reset Controller Driver
+ * Author: Alban Bedel
+ *
+ * Copyright (C) 2015 Alban Bedel <albeu@free.fr>
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+struct ath79_reset {
+ struct reset_controller_dev rcdev;
+ struct notifier_block restart_nb;
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+#define FULL_CHIP_RESET 24
+
+static int ath79_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct ath79_reset *ath79_reset =
+ container_of(rcdev, struct ath79_reset, rcdev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&ath79_reset->lock, flags);
+ val = readl(ath79_reset->base);
+ if (assert)
+ val |= BIT(id);
+ else
+ val &= ~BIT(id);
+ writel(val, ath79_reset->base);
+ spin_unlock_irqrestore(&ath79_reset->lock, flags);
+
+ return 0;
+}
+
+static int ath79_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ath79_reset_update(rcdev, id, true);
+}
+
+static int ath79_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ath79_reset_update(rcdev, id, false);
+}
+
+static int ath79_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ath79_reset *ath79_reset =
+ container_of(rcdev, struct ath79_reset, rcdev);
+ u32 val;
+
+ val = readl(ath79_reset->base);
+
+ return !!(val & BIT(id));
+}
+
+static const struct reset_control_ops ath79_reset_ops = {
+ .assert = ath79_reset_assert,
+ .deassert = ath79_reset_deassert,
+ .status = ath79_reset_status,
+};
+
+static int ath79_reset_restart_handler(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct ath79_reset *ath79_reset =
+ container_of(nb, struct ath79_reset, restart_nb);
+
+ ath79_reset_assert(&ath79_reset->rcdev, FULL_CHIP_RESET);
+
+ return NOTIFY_DONE;
+}
+
+static int ath79_reset_probe(struct platform_device *pdev)
+{
+ struct ath79_reset *ath79_reset;
+ int err;
+
+ ath79_reset = devm_kzalloc(&pdev->dev,
+ sizeof(*ath79_reset), GFP_KERNEL);
+ if (!ath79_reset)
+ return -ENOMEM;
+
+ ath79_reset->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ath79_reset->base))
+ return PTR_ERR(ath79_reset->base);
+
+ spin_lock_init(&ath79_reset->lock);
+ ath79_reset->rcdev.ops = &ath79_reset_ops;
+ ath79_reset->rcdev.owner = THIS_MODULE;
+ ath79_reset->rcdev.of_node = pdev->dev.of_node;
+ ath79_reset->rcdev.of_reset_n_cells = 1;
+ ath79_reset->rcdev.nr_resets = 32;
+
+ err = devm_reset_controller_register(&pdev->dev, &ath79_reset->rcdev);
+ if (err)
+ return err;
+
+ ath79_reset->restart_nb.notifier_call = ath79_reset_restart_handler;
+ ath79_reset->restart_nb.priority = 128;
+
+ err = register_restart_handler(&ath79_reset->restart_nb);
+ if (err)
+ dev_warn(&pdev->dev, "Failed to register restart handler\n");
+
+ return 0;
+}
+
+static const struct of_device_id ath79_reset_dt_ids[] = {
+ { .compatible = "qca,ar7100-reset", },
+ { },
+};
+
+static struct platform_driver ath79_reset_driver = {
+ .probe = ath79_reset_probe,
+ .driver = {
+ .name = "ath79-reset",
+ .of_match_table = ath79_reset_dt_ids,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(ath79_reset_driver);
diff --git a/drivers/reset/reset-axs10x.c b/drivers/reset/reset-axs10x.c
new file mode 100644
index 000000000000..115f69e0db33
--- /dev/null
+++ b/drivers/reset/reset-axs10x.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 Synopsys.
+ *
+ * Synopsys AXS10x reset driver.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#define to_axs10x_rst(p) container_of((p), struct axs10x_rst, rcdev)
+
+#define AXS10X_MAX_RESETS 32
+
+struct axs10x_rst {
+ void __iomem *regs_rst;
+ spinlock_t lock;
+ struct reset_controller_dev rcdev;
+};
+
+static int axs10x_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct axs10x_rst *rst = to_axs10x_rst(rcdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rst->lock, flags);
+ writel(BIT(id), rst->regs_rst);
+ spin_unlock_irqrestore(&rst->lock, flags);
+
+ return 0;
+}
+
+static const struct reset_control_ops axs10x_reset_ops = {
+ .reset = axs10x_reset_reset,
+};
+
+static int axs10x_reset_probe(struct platform_device *pdev)
+{
+ struct axs10x_rst *rst;
+
+ rst = devm_kzalloc(&pdev->dev, sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return -ENOMEM;
+
+ rst->regs_rst = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rst->regs_rst))
+ return PTR_ERR(rst->regs_rst);
+
+ spin_lock_init(&rst->lock);
+
+ rst->rcdev.owner = THIS_MODULE;
+ rst->rcdev.ops = &axs10x_reset_ops;
+ rst->rcdev.of_node = pdev->dev.of_node;
+ rst->rcdev.nr_resets = AXS10X_MAX_RESETS;
+
+ return devm_reset_controller_register(&pdev->dev, &rst->rcdev);
+}
+
+static const struct of_device_id axs10x_reset_dt_match[] = {
+ { .compatible = "snps,axs10x-reset" },
+ { },
+};
+
+static struct platform_driver axs10x_reset_driver = {
+ .probe = axs10x_reset_probe,
+ .driver = {
+ .name = "axs10x-reset",
+ .of_match_table = axs10x_reset_dt_match,
+ },
+};
+builtin_platform_driver(axs10x_reset_driver);
+
+MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
+MODULE_DESCRIPTION("Synopsys AXS10x reset driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-bcm6345.c b/drivers/reset/reset-bcm6345.c
new file mode 100644
index 000000000000..56518f7bfbb3
--- /dev/null
+++ b/drivers/reset/reset-bcm6345.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BCM6345 Reset Controller Driver
+ *
+ * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#define BCM6345_RESET_NUM 32
+#define BCM6345_RESET_SLEEP_MIN_US 10000
+#define BCM6345_RESET_SLEEP_MAX_US 20000
+
+struct bcm6345_reset {
+ struct reset_controller_dev rcdev;
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+static inline struct bcm6345_reset *
+to_bcm6345_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct bcm6345_reset, rcdev);
+}
+
+static int bcm6345_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev);
+ unsigned long flags;
+ uint32_t val;
+
+ spin_lock_irqsave(&bcm6345_reset->lock, flags);
+ val = __raw_readl(bcm6345_reset->base);
+ if (assert)
+ val &= ~BIT(id);
+ else
+ val |= BIT(id);
+ __raw_writel(val, bcm6345_reset->base);
+ spin_unlock_irqrestore(&bcm6345_reset->lock, flags);
+
+ return 0;
+}
+
+static int bcm6345_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return bcm6345_reset_update(rcdev, id, true);
+}
+
+static int bcm6345_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return bcm6345_reset_update(rcdev, id, false);
+}
+
+static int bcm6345_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ bcm6345_reset_update(rcdev, id, true);
+ usleep_range(BCM6345_RESET_SLEEP_MIN_US,
+ BCM6345_RESET_SLEEP_MAX_US);
+
+ bcm6345_reset_update(rcdev, id, false);
+ /*
+ * Ensure component is taken out reset state by sleeping also after
+ * deasserting the reset. Otherwise, the component may not be ready
+ * for operation.
+ */
+ usleep_range(BCM6345_RESET_SLEEP_MIN_US,
+ BCM6345_RESET_SLEEP_MAX_US);
+
+ return 0;
+}
+
+static int bcm6345_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev);
+
+ return !(__raw_readl(bcm6345_reset->base) & BIT(id));
+}
+
+static const struct reset_control_ops bcm6345_reset_ops = {
+ .assert = bcm6345_reset_assert,
+ .deassert = bcm6345_reset_deassert,
+ .reset = bcm6345_reset_reset,
+ .status = bcm6345_reset_status,
+};
+
+static int bcm6345_reset_probe(struct platform_device *pdev)
+{
+ struct bcm6345_reset *bcm6345_reset;
+
+ bcm6345_reset = devm_kzalloc(&pdev->dev,
+ sizeof(*bcm6345_reset), GFP_KERNEL);
+ if (!bcm6345_reset)
+ return -ENOMEM;
+
+ bcm6345_reset->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bcm6345_reset->base))
+ return PTR_ERR(bcm6345_reset->base);
+
+ spin_lock_init(&bcm6345_reset->lock);
+ bcm6345_reset->rcdev.ops = &bcm6345_reset_ops;
+ bcm6345_reset->rcdev.owner = THIS_MODULE;
+ bcm6345_reset->rcdev.of_node = pdev->dev.of_node;
+ bcm6345_reset->rcdev.of_reset_n_cells = 1;
+ bcm6345_reset->rcdev.nr_resets = BCM6345_RESET_NUM;
+
+ return devm_reset_controller_register(&pdev->dev,
+ &bcm6345_reset->rcdev);
+}
+
+static const struct of_device_id bcm6345_reset_of_match[] = {
+ { .compatible = "brcm,bcm6345-reset" },
+ { .compatible = "brcm,bcm63xx-ephy-ctrl" },
+ { /* sentinel */ },
+};
+
+static struct platform_driver bcm6345_reset_driver = {
+ .probe = bcm6345_reset_probe,
+ .driver = {
+ .name = "bcm6345-reset",
+ .of_match_table = bcm6345_reset_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(bcm6345_reset_driver);
diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c
new file mode 100644
index 000000000000..578fe867080c
--- /dev/null
+++ b/drivers/reset/reset-berlin.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Marvell Berlin reset driver
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define BERLIN_MAX_RESETS 32
+
+#define to_berlin_reset_priv(p) \
+ container_of((p), struct berlin_reset_priv, rcdev)
+
+struct berlin_reset_priv {
+ struct regmap *regmap;
+ struct reset_controller_dev rcdev;
+};
+
+static int berlin_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
+ int offset = id >> 8;
+ int mask = BIT(id & 0x1f);
+
+ regmap_write(priv->regmap, offset, mask);
+
+ /* let the reset be effective */
+ udelay(10);
+
+ return 0;
+}
+
+static const struct reset_control_ops berlin_reset_ops = {
+ .reset = berlin_reset_reset,
+};
+
+static int berlin_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned int offset, bit;
+
+ offset = reset_spec->args[0];
+ bit = reset_spec->args[1];
+
+ if (bit >= BERLIN_MAX_RESETS)
+ return -EINVAL;
+
+ return (offset << 8) | bit;
+}
+
+static int berlin2_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *parent_np;
+ struct berlin_reset_priv *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ parent_np = of_get_parent(pdev->dev.of_node);
+ priv->regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.ops = &berlin_reset_ops;
+ priv->rcdev.of_node = pdev->dev.of_node;
+ priv->rcdev.of_reset_n_cells = 2;
+ priv->rcdev.of_xlate = berlin_reset_xlate;
+
+ return reset_controller_register(&priv->rcdev);
+}
+
+static const struct of_device_id berlin_reset_dt_match[] = {
+ { .compatible = "marvell,berlin2-reset" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, berlin_reset_dt_match);
+
+static struct platform_driver berlin_reset_driver = {
+ .probe = berlin2_reset_probe,
+ .driver = {
+ .name = "berlin2-reset",
+ .of_match_table = berlin_reset_dt_match,
+ },
+};
+module_platform_driver(berlin_reset_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
+MODULE_DESCRIPTION("Synaptics Berlin reset controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-brcmstb-rescal.c b/drivers/reset/reset-brcmstb-rescal.c
new file mode 100644
index 000000000000..823317772bac
--- /dev/null
+++ b/drivers/reset/reset-brcmstb-rescal.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2020 Broadcom */
+
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#define BRCM_RESCAL_START 0x0
+#define BRCM_RESCAL_START_BIT BIT(0)
+#define BRCM_RESCAL_CTRL 0x4
+#define BRCM_RESCAL_STATUS 0x8
+#define BRCM_RESCAL_STATUS_BIT BIT(0)
+
+struct brcm_rescal_reset {
+ void __iomem *base;
+ struct device *dev;
+ struct reset_controller_dev rcdev;
+};
+
+static int brcm_rescal_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct brcm_rescal_reset *data =
+ container_of(rcdev, struct brcm_rescal_reset, rcdev);
+ void __iomem *base = data->base;
+ u32 reg;
+ int ret;
+
+ reg = readl(base + BRCM_RESCAL_START);
+ writel(reg | BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
+ reg = readl(base + BRCM_RESCAL_START);
+ if (!(reg & BRCM_RESCAL_START_BIT)) {
+ dev_err(data->dev, "failed to start SATA/PCIe rescal\n");
+ return -EIO;
+ }
+
+ ret = readl_poll_timeout(base + BRCM_RESCAL_STATUS, reg,
+ (reg & BRCM_RESCAL_STATUS_BIT), 100, 1000);
+ if (ret) {
+ dev_err(data->dev, "time out on SATA/PCIe rescal\n");
+ return ret;
+ }
+
+ reg = readl(base + BRCM_RESCAL_START);
+ writel(reg & ~BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
+
+ dev_dbg(data->dev, "SATA/PCIe rescal success\n");
+
+ return 0;
+}
+
+static int brcm_rescal_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ /* This is needed if #reset-cells == 0. */
+ return 0;
+}
+
+static const struct reset_control_ops brcm_rescal_reset_ops = {
+ .reset = brcm_rescal_reset_set,
+};
+
+static int brcm_rescal_reset_probe(struct platform_device *pdev)
+{
+ struct brcm_rescal_reset *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = 1;
+ data->rcdev.ops = &brcm_rescal_reset_ops;
+ data->rcdev.of_node = pdev->dev.of_node;
+ data->rcdev.of_xlate = brcm_rescal_reset_xlate;
+ data->dev = &pdev->dev;
+
+ return devm_reset_controller_register(&pdev->dev, &data->rcdev);
+}
+
+static const struct of_device_id brcm_rescal_reset_of_match[] = {
+ { .compatible = "brcm,bcm7216-pcie-sata-rescal" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, brcm_rescal_reset_of_match);
+
+static struct platform_driver brcm_rescal_reset_driver = {
+ .probe = brcm_rescal_reset_probe,
+ .driver = {
+ .name = "brcm-rescal-reset",
+ .of_match_table = brcm_rescal_reset_of_match,
+ }
+};
+module_platform_driver(brcm_rescal_reset_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Broadcom SATA/PCIe rescal reset controller");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-brcmstb.c b/drivers/reset/reset-brcmstb.c
new file mode 100644
index 000000000000..810fe76452d6
--- /dev/null
+++ b/drivers/reset/reset-brcmstb.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Broadcom STB generic reset controller for SW_INIT style reset controller
+ *
+ * Author: Florian Fainelli <f.fainelli@gmail.com>
+ * Copyright (C) 2018 Broadcom
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/types.h>
+
+struct brcmstb_reset {
+ void __iomem *base;
+ struct reset_controller_dev rcdev;
+};
+
+#define SW_INIT_SET 0x00
+#define SW_INIT_CLEAR 0x04
+#define SW_INIT_STATUS 0x08
+
+#define SW_INIT_BIT(id) BIT((id) & 0x1f)
+#define SW_INIT_BANK(id) ((id) >> 5)
+
+/* A full bank contains extra registers that we are not utilizing but still
+ * qualify as a single bank.
+ */
+#define SW_INIT_BANK_SIZE 0x18
+
+static inline
+struct brcmstb_reset *to_brcmstb(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct brcmstb_reset, rcdev);
+}
+
+static int brcmstb_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = to_brcmstb(rcdev);
+
+ writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_SET);
+
+ return 0;
+}
+
+static int brcmstb_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = to_brcmstb(rcdev);
+
+ writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_CLEAR);
+ /* Maximum reset delay after de-asserting a line and seeing block
+ * operation is typically 14us for the worst case, build some slack
+ * here.
+ */
+ usleep_range(100, 200);
+
+ return 0;
+}
+
+static int brcmstb_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE;
+ struct brcmstb_reset *priv = to_brcmstb(rcdev);
+
+ return readl_relaxed(priv->base + off + SW_INIT_STATUS) &
+ SW_INIT_BIT(id);
+}
+
+static const struct reset_control_ops brcmstb_reset_ops = {
+ .assert = brcmstb_reset_assert,
+ .deassert = brcmstb_reset_deassert,
+ .status = brcmstb_reset_status,
+};
+
+static int brcmstb_reset_probe(struct platform_device *pdev)
+{
+ struct device *kdev = &pdev->dev;
+ struct brcmstb_reset *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ dev_set_drvdata(kdev, priv);
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = DIV_ROUND_DOWN_ULL(resource_size(res),
+ SW_INIT_BANK_SIZE) * 32;
+ priv->rcdev.ops = &brcmstb_reset_ops;
+ priv->rcdev.of_node = kdev->of_node;
+ /* Use defaults: 1 cell and simple xlate function */
+
+ return devm_reset_controller_register(kdev, &priv->rcdev);
+}
+
+static const struct of_device_id brcmstb_reset_of_match[] = {
+ { .compatible = "brcm,brcmstb-reset" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, brcmstb_reset_of_match);
+
+static struct platform_driver brcmstb_reset_driver = {
+ .probe = brcmstb_reset_probe,
+ .driver = {
+ .name = "brcmstb-reset",
+ .of_match_table = brcmstb_reset_of_match,
+ },
+};
+module_platform_driver(brcmstb_reset_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Broadcom STB reset controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-eic7700.c b/drivers/reset/reset-eic7700.c
new file mode 100644
index 000000000000..b72283b18b08
--- /dev/null
+++ b/drivers/reset/reset-eic7700.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd..
+ * All rights reserved.
+ *
+ * ESWIN Reset Driver
+ *
+ * Authors:
+ * Yifeng Huang <huangyifeng@eswincomputing.com>
+ * Xuyang Dong <dongxuyang@eswincomputing.com>
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <dt-bindings/reset/eswin,eic7700-reset.h>
+
+#define SYSCRG_CLEAR_BOOT_INFO_OFFSET 0xC
+#define CLEAR_BOOT_FLAG_BIT BIT(0)
+#define SYSCRG_RESET_OFFSET 0x100
+
+/**
+ * struct eic7700_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @regmap: regmap handle containing the memory-mapped reset registers
+ */
+struct eic7700_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static const struct regmap_config eic7700_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x1fc,
+};
+
+struct eic7700_reg {
+ u32 reg;
+ u32 bit;
+};
+
+static inline struct eic7700_reset_data *
+to_eic7700_reset_data(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct eic7700_reset_data, rcdev);
+}
+
+#define EIC7700_RESET(id, reg, bit)[id] = \
+ { SYSCRG_RESET_OFFSET + (reg) * sizeof(u32), BIT(bit) }
+
+/* mapping table for reset ID to register offset and reset bit */
+static const struct eic7700_reg eic7700_reset[] = {
+ EIC7700_RESET(EIC7700_RESET_NOC_NSP, 0, 0),
+ EIC7700_RESET(EIC7700_RESET_NOC_CFG, 0, 1),
+ EIC7700_RESET(EIC7700_RESET_RNOC_NSP, 0, 2),
+ EIC7700_RESET(EIC7700_RESET_SNOC_TCU, 0, 3),
+ EIC7700_RESET(EIC7700_RESET_SNOC_U84, 0, 4),
+ EIC7700_RESET(EIC7700_RESET_SNOC_PCIE_XSR, 0, 5),
+ EIC7700_RESET(EIC7700_RESET_SNOC_PCIE_XMR, 0, 6),
+ EIC7700_RESET(EIC7700_RESET_SNOC_PCIE_PR, 0, 7),
+ EIC7700_RESET(EIC7700_RESET_SNOC_NPU, 0, 8),
+ EIC7700_RESET(EIC7700_RESET_SNOC_JTAG, 0, 9),
+ EIC7700_RESET(EIC7700_RESET_SNOC_DSP, 0, 10),
+ EIC7700_RESET(EIC7700_RESET_SNOC_DDRC1_P2, 0, 11),
+ EIC7700_RESET(EIC7700_RESET_SNOC_DDRC1_P1, 0, 12),
+ EIC7700_RESET(EIC7700_RESET_SNOC_DDRC0_P2, 0, 13),
+ EIC7700_RESET(EIC7700_RESET_SNOC_DDRC0_P1, 0, 14),
+ EIC7700_RESET(EIC7700_RESET_SNOC_D2D, 0, 15),
+ EIC7700_RESET(EIC7700_RESET_SNOC_AON, 0, 16),
+ EIC7700_RESET(EIC7700_RESET_GPU_AXI, 1, 0),
+ EIC7700_RESET(EIC7700_RESET_GPU_CFG, 1, 1),
+ EIC7700_RESET(EIC7700_RESET_GPU_GRAY, 1, 2),
+ EIC7700_RESET(EIC7700_RESET_GPU_JONES, 1, 3),
+ EIC7700_RESET(EIC7700_RESET_GPU_SPU, 1, 4),
+ EIC7700_RESET(EIC7700_RESET_DSP_AXI, 2, 0),
+ EIC7700_RESET(EIC7700_RESET_DSP_CFG, 2, 1),
+ EIC7700_RESET(EIC7700_RESET_DSP_DIV4, 2, 2),
+ EIC7700_RESET(EIC7700_RESET_DSP_DIV0, 2, 4),
+ EIC7700_RESET(EIC7700_RESET_DSP_DIV1, 2, 5),
+ EIC7700_RESET(EIC7700_RESET_DSP_DIV2, 2, 6),
+ EIC7700_RESET(EIC7700_RESET_DSP_DIV3, 2, 7),
+ EIC7700_RESET(EIC7700_RESET_D2D_AXI, 3, 0),
+ EIC7700_RESET(EIC7700_RESET_D2D_CFG, 3, 1),
+ EIC7700_RESET(EIC7700_RESET_D2D_PRST, 3, 2),
+ EIC7700_RESET(EIC7700_RESET_D2D_RAW_PCS, 3, 4),
+ EIC7700_RESET(EIC7700_RESET_D2D_RX, 3, 5),
+ EIC7700_RESET(EIC7700_RESET_D2D_TX, 3, 6),
+ EIC7700_RESET(EIC7700_RESET_D2D_CORE, 3, 7),
+ EIC7700_RESET(EIC7700_RESET_DDR1_ARST, 4, 0),
+ EIC7700_RESET(EIC7700_RESET_DDR1_TRACE, 4, 6),
+ EIC7700_RESET(EIC7700_RESET_DDR0_ARST, 4, 16),
+ EIC7700_RESET(EIC7700_RESET_DDR_CFG, 4, 21),
+ EIC7700_RESET(EIC7700_RESET_DDR0_TRACE, 4, 22),
+ EIC7700_RESET(EIC7700_RESET_DDR_CORE, 4, 23),
+ EIC7700_RESET(EIC7700_RESET_DDR_PRST, 4, 26),
+ EIC7700_RESET(EIC7700_RESET_TCU_AXI, 5, 0),
+ EIC7700_RESET(EIC7700_RESET_TCU_CFG, 5, 1),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU0, 5, 4),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU1, 5, 5),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU2, 5, 6),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU3, 5, 7),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU4, 5, 8),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU5, 5, 9),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU6, 5, 10),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU7, 5, 11),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU8, 5, 12),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU9, 5, 13),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU10, 5, 14),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU11, 5, 15),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU12, 5, 16),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU13, 5, 17),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU14, 5, 18),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU15, 5, 19),
+ EIC7700_RESET(EIC7700_RESET_TCU_TBU16, 5, 20),
+ EIC7700_RESET(EIC7700_RESET_NPU_AXI, 6, 0),
+ EIC7700_RESET(EIC7700_RESET_NPU_CFG, 6, 1),
+ EIC7700_RESET(EIC7700_RESET_NPU_CORE, 6, 2),
+ EIC7700_RESET(EIC7700_RESET_NPU_E31CORE, 6, 3),
+ EIC7700_RESET(EIC7700_RESET_NPU_E31BUS, 6, 4),
+ EIC7700_RESET(EIC7700_RESET_NPU_E31DBG, 6, 5),
+ EIC7700_RESET(EIC7700_RESET_NPU_LLC, 6, 6),
+ EIC7700_RESET(EIC7700_RESET_HSP_AXI, 7, 0),
+ EIC7700_RESET(EIC7700_RESET_HSP_CFG, 7, 1),
+ EIC7700_RESET(EIC7700_RESET_HSP_POR, 7, 2),
+ EIC7700_RESET(EIC7700_RESET_MSHC0_PHY, 7, 3),
+ EIC7700_RESET(EIC7700_RESET_MSHC1_PHY, 7, 4),
+ EIC7700_RESET(EIC7700_RESET_MSHC2_PHY, 7, 5),
+ EIC7700_RESET(EIC7700_RESET_MSHC0_TXRX, 7, 6),
+ EIC7700_RESET(EIC7700_RESET_MSHC1_TXRX, 7, 7),
+ EIC7700_RESET(EIC7700_RESET_MSHC2_TXRX, 7, 8),
+ EIC7700_RESET(EIC7700_RESET_SATA_ASIC0, 7, 9),
+ EIC7700_RESET(EIC7700_RESET_SATA_OOB, 7, 10),
+ EIC7700_RESET(EIC7700_RESET_SATA_PMALIVE, 7, 11),
+ EIC7700_RESET(EIC7700_RESET_SATA_RBC, 7, 12),
+ EIC7700_RESET(EIC7700_RESET_DMA0, 7, 13),
+ EIC7700_RESET(EIC7700_RESET_HSP_DMA, 7, 14),
+ EIC7700_RESET(EIC7700_RESET_USB0_VAUX, 7, 15),
+ EIC7700_RESET(EIC7700_RESET_USB1_VAUX, 7, 16),
+ EIC7700_RESET(EIC7700_RESET_HSP_SD1_PRST, 7, 17),
+ EIC7700_RESET(EIC7700_RESET_HSP_SD0_PRST, 7, 18),
+ EIC7700_RESET(EIC7700_RESET_HSP_EMMC_PRST, 7, 19),
+ EIC7700_RESET(EIC7700_RESET_HSP_DMA_PRST, 7, 20),
+ EIC7700_RESET(EIC7700_RESET_HSP_SD1_ARST, 7, 21),
+ EIC7700_RESET(EIC7700_RESET_HSP_SD0_ARST, 7, 22),
+ EIC7700_RESET(EIC7700_RESET_HSP_EMMC_ARST, 7, 23),
+ EIC7700_RESET(EIC7700_RESET_HSP_DMA_ARST, 7, 24),
+ EIC7700_RESET(EIC7700_RESET_HSP_ETH1_ARST, 7, 25),
+ EIC7700_RESET(EIC7700_RESET_HSP_ETH0_ARST, 7, 26),
+ EIC7700_RESET(EIC7700_RESET_SATA_ARST, 7, 27),
+ EIC7700_RESET(EIC7700_RESET_PCIE_CFG, 8, 0),
+ EIC7700_RESET(EIC7700_RESET_PCIE_POWEUP, 8, 1),
+ EIC7700_RESET(EIC7700_RESET_PCIE_PERST, 8, 2),
+ EIC7700_RESET(EIC7700_RESET_I2C0, 9, 0),
+ EIC7700_RESET(EIC7700_RESET_I2C1, 9, 1),
+ EIC7700_RESET(EIC7700_RESET_I2C2, 9, 2),
+ EIC7700_RESET(EIC7700_RESET_I2C3, 9, 3),
+ EIC7700_RESET(EIC7700_RESET_I2C4, 9, 4),
+ EIC7700_RESET(EIC7700_RESET_I2C5, 9, 5),
+ EIC7700_RESET(EIC7700_RESET_I2C6, 9, 6),
+ EIC7700_RESET(EIC7700_RESET_I2C7, 9, 7),
+ EIC7700_RESET(EIC7700_RESET_I2C8, 9, 8),
+ EIC7700_RESET(EIC7700_RESET_I2C9, 9, 9),
+ EIC7700_RESET(EIC7700_RESET_FAN, 10, 0),
+ EIC7700_RESET(EIC7700_RESET_PVT0, 11, 0),
+ EIC7700_RESET(EIC7700_RESET_PVT1, 11, 1),
+ EIC7700_RESET(EIC7700_RESET_MBOX0, 12, 0),
+ EIC7700_RESET(EIC7700_RESET_MBOX1, 12, 1),
+ EIC7700_RESET(EIC7700_RESET_MBOX2, 12, 2),
+ EIC7700_RESET(EIC7700_RESET_MBOX3, 12, 3),
+ EIC7700_RESET(EIC7700_RESET_MBOX4, 12, 4),
+ EIC7700_RESET(EIC7700_RESET_MBOX5, 12, 5),
+ EIC7700_RESET(EIC7700_RESET_MBOX6, 12, 6),
+ EIC7700_RESET(EIC7700_RESET_MBOX7, 12, 7),
+ EIC7700_RESET(EIC7700_RESET_MBOX8, 12, 8),
+ EIC7700_RESET(EIC7700_RESET_MBOX9, 12, 9),
+ EIC7700_RESET(EIC7700_RESET_MBOX10, 12, 10),
+ EIC7700_RESET(EIC7700_RESET_MBOX11, 12, 11),
+ EIC7700_RESET(EIC7700_RESET_MBOX12, 12, 12),
+ EIC7700_RESET(EIC7700_RESET_MBOX13, 12, 13),
+ EIC7700_RESET(EIC7700_RESET_MBOX14, 12, 14),
+ EIC7700_RESET(EIC7700_RESET_MBOX15, 12, 15),
+ EIC7700_RESET(EIC7700_RESET_UART0, 13, 0),
+ EIC7700_RESET(EIC7700_RESET_UART1, 13, 1),
+ EIC7700_RESET(EIC7700_RESET_UART2, 13, 2),
+ EIC7700_RESET(EIC7700_RESET_UART3, 13, 3),
+ EIC7700_RESET(EIC7700_RESET_UART4, 13, 4),
+ EIC7700_RESET(EIC7700_RESET_GPIO0, 14, 0),
+ EIC7700_RESET(EIC7700_RESET_GPIO1, 14, 1),
+ EIC7700_RESET(EIC7700_RESET_TIMER, 15, 0),
+ EIC7700_RESET(EIC7700_RESET_SSI0, 16, 0),
+ EIC7700_RESET(EIC7700_RESET_SSI1, 16, 1),
+ EIC7700_RESET(EIC7700_RESET_WDT0, 17, 0),
+ EIC7700_RESET(EIC7700_RESET_WDT1, 17, 1),
+ EIC7700_RESET(EIC7700_RESET_WDT2, 17, 2),
+ EIC7700_RESET(EIC7700_RESET_WDT3, 17, 3),
+ EIC7700_RESET(EIC7700_RESET_LSP_CFG, 18, 0),
+ EIC7700_RESET(EIC7700_RESET_U84_CORE0, 19, 0),
+ EIC7700_RESET(EIC7700_RESET_U84_CORE1, 19, 1),
+ EIC7700_RESET(EIC7700_RESET_U84_CORE2, 19, 2),
+ EIC7700_RESET(EIC7700_RESET_U84_CORE3, 19, 3),
+ EIC7700_RESET(EIC7700_RESET_U84_BUS, 19, 4),
+ EIC7700_RESET(EIC7700_RESET_U84_DBG, 19, 5),
+ EIC7700_RESET(EIC7700_RESET_U84_TRACECOM, 19, 6),
+ EIC7700_RESET(EIC7700_RESET_U84_TRACE0, 19, 8),
+ EIC7700_RESET(EIC7700_RESET_U84_TRACE1, 19, 9),
+ EIC7700_RESET(EIC7700_RESET_U84_TRACE2, 19, 10),
+ EIC7700_RESET(EIC7700_RESET_U84_TRACE3, 19, 11),
+ EIC7700_RESET(EIC7700_RESET_SCPU_CORE, 20, 0),
+ EIC7700_RESET(EIC7700_RESET_SCPU_BUS, 20, 1),
+ EIC7700_RESET(EIC7700_RESET_SCPU_DBG, 20, 2),
+ EIC7700_RESET(EIC7700_RESET_LPCPU_CORE, 21, 0),
+ EIC7700_RESET(EIC7700_RESET_LPCPU_BUS, 21, 1),
+ EIC7700_RESET(EIC7700_RESET_LPCPU_DBG, 21, 2),
+ EIC7700_RESET(EIC7700_RESET_VC_CFG, 22, 0),
+ EIC7700_RESET(EIC7700_RESET_VC_AXI, 22, 1),
+ EIC7700_RESET(EIC7700_RESET_VC_MONCFG, 22, 2),
+ EIC7700_RESET(EIC7700_RESET_JD_CFG, 23, 0),
+ EIC7700_RESET(EIC7700_RESET_JD_AXI, 23, 1),
+ EIC7700_RESET(EIC7700_RESET_JE_CFG, 24, 0),
+ EIC7700_RESET(EIC7700_RESET_JE_AXI, 24, 1),
+ EIC7700_RESET(EIC7700_RESET_VD_CFG, 25, 0),
+ EIC7700_RESET(EIC7700_RESET_VD_AXI, 25, 1),
+ EIC7700_RESET(EIC7700_RESET_VE_AXI, 26, 0),
+ EIC7700_RESET(EIC7700_RESET_VE_CFG, 26, 1),
+ EIC7700_RESET(EIC7700_RESET_G2D_CORE, 27, 0),
+ EIC7700_RESET(EIC7700_RESET_G2D_CFG, 27, 1),
+ EIC7700_RESET(EIC7700_RESET_G2D_AXI, 27, 2),
+ EIC7700_RESET(EIC7700_RESET_VI_AXI, 28, 0),
+ EIC7700_RESET(EIC7700_RESET_VI_CFG, 28, 1),
+ EIC7700_RESET(EIC7700_RESET_VI_DWE, 28, 2),
+ EIC7700_RESET(EIC7700_RESET_DVP, 29, 0),
+ EIC7700_RESET(EIC7700_RESET_ISP0, 30, 0),
+ EIC7700_RESET(EIC7700_RESET_ISP1, 31, 0),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR0, 32, 0),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR1, 32, 1),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR2, 32, 2),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR3, 32, 3),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR4, 32, 4),
+ EIC7700_RESET(EIC7700_RESET_SHUTTR5, 32, 5),
+ EIC7700_RESET(EIC7700_RESET_VO_MIPI, 33, 0),
+ EIC7700_RESET(EIC7700_RESET_VO_PRST, 33, 1),
+ EIC7700_RESET(EIC7700_RESET_VO_HDMI_PRST, 33, 3),
+ EIC7700_RESET(EIC7700_RESET_VO_HDMI_PHY, 33, 4),
+ EIC7700_RESET(EIC7700_RESET_VO_HDMI, 33, 5),
+ EIC7700_RESET(EIC7700_RESET_VO_I2S, 34, 0),
+ EIC7700_RESET(EIC7700_RESET_VO_I2S_PRST, 34, 1),
+ EIC7700_RESET(EIC7700_RESET_VO_AXI, 35, 0),
+ EIC7700_RESET(EIC7700_RESET_VO_CFG, 35, 1),
+ EIC7700_RESET(EIC7700_RESET_VO_DC, 35, 2),
+ EIC7700_RESET(EIC7700_RESET_VO_DC_PRST, 35, 3),
+ EIC7700_RESET(EIC7700_RESET_BOOTSPI_HRST, 36, 0),
+ EIC7700_RESET(EIC7700_RESET_BOOTSPI, 36, 1),
+ EIC7700_RESET(EIC7700_RESET_ANO1, 37, 0),
+ EIC7700_RESET(EIC7700_RESET_ANO0, 38, 0),
+ EIC7700_RESET(EIC7700_RESET_DMA1_ARST, 39, 0),
+ EIC7700_RESET(EIC7700_RESET_DMA1_HRST, 39, 1),
+ EIC7700_RESET(EIC7700_RESET_FPRT, 40, 0),
+ EIC7700_RESET(EIC7700_RESET_HBLOCK, 41, 0),
+ EIC7700_RESET(EIC7700_RESET_SECSR, 42, 0),
+ EIC7700_RESET(EIC7700_RESET_OTP, 43, 0),
+ EIC7700_RESET(EIC7700_RESET_PKA, 44, 0),
+ EIC7700_RESET(EIC7700_RESET_SPACC, 45, 0),
+ EIC7700_RESET(EIC7700_RESET_TRNG, 46, 0),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_0, 48, 0),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_1, 48, 1),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_2, 48, 2),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_3, 48, 3),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_4, 48, 4),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_5, 48, 5),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_6, 48, 6),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_7, 48, 7),
+ EIC7700_RESET(EIC7700_RESET_TIMER0_N, 48, 8),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_0, 49, 0),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_1, 49, 1),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_2, 49, 2),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_3, 49, 3),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_4, 49, 4),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_5, 49, 5),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_6, 49, 6),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_7, 49, 7),
+ EIC7700_RESET(EIC7700_RESET_TIMER1_N, 49, 8),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_0, 50, 0),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_1, 50, 1),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_2, 50, 2),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_3, 50, 3),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_4, 50, 4),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_5, 50, 5),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_6, 50, 6),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_7, 50, 7),
+ EIC7700_RESET(EIC7700_RESET_TIMER2_N, 50, 8),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_0, 51, 0),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_1, 51, 1),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_2, 51, 2),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_3, 51, 3),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_4, 51, 4),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_5, 51, 5),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_6, 51, 6),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_7, 51, 7),
+ EIC7700_RESET(EIC7700_RESET_TIMER3_N, 51, 8),
+ EIC7700_RESET(EIC7700_RESET_RTC, 52, 0),
+ EIC7700_RESET(EIC7700_RESET_MNOC_SNOC_NSP, 53, 0),
+ EIC7700_RESET(EIC7700_RESET_MNOC_VC, 53, 1),
+ EIC7700_RESET(EIC7700_RESET_MNOC_CFG, 53, 2),
+ EIC7700_RESET(EIC7700_RESET_MNOC_HSP, 53, 3),
+ EIC7700_RESET(EIC7700_RESET_MNOC_GPU, 53, 4),
+ EIC7700_RESET(EIC7700_RESET_MNOC_DDRC1_P3, 53, 5),
+ EIC7700_RESET(EIC7700_RESET_MNOC_DDRC0_P3, 53, 6),
+ EIC7700_RESET(EIC7700_RESET_RNOC_VO, 54, 0),
+ EIC7700_RESET(EIC7700_RESET_RNOC_VI, 54, 1),
+ EIC7700_RESET(EIC7700_RESET_RNOC_SNOC_NSP, 54, 2),
+ EIC7700_RESET(EIC7700_RESET_RNOC_CFG, 54, 3),
+ EIC7700_RESET(EIC7700_RESET_MNOC_DDRC1_P4, 54, 4),
+ EIC7700_RESET(EIC7700_RESET_MNOC_DDRC0_P4, 54, 5),
+ EIC7700_RESET(EIC7700_RESET_CNOC_VO_CFG, 55, 0),
+ EIC7700_RESET(EIC7700_RESET_CNOC_VI_CFG, 55, 1),
+ EIC7700_RESET(EIC7700_RESET_CNOC_VC_CFG, 55, 2),
+ EIC7700_RESET(EIC7700_RESET_CNOC_TCU_CFG, 55, 3),
+ EIC7700_RESET(EIC7700_RESET_CNOC_PCIE_CFG, 55, 4),
+ EIC7700_RESET(EIC7700_RESET_CNOC_NPU_CFG, 55, 5),
+ EIC7700_RESET(EIC7700_RESET_CNOC_LSP_CFG, 55, 6),
+ EIC7700_RESET(EIC7700_RESET_CNOC_HSP_CFG, 55, 7),
+ EIC7700_RESET(EIC7700_RESET_CNOC_GPU_CFG, 55, 8),
+ EIC7700_RESET(EIC7700_RESET_CNOC_DSPT_CFG, 55, 9),
+ EIC7700_RESET(EIC7700_RESET_CNOC_DDRT1_CFG, 55, 10),
+ EIC7700_RESET(EIC7700_RESET_CNOC_DDRT0_CFG, 55, 11),
+ EIC7700_RESET(EIC7700_RESET_CNOC_D2D_CFG, 55, 12),
+ EIC7700_RESET(EIC7700_RESET_CNOC_CFG, 55, 13),
+ EIC7700_RESET(EIC7700_RESET_CNOC_CLMM_CFG, 55, 14),
+ EIC7700_RESET(EIC7700_RESET_CNOC_AON_CFG, 55, 15),
+ EIC7700_RESET(EIC7700_RESET_LNOC_CFG, 56, 0),
+ EIC7700_RESET(EIC7700_RESET_LNOC_NPU_LLC, 56, 1),
+ EIC7700_RESET(EIC7700_RESET_LNOC_DDRC1_P0, 56, 2),
+ EIC7700_RESET(EIC7700_RESET_LNOC_DDRC0_P0, 56, 3),
+};
+
+static int eic7700_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct eic7700_reset_data *data = to_eic7700_reset_data(rcdev);
+
+ return regmap_clear_bits(data->regmap, eic7700_reset[id].reg,
+ eic7700_reset[id].bit);
+}
+
+static int eic7700_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct eic7700_reset_data *data = to_eic7700_reset_data(rcdev);
+
+ return regmap_set_bits(data->regmap, eic7700_reset[id].reg,
+ eic7700_reset[id].bit);
+}
+
+static int eic7700_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = eic7700_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ usleep_range(10, 15);
+
+ return eic7700_reset_deassert(rcdev, id);
+}
+
+static const struct reset_control_ops eic7700_reset_ops = {
+ .reset = eic7700_reset_reset,
+ .assert = eic7700_reset_assert,
+ .deassert = eic7700_reset_deassert,
+};
+
+static const struct of_device_id eic7700_reset_dt_ids[] = {
+ { .compatible = "eswin,eic7700-reset", },
+ { /* sentinel */ }
+};
+
+static int eic7700_reset_probe(struct platform_device *pdev)
+{
+ struct eic7700_reset_data *data;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ data->regmap = devm_regmap_init_mmio(dev, base, &eic7700_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "failed to get regmap!\n");
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &eic7700_reset_ops;
+ data->rcdev.of_node = dev->of_node;
+ data->rcdev.of_reset_n_cells = 1;
+ data->rcdev.dev = dev;
+ data->rcdev.nr_resets = ARRAY_SIZE(eic7700_reset);
+
+ /* clear boot flag so u84 and scpu could be reseted by software */
+ regmap_set_bits(data->regmap, SYSCRG_CLEAR_BOOT_INFO_OFFSET,
+ CLEAR_BOOT_FLAG_BIT);
+ msleep(50);
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver eic7700_reset_driver = {
+ .probe = eic7700_reset_probe,
+ .driver = {
+ .name = "eic7700-reset",
+ .of_match_table = eic7700_reset_dt_ids,
+ },
+};
+
+builtin_platform_driver(eic7700_reset_driver);
diff --git a/drivers/reset/reset-eyeq.c b/drivers/reset/reset-eyeq.c
new file mode 100644
index 000000000000..2d3998368a1c
--- /dev/null
+++ b/drivers/reset/reset-eyeq.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Reset driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H platforms.
+ *
+ * Controllers live in a shared register region called OLB. EyeQ5 and EyeQ6L
+ * have a single OLB instance for a single reset controller. EyeQ6H has seven
+ * OLB instances; three host reset controllers.
+ *
+ * Each reset controller has one or more domain. Domains are of a given type
+ * (see enum eqr_domain_type), with a valid offset mask (up to 32 resets per
+ * domain).
+ *
+ * Domain types define expected behavior: one-register-per-reset,
+ * one-bit-per-reset, status detection method, busywait duration, etc.
+ *
+ * We use eqr_ as prefix, as-in "EyeQ Reset", but way shorter.
+ *
+ * Known resets in EyeQ5 domain 0 (type EQR_EYEQ5_SARCR):
+ * 3. CAN0 4. CAN1 5. CAN2 6. SPI0
+ * 7. SPI1 8. SPI2 9. SPI3 10. UART0
+ * 11. UART1 12. UART2 13. I2C0 14. I2C1
+ * 15. I2C2 16. I2C3 17. I2C4 18. TIMER0
+ * 19. TIMER1 20. TIMER2 21. TIMER3 22. TIMER4
+ * 23. WD0 24. EXT0 25. EXT1 26. GPIO
+ * 27. WD1
+ *
+ * Known resets in EyeQ5 domain 1 (type EQR_EYEQ5_ACRP):
+ * 0. VMP0 1. VMP1 2. VMP2 3. VMP3
+ * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
+ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3
+ * 12. MPC4
+ *
+ * Known resets in EyeQ5 domain 2 (type EQR_EYEQ5_PCIE):
+ * 0. PCIE0_CORE 1. PCIE0_APB 2. PCIE0_LINK_AXI 3. PCIE0_LINK_MGMT
+ * 4. PCIE0_LINK_HOT 5. PCIE0_LINK_PIPE 6. PCIE1_CORE 7. PCIE1_APB
+ * 8. PCIE1_LINK_AXI 9. PCIE1_LINK_MGMT 10. PCIE1_LINK_HOT 11. PCIE1_LINK_PIPE
+ * 12. MULTIPHY 13. MULTIPHY_APB 15. PCIE0_LINK_MGMT 16. PCIE1_LINK_MGMT
+ * 17. PCIE0_LINK_PM 18. PCIE1_LINK_PM
+ *
+ * Known resets in EyeQ6L domain 0 (type EQR_EYEQ5_SARCR):
+ * 0. SPI0 1. SPI1 2. UART0 3. I2C0
+ * 4. I2C1 5. TIMER0 6. TIMER1 7. TIMER2
+ * 8. TIMER3 9. WD0 10. WD1 11. EXT0
+ * 12. EXT1 13. GPIO
+ *
+ * Known resets in EyeQ6L domain 1 (type EQR_EYEQ5_ACRP):
+ * 0. VMP0 1. VMP1 2. VMP2 3. VMP3
+ * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
+ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3
+ * 12. MPC4
+ *
+ * Known resets in EyeQ6H west/east (type EQR_EYEQ6H_SARCR):
+ * 0. CAN 1. SPI0 2. SPI1 3. UART0
+ * 4. UART1 5. I2C0 6. I2C1 7. -hole-
+ * 8. TIMER0 9. TIMER1 10. WD 11. EXT TIMER
+ * 12. GPIO
+ *
+ * Known resets in EyeQ6H acc (type EQR_EYEQ5_ACRP):
+ * 1. XNN0 2. XNN1 3. XNN2 4. XNN3
+ * 5. VMP0 6. VMP1 7. VMP2 8. VMP3
+ * 9. PMA0 10. PMA1 11. MPC0 12. MPC1
+ * 13. MPC2 14. MPC3 15. PERIPH
+ *
+ * Abbreviations:
+ * - PMA: Programmable Macro Array
+ * - MPC: Multi-threaded Processing Clusters
+ * - VMP: Vector Microcode Processors
+ *
+ * Copyright (C) 2024 Mobileye Vision Technologies Ltd.
+ */
+
+#include <linux/array_size.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/lockdep.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * A reset ID, as returned by eqr_of_xlate_*(), is a (domain, offset) pair.
+ * Low byte is domain, rest is offset.
+ */
+#define ID_DOMAIN_MASK GENMASK(7, 0)
+#define ID_OFFSET_MASK GENMASK(31, 8)
+
+enum eqr_domain_type {
+ EQR_EYEQ5_SARCR,
+ EQR_EYEQ5_ACRP,
+ EQR_EYEQ5_PCIE,
+ EQR_EYEQ6H_SARCR,
+};
+
+/*
+ * Domain type EQR_EYEQ5_SARCR register offsets.
+ */
+#define EQR_EYEQ5_SARCR_REQUEST (0x000)
+#define EQR_EYEQ5_SARCR_STATUS (0x004)
+
+/*
+ * Domain type EQR_EYEQ5_ACRP register masks.
+ * Registers are: base + 4 * offset.
+ */
+#define EQR_EYEQ5_ACRP_PD_REQ BIT(0)
+#define EQR_EYEQ5_ACRP_ST_POWER_DOWN BIT(27)
+#define EQR_EYEQ5_ACRP_ST_ACTIVE BIT(29)
+
+/*
+ * Domain type EQR_EYEQ6H_SARCR register offsets.
+ */
+#define EQR_EYEQ6H_SARCR_RST_REQUEST (0x000)
+#define EQR_EYEQ6H_SARCR_CLK_STATUS (0x004)
+#define EQR_EYEQ6H_SARCR_RST_STATUS (0x008)
+#define EQR_EYEQ6H_SARCR_CLK_REQUEST (0x00C)
+
+struct eqr_busy_wait_timings {
+ unsigned long sleep_us;
+ unsigned long timeout_us;
+};
+
+static const struct eqr_busy_wait_timings eqr_timings[] = {
+ [EQR_EYEQ5_SARCR] = {1, 10},
+ [EQR_EYEQ5_ACRP] = {1, 40 * USEC_PER_MSEC}, /* LBIST implies long timeout. */
+ /* EQR_EYEQ5_PCIE does no busy waiting. */
+ [EQR_EYEQ6H_SARCR] = {1, 400},
+};
+
+#define EQR_MAX_DOMAIN_COUNT 3
+
+struct eqr_domain_descriptor {
+ enum eqr_domain_type type;
+ u32 valid_mask;
+ unsigned int offset;
+};
+
+struct eqr_match_data {
+ unsigned int domain_count;
+ const struct eqr_domain_descriptor *domains;
+};
+
+struct eqr_private {
+ /*
+ * One mutex per domain for read-modify-write operations on registers.
+ * Some domains can be involved in LBIST which implies long critical
+ * sections; we wouldn't want other domains to be impacted by that.
+ */
+ struct mutex mutexes[EQR_MAX_DOMAIN_COUNT];
+ void __iomem *base;
+ const struct eqr_match_data *data;
+ struct reset_controller_dev rcdev;
+};
+
+static inline struct eqr_private *eqr_rcdev_to_priv(struct reset_controller_dev *x)
+{
+ return container_of(x, struct eqr_private, rcdev);
+}
+
+static u32 eqr_double_readl(void __iomem *addr_a, void __iomem *addr_b,
+ u32 *dest_a, u32 *dest_b)
+{
+ *dest_a = readl(addr_a);
+ *dest_b = readl(addr_b);
+ return 0; /* read_poll_timeout() op argument must return something. */
+}
+
+static int eqr_busy_wait_locked(struct eqr_private *priv, struct device *dev,
+ u32 domain, u32 offset, bool assert)
+{
+ void __iomem *base = priv->base + priv->data->domains[domain].offset;
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ unsigned long timeout_us = eqr_timings[domain_type].timeout_us;
+ unsigned long sleep_us = eqr_timings[domain_type].sleep_us;
+ u32 val, mask, rst_status, clk_status;
+ void __iomem *reg;
+ int ret;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_STATUS;
+ mask = BIT(offset);
+
+ ret = readl_poll_timeout(reg, val, !(val & mask) == assert,
+ sleep_us, timeout_us);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ if (assert)
+ mask = EQR_EYEQ5_ACRP_ST_POWER_DOWN;
+ else
+ mask = EQR_EYEQ5_ACRP_ST_ACTIVE;
+
+ ret = readl_poll_timeout(reg, val, !!(val & mask),
+ sleep_us, timeout_us);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ ret = 0; /* No busy waiting. */
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /*
+ * Wait until both bits change:
+ * readl(base + EQR_EYEQ6H_SARCR_RST_STATUS) & BIT(offset)
+ * readl(base + EQR_EYEQ6H_SARCR_CLK_STATUS) & BIT(offset)
+ */
+ mask = BIT(offset);
+ ret = read_poll_timeout(eqr_double_readl, val,
+ (!(rst_status & mask) == assert) &&
+ (!(clk_status & mask) == assert),
+ sleep_us, timeout_us, false,
+ base + EQR_EYEQ6H_SARCR_RST_STATUS,
+ base + EQR_EYEQ6H_SARCR_CLK_STATUS,
+ &rst_status, &clk_status);
+ break;
+
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == -ETIMEDOUT)
+ dev_dbg(dev, "%u-%u: timeout\n", domain, offset);
+ return ret;
+}
+
+static void eqr_assert_locked(struct eqr_private *priv, u32 domain, u32 offset)
+{
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+ u32 val;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_REQUEST;
+ writel(readl(reg) & ~BIT(offset), reg);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ writel(readl(reg) | EQR_EYEQ5_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ writel(readl(base) & ~BIT(offset), base);
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ val &= ~BIT(offset);
+ writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
+ break;
+
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static int eqr_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+
+ dev_dbg(rcdev->dev, "%u-%u: assert request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ eqr_assert_locked(priv, domain, offset);
+ return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, true);
+}
+
+static void eqr_deassert_locked(struct eqr_private *priv, u32 domain,
+ u32 offset)
+{
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+ u32 val;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_REQUEST;
+ writel(readl(reg) | BIT(offset), reg);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ writel(readl(reg) & ~EQR_EYEQ5_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ writel(readl(base) | BIT(offset), base);
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ val |= BIT(offset);
+ writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
+ break;
+
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static int eqr_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+
+ dev_dbg(rcdev->dev, "%u-%u: deassert request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ eqr_deassert_locked(priv, domain, offset);
+ return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, false);
+}
+
+static int eqr_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+
+ dev_dbg(rcdev->dev, "%u-%u: status request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_STATUS;
+ return !(readl(reg) & BIT(offset));
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ return !(readl(reg) & EQR_EYEQ5_ACRP_ST_ACTIVE);
+ case EQR_EYEQ5_PCIE:
+ return !(readl(base) & BIT(offset));
+ case EQR_EYEQ6H_SARCR:
+ reg = base + EQR_EYEQ6H_SARCR_RST_STATUS;
+ return !(readl(reg) & BIT(offset));
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct reset_control_ops eqr_ops = {
+ .assert = eqr_assert,
+ .deassert = eqr_deassert,
+ .status = eqr_status,
+};
+
+static int eqr_of_xlate_internal(struct reset_controller_dev *rcdev,
+ u32 domain, u32 offset)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+
+ if (domain >= priv->data->domain_count || offset > 31 ||
+ !(priv->data->domains[domain].valid_mask & BIT(offset))) {
+ dev_err(rcdev->dev, "%u-%u: invalid reset\n", domain, offset);
+ return -EINVAL;
+ }
+
+ return FIELD_PREP(ID_DOMAIN_MASK, domain) | FIELD_PREP(ID_OFFSET_MASK, offset);
+}
+
+static int eqr_of_xlate_onecell(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return eqr_of_xlate_internal(rcdev, 0, reset_spec->args[0]);
+}
+
+static int eqr_of_xlate_twocells(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return eqr_of_xlate_internal(rcdev, reset_spec->args[0], reset_spec->args[1]);
+}
+
+static void eqr_of_node_put(void *_dev)
+{
+ struct device *dev = _dev;
+
+ of_node_put(dev->of_node);
+}
+
+static int eqr_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ const struct of_device_id *match;
+ struct device *dev = &adev->dev;
+ struct eqr_private *priv;
+ unsigned int i;
+ int ret;
+
+ /*
+ * We are an auxiliary device of clk-eyeq. We do not have an OF node by
+ * default; let's reuse our parent's OF node.
+ */
+ WARN_ON(dev->of_node);
+ device_set_of_node_from_dev(dev, dev->parent);
+ if (!dev->of_node)
+ return -ENODEV;
+
+ ret = devm_add_action_or_reset(dev, eqr_of_node_put, dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Using our newfound OF node, we can get match data. We cannot use
+ * device_get_match_data() because it does not match reused OF nodes.
+ */
+ match = of_match_node(dev->driver->of_match_table, dev->of_node);
+ if (!match || !match->data)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = match->data;
+ priv->base = (void __iomem *)dev_get_platdata(dev);
+ priv->rcdev.ops = &eqr_ops;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.dev = dev;
+ priv->rcdev.of_node = dev->of_node;
+
+ if (priv->data->domain_count == 1) {
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = eqr_of_xlate_onecell;
+ } else {
+ priv->rcdev.of_reset_n_cells = 2;
+ priv->rcdev.of_xlate = eqr_of_xlate_twocells;
+ }
+
+ for (i = 0; i < priv->data->domain_count; i++)
+ mutex_init(&priv->mutexes[i]);
+
+ priv->rcdev.nr_resets = 0;
+ for (i = 0; i < priv->data->domain_count; i++)
+ priv->rcdev.nr_resets += hweight32(priv->data->domains[i].valid_mask);
+
+ ret = devm_reset_controller_register(dev, &priv->rcdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed registering reset controller\n");
+
+ return 0;
+}
+
+static const struct eqr_domain_descriptor eqr_eyeq5_domains[] = {
+ {
+ .type = EQR_EYEQ5_SARCR,
+ .valid_mask = 0xFFFFFF8,
+ .offset = 0x004,
+ },
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x0001FFF,
+ .offset = 0x200,
+ },
+ {
+ .type = EQR_EYEQ5_PCIE,
+ .valid_mask = 0x007BFFF,
+ .offset = 0x120,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq5_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq5_domains),
+ .domains = eqr_eyeq5_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq6l_domains[] = {
+ {
+ .type = EQR_EYEQ5_SARCR,
+ .valid_mask = 0x3FFF,
+ .offset = 0x004,
+ },
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x00FF,
+ .offset = 0x200,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6l_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6l_domains),
+ .domains = eqr_eyeq6l_domains,
+};
+
+/* West and east OLBs each have an instance. */
+static const struct eqr_domain_descriptor eqr_eyeq6h_we_domains[] = {
+ {
+ .type = EQR_EYEQ6H_SARCR,
+ .valid_mask = 0x1F7F,
+ .offset = 0x004,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6h_we_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6h_we_domains),
+ .domains = eqr_eyeq6h_we_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq6h_acc_domains[] = {
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x7FFF,
+ .offset = 0x000,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6h_acc_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6h_acc_domains),
+ .domains = eqr_eyeq6h_acc_domains,
+};
+
+/*
+ * Table describes OLB system-controller compatibles.
+ * It does not get used to match against devicetree node.
+ */
+static const struct of_device_id eqr_match_table[] = {
+ { .compatible = "mobileye,eyeq5-olb", .data = &eqr_eyeq5_data },
+ { .compatible = "mobileye,eyeq6l-olb", .data = &eqr_eyeq6l_data },
+ { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqr_eyeq6h_we_data },
+ { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqr_eyeq6h_we_data },
+ { .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqr_eyeq6h_acc_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, eqr_match_table);
+
+static const struct auxiliary_device_id eqr_id_table[] = {
+ { .name = "clk_eyeq.reset" },
+ { .name = "clk_eyeq.reset_west" },
+ { .name = "clk_eyeq.reset_east" },
+ { .name = "clk_eyeq.reset_acc" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, eqr_id_table);
+
+static struct auxiliary_driver eqr_driver = {
+ .probe = eqr_probe,
+ .id_table = eqr_id_table,
+ .driver = {
+ .of_match_table = eqr_match_table,
+ }
+};
+module_auxiliary_driver(eqr_driver);
diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c
new file mode 100644
index 000000000000..e5512b3b596b
--- /dev/null
+++ b/drivers/reset/reset-gpio.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/auxiliary_bus.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/reset-controller.h>
+
+struct reset_gpio_priv {
+ struct reset_controller_dev rc;
+ struct gpio_desc *reset;
+};
+
+static inline struct reset_gpio_priv
+*rc_to_reset_gpio(struct reset_controller_dev *rc)
+{
+ return container_of(rc, struct reset_gpio_priv, rc);
+}
+
+static int reset_gpio_assert(struct reset_controller_dev *rc, unsigned long id)
+{
+ struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
+
+ gpiod_set_value_cansleep(priv->reset, 1);
+
+ return 0;
+}
+
+static int reset_gpio_deassert(struct reset_controller_dev *rc,
+ unsigned long id)
+{
+ struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
+
+ gpiod_set_value_cansleep(priv->reset, 0);
+
+ return 0;
+}
+
+static int reset_gpio_status(struct reset_controller_dev *rc, unsigned long id)
+{
+ struct reset_gpio_priv *priv = rc_to_reset_gpio(rc);
+
+ return gpiod_get_value_cansleep(priv->reset);
+}
+
+static const struct reset_control_ops reset_gpio_ops = {
+ .assert = reset_gpio_assert,
+ .deassert = reset_gpio_deassert,
+ .status = reset_gpio_status,
+};
+
+static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return reset_spec->args[0];
+}
+
+static void reset_gpio_of_node_put(void *data)
+{
+ of_node_put(data);
+}
+
+static int reset_gpio_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct of_phandle_args *platdata = dev_get_platdata(dev);
+ struct reset_gpio_priv *priv;
+ int ret;
+
+ if (!platdata)
+ return -EINVAL;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ auxiliary_set_drvdata(adev, &priv->rc);
+
+ priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(dev, PTR_ERR(priv->reset),
+ "Could not get reset gpios\n");
+
+ priv->rc.ops = &reset_gpio_ops;
+ priv->rc.owner = THIS_MODULE;
+ priv->rc.dev = dev;
+ priv->rc.of_args = platdata;
+ ret = devm_add_action_or_reset(dev, reset_gpio_of_node_put,
+ priv->rc.of_node);
+ if (ret)
+ return ret;
+
+ /* Cells to match GPIO specifier, but it's not really used */
+ priv->rc.of_reset_n_cells = 2;
+ priv->rc.of_xlate = reset_gpio_of_xlate;
+ priv->rc.nr_resets = 1;
+
+ return devm_reset_controller_register(dev, &priv->rc);
+}
+
+static const struct auxiliary_device_id reset_gpio_ids[] = {
+ { .name = "reset.gpio" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, reset_gpio_ids);
+
+static struct auxiliary_driver reset_gpio_driver = {
+ .probe = reset_gpio_probe,
+ .id_table = reset_gpio_ids,
+ .driver = {
+ .name = "reset-gpio",
+ },
+};
+module_auxiliary_driver(reset_gpio_driver);
+
+MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
+MODULE_DESCRIPTION("Generic GPIO reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-hsdk.c b/drivers/reset/reset-hsdk.c
new file mode 100644
index 000000000000..98460e08752c
--- /dev/null
+++ b/drivers/reset/reset-hsdk.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 Synopsys.
+ *
+ * Synopsys HSDK Development platform reset driver.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define to_hsdk_rst(p) container_of((p), struct hsdk_rst, rcdev)
+
+struct hsdk_rst {
+ void __iomem *regs_ctl;
+ void __iomem *regs_rst;
+ spinlock_t lock;
+ struct reset_controller_dev rcdev;
+};
+
+static const u32 rst_map[] = {
+ BIT(16), /* APB_RST */
+ BIT(17), /* AXI_RST */
+ BIT(18), /* ETH_RST */
+ BIT(19), /* USB_RST */
+ BIT(20), /* SDIO_RST */
+ BIT(21), /* HDMI_RST */
+ BIT(22), /* GFX_RST */
+ BIT(25), /* DMAC_RST */
+ BIT(31), /* EBI_RST */
+};
+
+#define HSDK_MAX_RESETS ARRAY_SIZE(rst_map)
+
+#define CGU_SYS_RST_CTRL 0x0
+#define CGU_IP_SW_RESET 0x0
+#define CGU_IP_SW_RESET_DELAY_SHIFT 16
+#define CGU_IP_SW_RESET_DELAY_MASK GENMASK(31, CGU_IP_SW_RESET_DELAY_SHIFT)
+#define CGU_IP_SW_RESET_DELAY 0
+#define CGU_IP_SW_RESET_RESET BIT(0)
+#define SW_RESET_TIMEOUT 10000
+
+static void hsdk_reset_config(struct hsdk_rst *rst, unsigned long id)
+{
+ writel(rst_map[id], rst->regs_ctl + CGU_SYS_RST_CTRL);
+}
+
+static int hsdk_reset_do(struct hsdk_rst *rst)
+{
+ u32 reg;
+
+ reg = readl(rst->regs_rst + CGU_IP_SW_RESET);
+ reg &= ~CGU_IP_SW_RESET_DELAY_MASK;
+ reg |= CGU_IP_SW_RESET_DELAY << CGU_IP_SW_RESET_DELAY_SHIFT;
+ reg |= CGU_IP_SW_RESET_RESET;
+ writel(reg, rst->regs_rst + CGU_IP_SW_RESET);
+
+ /* wait till reset bit is back to 0 */
+ return readl_poll_timeout_atomic(rst->regs_rst + CGU_IP_SW_RESET, reg,
+ !(reg & CGU_IP_SW_RESET_RESET), 5, SW_RESET_TIMEOUT);
+}
+
+static int hsdk_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct hsdk_rst *rst = to_hsdk_rst(rcdev);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&rst->lock, flags);
+ hsdk_reset_config(rst, id);
+ ret = hsdk_reset_do(rst);
+ spin_unlock_irqrestore(&rst->lock, flags);
+
+ return ret;
+}
+
+static const struct reset_control_ops hsdk_reset_ops = {
+ .reset = hsdk_reset_reset,
+ .deassert = hsdk_reset_reset,
+};
+
+static int hsdk_reset_probe(struct platform_device *pdev)
+{
+ struct hsdk_rst *rst;
+
+ rst = devm_kzalloc(&pdev->dev, sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return -ENOMEM;
+
+ rst->regs_ctl = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rst->regs_ctl))
+ return PTR_ERR(rst->regs_ctl);
+
+ rst->regs_rst = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(rst->regs_rst))
+ return PTR_ERR(rst->regs_rst);
+
+ spin_lock_init(&rst->lock);
+
+ rst->rcdev.owner = THIS_MODULE;
+ rst->rcdev.ops = &hsdk_reset_ops;
+ rst->rcdev.of_node = pdev->dev.of_node;
+ rst->rcdev.nr_resets = HSDK_MAX_RESETS;
+ rst->rcdev.of_reset_n_cells = 1;
+
+ return reset_controller_register(&rst->rcdev);
+}
+
+static const struct of_device_id hsdk_reset_dt_match[] = {
+ { .compatible = "snps,hsdk-reset" },
+ { },
+};
+
+static struct platform_driver hsdk_reset_driver = {
+ .probe = hsdk_reset_probe,
+ .driver = {
+ .name = "hsdk-reset",
+ .of_match_table = hsdk_reset_dt_match,
+ },
+};
+builtin_platform_driver(hsdk_reset_driver);
+
+MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
+MODULE_DESCRIPTION("Synopsys HSDK SDP reset driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-imx-scu.c b/drivers/reset/reset-imx-scu.c
new file mode 100644
index 000000000000..919fc29f944c
--- /dev/null
+++ b/drivers/reset/reset-imx-scu.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ * Frank Li <Frank.Li@nxp.com>
+ */
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+struct imx_scu_reset {
+ struct reset_controller_dev rc;
+ struct imx_sc_ipc *ipc_handle;
+};
+
+static struct imx_scu_reset *to_imx_scu(struct reset_controller_dev *rc)
+{
+ return container_of(rc, struct imx_scu_reset, rc);
+}
+
+struct imx_scu_id_map {
+ u32 resource_id;
+ u32 command_id;
+};
+
+static const struct imx_scu_id_map imx_scu_id_map[] = {
+ { IMX_SC_R_CSI_0, IMX_SC_C_MIPI_RESET },
+ { IMX_SC_R_CSI_1, IMX_SC_C_MIPI_RESET },
+};
+
+static int imx_scu_reset_assert(struct reset_controller_dev *rc, unsigned long id)
+{
+ struct imx_scu_reset *priv = to_imx_scu(rc);
+
+ return imx_sc_misc_set_control(priv->ipc_handle, imx_scu_id_map[id].resource_id,
+ imx_scu_id_map[id].command_id, true);
+}
+
+static const struct reset_control_ops imx_scu_reset_ops = {
+ .assert = imx_scu_reset_assert,
+};
+
+static int imx_scu_xlate(struct reset_controller_dev *rc, const struct of_phandle_args *reset_spec)
+{
+ int i;
+
+ for (i = 0; i < rc->nr_resets; i++)
+ if (reset_spec->args[0] == imx_scu_id_map[i].resource_id)
+ return i;
+
+ return -EINVAL;
+}
+
+static int imx_scu_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_scu_reset *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, &priv->rc);
+
+ ret = imx_scu_get_handle(&priv->ipc_handle);
+ if (ret)
+ return dev_err_probe(dev, ret, "sc_misc_MIPI get ipc handle failed!\n");
+
+ priv->rc.ops = &imx_scu_reset_ops;
+ priv->rc.owner = THIS_MODULE;
+ priv->rc.of_node = dev->of_node;
+ priv->rc.of_reset_n_cells = 1;
+ priv->rc.of_xlate = imx_scu_xlate;
+ priv->rc.nr_resets = ARRAY_SIZE(imx_scu_id_map);
+
+ return devm_reset_controller_register(dev, &priv->rc);
+}
+
+static const struct of_device_id imx_scu_reset_ids[] = {
+ { .compatible = "fsl,imx-scu-reset", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, imx_scu_reset_ids);
+
+static struct platform_driver imx_scu_reset_driver = {
+ .probe = imx_scu_reset_probe,
+ .driver = {
+ .name = "scu-reset",
+ .of_match_table = imx_scu_reset_ids,
+ },
+};
+module_platform_driver(imx_scu_reset_driver);
+
+MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
+MODULE_DESCRIPTION("i.MX scu reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
new file mode 100644
index 000000000000..dd01fe11c5cb
--- /dev/null
+++ b/drivers/reset/reset-imx7.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2017, Impinj, Inc.
+ *
+ * i.MX7 System Reset Controller (SRC) driver
+ *
+ * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/regmap.h>
+#include <dt-bindings/reset/imx7-reset.h>
+#include <dt-bindings/reset/imx8mq-reset.h>
+#include <dt-bindings/reset/imx8mp-reset.h>
+
+struct imx7_src_signal {
+ unsigned int offset, bit;
+};
+
+struct imx7_src_variant {
+ const struct imx7_src_signal *signals;
+ unsigned int signals_num;
+ struct reset_control_ops ops;
+};
+
+struct imx7_src {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+ const struct imx7_src_signal *signals;
+};
+
+enum imx7_src_registers {
+ SRC_A7RCR0 = 0x0004,
+ SRC_M4RCR = 0x000c,
+ SRC_ERCR = 0x0014,
+ SRC_HSICPHY_RCR = 0x001c,
+ SRC_USBOPHY1_RCR = 0x0020,
+ SRC_USBOPHY2_RCR = 0x0024,
+ SRC_MIPIPHY_RCR = 0x0028,
+ SRC_PCIEPHY_RCR = 0x002c,
+ SRC_DDRC_RCR = 0x1000,
+};
+
+static int imx7_reset_update(struct imx7_src *imx7src,
+ unsigned long id, unsigned int value)
+{
+ const struct imx7_src_signal *signal = &imx7src->signals[id];
+
+ return regmap_update_bits(imx7src->regmap,
+ signal->offset, signal->bit, value);
+}
+
+static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
+ [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) },
+ [IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) },
+ [IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) },
+ [IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) },
+ [IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) },
+ [IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) },
+ [IMX7_RESET_A7_ETM_RESET0] = { SRC_A7RCR0, BIT(12) },
+ [IMX7_RESET_A7_ETM_RESET1] = { SRC_A7RCR0, BIT(13) },
+ [IMX7_RESET_A7_SOC_DBG_RESET] = { SRC_A7RCR0, BIT(20) },
+ [IMX7_RESET_A7_L2RESET] = { SRC_A7RCR0, BIT(21) },
+ [IMX7_RESET_SW_M4C_RST] = { SRC_M4RCR, BIT(1) },
+ [IMX7_RESET_SW_M4P_RST] = { SRC_M4RCR, BIT(2) },
+ [IMX7_RESET_EIM_RST] = { SRC_ERCR, BIT(0) },
+ [IMX7_RESET_HSICPHY_PORT_RST] = { SRC_HSICPHY_RCR, BIT(1) },
+ [IMX7_RESET_USBPHY1_POR] = { SRC_USBOPHY1_RCR, BIT(0) },
+ [IMX7_RESET_USBPHY1_PORT_RST] = { SRC_USBOPHY1_RCR, BIT(1) },
+ [IMX7_RESET_USBPHY2_POR] = { SRC_USBOPHY2_RCR, BIT(0) },
+ [IMX7_RESET_USBPHY2_PORT_RST] = { SRC_USBOPHY2_RCR, BIT(1) },
+ [IMX7_RESET_MIPI_PHY_MRST] = { SRC_MIPIPHY_RCR, BIT(1) },
+ [IMX7_RESET_MIPI_PHY_SRST] = { SRC_MIPIPHY_RCR, BIT(2) },
+ [IMX7_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) },
+ [IMX7_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
+ [IMX7_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
+ [IMX7_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) },
+ [IMX7_RESET_DDRC_PRST] = { SRC_DDRC_RCR, BIT(0) },
+ [IMX7_RESET_DDRC_CORE_RST] = { SRC_DDRC_RCR, BIT(1) },
+};
+
+static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct imx7_src, rcdev);
+}
+
+static int imx7_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx7_src *imx7src = to_imx7_src(rcdev);
+ const unsigned int bit = imx7src->signals[id].bit;
+ unsigned int value = assert ? bit : 0;
+
+ switch (id) {
+ case IMX7_RESET_PCIEPHY:
+ /*
+ * wait for more than 10us to release phy g_rst and
+ * btnrst
+ */
+ if (!assert)
+ udelay(10);
+ break;
+
+ case IMX7_RESET_PCIE_CTRL_APPS_EN:
+ value = assert ? 0 : bit;
+ break;
+ }
+
+ return imx7_reset_update(imx7src, id, value);
+}
+
+static int imx7_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx7_reset_set(rcdev, id, true);
+}
+
+static int imx7_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx7_reset_set(rcdev, id, false);
+}
+
+static const struct imx7_src_variant variant_imx7 = {
+ .signals = imx7_src_signals,
+ .signals_num = ARRAY_SIZE(imx7_src_signals),
+ .ops = {
+ .assert = imx7_reset_assert,
+ .deassert = imx7_reset_deassert,
+ },
+};
+
+enum imx8mq_src_registers {
+ SRC_A53RCR0 = 0x0004,
+ SRC_HDMI_RCR = 0x0030,
+ SRC_DISP_RCR = 0x0034,
+ SRC_GPU_RCR = 0x0040,
+ SRC_VPU_RCR = 0x0044,
+ SRC_PCIE2_RCR = 0x0048,
+ SRC_MIPIPHY1_RCR = 0x004c,
+ SRC_MIPIPHY2_RCR = 0x0050,
+ SRC_DDRC2_RCR = 0x1004,
+};
+
+enum imx8mp_src_registers {
+ SRC_SUPERMIX_RCR = 0x0018,
+ SRC_AUDIOMIX_RCR = 0x001c,
+ SRC_MLMIX_RCR = 0x0028,
+ SRC_GPU2D_RCR = 0x0038,
+ SRC_GPU3D_RCR = 0x003c,
+ SRC_VPU_G1_RCR = 0x0048,
+ SRC_VPU_G2_RCR = 0x004c,
+ SRC_VPUVC8KE_RCR = 0x0050,
+ SRC_NOC_RCR = 0x0054,
+};
+
+static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = {
+ [IMX8MQ_RESET_A53_CORE_POR_RESET0] = { SRC_A53RCR0, BIT(0) },
+ [IMX8MQ_RESET_A53_CORE_POR_RESET1] = { SRC_A53RCR0, BIT(1) },
+ [IMX8MQ_RESET_A53_CORE_POR_RESET2] = { SRC_A53RCR0, BIT(2) },
+ [IMX8MQ_RESET_A53_CORE_POR_RESET3] = { SRC_A53RCR0, BIT(3) },
+ [IMX8MQ_RESET_A53_CORE_RESET0] = { SRC_A53RCR0, BIT(4) },
+ [IMX8MQ_RESET_A53_CORE_RESET1] = { SRC_A53RCR0, BIT(5) },
+ [IMX8MQ_RESET_A53_CORE_RESET2] = { SRC_A53RCR0, BIT(6) },
+ [IMX8MQ_RESET_A53_CORE_RESET3] = { SRC_A53RCR0, BIT(7) },
+ [IMX8MQ_RESET_A53_DBG_RESET0] = { SRC_A53RCR0, BIT(8) },
+ [IMX8MQ_RESET_A53_DBG_RESET1] = { SRC_A53RCR0, BIT(9) },
+ [IMX8MQ_RESET_A53_DBG_RESET2] = { SRC_A53RCR0, BIT(10) },
+ [IMX8MQ_RESET_A53_DBG_RESET3] = { SRC_A53RCR0, BIT(11) },
+ [IMX8MQ_RESET_A53_ETM_RESET0] = { SRC_A53RCR0, BIT(12) },
+ [IMX8MQ_RESET_A53_ETM_RESET1] = { SRC_A53RCR0, BIT(13) },
+ [IMX8MQ_RESET_A53_ETM_RESET2] = { SRC_A53RCR0, BIT(14) },
+ [IMX8MQ_RESET_A53_ETM_RESET3] = { SRC_A53RCR0, BIT(15) },
+ [IMX8MQ_RESET_A53_SOC_DBG_RESET] = { SRC_A53RCR0, BIT(20) },
+ [IMX8MQ_RESET_A53_L2RESET] = { SRC_A53RCR0, BIT(21) },
+ [IMX8MQ_RESET_SW_NON_SCLR_M4C_RST] = { SRC_M4RCR, BIT(0) },
+ [IMX8MQ_RESET_SW_M4C_RST] = { SRC_M4RCR, BIT(1) },
+ [IMX8MQ_RESET_SW_M4P_RST] = { SRC_M4RCR, BIT(2) },
+ [IMX8MQ_RESET_M4_ENABLE] = { SRC_M4RCR, BIT(3) },
+ [IMX8MQ_RESET_OTG1_PHY_RESET] = { SRC_USBOPHY1_RCR, BIT(0) },
+ [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) },
+ [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) },
+ [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) },
+ [IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) },
+ [IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) },
+ [IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) },
+ [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR,
+ BIT(2) | BIT(1) },
+ [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
+ [IMX8MQ_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
+ [IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) },
+ [IMX8MQ_RESET_HDMI_PHY_APB_RESET] = { SRC_HDMI_RCR, BIT(0) },
+ [IMX8MQ_RESET_DISP_RESET] = { SRC_DISP_RCR, BIT(0) },
+ [IMX8MQ_RESET_GPU_RESET] = { SRC_GPU_RCR, BIT(0) },
+ [IMX8MQ_RESET_VPU_RESET] = { SRC_VPU_RCR, BIT(0) },
+ [IMX8MQ_RESET_PCIEPHY2] = { SRC_PCIE2_RCR,
+ BIT(2) | BIT(1) },
+ [IMX8MQ_RESET_PCIEPHY2_PERST] = { SRC_PCIE2_RCR, BIT(3) },
+ [IMX8MQ_RESET_PCIE2_CTRL_APPS_EN] = { SRC_PCIE2_RCR, BIT(6) },
+ [IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF] = { SRC_PCIE2_RCR, BIT(11) },
+ [IMX8MQ_RESET_MIPI_CSI1_CORE_RESET] = { SRC_MIPIPHY1_RCR, BIT(0) },
+ [IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET] = { SRC_MIPIPHY1_RCR, BIT(1) },
+ [IMX8MQ_RESET_MIPI_CSI1_ESC_RESET] = { SRC_MIPIPHY1_RCR, BIT(2) },
+ [IMX8MQ_RESET_MIPI_CSI2_CORE_RESET] = { SRC_MIPIPHY2_RCR, BIT(0) },
+ [IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET] = { SRC_MIPIPHY2_RCR, BIT(1) },
+ [IMX8MQ_RESET_MIPI_CSI2_ESC_RESET] = { SRC_MIPIPHY2_RCR, BIT(2) },
+ [IMX8MQ_RESET_DDRC1_PRST] = { SRC_DDRC_RCR, BIT(0) },
+ [IMX8MQ_RESET_DDRC1_CORE_RESET] = { SRC_DDRC_RCR, BIT(1) },
+ [IMX8MQ_RESET_DDRC1_PHY_RESET] = { SRC_DDRC_RCR, BIT(2) },
+ [IMX8MQ_RESET_DDRC2_PHY_RESET] = { SRC_DDRC2_RCR, BIT(0) },
+ [IMX8MQ_RESET_DDRC2_CORE_RESET] = { SRC_DDRC2_RCR, BIT(1) },
+ [IMX8MQ_RESET_DDRC2_PRST] = { SRC_DDRC2_RCR, BIT(2) },
+};
+
+static int imx8mq_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx7_src *imx7src = to_imx7_src(rcdev);
+ const unsigned int bit = imx7src->signals[id].bit;
+ unsigned int value = assert ? bit : 0;
+
+ switch (id) {
+ case IMX8MQ_RESET_PCIEPHY:
+ case IMX8MQ_RESET_PCIEPHY2:
+ /*
+ * wait for more than 10us to release phy g_rst and
+ * btnrst
+ */
+ if (!assert)
+ udelay(10);
+ break;
+
+ case IMX8MQ_RESET_PCIE_CTRL_APPS_EN:
+ case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN:
+ case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N:
+ case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N:
+ case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N:
+ case IMX8MQ_RESET_MIPI_DSI_RESET_N:
+ case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N:
+ case IMX8MQ_RESET_M4_ENABLE:
+ value = assert ? 0 : bit;
+ break;
+ }
+
+ return imx7_reset_update(imx7src, id, value);
+}
+
+static int imx8mq_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mq_reset_set(rcdev, id, true);
+}
+
+static int imx8mq_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mq_reset_set(rcdev, id, false);
+}
+
+static const struct imx7_src_variant variant_imx8mq = {
+ .signals = imx8mq_src_signals,
+ .signals_num = ARRAY_SIZE(imx8mq_src_signals),
+ .ops = {
+ .assert = imx8mq_reset_assert,
+ .deassert = imx8mq_reset_deassert,
+ },
+};
+
+static const struct imx7_src_signal imx8mp_src_signals[IMX8MP_RESET_NUM] = {
+ [IMX8MP_RESET_A53_CORE_POR_RESET0] = { SRC_A53RCR0, BIT(0) },
+ [IMX8MP_RESET_A53_CORE_POR_RESET1] = { SRC_A53RCR0, BIT(1) },
+ [IMX8MP_RESET_A53_CORE_POR_RESET2] = { SRC_A53RCR0, BIT(2) },
+ [IMX8MP_RESET_A53_CORE_POR_RESET3] = { SRC_A53RCR0, BIT(3) },
+ [IMX8MP_RESET_A53_CORE_RESET0] = { SRC_A53RCR0, BIT(4) },
+ [IMX8MP_RESET_A53_CORE_RESET1] = { SRC_A53RCR0, BIT(5) },
+ [IMX8MP_RESET_A53_CORE_RESET2] = { SRC_A53RCR0, BIT(6) },
+ [IMX8MP_RESET_A53_CORE_RESET3] = { SRC_A53RCR0, BIT(7) },
+ [IMX8MP_RESET_A53_DBG_RESET0] = { SRC_A53RCR0, BIT(8) },
+ [IMX8MP_RESET_A53_DBG_RESET1] = { SRC_A53RCR0, BIT(9) },
+ [IMX8MP_RESET_A53_DBG_RESET2] = { SRC_A53RCR0, BIT(10) },
+ [IMX8MP_RESET_A53_DBG_RESET3] = { SRC_A53RCR0, BIT(11) },
+ [IMX8MP_RESET_A53_ETM_RESET0] = { SRC_A53RCR0, BIT(12) },
+ [IMX8MP_RESET_A53_ETM_RESET1] = { SRC_A53RCR0, BIT(13) },
+ [IMX8MP_RESET_A53_ETM_RESET2] = { SRC_A53RCR0, BIT(14) },
+ [IMX8MP_RESET_A53_ETM_RESET3] = { SRC_A53RCR0, BIT(15) },
+ [IMX8MP_RESET_A53_SOC_DBG_RESET] = { SRC_A53RCR0, BIT(20) },
+ [IMX8MP_RESET_A53_L2RESET] = { SRC_A53RCR0, BIT(21) },
+ [IMX8MP_RESET_SW_NON_SCLR_M7C_RST] = { SRC_M4RCR, BIT(0) },
+ [IMX8MP_RESET_OTG1_PHY_RESET] = { SRC_USBOPHY1_RCR, BIT(0) },
+ [IMX8MP_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) },
+ [IMX8MP_RESET_SUPERMIX_RESET] = { SRC_SUPERMIX_RCR, BIT(0) },
+ [IMX8MP_RESET_AUDIOMIX_RESET] = { SRC_AUDIOMIX_RCR, BIT(0) },
+ [IMX8MP_RESET_MLMIX_RESET] = { SRC_MLMIX_RCR, BIT(0) },
+ [IMX8MP_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) },
+ [IMX8MP_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
+ [IMX8MP_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) },
+ [IMX8MP_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) },
+ [IMX8MP_RESET_HDMI_PHY_APB_RESET] = { SRC_HDMI_RCR, BIT(0) },
+ [IMX8MP_RESET_MEDIA_RESET] = { SRC_DISP_RCR, BIT(0) },
+ [IMX8MP_RESET_GPU2D_RESET] = { SRC_GPU2D_RCR, BIT(0) },
+ [IMX8MP_RESET_GPU3D_RESET] = { SRC_GPU3D_RCR, BIT(0) },
+ [IMX8MP_RESET_GPU_RESET] = { SRC_GPU_RCR, BIT(0) },
+ [IMX8MP_RESET_VPU_RESET] = { SRC_VPU_RCR, BIT(0) },
+ [IMX8MP_RESET_VPU_G1_RESET] = { SRC_VPU_G1_RCR, BIT(0) },
+ [IMX8MP_RESET_VPU_G2_RESET] = { SRC_VPU_G2_RCR, BIT(0) },
+ [IMX8MP_RESET_VPUVC8KE_RESET] = { SRC_VPUVC8KE_RCR, BIT(0) },
+ [IMX8MP_RESET_NOC_RESET] = { SRC_NOC_RCR, BIT(0) },
+};
+
+static int imx8mp_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx7_src *imx7src = to_imx7_src(rcdev);
+ const unsigned int bit = imx7src->signals[id].bit;
+ unsigned int value = assert ? bit : 0;
+
+ switch (id) {
+ case IMX8MP_RESET_PCIEPHY:
+ /*
+ * wait for more than 10us to release phy g_rst and
+ * btnrst
+ */
+ if (!assert)
+ udelay(10);
+ break;
+
+ case IMX8MP_RESET_PCIE_CTRL_APPS_EN:
+ case IMX8MP_RESET_PCIEPHY_PERST:
+ value = assert ? 0 : bit;
+ break;
+ }
+
+ return imx7_reset_update(imx7src, id, value);
+}
+
+static int imx8mp_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_reset_set(rcdev, id, true);
+}
+
+static int imx8mp_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_reset_set(rcdev, id, false);
+}
+
+static const struct imx7_src_variant variant_imx8mp = {
+ .signals = imx8mp_src_signals,
+ .signals_num = ARRAY_SIZE(imx8mp_src_signals),
+ .ops = {
+ .assert = imx8mp_reset_assert,
+ .deassert = imx8mp_reset_deassert,
+ },
+};
+
+static int imx7_reset_probe(struct platform_device *pdev)
+{
+ struct imx7_src *imx7src;
+ struct device *dev = &pdev->dev;
+ struct regmap_config config = { .name = "src" };
+ const struct imx7_src_variant *variant = of_device_get_match_data(dev);
+
+ imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL);
+ if (!imx7src)
+ return -ENOMEM;
+
+ imx7src->signals = variant->signals;
+ imx7src->regmap = syscon_node_to_regmap(dev->of_node);
+ if (IS_ERR(imx7src->regmap)) {
+ dev_err(dev, "Unable to get imx7-src regmap");
+ return PTR_ERR(imx7src->regmap);
+ }
+ regmap_attach_dev(dev, imx7src->regmap, &config);
+
+ imx7src->rcdev.owner = THIS_MODULE;
+ imx7src->rcdev.nr_resets = variant->signals_num;
+ imx7src->rcdev.ops = &variant->ops;
+ imx7src->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &imx7src->rcdev);
+}
+
+static const struct of_device_id imx7_reset_dt_ids[] = {
+ { .compatible = "fsl,imx7d-src", .data = &variant_imx7 },
+ { .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq },
+ { .compatible = "fsl,imx8mp-src", .data = &variant_imx8mp },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx7_reset_dt_ids);
+
+static struct platform_driver imx7_reset_driver = {
+ .probe = imx7_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = imx7_reset_dt_ids,
+ },
+};
+module_platform_driver(imx7_reset_driver);
+
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("NXP i.MX7 reset driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c
new file mode 100644
index 000000000000..eceb37ff5dc5
--- /dev/null
+++ b/drivers/reset/reset-imx8mp-audiomix.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <dt-bindings/reset/imx8mp-reset-audiomix.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/reset-controller.h>
+
+#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET 0x200
+#define IMX8MP_AUDIOMIX_EARC_RESET_MASK BIT(0)
+#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(1)
+
+#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET 0x108
+#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK BIT(5)
+
+struct imx8mp_reset_map {
+ unsigned int offset;
+ unsigned int mask;
+ bool active_low;
+};
+
+static const struct imx8mp_reset_map reset_map[] = {
+ [IMX8MP_AUDIOMIX_EARC_RESET] = {
+ .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
+ .mask = IMX8MP_AUDIOMIX_EARC_RESET_MASK,
+ .active_low = true,
+ },
+ [IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
+ .offset = IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
+ .mask = IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
+ .active_low = true,
+ },
+ [IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
+ .offset = IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
+ .mask = IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
+ .active_low = false,
+ },
+};
+
+struct imx8mp_audiomix_reset {
+ struct reset_controller_dev rcdev;
+ spinlock_t lock; /* protect register read-modify-write cycle */
+ void __iomem *base;
+};
+
+static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct imx8mp_audiomix_reset, rcdev);
+}
+
+static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
+ void __iomem *reg_addr = priv->base;
+ unsigned int mask, offset, active_low;
+ unsigned long reg, flags;
+
+ mask = reset_map[id].mask;
+ offset = reset_map[id].offset;
+ active_low = reset_map[id].active_low;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ reg = readl(reg_addr + offset);
+ if (active_low ^ assert)
+ reg |= mask;
+ else
+ reg &= ~mask;
+ writel(reg, reg_addr + offset);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_audiomix_update(rcdev, id, true);
+}
+
+static int imx8mp_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return imx8mp_audiomix_update(rcdev, id, false);
+}
+
+static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
+ .assert = imx8mp_audiomix_reset_assert,
+ .deassert = imx8mp_audiomix_reset_deassert,
+};
+
+static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct imx8mp_audiomix_reset *priv;
+ struct device *dev = &adev->dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
+ priv->rcdev.ops = &imx8mp_audiomix_reset_ops;
+ priv->rcdev.of_node = dev->parent->of_node;
+ priv->rcdev.dev = dev;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->base = of_iomap(dev->parent->of_node, 0);
+ if (!priv->base)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_reset_controller_register(dev, &priv->rcdev);
+ if (ret)
+ goto out_unmap;
+
+ return 0;
+
+out_unmap:
+ iounmap(priv->base);
+ return ret;
+}
+
+static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
+{
+ struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
+
+ iounmap(priv->base);
+}
+
+static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
+ {
+ .name = "clk_imx8mp_audiomix.reset",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
+
+static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
+ .probe = imx8mp_audiomix_reset_probe,
+ .remove = imx8mp_audiomix_reset_remove,
+ .id_table = imx8mp_audiomix_reset_ids,
+};
+
+module_auxiliary_driver(imx8mp_audiomix_reset_driver);
+
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-intel-gw.c b/drivers/reset/reset-intel-gw.c
new file mode 100644
index 000000000000..a5ce3350cb5e
--- /dev/null
+++ b/drivers/reset/reset-intel-gw.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Intel Corporation.
+ * Lei Chuanhua <Chuanhua.lei@intel.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#define RCU_RST_STAT 0x0024
+#define RCU_RST_REQ 0x0048
+
+#define REG_OFFSET_MASK GENMASK(31, 16)
+#define BIT_OFFSET_MASK GENMASK(15, 8)
+#define STAT_BIT_OFFSET_MASK GENMASK(7, 0)
+
+#define to_reset_data(x) container_of(x, struct intel_reset_data, rcdev)
+
+struct intel_reset_soc {
+ bool legacy;
+ u32 reset_cell_count;
+};
+
+struct intel_reset_data {
+ struct reset_controller_dev rcdev;
+ struct notifier_block restart_nb;
+ const struct intel_reset_soc *soc_data;
+ struct regmap *regmap;
+ struct device *dev;
+ u32 reboot_id;
+};
+
+static const struct regmap_config intel_rcu_regmap_config = {
+ .name = "intel-reset",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+/*
+ * Reset status register offset relative to
+ * the reset control register(X) is X + 4
+ */
+static u32 id_to_reg_and_bit_offsets(struct intel_reset_data *data,
+ unsigned long id, u32 *rst_req,
+ u32 *req_bit, u32 *stat_bit)
+{
+ *rst_req = FIELD_GET(REG_OFFSET_MASK, id);
+ *req_bit = FIELD_GET(BIT_OFFSET_MASK, id);
+
+ if (data->soc_data->legacy)
+ *stat_bit = FIELD_GET(STAT_BIT_OFFSET_MASK, id);
+ else
+ *stat_bit = *req_bit;
+
+ if (data->soc_data->legacy && *rst_req == RCU_RST_REQ)
+ return RCU_RST_STAT;
+ else
+ return *rst_req + 0x4;
+}
+
+static int intel_set_clr_bits(struct intel_reset_data *data, unsigned long id,
+ bool set)
+{
+ u32 rst_req, req_bit, rst_stat, stat_bit, val;
+ int ret;
+
+ rst_stat = id_to_reg_and_bit_offsets(data, id, &rst_req,
+ &req_bit, &stat_bit);
+
+ val = set ? BIT(req_bit) : 0;
+ ret = regmap_update_bits(data->regmap, rst_req, BIT(req_bit), val);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(data->regmap, rst_stat, val,
+ set == !!(val & BIT(stat_bit)), 20,
+ 200);
+}
+
+static int intel_assert_device(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct intel_reset_data *data = to_reset_data(rcdev);
+ int ret;
+
+ ret = intel_set_clr_bits(data, id, true);
+ if (ret)
+ dev_err(data->dev, "Reset assert failed %d\n", ret);
+
+ return ret;
+}
+
+static int intel_deassert_device(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct intel_reset_data *data = to_reset_data(rcdev);
+ int ret;
+
+ ret = intel_set_clr_bits(data, id, false);
+ if (ret)
+ dev_err(data->dev, "Reset deassert failed %d\n", ret);
+
+ return ret;
+}
+
+static int intel_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct intel_reset_data *data = to_reset_data(rcdev);
+ u32 rst_req, req_bit, rst_stat, stat_bit, val;
+ int ret;
+
+ rst_stat = id_to_reg_and_bit_offsets(data, id, &rst_req,
+ &req_bit, &stat_bit);
+ ret = regmap_read(data->regmap, rst_stat, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(stat_bit));
+}
+
+static const struct reset_control_ops intel_reset_ops = {
+ .assert = intel_assert_device,
+ .deassert = intel_deassert_device,
+ .status = intel_reset_status,
+};
+
+static int intel_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *spec)
+{
+ struct intel_reset_data *data = to_reset_data(rcdev);
+ u32 id;
+
+ if (spec->args[1] > 31)
+ return -EINVAL;
+
+ id = FIELD_PREP(REG_OFFSET_MASK, spec->args[0]);
+ id |= FIELD_PREP(BIT_OFFSET_MASK, spec->args[1]);
+
+ if (data->soc_data->legacy) {
+ if (spec->args[2] > 31)
+ return -EINVAL;
+
+ id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, spec->args[2]);
+ }
+
+ return id;
+}
+
+static int intel_reset_restart_handler(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct intel_reset_data *reset_data;
+
+ reset_data = container_of(nb, struct intel_reset_data, restart_nb);
+ intel_assert_device(&reset_data->rcdev, reset_data->reboot_id);
+
+ return NOTIFY_DONE;
+}
+
+static int intel_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct intel_reset_data *data;
+ void __iomem *base;
+ u32 rb_id[3];
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->soc_data = of_device_get_match_data(dev);
+ if (!data->soc_data)
+ return -ENODEV;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ data->regmap = devm_regmap_init_mmio(dev, base,
+ &intel_rcu_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "regmap initialization failed\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = device_property_read_u32_array(dev, "intel,global-reset", rb_id,
+ data->soc_data->reset_cell_count);
+ if (ret) {
+ dev_err(dev, "Failed to get global reset offset!\n");
+ return ret;
+ }
+
+ data->dev = dev;
+ data->rcdev.of_node = np;
+ data->rcdev.owner = dev->driver->owner;
+ data->rcdev.ops = &intel_reset_ops;
+ data->rcdev.of_xlate = intel_reset_xlate;
+ data->rcdev.of_reset_n_cells = data->soc_data->reset_cell_count;
+ ret = devm_reset_controller_register(&pdev->dev, &data->rcdev);
+ if (ret)
+ return ret;
+
+ data->reboot_id = FIELD_PREP(REG_OFFSET_MASK, rb_id[0]);
+ data->reboot_id |= FIELD_PREP(BIT_OFFSET_MASK, rb_id[1]);
+
+ if (data->soc_data->legacy)
+ data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]);
+
+ data->restart_nb.notifier_call = intel_reset_restart_handler;
+ data->restart_nb.priority = 128;
+ register_restart_handler(&data->restart_nb);
+
+ return 0;
+}
+
+static const struct intel_reset_soc xrx200_data = {
+ .legacy = true,
+ .reset_cell_count = 3,
+};
+
+static const struct intel_reset_soc lgm_data = {
+ .legacy = false,
+ .reset_cell_count = 2,
+};
+
+static const struct of_device_id intel_reset_match[] = {
+ { .compatible = "intel,rcu-lgm", .data = &lgm_data },
+ { .compatible = "intel,rcu-xrx200", .data = &xrx200_data },
+ {}
+};
+
+static struct platform_driver intel_reset_driver = {
+ .probe = intel_reset_probe,
+ .driver = {
+ .name = "intel-reset",
+ .of_match_table = intel_reset_match,
+ },
+};
+
+static int __init intel_reset_init(void)
+{
+ return platform_driver_register(&intel_reset_driver);
+}
+
+/*
+ * RCU is system core entity which is in Always On Domain whose clocks
+ * or resource initialization happens in system core initialization.
+ * Also, it is required for most of the platform or architecture
+ * specific devices to perform reset operation as part of initialization.
+ * So perform RCU as post core initialization.
+ */
+postcore_initcall(intel_reset_init);
diff --git a/drivers/reset/reset-k210.c b/drivers/reset/reset-k210.c
new file mode 100644
index 000000000000..e77e4cca377d
--- /dev/null
+++ b/drivers/reset/reset-k210.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ */
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <soc/canaan/k210-sysctl.h>
+
+#include <dt-bindings/reset/k210-rst.h>
+
+#define K210_RST_MASK 0x27FFFFFF
+
+struct k210_rst {
+ struct regmap *map;
+ struct reset_controller_dev rcdev;
+};
+
+static inline struct k210_rst *
+to_k210_rst(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct k210_rst, rcdev);
+}
+
+static inline int k210_rst_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct k210_rst *ksr = to_k210_rst(rcdev);
+
+ return regmap_update_bits(ksr->map, K210_SYSCTL_PERI_RESET, BIT(id), 1);
+}
+
+static inline int k210_rst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct k210_rst *ksr = to_k210_rst(rcdev);
+
+ return regmap_update_bits(ksr->map, K210_SYSCTL_PERI_RESET, BIT(id), 0);
+}
+
+static int k210_rst_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = k210_rst_assert(rcdev, id);
+ if (ret == 0) {
+ udelay(10);
+ ret = k210_rst_deassert(rcdev, id);
+ }
+
+ return ret;
+}
+
+static int k210_rst_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct k210_rst *ksr = to_k210_rst(rcdev);
+ u32 reg, bit = BIT(id);
+ int ret;
+
+ ret = regmap_read(ksr->map, K210_SYSCTL_PERI_RESET, &reg);
+ if (ret)
+ return ret;
+
+ return reg & bit;
+}
+
+static int k210_rst_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned long id = reset_spec->args[0];
+
+ if (!(BIT(id) & K210_RST_MASK))
+ return -EINVAL;
+
+ return id;
+}
+
+static const struct reset_control_ops k210_rst_ops = {
+ .assert = k210_rst_assert,
+ .deassert = k210_rst_deassert,
+ .reset = k210_rst_reset,
+ .status = k210_rst_status,
+};
+
+static int k210_rst_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *parent_np;
+ struct k210_rst *ksr;
+
+ dev_info(dev, "K210 reset controller\n");
+
+ ksr = devm_kzalloc(dev, sizeof(*ksr), GFP_KERNEL);
+ if (!ksr)
+ return -ENOMEM;
+
+ parent_np = of_get_parent(dev->of_node);
+ ksr->map = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(ksr->map))
+ return PTR_ERR(ksr->map);
+
+ ksr->rcdev.owner = THIS_MODULE;
+ ksr->rcdev.dev = dev;
+ ksr->rcdev.of_node = dev->of_node;
+ ksr->rcdev.ops = &k210_rst_ops;
+ ksr->rcdev.nr_resets = fls(K210_RST_MASK);
+ ksr->rcdev.of_reset_n_cells = 1;
+ ksr->rcdev.of_xlate = k210_rst_xlate;
+
+ return devm_reset_controller_register(dev, &ksr->rcdev);
+}
+
+static const struct of_device_id k210_rst_dt_ids[] = {
+ { .compatible = "canaan,k210-rst" },
+ { /* sentinel */ },
+};
+
+static struct platform_driver k210_rst_driver = {
+ .probe = k210_rst_probe,
+ .driver = {
+ .name = "k210-rst",
+ .of_match_table = k210_rst_dt_ids,
+ },
+};
+builtin_platform_driver(k210_rst_driver);
diff --git a/drivers/reset/reset-k230.c b/drivers/reset/reset-k230.c
new file mode 100644
index 000000000000..c81045bb4a14
--- /dev/null
+++ b/drivers/reset/reset-k230.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2022-2024 Canaan Bright Sight Co., Ltd
+ * Copyright (C) 2024-2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ *
+ * The reset management module in the K230 SoC provides reset time control
+ * registers. For RST_TYPE_CPU0, RST_TYPE_CPU1 and RST_TYPE_SW_DONE, the period
+ * during which reset is applied or removed while the clock is stopped can be
+ * set up to 15 * 0.25 = 3.75 µs. For RST_TYPE_HW_DONE, that period can be set
+ * up to 255 * 0.25 = 63.75 µs. For RST_TYPE_FLUSH, the reset bit is
+ * automatically cleared by hardware when flush completes.
+ *
+ * Although this driver does not configure the reset time registers, delays have
+ * been added to the assert, deassert, and reset operations to cover the maximum
+ * reset time. Some reset types include done bits whose toggle does not
+ * unambiguously signal whether hardware reset removal or clock-stop period
+ * expiration occurred first. Delays are therefore retained for types with done
+ * bits to ensure safe timing.
+ *
+ * Reference: K230 Technical Reference Manual V0.3.1
+ * https://kendryte-download.canaan-creative.com/developer/k230/HDK/K230%E7%A1%AC%E4%BB%B6%E6%96%87%E6%A1%A3/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ */
+
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+#include <dt-bindings/reset/canaan,k230-rst.h>
+
+/**
+ * enum k230_rst_type - K230 reset types
+ * @RST_TYPE_CPU0: Reset type for CPU0
+ * Automatically clears, has write enable and done bit, active high
+ * @RST_TYPE_CPU1: Reset type for CPU1
+ * Manually clears, has write enable and done bit, active high
+ * @RST_TYPE_FLUSH: Reset type for CPU L2 cache flush
+ * Automatically clears, has write enable, no done bit, active high
+ * @RST_TYPE_HW_DONE: Reset type for hardware auto clear
+ * Automatically clears, no write enable, has done bit, active high
+ * @RST_TYPE_SW_DONE: Reset type for software manual clear
+ * Manually clears, no write enable and done bit,
+ * active high if ID is RST_SPI2AXI, otherwise active low
+ */
+enum k230_rst_type {
+ RST_TYPE_CPU0,
+ RST_TYPE_CPU1,
+ RST_TYPE_FLUSH,
+ RST_TYPE_HW_DONE,
+ RST_TYPE_SW_DONE,
+};
+
+struct k230_rst_map {
+ u32 offset;
+ enum k230_rst_type type;
+ u32 done;
+ u32 reset;
+};
+
+struct k230_rst {
+ struct reset_controller_dev rcdev;
+ void __iomem *base;
+ /* protect register read-modify-write */
+ spinlock_t lock;
+};
+
+static const struct k230_rst_map k230_resets[] = {
+ [RST_CPU0] = { 0x4, RST_TYPE_CPU0, BIT(12), BIT(0) },
+ [RST_CPU1] = { 0xc, RST_TYPE_CPU1, BIT(12), BIT(0) },
+ [RST_CPU0_FLUSH] = { 0x4, RST_TYPE_FLUSH, 0, BIT(4) },
+ [RST_CPU1_FLUSH] = { 0xc, RST_TYPE_FLUSH, 0, BIT(4) },
+ [RST_AI] = { 0x14, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_VPU] = { 0x1c, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_HISYS] = { 0x2c, RST_TYPE_HW_DONE, BIT(4), BIT(0) },
+ [RST_HISYS_AHB] = { 0x2c, RST_TYPE_HW_DONE, BIT(5), BIT(1) },
+ [RST_SDIO0] = { 0x34, RST_TYPE_HW_DONE, BIT(28), BIT(0) },
+ [RST_SDIO1] = { 0x34, RST_TYPE_HW_DONE, BIT(29), BIT(1) },
+ [RST_SDIO_AXI] = { 0x34, RST_TYPE_HW_DONE, BIT(30), BIT(2) },
+ [RST_USB0] = { 0x3c, RST_TYPE_HW_DONE, BIT(28), BIT(0) },
+ [RST_USB1] = { 0x3c, RST_TYPE_HW_DONE, BIT(29), BIT(1) },
+ [RST_USB0_AHB] = { 0x3c, RST_TYPE_HW_DONE, BIT(30), BIT(0) },
+ [RST_USB1_AHB] = { 0x3c, RST_TYPE_HW_DONE, BIT(31), BIT(1) },
+ [RST_SPI0] = { 0x44, RST_TYPE_HW_DONE, BIT(28), BIT(0) },
+ [RST_SPI1] = { 0x44, RST_TYPE_HW_DONE, BIT(29), BIT(1) },
+ [RST_SPI2] = { 0x44, RST_TYPE_HW_DONE, BIT(30), BIT(2) },
+ [RST_SEC] = { 0x4c, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_PDMA] = { 0x54, RST_TYPE_HW_DONE, BIT(28), BIT(0) },
+ [RST_SDMA] = { 0x54, RST_TYPE_HW_DONE, BIT(29), BIT(1) },
+ [RST_DECOMPRESS] = { 0x5c, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_SRAM] = { 0x64, RST_TYPE_HW_DONE, BIT(28), BIT(0) },
+ [RST_SHRM_AXIM] = { 0x64, RST_TYPE_HW_DONE, BIT(30), BIT(2) },
+ [RST_SHRM_AXIS] = { 0x64, RST_TYPE_HW_DONE, BIT(31), BIT(3) },
+ [RST_NONAI2D] = { 0x6c, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_MCTL] = { 0x74, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_ISP] = { 0x80, RST_TYPE_HW_DONE, BIT(29), BIT(6) },
+ [RST_ISP_DW] = { 0x80, RST_TYPE_HW_DONE, BIT(28), BIT(5) },
+ [RST_DPU] = { 0x88, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_DISP] = { 0x90, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_GPU] = { 0x98, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_AUDIO] = { 0xa4, RST_TYPE_HW_DONE, BIT(31), BIT(0) },
+ [RST_TIMER0] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(0) },
+ [RST_TIMER1] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(1) },
+ [RST_TIMER2] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(2) },
+ [RST_TIMER3] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(3) },
+ [RST_TIMER4] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(4) },
+ [RST_TIMER5] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(5) },
+ [RST_TIMER_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(6) },
+ [RST_HDI] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(7) },
+ [RST_WDT0] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(12) },
+ [RST_WDT1] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(13) },
+ [RST_WDT0_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(14) },
+ [RST_WDT1_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(15) },
+ [RST_TS_APB] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(16) },
+ [RST_MAILBOX] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(17) },
+ [RST_STC] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(18) },
+ [RST_PMU] = { 0x20, RST_TYPE_SW_DONE, 0, BIT(19) },
+ [RST_LOSYS_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(0) },
+ [RST_UART0] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(1) },
+ [RST_UART1] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(2) },
+ [RST_UART2] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(3) },
+ [RST_UART3] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(4) },
+ [RST_UART4] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(5) },
+ [RST_I2C0] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(6) },
+ [RST_I2C1] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(7) },
+ [RST_I2C2] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(8) },
+ [RST_I2C3] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(9) },
+ [RST_I2C4] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(10) },
+ [RST_JAMLINK0_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(11) },
+ [RST_JAMLINK1_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(12) },
+ [RST_JAMLINK2_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(13) },
+ [RST_JAMLINK3_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(14) },
+ [RST_CODEC_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(17) },
+ [RST_GPIO_DB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(18) },
+ [RST_GPIO_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(19) },
+ [RST_ADC] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(20) },
+ [RST_ADC_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(21) },
+ [RST_PWM_APB] = { 0x24, RST_TYPE_SW_DONE, 0, BIT(22) },
+ [RST_SHRM_APB] = { 0x64, RST_TYPE_SW_DONE, 0, BIT(1) },
+ [RST_CSI0] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(0) },
+ [RST_CSI1] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(1) },
+ [RST_CSI2] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(2) },
+ [RST_CSI_DPHY] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(3) },
+ [RST_ISP_AHB] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(4) },
+ [RST_M0] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(7) },
+ [RST_M1] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(8) },
+ [RST_M2] = { 0x80, RST_TYPE_SW_DONE, 0, BIT(9) },
+ [RST_SPI2AXI] = { 0xa8, RST_TYPE_SW_DONE, 0, BIT(0) }
+};
+
+static inline struct k230_rst *to_k230_rst(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct k230_rst, rcdev);
+}
+
+static void k230_rst_clear_done(struct k230_rst *rstc, unsigned long id,
+ bool write_en)
+{
+ const struct k230_rst_map *rmap = &k230_resets[id];
+ u32 reg;
+
+ guard(spinlock_irqsave)(&rstc->lock);
+
+ reg = readl(rstc->base + rmap->offset);
+ reg |= rmap->done; /* write 1 to clear */
+ if (write_en)
+ reg |= rmap->done << 16;
+ writel(reg, rstc->base + rmap->offset);
+}
+
+static int k230_rst_wait_and_clear_done(struct k230_rst *rstc, unsigned long id,
+ bool write_en)
+{
+ const struct k230_rst_map *rmap = &k230_resets[id];
+ u32 reg;
+ int ret;
+
+ ret = readl_poll_timeout(rstc->base + rmap->offset, reg,
+ reg & rmap->done, 10, 1000);
+ if (ret) {
+ dev_err(rstc->rcdev.dev, "Wait for reset done timeout\n");
+ return ret;
+ }
+
+ k230_rst_clear_done(rstc, id, write_en);
+
+ return 0;
+}
+
+static void k230_rst_update(struct k230_rst *rstc, unsigned long id,
+ bool assert, bool write_en, bool active_low)
+{
+ const struct k230_rst_map *rmap = &k230_resets[id];
+ u32 reg;
+
+ guard(spinlock_irqsave)(&rstc->lock);
+
+ reg = readl(rstc->base + rmap->offset);
+ if (assert ^ active_low)
+ reg |= rmap->reset;
+ else
+ reg &= ~rmap->reset;
+ if (write_en)
+ reg |= rmap->reset << 16;
+ writel(reg, rstc->base + rmap->offset);
+}
+
+static int k230_rst_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct k230_rst *rstc = to_k230_rst(rcdev);
+
+ switch (k230_resets[id].type) {
+ case RST_TYPE_CPU1:
+ k230_rst_update(rstc, id, true, true, false);
+ break;
+ case RST_TYPE_SW_DONE:
+ k230_rst_update(rstc, id, true, false,
+ id == RST_SPI2AXI ? false : true);
+ break;
+ case RST_TYPE_CPU0:
+ case RST_TYPE_FLUSH:
+ case RST_TYPE_HW_DONE:
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * The time period when reset is applied but the clock is stopped for
+ * RST_TYPE_CPU1 and RST_TYPE_SW_DONE can be set up to 3.75us. Delay
+ * 10us to ensure proper reset timing.
+ */
+ udelay(10);
+
+ return 0;
+}
+
+static int k230_rst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct k230_rst *rstc = to_k230_rst(rcdev);
+ int ret = 0;
+
+ switch (k230_resets[id].type) {
+ case RST_TYPE_CPU1:
+ k230_rst_update(rstc, id, false, true, false);
+ ret = k230_rst_wait_and_clear_done(rstc, id, true);
+ break;
+ case RST_TYPE_SW_DONE:
+ k230_rst_update(rstc, id, false, false,
+ id == RST_SPI2AXI ? false : true);
+ break;
+ case RST_TYPE_CPU0:
+ case RST_TYPE_FLUSH:
+ case RST_TYPE_HW_DONE:
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * The time period when reset is removed but the clock is stopped for
+ * RST_TYPE_CPU1 and RST_TYPE_SW_DONE can be set up to 3.75us. Delay
+ * 10us to ensure proper reset timing.
+ */
+ udelay(10);
+
+ return ret;
+}
+
+static int k230_rst_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct k230_rst *rstc = to_k230_rst(rcdev);
+ const struct k230_rst_map *rmap = &k230_resets[id];
+ u32 reg;
+ int ret = 0;
+
+ switch (rmap->type) {
+ case RST_TYPE_CPU0:
+ k230_rst_clear_done(rstc, id, true);
+ k230_rst_update(rstc, id, true, true, false);
+ ret = k230_rst_wait_and_clear_done(rstc, id, true);
+
+ /*
+ * The time period when reset is applied and removed but the
+ * clock is stopped for RST_TYPE_CPU0 can be set up to 7.5us.
+ * Delay 10us to ensure proper reset timing.
+ */
+ udelay(10);
+
+ break;
+ case RST_TYPE_FLUSH:
+ k230_rst_update(rstc, id, true, true, false);
+
+ /* Wait flush request bit auto cleared by hardware */
+ ret = readl_poll_timeout(rstc->base + rmap->offset, reg,
+ !(reg & rmap->reset), 10, 1000);
+ if (ret)
+ dev_err(rcdev->dev, "Wait for flush done timeout\n");
+
+ break;
+ case RST_TYPE_HW_DONE:
+ k230_rst_clear_done(rstc, id, false);
+ k230_rst_update(rstc, id, true, false, false);
+ ret = k230_rst_wait_and_clear_done(rstc, id, false);
+
+ /*
+ * The time period when reset is applied and removed but the
+ * clock is stopped for RST_TYPE_HW_DONE can be set up to
+ * 127.5us. Delay 200us to ensure proper reset timing.
+ */
+ fsleep(200);
+
+ break;
+ case RST_TYPE_CPU1:
+ case RST_TYPE_SW_DONE:
+ k230_rst_assert(rcdev, id);
+ ret = k230_rst_deassert(rcdev, id);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct reset_control_ops k230_rst_ops = {
+ .reset = k230_rst_reset,
+ .assert = k230_rst_assert,
+ .deassert = k230_rst_deassert,
+};
+
+static int k230_rst_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct k230_rst *rstc;
+
+ rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
+ if (!rstc)
+ return -ENOMEM;
+
+ rstc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rstc->base))
+ return PTR_ERR(rstc->base);
+
+ spin_lock_init(&rstc->lock);
+
+ rstc->rcdev.dev = dev;
+ rstc->rcdev.owner = THIS_MODULE;
+ rstc->rcdev.ops = &k230_rst_ops;
+ rstc->rcdev.nr_resets = ARRAY_SIZE(k230_resets);
+ rstc->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &rstc->rcdev);
+}
+
+static const struct of_device_id k230_rst_match[] = {
+ { .compatible = "canaan,k230-rst", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, k230_rst_match);
+
+static struct platform_driver k230_rst_driver = {
+ .probe = k230_rst_probe,
+ .driver = {
+ .name = "k230-rst",
+ .of_match_table = k230_rst_match,
+ }
+};
+module_platform_driver(k230_rst_driver);
+
+MODULE_AUTHOR("Junhui Liu <junhui.liu@pigmoral.tech>");
+MODULE_DESCRIPTION("Canaan K230 reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-lantiq.c b/drivers/reset/reset-lantiq.c
new file mode 100644
index 000000000000..652a45890cb2
--- /dev/null
+++ b/drivers/reset/reset-lantiq.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (C) 2010 John Crispin <blogic@phrozen.org>
+ * Copyright (C) 2013-2015 Lantiq Beteiligungs-GmbH & Co.KG
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2017 Hauke Mehrtens <hauke@hauke-m.de>
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define LANTIQ_RCU_RESET_TIMEOUT 10000
+
+struct lantiq_rcu_reset_priv {
+ struct reset_controller_dev rcdev;
+ struct device *dev;
+ struct regmap *regmap;
+ u32 reset_offset;
+ u32 status_offset;
+};
+
+static struct lantiq_rcu_reset_priv *to_lantiq_rcu_reset_priv(
+ struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct lantiq_rcu_reset_priv, rcdev);
+}
+
+static int lantiq_rcu_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct lantiq_rcu_reset_priv *priv = to_lantiq_rcu_reset_priv(rcdev);
+ unsigned int status = (id >> 8) & 0x1f;
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, priv->status_offset, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(status));
+}
+
+static int lantiq_rcu_reset_status_timeout(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ int ret;
+ int retry = LANTIQ_RCU_RESET_TIMEOUT;
+
+ do {
+ ret = lantiq_rcu_reset_status(rcdev, id);
+ if (ret < 0)
+ return ret;
+ if (ret == assert)
+ return 0;
+ usleep_range(20, 40);
+ } while (--retry);
+
+ return -ETIMEDOUT;
+}
+
+static int lantiq_rcu_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct lantiq_rcu_reset_priv *priv = to_lantiq_rcu_reset_priv(rcdev);
+ unsigned int set = id & 0x1f;
+ u32 val = assert ? BIT(set) : 0;
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, priv->reset_offset, BIT(set),
+ val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set reset bit %u\n", set);
+ return ret;
+ }
+
+
+ ret = lantiq_rcu_reset_status_timeout(rcdev, id, assert);
+ if (ret)
+ dev_err(priv->dev, "Failed to %s bit %u\n",
+ assert ? "assert" : "deassert", set);
+
+ return ret;
+}
+
+static int lantiq_rcu_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lantiq_rcu_reset_update(rcdev, id, true);
+}
+
+static int lantiq_rcu_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lantiq_rcu_reset_update(rcdev, id, false);
+}
+
+static int lantiq_rcu_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = lantiq_rcu_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ return lantiq_rcu_reset_deassert(rcdev, id);
+}
+
+static const struct reset_control_ops lantiq_rcu_reset_ops = {
+ .assert = lantiq_rcu_reset_assert,
+ .deassert = lantiq_rcu_reset_deassert,
+ .status = lantiq_rcu_reset_status,
+ .reset = lantiq_rcu_reset_reset,
+};
+
+static int lantiq_rcu_reset_of_parse(struct platform_device *pdev,
+ struct lantiq_rcu_reset_priv *priv)
+{
+ struct device *dev = &pdev->dev;
+ const __be32 *offset;
+
+ priv->regmap = syscon_node_to_regmap(dev->of_node->parent);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&pdev->dev, "Failed to lookup RCU regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ offset = of_get_address(dev->of_node, 0, NULL, NULL);
+ if (!offset) {
+ dev_err(&pdev->dev, "Failed to get RCU reset offset\n");
+ return -ENOENT;
+ }
+ priv->reset_offset = __be32_to_cpu(*offset);
+
+ offset = of_get_address(dev->of_node, 1, NULL, NULL);
+ if (!offset) {
+ dev_err(&pdev->dev, "Failed to get RCU status offset\n");
+ return -ENOENT;
+ }
+ priv->status_offset = __be32_to_cpu(*offset);
+
+ return 0;
+}
+
+static int lantiq_rcu_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned int status, set;
+
+ set = reset_spec->args[0];
+ status = reset_spec->args[1];
+
+ if (set >= rcdev->nr_resets || status >= rcdev->nr_resets)
+ return -EINVAL;
+
+ return (status << 8) | set;
+}
+
+static int lantiq_rcu_reset_probe(struct platform_device *pdev)
+{
+ struct lantiq_rcu_reset_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+
+ err = lantiq_rcu_reset_of_parse(pdev, priv);
+ if (err)
+ return err;
+
+ priv->rcdev.ops = &lantiq_rcu_reset_ops;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.of_node = pdev->dev.of_node;
+ priv->rcdev.nr_resets = 32;
+ priv->rcdev.of_xlate = lantiq_rcu_reset_xlate;
+ priv->rcdev.of_reset_n_cells = 2;
+
+ return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
+}
+
+static const struct of_device_id lantiq_rcu_reset_dt_ids[] = {
+ { .compatible = "lantiq,danube-reset", },
+ { .compatible = "lantiq,xrx200-reset", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lantiq_rcu_reset_dt_ids);
+
+static struct platform_driver lantiq_rcu_reset_driver = {
+ .probe = lantiq_rcu_reset_probe,
+ .driver = {
+ .name = "lantiq-reset",
+ .of_match_table = lantiq_rcu_reset_dt_ids,
+ },
+};
+module_platform_driver(lantiq_rcu_reset_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Lantiq XWAY RCU Reset Controller Driver");
diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c
new file mode 100644
index 000000000000..e42b2f24a93d
--- /dev/null
+++ b/drivers/reset/reset-lpc18xx.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Reset driver for NXP LPC18xx/43xx Reset Generation Unit (RGU).
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+/* LPC18xx RGU registers */
+#define LPC18XX_RGU_CTRL0 0x100
+#define LPC18XX_RGU_CTRL1 0x104
+#define LPC18XX_RGU_ACTIVE_STATUS0 0x150
+#define LPC18XX_RGU_ACTIVE_STATUS1 0x154
+
+#define LPC18XX_RGU_RESETS_PER_REG 32
+
+/* Internal reset outputs */
+#define LPC18XX_RGU_CORE_RST 0
+#define LPC43XX_RGU_M0SUB_RST 12
+#define LPC43XX_RGU_M0APP_RST 56
+
+struct lpc18xx_rgu_data {
+ struct reset_controller_dev rcdev;
+ struct notifier_block restart_nb;
+ struct clk *clk_delay;
+ struct clk *clk_reg;
+ void __iomem *base;
+ spinlock_t lock;
+ u32 delay_us;
+};
+
+#define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev)
+
+static int lpc18xx_rgu_restart(struct notifier_block *nb, unsigned long mode,
+ void *cmd)
+{
+ struct lpc18xx_rgu_data *rc = container_of(nb, struct lpc18xx_rgu_data,
+ restart_nb);
+
+ writel(BIT(LPC18XX_RGU_CORE_RST), rc->base + LPC18XX_RGU_CTRL0);
+ mdelay(2000);
+
+ pr_emerg("%s: unable to restart system\n", __func__);
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * The LPC18xx RGU has mostly self-deasserting resets except for the
+ * two reset lines going to the internal Cortex-M0 cores.
+ *
+ * To prevent the M0 core resets from accidentally getting deasserted
+ * status register must be check and bits in control register set to
+ * preserve the state.
+ */
+static int lpc18xx_rgu_setclear_reset(struct reset_controller_dev *rcdev,
+ unsigned long id, bool set)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+ u32 stat_offset = LPC18XX_RGU_ACTIVE_STATUS0;
+ u32 ctrl_offset = LPC18XX_RGU_CTRL0;
+ unsigned long flags;
+ u32 stat, rst_bit;
+
+ stat_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ ctrl_offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ rst_bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
+
+ spin_lock_irqsave(&rc->lock, flags);
+ stat = ~readl(rc->base + stat_offset);
+ if (set)
+ writel(stat | rst_bit, rc->base + ctrl_offset);
+ else
+ writel(stat & ~rst_bit, rc->base + ctrl_offset);
+ spin_unlock_irqrestore(&rc->lock, flags);
+
+ return 0;
+}
+
+static int lpc18xx_rgu_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lpc18xx_rgu_setclear_reset(rcdev, id, true);
+}
+
+static int lpc18xx_rgu_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return lpc18xx_rgu_setclear_reset(rcdev, id, false);
+}
+
+/* Only M0 cores require explicit reset deassert */
+static int lpc18xx_rgu_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+
+ lpc18xx_rgu_assert(rcdev, id);
+ udelay(rc->delay_us);
+
+ switch (id) {
+ case LPC43XX_RGU_M0SUB_RST:
+ case LPC43XX_RGU_M0APP_RST:
+ lpc18xx_rgu_setclear_reset(rcdev, id, false);
+ }
+
+ return 0;
+}
+
+static int lpc18xx_rgu_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct lpc18xx_rgu_data *rc = to_rgu_data(rcdev);
+ u32 bit, offset = LPC18XX_RGU_ACTIVE_STATUS0;
+
+ offset += (id / LPC18XX_RGU_RESETS_PER_REG) * sizeof(u32);
+ bit = 1 << (id % LPC18XX_RGU_RESETS_PER_REG);
+
+ return !(readl(rc->base + offset) & bit);
+}
+
+static const struct reset_control_ops lpc18xx_rgu_ops = {
+ .reset = lpc18xx_rgu_reset,
+ .assert = lpc18xx_rgu_assert,
+ .deassert = lpc18xx_rgu_deassert,
+ .status = lpc18xx_rgu_status,
+};
+
+static int lpc18xx_rgu_probe(struct platform_device *pdev)
+{
+ struct lpc18xx_rgu_data *rc;
+ u32 fcclk, firc;
+ int ret;
+
+ rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ rc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rc->base))
+ return PTR_ERR(rc->base);
+
+ rc->clk_reg = devm_clk_get_enabled(&pdev->dev, "reg");
+ if (IS_ERR(rc->clk_reg))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rc->clk_reg),
+ "reg clock not found\n");
+
+ rc->clk_delay = devm_clk_get_enabled(&pdev->dev, "delay");
+ if (IS_ERR(rc->clk_delay))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rc->clk_delay),
+ "delay clock not found\n");
+
+ fcclk = clk_get_rate(rc->clk_reg) / USEC_PER_SEC;
+ firc = clk_get_rate(rc->clk_delay) / USEC_PER_SEC;
+ if (fcclk == 0 || firc == 0)
+ rc->delay_us = 2;
+ else
+ rc->delay_us = DIV_ROUND_UP(fcclk, firc * firc);
+
+ spin_lock_init(&rc->lock);
+
+ rc->rcdev.owner = THIS_MODULE;
+ rc->rcdev.nr_resets = 64;
+ rc->rcdev.ops = &lpc18xx_rgu_ops;
+ rc->rcdev.of_node = pdev->dev.of_node;
+
+ ret = reset_controller_register(&rc->rcdev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "unable to register device\n");
+
+ rc->restart_nb.priority = 192,
+ rc->restart_nb.notifier_call = lpc18xx_rgu_restart,
+ ret = register_restart_handler(&rc->restart_nb);
+ if (ret)
+ dev_warn(&pdev->dev, "failed to register restart handler\n");
+
+ return 0;
+}
+
+static const struct of_device_id lpc18xx_rgu_match[] = {
+ { .compatible = "nxp,lpc1850-rgu" },
+ { }
+};
+
+static struct platform_driver lpc18xx_rgu_driver = {
+ .probe = lpc18xx_rgu_probe,
+ .driver = {
+ .name = "lpc18xx-reset",
+ .of_match_table = lpc18xx_rgu_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(lpc18xx_rgu_driver);
diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c
new file mode 100644
index 000000000000..54e53863c98a
--- /dev/null
+++ b/drivers/reset/reset-ma35d1.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 Nuvoton Technology Corp.
+ * Author: Chi-Fang Li <cfli0@nuvoton.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+struct ma35d1_reset_data {
+ struct reset_controller_dev rcdev;
+ struct notifier_block restart_handler;
+ void __iomem *base;
+ /* protect registers against concurrent read-modify-write */
+ spinlock_t lock;
+};
+
+static const struct {
+ u32 reg_ofs;
+ u32 bit;
+} ma35d1_reset_map[] = {
+ [MA35D1_RESET_CHIP] = {0x20, 0},
+ [MA35D1_RESET_CA35CR0] = {0x20, 1},
+ [MA35D1_RESET_CA35CR1] = {0x20, 2},
+ [MA35D1_RESET_CM4] = {0x20, 3},
+ [MA35D1_RESET_PDMA0] = {0x20, 4},
+ [MA35D1_RESET_PDMA1] = {0x20, 5},
+ [MA35D1_RESET_PDMA2] = {0x20, 6},
+ [MA35D1_RESET_PDMA3] = {0x20, 7},
+ [MA35D1_RESET_DISP] = {0x20, 9},
+ [MA35D1_RESET_VCAP0] = {0x20, 10},
+ [MA35D1_RESET_VCAP1] = {0x20, 11},
+ [MA35D1_RESET_GFX] = {0x20, 12},
+ [MA35D1_RESET_VDEC] = {0x20, 13},
+ [MA35D1_RESET_WHC0] = {0x20, 14},
+ [MA35D1_RESET_WHC1] = {0x20, 15},
+ [MA35D1_RESET_GMAC0] = {0x20, 16},
+ [MA35D1_RESET_GMAC1] = {0x20, 17},
+ [MA35D1_RESET_HWSEM] = {0x20, 18},
+ [MA35D1_RESET_EBI] = {0x20, 19},
+ [MA35D1_RESET_HSUSBH0] = {0x20, 20},
+ [MA35D1_RESET_HSUSBH1] = {0x20, 21},
+ [MA35D1_RESET_HSUSBD] = {0x20, 22},
+ [MA35D1_RESET_USBHL] = {0x20, 23},
+ [MA35D1_RESET_SDH0] = {0x20, 24},
+ [MA35D1_RESET_SDH1] = {0x20, 25},
+ [MA35D1_RESET_NAND] = {0x20, 26},
+ [MA35D1_RESET_GPIO] = {0x20, 27},
+ [MA35D1_RESET_MCTLP] = {0x20, 28},
+ [MA35D1_RESET_MCTLC] = {0x20, 29},
+ [MA35D1_RESET_DDRPUB] = {0x20, 30},
+ [MA35D1_RESET_TMR0] = {0x24, 2},
+ [MA35D1_RESET_TMR1] = {0x24, 3},
+ [MA35D1_RESET_TMR2] = {0x24, 4},
+ [MA35D1_RESET_TMR3] = {0x24, 5},
+ [MA35D1_RESET_I2C0] = {0x24, 8},
+ [MA35D1_RESET_I2C1] = {0x24, 9},
+ [MA35D1_RESET_I2C2] = {0x24, 10},
+ [MA35D1_RESET_I2C3] = {0x24, 11},
+ [MA35D1_RESET_QSPI0] = {0x24, 12},
+ [MA35D1_RESET_SPI0] = {0x24, 13},
+ [MA35D1_RESET_SPI1] = {0x24, 14},
+ [MA35D1_RESET_SPI2] = {0x24, 15},
+ [MA35D1_RESET_UART0] = {0x24, 16},
+ [MA35D1_RESET_UART1] = {0x24, 17},
+ [MA35D1_RESET_UART2] = {0x24, 18},
+ [MA35D1_RESET_UART3] = {0x24, 19},
+ [MA35D1_RESET_UART4] = {0x24, 20},
+ [MA35D1_RESET_UART5] = {0x24, 21},
+ [MA35D1_RESET_UART6] = {0x24, 22},
+ [MA35D1_RESET_UART7] = {0x24, 23},
+ [MA35D1_RESET_CANFD0] = {0x24, 24},
+ [MA35D1_RESET_CANFD1] = {0x24, 25},
+ [MA35D1_RESET_EADC0] = {0x24, 28},
+ [MA35D1_RESET_I2S0] = {0x24, 29},
+ [MA35D1_RESET_SC0] = {0x28, 0},
+ [MA35D1_RESET_SC1] = {0x28, 1},
+ [MA35D1_RESET_QSPI1] = {0x28, 4},
+ [MA35D1_RESET_SPI3] = {0x28, 6},
+ [MA35D1_RESET_EPWM0] = {0x28, 16},
+ [MA35D1_RESET_EPWM1] = {0x28, 17},
+ [MA35D1_RESET_QEI0] = {0x28, 22},
+ [MA35D1_RESET_QEI1] = {0x28, 23},
+ [MA35D1_RESET_ECAP0] = {0x28, 26},
+ [MA35D1_RESET_ECAP1] = {0x28, 27},
+ [MA35D1_RESET_CANFD2] = {0x28, 28},
+ [MA35D1_RESET_ADC0] = {0x28, 31},
+ [MA35D1_RESET_TMR4] = {0x2C, 0},
+ [MA35D1_RESET_TMR5] = {0x2C, 1},
+ [MA35D1_RESET_TMR6] = {0x2C, 2},
+ [MA35D1_RESET_TMR7] = {0x2C, 3},
+ [MA35D1_RESET_TMR8] = {0x2C, 4},
+ [MA35D1_RESET_TMR9] = {0x2C, 5},
+ [MA35D1_RESET_TMR10] = {0x2C, 6},
+ [MA35D1_RESET_TMR11] = {0x2C, 7},
+ [MA35D1_RESET_UART8] = {0x2C, 8},
+ [MA35D1_RESET_UART9] = {0x2C, 9},
+ [MA35D1_RESET_UART10] = {0x2C, 10},
+ [MA35D1_RESET_UART11] = {0x2C, 11},
+ [MA35D1_RESET_UART12] = {0x2C, 12},
+ [MA35D1_RESET_UART13] = {0x2C, 13},
+ [MA35D1_RESET_UART14] = {0x2C, 14},
+ [MA35D1_RESET_UART15] = {0x2C, 15},
+ [MA35D1_RESET_UART16] = {0x2C, 16},
+ [MA35D1_RESET_I2S1] = {0x2C, 17},
+ [MA35D1_RESET_I2C4] = {0x2C, 18},
+ [MA35D1_RESET_I2C5] = {0x2C, 19},
+ [MA35D1_RESET_EPWM2] = {0x2C, 20},
+ [MA35D1_RESET_ECAP2] = {0x2C, 21},
+ [MA35D1_RESET_QEI2] = {0x2C, 22},
+ [MA35D1_RESET_CANFD3] = {0x2C, 23},
+ [MA35D1_RESET_KPI] = {0x2C, 24},
+ [MA35D1_RESET_GIC] = {0x2C, 28},
+ [MA35D1_RESET_SSMCC] = {0x2C, 30},
+ [MA35D1_RESET_SSPCC] = {0x2C, 31}
+};
+
+static int ma35d1_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd)
+{
+ struct ma35d1_reset_data *data =
+ container_of(this, struct ma35d1_reset_data, restart_handler);
+ u32 id = MA35D1_RESET_CHIP;
+
+ writel_relaxed(BIT(ma35d1_reset_map[id].bit),
+ data->base + ma35d1_reset_map[id].reg_ofs);
+ return 0;
+}
+
+static int ma35d1_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert)
+{
+ struct ma35d1_reset_data *data = container_of(rcdev, struct ma35d1_reset_data, rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ if (WARN_ON_ONCE(id >= ARRAY_SIZE(ma35d1_reset_map)))
+ return -EINVAL;
+
+ spin_lock_irqsave(&data->lock, flags);
+ reg = readl_relaxed(data->base + ma35d1_reset_map[id].reg_ofs);
+ if (assert)
+ reg |= BIT(ma35d1_reset_map[id].bit);
+ else
+ reg &= ~(BIT(ma35d1_reset_map[id].bit));
+ writel_relaxed(reg, data->base + ma35d1_reset_map[id].reg_ofs);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ return 0;
+}
+
+static int ma35d1_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ return ma35d1_reset_update(rcdev, id, true);
+}
+
+static int ma35d1_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ return ma35d1_reset_update(rcdev, id, false);
+}
+
+static int ma35d1_reset_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct ma35d1_reset_data *data = container_of(rcdev, struct ma35d1_reset_data, rcdev);
+ u32 reg;
+
+ if (WARN_ON_ONCE(id >= ARRAY_SIZE(ma35d1_reset_map)))
+ return -EINVAL;
+
+ reg = readl_relaxed(data->base + ma35d1_reset_map[id].reg_ofs);
+ return !!(reg & BIT(ma35d1_reset_map[id].bit));
+}
+
+static const struct reset_control_ops ma35d1_reset_ops = {
+ .assert = ma35d1_reset_assert,
+ .deassert = ma35d1_reset_deassert,
+ .status = ma35d1_reset_status,
+};
+
+static const struct of_device_id ma35d1_reset_dt_ids[] = {
+ { .compatible = "nuvoton,ma35d1-reset" },
+ { },
+};
+
+static int ma35d1_reset_probe(struct platform_device *pdev)
+{
+ struct ma35d1_reset_data *reset_data;
+ struct device *dev = &pdev->dev;
+ int err;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "Device tree node not found\n");
+ return -EINVAL;
+ }
+
+ reset_data = devm_kzalloc(dev, sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return -ENOMEM;
+
+ reset_data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reset_data->base))
+ return PTR_ERR(reset_data->base);
+
+ reset_data->rcdev.owner = THIS_MODULE;
+ reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT;
+ reset_data->rcdev.ops = &ma35d1_reset_ops;
+ reset_data->rcdev.of_node = dev->of_node;
+ reset_data->restart_handler.notifier_call = ma35d1_restart_handler;
+ reset_data->restart_handler.priority = 192;
+ spin_lock_init(&reset_data->lock);
+
+ err = register_restart_handler(&reset_data->restart_handler);
+ if (err)
+ dev_warn(&pdev->dev, "failed to register restart handler\n");
+
+ return devm_reset_controller_register(dev, &reset_data->rcdev);
+}
+
+static struct platform_driver ma35d1_reset_driver = {
+ .probe = ma35d1_reset_probe,
+ .driver = {
+ .name = "ma35d1-reset",
+ .of_match_table = ma35d1_reset_dt_ids,
+ },
+};
+
+builtin_platform_driver(ma35d1_reset_driver);
diff --git a/drivers/reset/reset-microchip-sparx5.c b/drivers/reset/reset-microchip-sparx5.c
new file mode 100644
index 000000000000..6d3e75b33260
--- /dev/null
+++ b/drivers/reset/reset-microchip-sparx5.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 Switch Reset driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ *
+ * The Sparx5 Chip Register Model can be browsed at this location:
+ * https://github.com/microchip-ung/sparx-5_reginfo
+ */
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+struct reset_props {
+ u32 protect_reg;
+ u32 protect_bit;
+ u32 reset_reg;
+ u32 reset_bit;
+};
+
+struct mchp_reset_context {
+ struct regmap *cpu_ctrl;
+ struct regmap *gcb_ctrl;
+ struct reset_controller_dev rcdev;
+ const struct reset_props *props;
+};
+
+static struct regmap_config sparx5_reset_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int sparx5_switch_reset(struct mchp_reset_context *ctx)
+{
+ u32 val;
+
+ /* Make sure the core is PROTECTED from reset */
+ regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg,
+ ctx->props->protect_bit, ctx->props->protect_bit);
+
+ /* Start soft reset */
+ regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg,
+ ctx->props->reset_bit);
+
+ /* Wait for soft reset done */
+ return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg, val,
+ (val & ctx->props->reset_bit) == 0,
+ 1, 100);
+}
+
+static int sparx5_reset_noop(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return 0;
+}
+
+static const struct reset_control_ops sparx5_reset_ops = {
+ .reset = sparx5_reset_noop,
+};
+
+static const struct regmap_config mchp_lan966x_syscon_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static struct regmap *mchp_lan966x_syscon_to_regmap(struct device *dev,
+ struct device_node *syscon_np)
+{
+ struct regmap_config regmap_config = mchp_lan966x_syscon_regmap_config;
+ struct resource res;
+ void __iomem *base;
+ int err;
+
+ err = of_address_to_resource(syscon_np, 0, &res);
+ if (err)
+ return ERR_PTR(err);
+
+ /* It is not possible to use devm_of_iomap because this resource is
+ * shared with other drivers.
+ */
+ base = devm_ioremap(dev, res.start, resource_size(&res));
+ if (!base)
+ return ERR_PTR(-ENOMEM);
+
+ regmap_config.max_register = resource_size(&res) - 4;
+
+ return devm_regmap_init_mmio(dev, base, &regmap_config);
+}
+
+static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name,
+ struct regmap **target)
+{
+ struct device_node *syscon_np;
+ struct regmap *regmap;
+ int err;
+
+ syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0);
+ if (!syscon_np)
+ return -ENODEV;
+
+ /*
+ * The syscon API doesn't support syscon device removal.
+ * When used in LAN966x PCI device, the cpu-syscon device needs to be
+ * removed when the PCI device is removed.
+ * In case of LAN966x, map the syscon device locally to support the
+ * device removal.
+ */
+ if (of_device_is_compatible(pdev->dev.of_node, "microchip,lan966x-switch-reset"))
+ regmap = mchp_lan966x_syscon_to_regmap(&pdev->dev, syscon_np);
+ else
+ regmap = syscon_node_to_regmap(syscon_np);
+ of_node_put(syscon_np);
+ if (IS_ERR(regmap)) {
+ err = PTR_ERR(regmap);
+ dev_err(&pdev->dev, "No '%s' map: %d\n", name, err);
+ return err;
+ }
+ *target = regmap;
+ return 0;
+}
+
+static int mchp_sparx5_map_io(struct platform_device *pdev, int index,
+ struct regmap **target)
+{
+ struct resource *res;
+ struct regmap *map;
+ void __iomem *mem;
+
+ mem = devm_platform_get_and_ioremap_resource(pdev, index, &res);
+ if (IS_ERR(mem)) {
+ dev_err(&pdev->dev, "Could not map resource %d\n", index);
+ return PTR_ERR(mem);
+ }
+ sparx5_reset_regmap_config.name = res->name;
+ map = devm_regmap_init_mmio(&pdev->dev, mem, &sparx5_reset_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ *target = map;
+ return 0;
+}
+
+static int mchp_sparx5_reset_probe(struct platform_device *pdev)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ struct mchp_reset_context *ctx;
+ int err;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ err = mchp_sparx5_map_syscon(pdev, "cpu-syscon", &ctx->cpu_ctrl);
+ if (err)
+ return err;
+ err = mchp_sparx5_map_io(pdev, 0, &ctx->gcb_ctrl);
+ if (err)
+ return err;
+
+ ctx->rcdev.owner = THIS_MODULE;
+ ctx->rcdev.dev = &pdev->dev;
+ ctx->rcdev.nr_resets = 1;
+ ctx->rcdev.ops = &sparx5_reset_ops;
+ ctx->rcdev.of_node = dn;
+ ctx->props = device_get_match_data(&pdev->dev);
+
+ /* Issue the reset very early, our actual reset callback is a noop. */
+ err = sparx5_switch_reset(ctx);
+ if (err)
+ return err;
+
+ return devm_reset_controller_register(&pdev->dev, &ctx->rcdev);
+}
+
+static const struct reset_props reset_props_sparx5 = {
+ .protect_reg = 0x84,
+ .protect_bit = BIT(10),
+ .reset_reg = 0x0,
+ .reset_bit = BIT(1),
+};
+
+static const struct reset_props reset_props_lan966x = {
+ .protect_reg = 0x88,
+ .protect_bit = BIT(5),
+ .reset_reg = 0x0,
+ .reset_bit = BIT(1),
+};
+
+static const struct of_device_id mchp_sparx5_reset_of_match[] = {
+ {
+ .compatible = "microchip,sparx5-switch-reset",
+ .data = &reset_props_sparx5,
+ }, {
+ .compatible = "microchip,lan966x-switch-reset",
+ .data = &reset_props_lan966x,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mchp_sparx5_reset_of_match);
+
+static struct platform_driver mchp_sparx5_reset_driver = {
+ .probe = mchp_sparx5_reset_probe,
+ .driver = {
+ .name = "sparx5-switch-reset",
+ .of_match_table = mchp_sparx5_reset_of_match,
+ },
+};
+
+static int __init mchp_sparx5_reset_init(void)
+{
+ return platform_driver_register(&mchp_sparx5_reset_driver);
+}
+
+/*
+ * Because this is a global reset, keep this postcore_initcall() to issue the
+ * reset as early as possible during the kernel startup.
+ */
+postcore_initcall(mchp_sparx5_reset_init);
+
+MODULE_DESCRIPTION("Microchip Sparx5 switch reset driver");
+MODULE_AUTHOR("Steen Hegelund <steen.hegelund@microchip.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c
new file mode 100644
index 000000000000..8ffcc54ee6f6
--- /dev/null
+++ b/drivers/reset/reset-mpfs.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PolarFire SoC (MPFS) Peripheral Clock Reset Controller
+ *
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
+ *
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <dt-bindings/clock/microchip,mpfs-clock.h>
+#include <soc/microchip/mpfs.h>
+
+/*
+ * The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
+ * defines in the dt to make things easier to configure - so this is accounting
+ * for the offset of 3 there.
+ */
+#define MPFS_PERIPH_OFFSET CLK_ENVM
+#define MPFS_NUM_RESETS 30u
+#define MPFS_SLEEP_MIN_US 100
+#define MPFS_SLEEP_MAX_US 200
+
+#define REG_SUBBLK_RESET_CR 0x88u
+
+struct mpfs_reset {
+ struct regmap *regmap;
+ struct reset_controller_dev rcdev;
+};
+
+static inline struct mpfs_reset *to_mpfs_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct mpfs_reset, rcdev);
+}
+
+/*
+ * Peripheral clock resets
+ */
+static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct mpfs_reset *rst = to_mpfs_reset(rcdev);
+
+ return regmap_set_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id));
+
+}
+
+static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct mpfs_reset *rst = to_mpfs_reset(rcdev);
+
+ return regmap_clear_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id));
+
+}
+
+static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct mpfs_reset *rst = to_mpfs_reset(rcdev);
+ u32 reg;
+
+ regmap_read(rst->regmap, REG_SUBBLK_RESET_CR, &reg);
+
+ /*
+ * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit
+ * is never hit.
+ */
+ return (reg & BIT(id));
+}
+
+static int mpfs_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ mpfs_assert(rcdev, id);
+
+ usleep_range(MPFS_SLEEP_MIN_US, MPFS_SLEEP_MAX_US);
+
+ mpfs_deassert(rcdev, id);
+
+ return 0;
+}
+
+static const struct reset_control_ops mpfs_reset_ops = {
+ .reset = mpfs_reset,
+ .assert = mpfs_assert,
+ .deassert = mpfs_deassert,
+ .status = mpfs_status,
+};
+
+static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned int index = reset_spec->args[0];
+
+ /*
+ * CLK_RESERVED does not map to a clock, but it does map to a reset,
+ * so it has to be accounted for here. It is the reset for the fabric,
+ * so if this reset gets called - do not reset it.
+ */
+ if (index == CLK_RESERVED) {
+ dev_err(rcdev->dev, "Resetting the fabric is not supported\n");
+ return -EINVAL;
+ }
+
+ if (index < MPFS_PERIPH_OFFSET || index >= (MPFS_PERIPH_OFFSET + rcdev->nr_resets)) {
+ dev_err(rcdev->dev, "Invalid reset index %u\n", index);
+ return -EINVAL;
+ }
+
+ return index - MPFS_PERIPH_OFFSET;
+}
+
+static int mpfs_reset_mfd_probe(struct platform_device *pdev)
+{
+ struct reset_controller_dev *rcdev;
+ struct device *dev = &pdev->dev;
+ struct mpfs_reset *rst;
+
+ rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return -ENOMEM;
+
+ rcdev = &rst->rcdev;
+ rcdev->dev = dev;
+ rcdev->ops = &mpfs_reset_ops;
+
+ rcdev->of_node = pdev->dev.parent->of_node;
+ rcdev->of_reset_n_cells = 1;
+ rcdev->of_xlate = mpfs_reset_xlate;
+ rcdev->nr_resets = MPFS_NUM_RESETS;
+
+ rst->regmap = device_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(rst->regmap))
+ return dev_err_probe(dev, PTR_ERR(rst->regmap),
+ "Failed to find syscon regmap\n");
+
+ return devm_reset_controller_register(dev, rcdev);
+}
+
+static struct platform_driver mpfs_reset_mfd_driver = {
+ .probe = mpfs_reset_mfd_probe,
+ .driver = {
+ .name = "mpfs-reset",
+ },
+};
+module_platform_driver(mpfs_reset_mfd_driver);
+
+static int mpfs_reset_adev_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct reset_controller_dev *rcdev;
+ struct device *dev = &adev->dev;
+ struct mpfs_reset *rst;
+
+ rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return -ENOMEM;
+
+ rst->regmap = (struct regmap *)adev->dev.platform_data;
+
+ rcdev = &rst->rcdev;
+ rcdev->dev = dev;
+ rcdev->ops = &mpfs_reset_ops;
+
+ rcdev->of_node = dev->parent->of_node;
+ rcdev->of_reset_n_cells = 1;
+ rcdev->of_xlate = mpfs_reset_xlate;
+ rcdev->nr_resets = MPFS_NUM_RESETS;
+
+ return devm_reset_controller_register(dev, rcdev);
+}
+
+int mpfs_reset_controller_register(struct device *clk_dev, struct regmap *map)
+{
+ struct auxiliary_device *adev;
+
+ adev = devm_auxiliary_device_create(clk_dev, "reset-mpfs", (void *)map);
+ if (!adev)
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mpfs_reset_controller_register, "MCHP_CLK_MPFS");
+
+static const struct auxiliary_device_id mpfs_reset_ids[] = {
+ {
+ .name = "reset_mpfs.reset-mpfs",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
+
+static struct auxiliary_driver mpfs_reset_aux_driver = {
+ .probe = mpfs_reset_adev_probe,
+ .id_table = mpfs_reset_ids,
+};
+
+module_auxiliary_driver(mpfs_reset_aux_driver);
+
+MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_IMPORT_NS("MCHP_CLK_MPFS");
diff --git a/drivers/reset/reset-npcm.c b/drivers/reset/reset-npcm.c
new file mode 100644
index 000000000000..e5b6127783a7
--- /dev/null
+++ b/drivers/reset/reset-npcm.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+
+#include <soc/nuvoton/clock-npcm8xx.h>
+
+/* NPCM7xx GCR registers */
+#define NPCM_MDLR_OFFSET 0x7C
+#define NPCM7XX_MDLR_USBD0 BIT(9)
+#define NPCM7XX_MDLR_USBD1 BIT(8)
+#define NPCM7XX_MDLR_USBD2_4 BIT(21)
+#define NPCM7XX_MDLR_USBD5_9 BIT(22)
+
+/* NPCM8xx MDLR bits */
+#define NPCM8XX_MDLR_USBD0_3 BIT(9)
+#define NPCM8XX_MDLR_USBD4_7 BIT(22)
+#define NPCM8XX_MDLR_USBD8 BIT(24)
+#define NPCM8XX_MDLR_USBD9 BIT(21)
+
+#define NPCM_USB1PHYCTL_OFFSET 0x140
+#define NPCM_USB2PHYCTL_OFFSET 0x144
+#define NPCM_USB3PHYCTL_OFFSET 0x148
+#define NPCM_USBXPHYCTL_RS BIT(28)
+
+/* NPCM7xx Reset registers */
+#define NPCM_SWRSTR 0x14
+#define NPCM_SWRST BIT(2)
+
+#define NPCM_IPSRST1 0x20
+#define NPCM_IPSRST1_USBD1 BIT(5)
+#define NPCM_IPSRST1_USBD2 BIT(8)
+#define NPCM_IPSRST1_USBD3 BIT(25)
+#define NPCM_IPSRST1_USBD4 BIT(22)
+#define NPCM_IPSRST1_USBD5 BIT(23)
+#define NPCM_IPSRST1_USBD6 BIT(24)
+
+#define NPCM_IPSRST2 0x24
+#define NPCM_IPSRST2_USB_HOST BIT(26)
+
+#define NPCM_IPSRST3 0x34
+#define NPCM_IPSRST3_USBD0 BIT(4)
+#define NPCM_IPSRST3_USBD7 BIT(5)
+#define NPCM_IPSRST3_USBD8 BIT(6)
+#define NPCM_IPSRST3_USBD9 BIT(7)
+#define NPCM_IPSRST3_USBPHY1 BIT(24)
+#define NPCM_IPSRST3_USBPHY2 BIT(25)
+
+#define NPCM_IPSRST4 0x74
+#define NPCM_IPSRST4_USBPHY3 BIT(25)
+#define NPCM_IPSRST4_USB_HOST2 BIT(31)
+
+#define NPCM_RC_RESETS_PER_REG 32
+#define NPCM_MASK_RESETS GENMASK(4, 0)
+
+enum {
+ BMC_NPCM7XX = 0,
+ BMC_NPCM8XX,
+};
+
+static const u32 npxm7xx_ipsrst[] = {NPCM_IPSRST1, NPCM_IPSRST2, NPCM_IPSRST3};
+static const u32 npxm8xx_ipsrst[] = {NPCM_IPSRST1, NPCM_IPSRST2, NPCM_IPSRST3,
+ NPCM_IPSRST4};
+
+struct npcm_reset_info {
+ u32 bmc_id;
+ u32 num_ipsrst;
+ const u32 *ipsrst;
+};
+
+static const struct npcm_reset_info npxm7xx_reset_info[] = {
+ {.bmc_id = BMC_NPCM7XX, .num_ipsrst = 3, .ipsrst = npxm7xx_ipsrst}};
+static const struct npcm_reset_info npxm8xx_reset_info[] = {
+ {.bmc_id = BMC_NPCM8XX, .num_ipsrst = 4, .ipsrst = npxm8xx_ipsrst}};
+
+struct npcm_rc_data {
+ struct reset_controller_dev rcdev;
+ struct notifier_block restart_nb;
+ const struct npcm_reset_info *info;
+ struct regmap *gcr_regmap;
+ u32 sw_reset_number;
+ struct device *dev;
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+#define to_rc_data(p) container_of(p, struct npcm_rc_data, rcdev)
+
+static int npcm_rc_restart(struct notifier_block *nb, unsigned long mode,
+ void *cmd)
+{
+ struct npcm_rc_data *rc = container_of(nb, struct npcm_rc_data,
+ restart_nb);
+
+ writel(NPCM_SWRST << rc->sw_reset_number, rc->base + NPCM_SWRSTR);
+ mdelay(1000);
+
+ pr_emerg("%s: unable to restart system\n", __func__);
+
+ return NOTIFY_DONE;
+}
+
+static int npcm_rc_setclear_reset(struct reset_controller_dev *rcdev,
+ unsigned long id, bool set)
+{
+ struct npcm_rc_data *rc = to_rc_data(rcdev);
+ unsigned int rst_bit = BIT(id & NPCM_MASK_RESETS);
+ unsigned int ctrl_offset = id >> 8;
+ unsigned long flags;
+ u32 stat;
+
+ spin_lock_irqsave(&rc->lock, flags);
+ stat = readl(rc->base + ctrl_offset);
+ if (set)
+ writel(stat | rst_bit, rc->base + ctrl_offset);
+ else
+ writel(stat & ~rst_bit, rc->base + ctrl_offset);
+ spin_unlock_irqrestore(&rc->lock, flags);
+
+ return 0;
+}
+
+static int npcm_rc_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ return npcm_rc_setclear_reset(rcdev, id, true);
+}
+
+static int npcm_rc_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return npcm_rc_setclear_reset(rcdev, id, false);
+}
+
+static int npcm_rc_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct npcm_rc_data *rc = to_rc_data(rcdev);
+ unsigned int rst_bit = BIT(id & NPCM_MASK_RESETS);
+ unsigned int ctrl_offset = id >> 8;
+
+ return (readl(rc->base + ctrl_offset) & rst_bit);
+}
+
+static int npcm_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct npcm_rc_data *rc = to_rc_data(rcdev);
+ unsigned int offset, bit;
+ bool offset_found = false;
+ int off_num;
+
+ offset = reset_spec->args[0];
+ for (off_num = 0 ; off_num < rc->info->num_ipsrst ; off_num++) {
+ if (offset == rc->info->ipsrst[off_num]) {
+ offset_found = true;
+ break;
+ }
+ }
+
+ if (!offset_found) {
+ dev_err(rcdev->dev, "Error reset register (0x%x)\n", offset);
+ return -EINVAL;
+ }
+
+ bit = reset_spec->args[1];
+ if (bit >= NPCM_RC_RESETS_PER_REG) {
+ dev_err(rcdev->dev, "Error reset number (%d)\n", bit);
+ return -EINVAL;
+ }
+
+ return (offset << 8) | bit;
+}
+
+static const struct of_device_id npcm_rc_match[] = {
+ { .compatible = "nuvoton,npcm750-reset", .data = &npxm7xx_reset_info},
+ { .compatible = "nuvoton,npcm845-reset", .data = &npxm8xx_reset_info},
+ { }
+};
+
+static void npcm_usb_reset_npcm7xx(struct npcm_rc_data *rc)
+{
+ u32 mdlr, iprst1, iprst2, iprst3;
+ u32 ipsrst1_bits = 0;
+ u32 ipsrst2_bits = NPCM_IPSRST2_USB_HOST;
+ u32 ipsrst3_bits = 0;
+
+ /* checking which USB device is enabled */
+ regmap_read(rc->gcr_regmap, NPCM_MDLR_OFFSET, &mdlr);
+ if (!(mdlr & NPCM7XX_MDLR_USBD0))
+ ipsrst3_bits |= NPCM_IPSRST3_USBD0;
+ if (!(mdlr & NPCM7XX_MDLR_USBD1))
+ ipsrst1_bits |= NPCM_IPSRST1_USBD1;
+ if (!(mdlr & NPCM7XX_MDLR_USBD2_4))
+ ipsrst1_bits |= (NPCM_IPSRST1_USBD2 |
+ NPCM_IPSRST1_USBD3 |
+ NPCM_IPSRST1_USBD4);
+ if (!(mdlr & NPCM7XX_MDLR_USBD0)) {
+ ipsrst1_bits |= (NPCM_IPSRST1_USBD5 |
+ NPCM_IPSRST1_USBD6);
+ ipsrst3_bits |= (NPCM_IPSRST3_USBD7 |
+ NPCM_IPSRST3_USBD8 |
+ NPCM_IPSRST3_USBD9);
+ }
+
+ /* assert reset USB PHY and USB devices */
+ iprst1 = readl(rc->base + NPCM_IPSRST1);
+ iprst2 = readl(rc->base + NPCM_IPSRST2);
+ iprst3 = readl(rc->base + NPCM_IPSRST3);
+
+ iprst1 |= ipsrst1_bits;
+ iprst2 |= ipsrst2_bits;
+ iprst3 |= (ipsrst3_bits | NPCM_IPSRST3_USBPHY1 |
+ NPCM_IPSRST3_USBPHY2);
+
+ writel(iprst1, rc->base + NPCM_IPSRST1);
+ writel(iprst2, rc->base + NPCM_IPSRST2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+
+ /* clear USB PHY RS bit */
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, 0);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, 0);
+
+ /* deassert reset USB PHY */
+ iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+
+ udelay(50);
+
+ /* set USB PHY RS bit */
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS);
+
+ /* deassert reset USB devices*/
+ iprst1 &= ~ipsrst1_bits;
+ iprst2 &= ~ipsrst2_bits;
+ iprst3 &= ~ipsrst3_bits;
+
+ writel(iprst1, rc->base + NPCM_IPSRST1);
+ writel(iprst2, rc->base + NPCM_IPSRST2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+}
+
+static void npcm_usb_reset_npcm8xx(struct npcm_rc_data *rc)
+{
+ u32 mdlr, iprst1, iprst2, iprst3, iprst4;
+ u32 ipsrst1_bits = 0;
+ u32 ipsrst2_bits = NPCM_IPSRST2_USB_HOST;
+ u32 ipsrst3_bits = 0;
+ u32 ipsrst4_bits = NPCM_IPSRST4_USB_HOST2 | NPCM_IPSRST4_USBPHY3;
+
+ /* checking which USB device is enabled */
+ regmap_read(rc->gcr_regmap, NPCM_MDLR_OFFSET, &mdlr);
+ if (!(mdlr & NPCM8XX_MDLR_USBD0_3)) {
+ ipsrst3_bits |= NPCM_IPSRST3_USBD0;
+ ipsrst1_bits |= (NPCM_IPSRST1_USBD1 |
+ NPCM_IPSRST1_USBD2 |
+ NPCM_IPSRST1_USBD3);
+ }
+ if (!(mdlr & NPCM8XX_MDLR_USBD4_7)) {
+ ipsrst1_bits |= (NPCM_IPSRST1_USBD4 |
+ NPCM_IPSRST1_USBD5 |
+ NPCM_IPSRST1_USBD6);
+ ipsrst3_bits |= NPCM_IPSRST3_USBD7;
+ }
+
+ if (!(mdlr & NPCM8XX_MDLR_USBD8))
+ ipsrst3_bits |= NPCM_IPSRST3_USBD8;
+ if (!(mdlr & NPCM8XX_MDLR_USBD9))
+ ipsrst3_bits |= NPCM_IPSRST3_USBD9;
+
+ /* assert reset USB PHY and USB devices */
+ iprst1 = readl(rc->base + NPCM_IPSRST1);
+ iprst2 = readl(rc->base + NPCM_IPSRST2);
+ iprst3 = readl(rc->base + NPCM_IPSRST3);
+ iprst4 = readl(rc->base + NPCM_IPSRST4);
+
+ iprst1 |= ipsrst1_bits;
+ iprst2 |= ipsrst2_bits;
+ iprst3 |= (ipsrst3_bits | NPCM_IPSRST3_USBPHY1 |
+ NPCM_IPSRST3_USBPHY2);
+ iprst4 |= ipsrst4_bits;
+
+ writel(iprst1, rc->base + NPCM_IPSRST1);
+ writel(iprst2, rc->base + NPCM_IPSRST2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+ writel(iprst4, rc->base + NPCM_IPSRST4);
+
+ /* clear USB PHY RS bit */
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, 0);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, 0);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB3PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, 0);
+
+ /* deassert reset USB PHY */
+ iprst3 &= ~(NPCM_IPSRST3_USBPHY1 | NPCM_IPSRST3_USBPHY2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+ iprst4 &= ~NPCM_IPSRST4_USBPHY3;
+ writel(iprst4, rc->base + NPCM_IPSRST4);
+
+ /* set USB PHY RS bit */
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB1PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB2PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS);
+ regmap_update_bits(rc->gcr_regmap, NPCM_USB3PHYCTL_OFFSET,
+ NPCM_USBXPHYCTL_RS, NPCM_USBXPHYCTL_RS);
+
+ /* deassert reset USB devices*/
+ iprst1 &= ~ipsrst1_bits;
+ iprst2 &= ~ipsrst2_bits;
+ iprst3 &= ~ipsrst3_bits;
+ iprst4 &= ~ipsrst4_bits;
+
+ writel(iprst1, rc->base + NPCM_IPSRST1);
+ writel(iprst2, rc->base + NPCM_IPSRST2);
+ writel(iprst3, rc->base + NPCM_IPSRST3);
+ writel(iprst4, rc->base + NPCM_IPSRST4);
+}
+
+/*
+ * The following procedure should be observed in USB PHY, USB device and
+ * USB host initialization at BMC boot
+ */
+static int npcm_usb_reset(struct platform_device *pdev, struct npcm_rc_data *rc)
+{
+ struct device *dev = &pdev->dev;
+
+ rc->gcr_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "nuvoton,sysgcr");
+ if (IS_ERR(rc->gcr_regmap)) {
+ dev_warn(&pdev->dev, "Failed to find nuvoton,sysgcr property, please update the device tree\n");
+ dev_info(&pdev->dev, "Using nuvoton,npcm750-gcr for Poleg backward compatibility\n");
+ rc->gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+ if (IS_ERR(rc->gcr_regmap)) {
+ dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-gcr");
+ return PTR_ERR(rc->gcr_regmap);
+ }
+ }
+
+ rc->info = device_get_match_data(dev);
+ switch (rc->info->bmc_id) {
+ case BMC_NPCM7XX:
+ npcm_usb_reset_npcm7xx(rc);
+ break;
+ case BMC_NPCM8XX:
+ npcm_usb_reset_npcm8xx(rc);
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static const struct reset_control_ops npcm_rc_ops = {
+ .assert = npcm_rc_assert,
+ .deassert = npcm_rc_deassert,
+ .status = npcm_rc_status,
+};
+
+static void npcm_clock_unregister_adev(void *_adev)
+{
+ struct auxiliary_device *adev = _adev;
+
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static void npcm_clock_adev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct npcm_clock_adev *rdev = to_npcm_clock_adev(adev);
+
+ kfree(rdev);
+}
+
+static struct auxiliary_device *npcm_clock_adev_alloc(struct npcm_rc_data *rst_data, char *clk_name)
+{
+ struct npcm_clock_adev *rdev;
+ struct auxiliary_device *adev;
+ int ret;
+
+ rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
+ if (!rdev)
+ return ERR_PTR(-ENOMEM);
+
+ rdev->base = rst_data->base;
+
+ adev = &rdev->adev;
+ adev->name = clk_name;
+ adev->dev.parent = rst_data->dev;
+ adev->dev.release = npcm_clock_adev_release;
+ adev->id = 555u;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(rdev);
+ return ERR_PTR(ret);
+ }
+
+ return adev;
+}
+
+static int npcm8xx_clock_controller_register(struct npcm_rc_data *rst_data, char *clk_name)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = npcm_clock_adev_alloc(rst_data, clk_name);
+ if (IS_ERR(adev))
+ return PTR_ERR(adev);
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(rst_data->dev, npcm_clock_unregister_adev, adev);
+}
+
+static int npcm_rc_probe(struct platform_device *pdev)
+{
+ struct npcm_rc_data *rc;
+ int ret;
+
+ rc = devm_kzalloc(&pdev->dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ rc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rc->base))
+ return PTR_ERR(rc->base);
+
+ spin_lock_init(&rc->lock);
+
+ rc->rcdev.owner = THIS_MODULE;
+ rc->rcdev.ops = &npcm_rc_ops;
+ rc->rcdev.of_node = pdev->dev.of_node;
+ rc->rcdev.of_reset_n_cells = 2;
+ rc->rcdev.of_xlate = npcm_reset_xlate;
+ rc->dev = &pdev->dev;
+
+ ret = devm_reset_controller_register(&pdev->dev, &rc->rcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ return ret;
+ }
+
+ if (npcm_usb_reset(pdev, rc))
+ dev_warn(&pdev->dev, "NPCM USB reset failed, can cause issues with UDC and USB host\n");
+
+ if (!of_property_read_u32(pdev->dev.of_node, "nuvoton,sw-reset-number",
+ &rc->sw_reset_number)) {
+ if (rc->sw_reset_number && rc->sw_reset_number < 5) {
+ rc->restart_nb.priority = 192;
+ rc->restart_nb.notifier_call = npcm_rc_restart;
+ ret = register_restart_handler(&rc->restart_nb);
+ if (ret) {
+ dev_warn(&pdev->dev, "failed to register restart handler\n");
+ return ret;
+ }
+ }
+ }
+
+ switch (rc->info->bmc_id) {
+ case BMC_NPCM8XX:
+ return npcm8xx_clock_controller_register(rc, "clk-npcm8xx");
+ default:
+ return 0;
+ }
+}
+
+static struct platform_driver npcm_rc_driver = {
+ .probe = npcm_rc_probe,
+ .driver = {
+ .name = "npcm-reset",
+ .of_match_table = npcm_rc_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(npcm_rc_driver);
diff --git a/drivers/reset/reset-pistachio.c b/drivers/reset/reset-pistachio.c
new file mode 100644
index 000000000000..151ceeb42c30
--- /dev/null
+++ b/drivers/reset/reset-pistachio.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Pistachio SoC Reset Controller driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+
+#include <dt-bindings/reset/pistachio-resets.h>
+
+#define PISTACHIO_SOFT_RESET 0
+
+struct pistachio_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *periph_regs;
+};
+
+static inline int pistachio_reset_shift(unsigned long id)
+{
+ switch (id) {
+ case PISTACHIO_RESET_I2C0:
+ case PISTACHIO_RESET_I2C1:
+ case PISTACHIO_RESET_I2C2:
+ case PISTACHIO_RESET_I2C3:
+ case PISTACHIO_RESET_I2S_IN:
+ case PISTACHIO_RESET_PRL_OUT:
+ case PISTACHIO_RESET_SPDIF_OUT:
+ case PISTACHIO_RESET_SPI:
+ case PISTACHIO_RESET_PWM_PDM:
+ case PISTACHIO_RESET_UART0:
+ case PISTACHIO_RESET_UART1:
+ case PISTACHIO_RESET_QSPI:
+ case PISTACHIO_RESET_MDC:
+ case PISTACHIO_RESET_SDHOST:
+ case PISTACHIO_RESET_ETHERNET:
+ case PISTACHIO_RESET_IR:
+ case PISTACHIO_RESET_HASH:
+ case PISTACHIO_RESET_TIMER:
+ return id;
+ case PISTACHIO_RESET_I2S_OUT:
+ case PISTACHIO_RESET_SPDIF_IN:
+ case PISTACHIO_RESET_EVT:
+ return id + 6;
+ case PISTACHIO_RESET_USB_H:
+ case PISTACHIO_RESET_USB_PR:
+ case PISTACHIO_RESET_USB_PHY_PR:
+ case PISTACHIO_RESET_USB_PHY_PON:
+ return id + 7;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pistachio_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct pistachio_reset_data *rd;
+ u32 mask;
+ int shift;
+
+ rd = container_of(rcdev, struct pistachio_reset_data, rcdev);
+ shift = pistachio_reset_shift(id);
+ if (shift < 0)
+ return shift;
+ mask = BIT(shift);
+
+ return regmap_update_bits(rd->periph_regs, PISTACHIO_SOFT_RESET,
+ mask, mask);
+}
+
+static int pistachio_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct pistachio_reset_data *rd;
+ u32 mask;
+ int shift;
+
+ rd = container_of(rcdev, struct pistachio_reset_data, rcdev);
+ shift = pistachio_reset_shift(id);
+ if (shift < 0)
+ return shift;
+ mask = BIT(shift);
+
+ return regmap_update_bits(rd->periph_regs, PISTACHIO_SOFT_RESET,
+ mask, 0);
+}
+
+static const struct reset_control_ops pistachio_reset_ops = {
+ .assert = pistachio_reset_assert,
+ .deassert = pistachio_reset_deassert,
+};
+
+static int pistachio_reset_probe(struct platform_device *pdev)
+{
+ struct pistachio_reset_data *rd;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+
+ rd = devm_kzalloc(dev, sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
+ rd->periph_regs = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(rd->periph_regs))
+ return PTR_ERR(rd->periph_regs);
+
+ rd->rcdev.owner = THIS_MODULE;
+ rd->rcdev.nr_resets = PISTACHIO_RESET_MAX + 1;
+ rd->rcdev.ops = &pistachio_reset_ops;
+ rd->rcdev.of_node = np;
+
+ return devm_reset_controller_register(dev, &rd->rcdev);
+}
+
+static const struct of_device_id pistachio_reset_dt_ids[] = {
+ { .compatible = "img,pistachio-reset", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver pistachio_reset_driver = {
+ .probe = pistachio_reset_probe,
+ .driver = {
+ .name = "pistachio-reset",
+ .of_match_table = pistachio_reset_dt_ids,
+ },
+};
+builtin_platform_driver(pistachio_reset_driver);
diff --git a/drivers/reset/reset-qcom-aoss.c b/drivers/reset/reset-qcom-aoss.c
new file mode 100644
index 000000000000..93c84d70ef64
--- /dev/null
+++ b/drivers/reset/reset-qcom-aoss.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <dt-bindings/reset/qcom,sdm845-aoss.h>
+
+struct qcom_aoss_reset_map {
+ unsigned int reg;
+};
+
+struct qcom_aoss_desc {
+ const struct qcom_aoss_reset_map *resets;
+ size_t num_resets;
+};
+
+struct qcom_aoss_reset_data {
+ struct reset_controller_dev rcdev;
+ void __iomem *base;
+ const struct qcom_aoss_desc *desc;
+};
+
+static const struct qcom_aoss_reset_map sdm845_aoss_resets[] = {
+ [AOSS_CC_MSS_RESTART] = {0x10000},
+ [AOSS_CC_CAMSS_RESTART] = {0x11000},
+ [AOSS_CC_VENUS_RESTART] = {0x12000},
+ [AOSS_CC_GPU_RESTART] = {0x13000},
+ [AOSS_CC_DISPSS_RESTART] = {0x14000},
+ [AOSS_CC_WCSS_RESTART] = {0x20000},
+ [AOSS_CC_LPASS_RESTART] = {0x30000},
+};
+
+static const struct qcom_aoss_desc sdm845_aoss_desc = {
+ .resets = sdm845_aoss_resets,
+ .num_resets = ARRAY_SIZE(sdm845_aoss_resets),
+};
+
+static inline struct qcom_aoss_reset_data *to_qcom_aoss_reset_data(
+ struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct qcom_aoss_reset_data, rcdev);
+}
+
+static int qcom_aoss_control_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct qcom_aoss_reset_data *data = to_qcom_aoss_reset_data(rcdev);
+ const struct qcom_aoss_reset_map *map = &data->desc->resets[idx];
+
+ writel(1, data->base + map->reg);
+ /* Wait 6 32kHz sleep cycles for reset */
+ usleep_range(200, 300);
+ return 0;
+}
+
+static int qcom_aoss_control_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct qcom_aoss_reset_data *data = to_qcom_aoss_reset_data(rcdev);
+ const struct qcom_aoss_reset_map *map = &data->desc->resets[idx];
+
+ writel(0, data->base + map->reg);
+ /* Wait 6 32kHz sleep cycles for reset */
+ usleep_range(200, 300);
+ return 0;
+}
+
+static int qcom_aoss_control_reset(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ qcom_aoss_control_assert(rcdev, idx);
+
+ return qcom_aoss_control_deassert(rcdev, idx);
+}
+
+static const struct reset_control_ops qcom_aoss_reset_ops = {
+ .reset = qcom_aoss_control_reset,
+ .assert = qcom_aoss_control_assert,
+ .deassert = qcom_aoss_control_deassert,
+};
+
+static int qcom_aoss_reset_probe(struct platform_device *pdev)
+{
+ struct qcom_aoss_reset_data *data;
+ struct device *dev = &pdev->dev;
+ const struct qcom_aoss_desc *desc;
+
+ desc = of_device_get_match_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->desc = desc;
+ data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &qcom_aoss_reset_ops;
+ data->rcdev.nr_resets = desc->num_resets;
+ data->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static const struct of_device_id qcom_aoss_reset_of_match[] = {
+ { .compatible = "qcom,sdm845-aoss-cc", .data = &sdm845_aoss_desc },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_aoss_reset_of_match);
+
+static struct platform_driver qcom_aoss_reset_driver = {
+ .probe = qcom_aoss_reset_probe,
+ .driver = {
+ .name = "qcom_aoss_reset",
+ .of_match_table = qcom_aoss_reset_of_match,
+ },
+};
+
+module_platform_driver(qcom_aoss_reset_driver);
+
+MODULE_DESCRIPTION("Qualcomm AOSS Reset Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-qcom-pdc.c b/drivers/reset/reset-qcom-pdc.c
new file mode 100644
index 000000000000..ae2b5aba7a59
--- /dev/null
+++ b/drivers/reset/reset-qcom-pdc.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/qcom,sdm845-pdc.h>
+
+#define RPMH_SDM845_PDC_SYNC_RESET 0x100
+#define RPMH_SC7280_PDC_SYNC_RESET 0x1000
+
+struct qcom_pdc_reset_map {
+ u8 bit;
+};
+
+struct qcom_pdc_reset_desc {
+ const struct qcom_pdc_reset_map *resets;
+ size_t num_resets;
+ unsigned int offset;
+};
+
+struct qcom_pdc_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+ const struct qcom_pdc_reset_desc *desc;
+};
+
+static const struct regmap_config pdc_regmap_config = {
+ .name = "pdc-reset",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x20000,
+};
+
+static const struct qcom_pdc_reset_map sdm845_pdc_resets[] = {
+ [PDC_APPS_SYNC_RESET] = {0},
+ [PDC_SP_SYNC_RESET] = {1},
+ [PDC_AUDIO_SYNC_RESET] = {2},
+ [PDC_SENSORS_SYNC_RESET] = {3},
+ [PDC_AOP_SYNC_RESET] = {4},
+ [PDC_DEBUG_SYNC_RESET] = {5},
+ [PDC_GPU_SYNC_RESET] = {6},
+ [PDC_DISPLAY_SYNC_RESET] = {7},
+ [PDC_COMPUTE_SYNC_RESET] = {8},
+ [PDC_MODEM_SYNC_RESET] = {9},
+};
+
+static const struct qcom_pdc_reset_desc sdm845_pdc_reset_desc = {
+ .resets = sdm845_pdc_resets,
+ .num_resets = ARRAY_SIZE(sdm845_pdc_resets),
+ .offset = RPMH_SDM845_PDC_SYNC_RESET,
+};
+
+static const struct qcom_pdc_reset_map sc7280_pdc_resets[] = {
+ [PDC_APPS_SYNC_RESET] = {0},
+ [PDC_SP_SYNC_RESET] = {1},
+ [PDC_AUDIO_SYNC_RESET] = {2},
+ [PDC_SENSORS_SYNC_RESET] = {3},
+ [PDC_AOP_SYNC_RESET] = {4},
+ [PDC_DEBUG_SYNC_RESET] = {5},
+ [PDC_GPU_SYNC_RESET] = {6},
+ [PDC_DISPLAY_SYNC_RESET] = {7},
+ [PDC_COMPUTE_SYNC_RESET] = {8},
+ [PDC_MODEM_SYNC_RESET] = {9},
+ [PDC_WLAN_RF_SYNC_RESET] = {10},
+ [PDC_WPSS_SYNC_RESET] = {11},
+};
+
+static const struct qcom_pdc_reset_desc sc7280_pdc_reset_desc = {
+ .resets = sc7280_pdc_resets,
+ .num_resets = ARRAY_SIZE(sc7280_pdc_resets),
+ .offset = RPMH_SC7280_PDC_SYNC_RESET,
+};
+
+static inline struct qcom_pdc_reset_data *to_qcom_pdc_reset_data(
+ struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct qcom_pdc_reset_data, rcdev);
+}
+
+static int qcom_pdc_control_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
+ u32 mask = BIT(data->desc->resets[idx].bit);
+
+ return regmap_update_bits(data->regmap, data->desc->offset, mask, mask);
+}
+
+static int qcom_pdc_control_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct qcom_pdc_reset_data *data = to_qcom_pdc_reset_data(rcdev);
+ u32 mask = BIT(data->desc->resets[idx].bit);
+
+ return regmap_update_bits(data->regmap, data->desc->offset, mask, 0);
+}
+
+static const struct reset_control_ops qcom_pdc_reset_ops = {
+ .assert = qcom_pdc_control_assert,
+ .deassert = qcom_pdc_control_deassert,
+};
+
+static int qcom_pdc_reset_probe(struct platform_device *pdev)
+{
+ const struct qcom_pdc_reset_desc *desc;
+ struct qcom_pdc_reset_data *data;
+ struct device *dev = &pdev->dev;
+ void __iomem *base;
+
+ desc = device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->desc = desc;
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ data->regmap = devm_regmap_init_mmio(dev, base, &pdc_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "Unable to initialize regmap\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &qcom_pdc_reset_ops;
+ data->rcdev.nr_resets = desc->num_resets;
+ data->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static const struct of_device_id qcom_pdc_reset_of_match[] = {
+ { .compatible = "qcom,sc7280-pdc-global", .data = &sc7280_pdc_reset_desc },
+ { .compatible = "qcom,sdm845-pdc-global", .data = &sdm845_pdc_reset_desc },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_pdc_reset_of_match);
+
+static struct platform_driver qcom_pdc_reset_driver = {
+ .probe = qcom_pdc_reset_probe,
+ .driver = {
+ .name = "qcom_pdc_reset",
+ .of_match_table = qcom_pdc_reset_of_match,
+ },
+};
+module_platform_driver(qcom_pdc_reset_driver);
+
+MODULE_DESCRIPTION("Qualcomm PDC Reset Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-raspberrypi.c b/drivers/reset/reset-raspberrypi.c
new file mode 100644
index 000000000000..fa23db554bcf
--- /dev/null
+++ b/drivers/reset/reset-raspberrypi.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Raspberry Pi 4 firmware reset driver
+ *
+ * Copyright (C) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
+
+struct rpi_reset {
+ struct reset_controller_dev rcdev;
+ struct rpi_firmware *fw;
+};
+
+static inline struct rpi_reset *to_rpi(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct rpi_reset, rcdev);
+}
+
+static int rpi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct rpi_reset *priv = to_rpi(rcdev);
+ u32 dev_addr;
+ int ret;
+
+ switch (id) {
+ case RASPBERRYPI_FIRMWARE_RESET_ID_USB:
+ /*
+ * The Raspberry Pi 4 gets its USB functionality from VL805, a
+ * PCIe chip that implements xHCI. After a PCI reset, VL805's
+ * firmware may either be loaded directly from an EEPROM or, if
+ * not present, by the SoC's co-processor, VideoCore. rpi's
+ * VideoCore OS contains both the non public firmware load
+ * logic and the VL805 firmware blob. This triggers the
+ * aforementioned process.
+ *
+ * The pci device address is expected is expected by the
+ * firmware encoded like this:
+ *
+ * PCI_BUS << 20 | PCI_SLOT << 15 | PCI_FUNC << 12
+ *
+ * But since rpi's PCIe is hardwired, we know the address in
+ * advance.
+ */
+ dev_addr = 0x100000;
+ ret = rpi_firmware_property(priv->fw, RPI_FIRMWARE_NOTIFY_XHCI_RESET,
+ &dev_addr, sizeof(dev_addr));
+ if (ret)
+ return ret;
+
+ /* Wait for vl805 to startup */
+ usleep_range(200, 1000);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct reset_control_ops rpi_reset_ops = {
+ .reset = rpi_reset_reset,
+};
+
+static int rpi_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpi_firmware *fw;
+ struct device_node *np;
+ struct rpi_reset *priv;
+
+ np = of_get_parent(dev->of_node);
+ if (!np) {
+ dev_err(dev, "Missing firmware node\n");
+ return -ENOENT;
+ }
+
+ fw = devm_rpi_firmware_get(&pdev->dev, np);
+ of_node_put(np);
+ if (!fw)
+ return -EPROBE_DEFER;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ priv->fw = fw;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = RASPBERRYPI_FIRMWARE_RESET_NUM_IDS;
+ priv->rcdev.ops = &rpi_reset_ops;
+ priv->rcdev.of_node = dev->of_node;
+
+ return devm_reset_controller_register(dev, &priv->rcdev);
+}
+
+static const struct of_device_id rpi_reset_of_match[] = {
+ { .compatible = "raspberrypi,firmware-reset" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rpi_reset_of_match);
+
+static struct platform_driver rpi_reset_driver = {
+ .probe = rpi_reset_probe,
+ .driver = {
+ .name = "raspberrypi-reset",
+ .of_match_table = rpi_reset_of_match,
+ },
+};
+module_platform_driver(rpi_reset_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
+MODULE_DESCRIPTION("Raspberry Pi 4 firmware reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
new file mode 100644
index 000000000000..4ecb9acb2641
--- /dev/null
+++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G2L USBPHY control driver
+ *
+ * Copyright (C) 2021 Renesas Electronics Corporation
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+#include <linux/mfd/syscon.h>
+
+#define RESET 0x000
+#define VBENCTL 0x03c
+
+#define RESET_SEL_PLLRESET BIT(12)
+#define RESET_PLLRESET BIT(8)
+
+#define RESET_SEL_P2RESET BIT(5)
+#define RESET_SEL_P1RESET BIT(4)
+#define RESET_PHYRST_2 BIT(1)
+#define RESET_PHYRST_1 BIT(0)
+
+#define PHY_RESET_PORT2 (RESET_SEL_P2RESET | RESET_PHYRST_2)
+#define PHY_RESET_PORT1 (RESET_SEL_P1RESET | RESET_PHYRST_1)
+
+#define NUM_PORTS 2
+
+struct rzg2l_usbphy_ctrl_priv {
+ struct reset_controller_dev rcdev;
+ struct reset_control *rstc;
+ void __iomem *base;
+ struct platform_device *vdev;
+
+ spinlock_t lock;
+};
+
+#define rcdev_to_priv(x) container_of(x, struct rzg2l_usbphy_ctrl_priv, rcdev)
+
+static int rzg2l_usbphy_ctrl_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+ u32 port_mask = PHY_RESET_PORT1 | PHY_RESET_PORT2;
+ void __iomem *base = priv->base;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(base + RESET);
+ val |= id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
+ if (port_mask == (val & port_mask))
+ val |= RESET_PLLRESET;
+ writel(val, base + RESET);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rzg2l_usbphy_ctrl_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+ void __iomem *base = priv->base;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(base + RESET);
+
+ val |= RESET_SEL_PLLRESET;
+ val &= ~(RESET_PLLRESET | (id ? PHY_RESET_PORT2 : PHY_RESET_PORT1));
+ writel(val, base + RESET);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = rcdev_to_priv(rcdev);
+ u32 port_mask;
+
+ port_mask = id ? PHY_RESET_PORT2 : PHY_RESET_PORT1;
+
+ return !!(readl(priv->base + RESET) & port_mask);
+}
+
+#define RZG2L_USBPHY_CTRL_PWRRDY 1
+
+static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = {
+ { .compatible = "renesas,rzg2l-usbphy-ctrl" },
+ {
+ .compatible = "renesas,r9a08g045-usbphy-ctrl",
+ .data = (void *)RZG2L_USBPHY_CTRL_PWRRDY
+ },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg2l_usbphy_ctrl_match_table);
+
+static const struct reset_control_ops rzg2l_usbphy_ctrl_reset_ops = {
+ .assert = rzg2l_usbphy_ctrl_assert,
+ .deassert = rzg2l_usbphy_ctrl_deassert,
+ .status = rzg2l_usbphy_ctrl_status,
+};
+
+static const struct regmap_config rzg2l_usb_regconf = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 1,
+};
+
+static void rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy,
+ bool power_on)
+{
+ u32 val = power_on ? 0 : 1;
+
+ /* The initialization path guarantees that the mask is 1 bit long. */
+ regmap_field_update_bits(pwrrdy, 1, val);
+}
+
+static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data)
+{
+ rzg2l_usbphy_ctrl_set_pwrrdy(data, false);
+}
+
+static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev)
+{
+ struct regmap_field *pwrrdy;
+ struct reg_field field;
+ struct regmap *regmap;
+ const int *data;
+ u32 args[2];
+
+ data = device_get_match_data(dev);
+ if ((uintptr_t)data != RZG2L_USBPHY_CTRL_PWRRDY)
+ return 0;
+
+ regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "renesas,sysc-pwrrdy",
+ ARRAY_SIZE(args), args);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Don't allow more than one bit in mask. */
+ if (hweight32(args[1]) != 1)
+ return -EINVAL;
+
+ field.reg = args[0];
+ field.lsb = __ffs(args[1]);
+ field.msb = __fls(args[1]);
+
+ pwrrdy = devm_regmap_field_alloc(dev, regmap, field);
+ if (IS_ERR(pwrrdy))
+ return PTR_ERR(pwrrdy);
+
+ rzg2l_usbphy_ctrl_set_pwrrdy(pwrrdy, true);
+
+ return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy);
+}
+
+static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzg2l_usbphy_ctrl_priv *priv;
+ struct platform_device *vdev;
+ struct regmap *regmap;
+ unsigned long flags;
+ int error;
+ u32 val;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ regmap = devm_regmap_init_mmio(dev, priv->base + VBENCTL, &rzg2l_usb_regconf);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ error = rzg2l_usbphy_ctrl_pwrrdy_init(dev);
+ if (error)
+ return error;
+
+ priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(priv->rstc))
+ return dev_err_probe(dev, PTR_ERR(priv->rstc),
+ "failed to get reset\n");
+
+ error = reset_control_deassert(priv->rstc);
+ if (error)
+ return error;
+
+ spin_lock_init(&priv->lock);
+ dev_set_drvdata(dev, priv);
+
+ pm_runtime_enable(&pdev->dev);
+ error = pm_runtime_resume_and_get(&pdev->dev);
+ if (error < 0) {
+ dev_err_probe(&pdev->dev, error, "pm_runtime_resume_and_get failed");
+ goto err_pm_disable_reset_deassert;
+ }
+
+ /* put pll and phy into reset state */
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(priv->base + RESET);
+ val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1;
+ writel(val, priv->base + RESET);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.nr_resets = NUM_PORTS;
+ priv->rcdev.of_node = dev->of_node;
+ priv->rcdev.dev = dev;
+
+ error = devm_reset_controller_register(dev, &priv->rcdev);
+ if (error)
+ goto err_pm_runtime_put;
+
+ vdev = platform_device_alloc("rzg2l-usb-vbus-regulator", pdev->id);
+ if (!vdev) {
+ error = -ENOMEM;
+ goto err_pm_runtime_put;
+ }
+ vdev->dev.parent = dev;
+ priv->vdev = vdev;
+
+ device_set_of_node_from_dev(&vdev->dev, dev);
+ error = platform_device_add(vdev);
+ if (error)
+ goto err_device_put;
+
+ return 0;
+
+err_device_put:
+ platform_device_put(vdev);
+err_pm_runtime_put:
+ pm_runtime_put(&pdev->dev);
+err_pm_disable_reset_deassert:
+ pm_runtime_disable(&pdev->dev);
+ reset_control_assert(priv->rstc);
+ return error;
+}
+
+static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev)
+{
+ struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ platform_device_unregister(priv->vdev);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ reset_control_assert(priv->rstc);
+}
+
+static struct platform_driver rzg2l_usbphy_ctrl_driver = {
+ .driver = {
+ .name = "rzg2l_usbphy_ctrl",
+ .of_match_table = rzg2l_usbphy_ctrl_match_table,
+ },
+ .probe = rzg2l_usbphy_ctrl_probe,
+ .remove = rzg2l_usbphy_ctrl_remove,
+};
+module_platform_driver(rzg2l_usbphy_ctrl_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas RZ/G2L USBPHY Control");
+MODULE_AUTHOR("biju.das.jz@bp.renesas.com>");
diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c
new file mode 100644
index 000000000000..ae643575b067
--- /dev/null
+++ b/drivers/reset/reset-rzv2h-usb2phy.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/V2H(P) USB2PHY Port reset control driver
+ *
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+
+struct rzv2h_usb2phy_regval {
+ u16 reg;
+ u16 val;
+};
+
+struct rzv2h_usb2phy_reset_of_data {
+ const struct rzv2h_usb2phy_regval *init_vals;
+ unsigned int init_val_count;
+
+ u16 reset_reg;
+ u16 reset_assert_val;
+ u16 reset_deassert_val;
+ u16 reset_status_bits;
+ u16 reset_release_val;
+
+ u16 reset2_reg;
+ u16 reset2_acquire_val;
+ u16 reset2_release_val;
+};
+
+struct rzv2h_usb2phy_reset_priv {
+ const struct rzv2h_usb2phy_reset_of_data *data;
+ void __iomem *base;
+ struct device *dev;
+ struct reset_controller_dev rcdev;
+ spinlock_t lock; /* protects register accesses */
+};
+
+static inline struct rzv2h_usb2phy_reset_priv
+*rzv2h_usbphy_rcdev_to_priv(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev);
+}
+
+/* This function must be called only after pm_runtime_resume_and_get() has been called */
+static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv)
+{
+ const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
+
+ scoped_guard(spinlock, &priv->lock) {
+ writel(data->reset2_acquire_val, priv->base + data->reset2_reg);
+ writel(data->reset_assert_val, priv->base + data->reset_reg);
+ }
+
+ usleep_range(11, 20);
+}
+
+static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+ struct device *dev = priv->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_resume_and_get failed\n");
+ return ret;
+ }
+
+ rzv2h_usbphy_assert_helper(priv);
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+ const struct rzv2h_usb2phy_reset_of_data *data = priv->data;
+ struct device *dev = priv->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_resume_and_get failed\n");
+ return ret;
+ }
+
+ scoped_guard(spinlock, &priv->lock) {
+ writel(data->reset_deassert_val, priv->base + data->reset_reg);
+ writel(data->reset2_release_val, priv->base + data->reset2_reg);
+ writel(data->reset_release_val, priv->base + data->reset_reg);
+ }
+
+ pm_runtime_put(dev);
+
+ return 0;
+}
+
+static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev);
+ struct device *dev = priv->dev;
+ int ret;
+ u32 reg;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "pm_runtime_resume_and_get failed\n");
+ return ret;
+ }
+
+ reg = readl(priv->base + priv->data->reset_reg);
+
+ pm_runtime_put(dev);
+
+ return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits;
+}
+
+static const struct reset_control_ops rzv2h_usbphy_reset_ops = {
+ .assert = rzv2h_usbphy_reset_assert,
+ .deassert = rzv2h_usbphy_reset_deassert,
+ .status = rzv2h_usbphy_reset_status,
+};
+
+static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ /* No special handling needed, we have only one reset line per device */
+ return 0;
+}
+
+static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev)
+{
+ const struct rzv2h_usb2phy_reset_of_data *data;
+ struct rzv2h_usb2phy_reset_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rstc;
+ int error;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ data = of_device_get_match_data(dev);
+ priv->data = data;
+ priv->dev = dev;
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ rstc = devm_reset_control_get_shared_deasserted(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "failed to get deasserted reset\n");
+
+ spin_lock_init(&priv->lock);
+
+ error = devm_pm_runtime_enable(dev);
+ if (error)
+ return dev_err_probe(dev, error, "Failed to enable pm_runtime\n");
+
+ error = pm_runtime_resume_and_get(dev);
+ if (error)
+ return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n");
+
+ for (unsigned int i = 0; i < data->init_val_count; i++)
+ writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg);
+
+ /* keep usb2phy in asserted state */
+ rzv2h_usbphy_assert_helper(priv);
+
+ pm_runtime_put(dev);
+
+ priv->rcdev.ops = &rzv2h_usbphy_reset_ops;
+ priv->rcdev.of_reset_n_cells = 0;
+ priv->rcdev.nr_resets = 1;
+ priv->rcdev.of_xlate = rzv2h_usb2phy_reset_of_xlate;
+ priv->rcdev.of_node = dev->of_node;
+ priv->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &priv->rcdev);
+}
+
+/*
+ * initialization values required to prepare the PHY to receive
+ * assert and deassert requests.
+ */
+static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = {
+ { .reg = 0xc10, .val = 0x67c },
+ { .reg = 0xc14, .val = 0x1f },
+ { .reg = 0x600, .val = 0x909 },
+};
+
+static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = {
+ .init_vals = rzv2h_init_vals,
+ .init_val_count = ARRAY_SIZE(rzv2h_init_vals),
+ .reset_reg = 0,
+ .reset_assert_val = 0x206,
+ .reset_status_bits = BIT(2),
+ .reset_deassert_val = 0x200,
+ .reset_release_val = 0x0,
+ .reset2_reg = 0xb04,
+ .reset2_acquire_val = 0x303,
+ .reset2_release_val = 0x3,
+};
+
+static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = {
+ { .compatible = "renesas,r9a09g057-usb2phy-reset", .data = &rzv2h_reset_of_data },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzv2h_usb2phy_reset_of_match);
+
+static struct platform_driver rzv2h_usb2phy_reset_driver = {
+ .driver = {
+ .name = "rzv2h_usb2phy_reset",
+ .of_match_table = rzv2h_usb2phy_reset_of_match,
+ },
+ .probe = rzv2h_usb2phy_reset_probe,
+};
+module_platform_driver(rzv2h_usb2phy_reset_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
+MODULE_DESCRIPTION("Renesas RZ/V2H(P) USB2PHY Control");
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
new file mode 100644
index 000000000000..4335811e0cfa
--- /dev/null
+++ b/drivers/reset/reset-scmi.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM System Control and Management Interface (ARM SCMI) reset driver
+ *
+ * Copyright (C) 2019-2021 ARM Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/reset-controller.h>
+#include <linux/scmi_protocol.h>
+
+static const struct scmi_reset_proto_ops *reset_ops;
+
+/**
+ * struct scmi_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @ph: ARM SCMI protocol handle used for communication with system controller
+ */
+struct scmi_reset_data {
+ struct reset_controller_dev rcdev;
+ const struct scmi_protocol_handle *ph;
+};
+
+#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev)
+#define to_scmi_handle(p) (to_scmi_reset_data(p)->ph)
+
+/**
+ * scmi_reset_assert() - assert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be asserted
+ *
+ * This function implements the reset driver op to assert a device's reset
+ * using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->assert(ph, id);
+}
+
+/**
+ * scmi_reset_deassert() - deassert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be deasserted
+ *
+ * This function implements the reset driver op to deassert a device's reset
+ * using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->deassert(ph, id);
+}
+
+/**
+ * scmi_reset_reset() - reset the device
+ * @rcdev: reset controller entity
+ * @id: ID of the reset signal to be reset(assert + deassert)
+ *
+ * This function implements the reset driver op to trigger a device's
+ * reset signal using the ARM SCMI protocol.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int
+scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
+
+ return reset_ops->reset(ph, id);
+}
+
+static const struct reset_control_ops scmi_reset_ops = {
+ .assert = scmi_reset_assert,
+ .deassert = scmi_reset_deassert,
+ .reset = scmi_reset_reset,
+};
+
+static int scmi_reset_probe(struct scmi_device *sdev)
+{
+ struct scmi_reset_data *data;
+ struct device *dev = &sdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct scmi_handle *handle = sdev->handle;
+ struct scmi_protocol_handle *ph;
+
+ if (!handle)
+ return -ENODEV;
+
+ reset_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
+ if (IS_ERR(reset_ops))
+ return PTR_ERR(reset_ops);
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->rcdev.ops = &scmi_reset_ops;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.of_node = np;
+ data->rcdev.nr_resets = reset_ops->num_domains_get(ph);
+ data->ph = ph;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+ { SCMI_PROTOCOL_RESET, "reset" },
+ { },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_reset_driver = {
+ .name = "scmi-reset",
+ .probe = scmi_reset_probe,
+ .id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_reset_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI reset controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c
new file mode 100644
index 000000000000..79e94ecfe4f5
--- /dev/null
+++ b/drivers/reset/reset-simple.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Simple Reset Controller Driver
+ *
+ * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
+ *
+ * Based on Allwinner SoCs Reset Controller driver
+ *
+ * Copyright 2013 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/spinlock.h>
+
+static inline struct reset_simple_data *
+to_reset_simple_data(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct reset_simple_data, rcdev);
+}
+
+static int reset_simple_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct reset_simple_data *data = to_reset_simple_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ reg = readl(data->membase + (bank * reg_width));
+ if (assert ^ data->active_low)
+ reg |= BIT(offset);
+ else
+ reg &= ~BIT(offset);
+ writel(reg, data->membase + (bank * reg_width));
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ return 0;
+}
+
+static int reset_simple_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return reset_simple_update(rcdev, id, true);
+}
+
+static int reset_simple_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return reset_simple_update(rcdev, id, false);
+}
+
+static int reset_simple_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct reset_simple_data *data = to_reset_simple_data(rcdev);
+ int ret;
+
+ if (!data->reset_us)
+ return -ENOTSUPP;
+
+ ret = reset_simple_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ usleep_range(data->reset_us, data->reset_us * 2);
+
+ return reset_simple_deassert(rcdev, id);
+}
+
+static int reset_simple_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct reset_simple_data *data = to_reset_simple_data(rcdev);
+ int reg_width = sizeof(u32);
+ int bank = id / (reg_width * BITS_PER_BYTE);
+ int offset = id % (reg_width * BITS_PER_BYTE);
+ u32 reg;
+
+ reg = readl(data->membase + (bank * reg_width));
+
+ return !(reg & BIT(offset)) ^ !data->status_active_low;
+}
+
+const struct reset_control_ops reset_simple_ops = {
+ .assert = reset_simple_assert,
+ .deassert = reset_simple_deassert,
+ .reset = reset_simple_reset,
+ .status = reset_simple_status,
+};
+EXPORT_SYMBOL_GPL(reset_simple_ops);
+
+/**
+ * struct reset_simple_devdata - simple reset controller properties
+ * @reg_offset: offset between base address and first reset register.
+ * @nr_resets: number of resets. If not set, default to resource size in bits.
+ * @active_low: if true, bits are cleared to assert the reset. Otherwise, bits
+ * are set to assert the reset.
+ * @status_active_low: if true, bits read back as cleared while the reset is
+ * asserted. Otherwise, bits read back as set while the
+ * reset is asserted.
+ */
+struct reset_simple_devdata {
+ u32 reg_offset;
+ u32 nr_resets;
+ bool active_low;
+ bool status_active_low;
+};
+
+#define SOCFPGA_NR_BANKS 8
+
+static const struct reset_simple_devdata reset_simple_socfpga = {
+ .reg_offset = 0x20,
+ .nr_resets = SOCFPGA_NR_BANKS * 32,
+ .status_active_low = true,
+};
+
+static const struct reset_simple_devdata reset_simple_active_low = {
+ .active_low = true,
+ .status_active_low = true,
+};
+
+static const struct of_device_id reset_simple_dt_ids[] = {
+ { .compatible = "altr,stratix10-rst-mgr",
+ .data = &reset_simple_socfpga },
+ { .compatible = "st,stm32-rcc", },
+ { .compatible = "allwinner,sun6i-a31-clock-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "zte,zx296718-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "aspeed,ast2400-lpc-reset" },
+ { .compatible = "aspeed,ast2500-lpc-reset" },
+ { .compatible = "aspeed,ast2600-lpc-reset" },
+ { .compatible = "bitmain,bm1880-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "brcm,bcm4908-misc-pcie-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "snps,dw-high-reset" },
+ { .compatible = "snps,dw-low-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "sophgo,cv1800b-reset",
+ .data = &reset_simple_active_low },
+ { .compatible = "sophgo,sg2042-reset",
+ .data = &reset_simple_active_low },
+ { /* sentinel */ },
+};
+
+static int reset_simple_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct reset_simple_devdata *devdata;
+ struct reset_simple_data *data;
+ void __iomem *membase;
+ struct resource *res;
+ u32 reg_offset = 0;
+
+ devdata = of_device_get_match_data(dev);
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(membase))
+ return PTR_ERR(membase);
+
+ spin_lock_init(&data->lock);
+ data->membase = membase;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
+ data->rcdev.ops = &reset_simple_ops;
+ data->rcdev.of_node = dev->of_node;
+
+ if (devdata) {
+ reg_offset = devdata->reg_offset;
+ if (devdata->nr_resets)
+ data->rcdev.nr_resets = devdata->nr_resets;
+ data->active_low = devdata->active_low;
+ data->status_active_low = devdata->status_active_low;
+ }
+
+ data->membase += reg_offset;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver reset_simple_driver = {
+ .probe = reset_simple_probe,
+ .driver = {
+ .name = "simple-reset",
+ .of_match_table = reset_simple_dt_ids,
+ },
+};
+builtin_platform_driver(reset_simple_driver);
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
new file mode 100644
index 000000000000..8c6492e5693c
--- /dev/null
+++ b/drivers/reset/reset-socfpga.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018, Intel Corporation
+ * Copied from reset-sunxi.c
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/reset/socfpga.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#define SOCFPGA_NR_BANKS 8
+
+static int a10_reset_init(struct device_node *np)
+{
+ struct reset_simple_data *data;
+ struct resource res;
+ resource_size_t size;
+ int ret;
+ u32 reg_offset = 0x10;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret)
+ goto err_alloc;
+
+ size = resource_size(&res);
+ if (!request_mem_region(res.start, size, np->name)) {
+ ret = -EBUSY;
+ goto err_alloc;
+ }
+
+ data->membase = ioremap(res.start, size);
+ if (!data->membase) {
+ ret = -ENOMEM;
+ goto release_region;
+ }
+
+ if (of_property_read_u32(np, "altr,modrst-offset", &reg_offset))
+ pr_warn("missing altr,modrst-offset property, assuming 0x10\n");
+ data->membase += reg_offset;
+
+ spin_lock_init(&data->lock);
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = SOCFPGA_NR_BANKS * 32;
+ data->rcdev.ops = &reset_simple_ops;
+ data->rcdev.of_node = np;
+ data->status_active_low = true;
+
+ ret = reset_controller_register(&data->rcdev);
+ if (ret)
+ pr_err("unable to register device\n");
+
+ return ret;
+
+release_region:
+ release_mem_region(res.start, size);
+
+err_alloc:
+ kfree(data);
+ return ret;
+};
+
+/*
+ * These are the reset controller we need to initialize early on in
+ * our system, before we can even think of using a regular device
+ * driver for it.
+ * The controllers that we can register through the regular device
+ * model are handled by the simple reset driver directly.
+ */
+static const struct of_device_id socfpga_early_reset_dt_ids[] __initconst = {
+ { .compatible = "altr,rst-mgr", },
+ { /* sentinel */ },
+};
+
+void __init socfpga_reset_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, socfpga_early_reset_dt_ids)
+ a10_reset_init(np);
+}
+
+/*
+ * The early driver is problematic, because it doesn't register
+ * itself as a driver. This causes certain device links to prevent
+ * consumer devices from probing. The hacky solution is to register
+ * an empty driver, whose only job is to attach itself to the reset
+ * manager and call probe.
+ */
+static const struct of_device_id socfpga_reset_dt_ids[] = {
+ { .compatible = "altr,rst-mgr", },
+ { /* sentinel */ },
+};
+
+static int reset_simple_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver reset_socfpga_driver = {
+ .probe = reset_simple_probe,
+ .driver = {
+ .name = "socfpga-reset",
+ .of_match_table = socfpga_reset_dt_ids,
+ },
+};
+builtin_platform_driver(reset_socfpga_driver);
diff --git a/drivers/reset/reset-spacemit.c b/drivers/reset/reset-spacemit.c
new file mode 100644
index 000000000000..e1272aff28f7
--- /dev/null
+++ b/drivers/reset/reset-spacemit.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* SpacemiT reset controller driver */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <linux/types.h>
+
+#include <soc/spacemit/k1-syscon.h>
+#include <dt-bindings/clock/spacemit,k1-syscon.h>
+
+struct ccu_reset_data {
+ u32 offset;
+ u32 assert_mask;
+ u32 deassert_mask;
+};
+
+struct ccu_reset_controller_data {
+ const struct ccu_reset_data *reset_data; /* array */
+ size_t count;
+};
+
+struct ccu_reset_controller {
+ struct reset_controller_dev rcdev;
+ const struct ccu_reset_controller_data *data;
+ struct regmap *regmap;
+};
+
+#define RESET_DATA(_offset, _assert_mask, _deassert_mask) \
+ { \
+ .offset = (_offset), \
+ .assert_mask = (_assert_mask), \
+ .deassert_mask = (_deassert_mask), \
+ }
+
+static const struct ccu_reset_data k1_mpmu_resets[] = {
+ [RESET_WDT] = RESET_DATA(MPMU_WDTPCR, BIT(2), 0),
+};
+
+static const struct ccu_reset_controller_data k1_mpmu_reset_data = {
+ .reset_data = k1_mpmu_resets,
+ .count = ARRAY_SIZE(k1_mpmu_resets),
+};
+
+static const struct ccu_reset_data k1_apbc_resets[] = {
+ [RESET_UART0] = RESET_DATA(APBC_UART1_CLK_RST, BIT(2), 0),
+ [RESET_UART2] = RESET_DATA(APBC_UART2_CLK_RST, BIT(2), 0),
+ [RESET_GPIO] = RESET_DATA(APBC_GPIO_CLK_RST, BIT(2), 0),
+ [RESET_PWM0] = RESET_DATA(APBC_PWM0_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM1] = RESET_DATA(APBC_PWM1_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM2] = RESET_DATA(APBC_PWM2_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM3] = RESET_DATA(APBC_PWM3_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM4] = RESET_DATA(APBC_PWM4_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM5] = RESET_DATA(APBC_PWM5_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM6] = RESET_DATA(APBC_PWM6_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM7] = RESET_DATA(APBC_PWM7_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM8] = RESET_DATA(APBC_PWM8_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM9] = RESET_DATA(APBC_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM10] = RESET_DATA(APBC_PWM10_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM11] = RESET_DATA(APBC_PWM11_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM12] = RESET_DATA(APBC_PWM12_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM13] = RESET_DATA(APBC_PWM13_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM14] = RESET_DATA(APBC_PWM14_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM15] = RESET_DATA(APBC_PWM15_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM16] = RESET_DATA(APBC_PWM16_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM17] = RESET_DATA(APBC_PWM17_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM18] = RESET_DATA(APBC_PWM18_CLK_RST, BIT(2), BIT(0)),
+ [RESET_PWM19] = RESET_DATA(APBC_PWM19_CLK_RST, BIT(2), BIT(0)),
+ [RESET_SSP3] = RESET_DATA(APBC_SSP3_CLK_RST, BIT(2), 0),
+ [RESET_UART3] = RESET_DATA(APBC_UART3_CLK_RST, BIT(2), 0),
+ [RESET_RTC] = RESET_DATA(APBC_RTC_CLK_RST, BIT(2), 0),
+ [RESET_TWSI0] = RESET_DATA(APBC_TWSI0_CLK_RST, BIT(2), 0),
+ [RESET_TIMERS1] = RESET_DATA(APBC_TIMERS1_CLK_RST, BIT(2), 0),
+ [RESET_AIB] = RESET_DATA(APBC_AIB_CLK_RST, BIT(2), 0),
+ [RESET_TIMERS2] = RESET_DATA(APBC_TIMERS2_CLK_RST, BIT(2), 0),
+ [RESET_ONEWIRE] = RESET_DATA(APBC_ONEWIRE_CLK_RST, BIT(2), 0),
+ [RESET_SSPA0] = RESET_DATA(APBC_SSPA0_CLK_RST, BIT(2), 0),
+ [RESET_SSPA1] = RESET_DATA(APBC_SSPA1_CLK_RST, BIT(2), 0),
+ [RESET_DRO] = RESET_DATA(APBC_DRO_CLK_RST, BIT(2), 0),
+ [RESET_IR] = RESET_DATA(APBC_IR_CLK_RST, BIT(2), 0),
+ [RESET_TWSI1] = RESET_DATA(APBC_TWSI1_CLK_RST, BIT(2), 0),
+ [RESET_TSEN] = RESET_DATA(APBC_TSEN_CLK_RST, BIT(2), 0),
+ [RESET_TWSI2] = RESET_DATA(APBC_TWSI2_CLK_RST, BIT(2), 0),
+ [RESET_TWSI4] = RESET_DATA(APBC_TWSI4_CLK_RST, BIT(2), 0),
+ [RESET_TWSI5] = RESET_DATA(APBC_TWSI5_CLK_RST, BIT(2), 0),
+ [RESET_TWSI6] = RESET_DATA(APBC_TWSI6_CLK_RST, BIT(2), 0),
+ [RESET_TWSI7] = RESET_DATA(APBC_TWSI7_CLK_RST, BIT(2), 0),
+ [RESET_TWSI8] = RESET_DATA(APBC_TWSI8_CLK_RST, BIT(2), 0),
+ [RESET_IPC_AP2AUD] = RESET_DATA(APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0),
+ [RESET_UART4] = RESET_DATA(APBC_UART4_CLK_RST, BIT(2), 0),
+ [RESET_UART5] = RESET_DATA(APBC_UART5_CLK_RST, BIT(2), 0),
+ [RESET_UART6] = RESET_DATA(APBC_UART6_CLK_RST, BIT(2), 0),
+ [RESET_UART7] = RESET_DATA(APBC_UART7_CLK_RST, BIT(2), 0),
+ [RESET_UART8] = RESET_DATA(APBC_UART8_CLK_RST, BIT(2), 0),
+ [RESET_UART9] = RESET_DATA(APBC_UART9_CLK_RST, BIT(2), 0),
+ [RESET_CAN0] = RESET_DATA(APBC_CAN0_CLK_RST, BIT(2), 0),
+};
+
+static const struct ccu_reset_controller_data k1_apbc_reset_data = {
+ .reset_data = k1_apbc_resets,
+ .count = ARRAY_SIZE(k1_apbc_resets),
+};
+
+static const struct ccu_reset_data k1_apmu_resets[] = {
+ [RESET_CCIC_4X] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_CCIC1_PHY] = RESET_DATA(APMU_CCIC_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_SDH_AXI] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_USBP1_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_USB_AXI] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_USB30_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(9)),
+ [RESET_USB30_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(10)),
+ [RESET_USB30_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(11)),
+ [RESET_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AES] = RESET_DATA(APMU_AES_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_VPU] = RESET_DATA(APMU_VPU_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_GPU] = RESET_DATA(APMU_GPU_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMMC] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMMC_X] = RESET_DATA(APMU_PMUA_EM_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AUDIO_SYS] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_AUDIO_MCU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_AUDIO_APMU] = RESET_DATA(APMU_AUDIO_CLK_RES_CTRL, 0, BIT(3)),
+ [RESET_HDMI] = RESET_DATA(APMU_HDMI_CLK_RES_CTRL, 0, BIT(9)),
+ [RESET_PCIE0_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(3)),
+ [RESET_PCIE0_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(4)),
+ [RESET_PCIE0_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, 0, BIT(5)),
+ [RESET_PCIE0_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_0, BIT(8), 0),
+ [RESET_PCIE1_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(3)),
+ [RESET_PCIE1_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(4)),
+ [RESET_PCIE1_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, 0, BIT(5)),
+ [RESET_PCIE1_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_1, BIT(8), 0),
+ [RESET_PCIE2_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(3)),
+ [RESET_PCIE2_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(4)),
+ [RESET_PCIE2_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, 0, BIT(5)),
+ [RESET_PCIE2_GLOBAL] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_2, BIT(8), 0),
+ [RESET_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_JPG] = RESET_DATA(APMU_JPG_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_CCIC2PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_CCIC3PHY] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(29)),
+ [RESET_CSI] = RESET_DATA(APMU_CSI_CCIC2_CLK_RES_CTRL, 0, BIT(1)),
+ [RESET_ISP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(0)),
+ [RESET_ISP_CPP] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(27)),
+ [RESET_ISP_BUS] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(3)),
+ [RESET_ISP_CI] = RESET_DATA(APMU_ISP_CLK_RES_CTRL, 0, BIT(16)),
+ [RESET_DPU_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL2, 0, BIT(9)),
+ [RESET_DPU_ESC] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(3)),
+ [RESET_DPU_HCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(4)),
+ [RESET_DPU_SPIBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(4)),
+ [RESET_DPU_SPI_HBUS] = RESET_DATA(APMU_LCD_SPI_CLK_RES_CTRL, 0, BIT(2)),
+ [RESET_V2D] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(27)),
+ [RESET_MIPI] = RESET_DATA(APMU_LCD_CLK_RES_CTRL1, 0, BIT(15)),
+ [RESET_MC] = RESET_DATA(APMU_PMUA_MC_CTRL, 0, BIT(0)),
+};
+
+static const struct ccu_reset_controller_data k1_apmu_reset_data = {
+ .reset_data = k1_apmu_resets,
+ .count = ARRAY_SIZE(k1_apmu_resets),
+};
+
+static const struct ccu_reset_data k1_rcpu_resets[] = {
+ [RESET_RCPU_SSP0] = RESET_DATA(RCPU_SSP0_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_I2C0] = RESET_DATA(RCPU_I2C0_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_UART1] = RESET_DATA(RCPU_UART1_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_IR] = RESET_DATA(RCPU_CAN_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_CAN] = RESET_DATA(RCPU_IR_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_UART0] = RESET_DATA(RCPU_UART0_CLK_RST, 0, BIT(0)),
+ [RESET_RCPU_HDMI_AUDIO] = RESET_DATA(AUDIO_HDMI_CLK_CTRL, 0, BIT(0)),
+};
+
+static const struct ccu_reset_controller_data k1_rcpu_reset_data = {
+ .reset_data = k1_rcpu_resets,
+ .count = ARRAY_SIZE(k1_rcpu_resets),
+};
+
+static const struct ccu_reset_data k1_rcpu2_resets[] = {
+ [RESET_RCPU2_PWM0] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM1] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM2] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM3] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM4] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM5] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM6] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM7] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM8] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+ [RESET_RCPU2_PWM9] = RESET_DATA(RCPU2_PWM9_CLK_RST, BIT(2), BIT(0)),
+};
+
+static const struct ccu_reset_controller_data k1_rcpu2_reset_data = {
+ .reset_data = k1_rcpu2_resets,
+ .count = ARRAY_SIZE(k1_rcpu2_resets),
+};
+
+static const struct ccu_reset_data k1_apbc2_resets[] = {
+ [RESET_APBC2_UART1] = RESET_DATA(APBC2_UART1_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_SSP2] = RESET_DATA(APBC2_SSP2_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_TWSI3] = RESET_DATA(APBC2_TWSI3_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_RTC] = RESET_DATA(APBC2_RTC_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_TIMERS0] = RESET_DATA(APBC2_TIMERS0_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_KPC] = RESET_DATA(APBC2_KPC_CLK_RST, BIT(2), 0),
+ [RESET_APBC2_GPIO] = RESET_DATA(APBC2_GPIO_CLK_RST, BIT(2), 0),
+};
+
+static const struct ccu_reset_controller_data k1_apbc2_reset_data = {
+ .reset_data = k1_apbc2_resets,
+ .count = ARRAY_SIZE(k1_apbc2_resets),
+};
+
+static int spacemit_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct ccu_reset_controller *controller;
+ const struct ccu_reset_data *data;
+ u32 mask;
+ u32 val;
+
+ controller = container_of(rcdev, struct ccu_reset_controller, rcdev);
+ data = &controller->data->reset_data[id];
+ mask = data->assert_mask | data->deassert_mask;
+ val = assert ? data->assert_mask : data->deassert_mask;
+
+ return regmap_update_bits(controller->regmap, data->offset, mask, val);
+}
+
+static int spacemit_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return spacemit_reset_update(rcdev, id, true);
+}
+
+static int spacemit_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return spacemit_reset_update(rcdev, id, false);
+}
+
+static const struct reset_control_ops spacemit_reset_control_ops = {
+ .assert = spacemit_reset_assert,
+ .deassert = spacemit_reset_deassert,
+};
+
+static int spacemit_reset_controller_register(struct device *dev,
+ struct ccu_reset_controller *controller)
+{
+ struct reset_controller_dev *rcdev = &controller->rcdev;
+
+ rcdev->ops = &spacemit_reset_control_ops;
+ rcdev->owner = THIS_MODULE;
+ rcdev->of_node = dev->of_node;
+ rcdev->nr_resets = controller->data->count;
+
+ return devm_reset_controller_register(dev, &controller->rcdev);
+}
+
+static int spacemit_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
+ struct ccu_reset_controller *controller;
+ struct device *dev = &adev->dev;
+
+ controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+ controller->data = (const struct ccu_reset_controller_data *)id->driver_data;
+ controller->regmap = rdev->regmap;
+
+ return spacemit_reset_controller_register(dev, controller);
+}
+
+#define K1_AUX_DEV_ID(_unit) \
+ { \
+ .name = "spacemit_ccu_k1." #_unit "-reset", \
+ .driver_data = (kernel_ulong_t)&k1_ ## _unit ## _reset_data, \
+ }
+
+static const struct auxiliary_device_id spacemit_reset_ids[] = {
+ K1_AUX_DEV_ID(mpmu),
+ K1_AUX_DEV_ID(apbc),
+ K1_AUX_DEV_ID(apmu),
+ K1_AUX_DEV_ID(rcpu),
+ K1_AUX_DEV_ID(rcpu2),
+ K1_AUX_DEV_ID(apbc2),
+ { },
+};
+MODULE_DEVICE_TABLE(auxiliary, spacemit_reset_ids);
+
+static struct auxiliary_driver spacemit_k1_reset_driver = {
+ .probe = spacemit_reset_probe,
+ .id_table = spacemit_reset_ids,
+};
+module_auxiliary_driver(spacemit_k1_reset_driver);
+
+MODULE_AUTHOR("Alex Elder <elder@kernel.org>");
+MODULE_DESCRIPTION("SpacemiT reset controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c
new file mode 100644
index 000000000000..df58decab64d
--- /dev/null
+++ b/drivers/reset/reset-sunplus.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * SP7021 reset driver
+ *
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+/* HIWORD_MASK_REG BITS */
+#define BITS_PER_HWM_REG 16
+
+/* resets HW info: reg_index_shift */
+static const u32 sp_resets[] = {
+/* SP7021: mo_reset0 ~ mo_reset9 */
+ 0x00,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0a,
+ 0x0b,
+ 0x0d,
+ 0x0e,
+ 0x0f,
+ 0x10,
+ 0x12,
+ 0x14,
+ 0x15,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x19,
+ 0x1a,
+ 0x1b,
+ 0x1c,
+ 0x1d,
+ 0x1e,
+ 0x1f,
+ 0x20,
+ 0x21,
+ 0x22,
+ 0x23,
+ 0x24,
+ 0x25,
+ 0x26,
+ 0x2a,
+ 0x2b,
+ 0x2d,
+ 0x2e,
+ 0x30,
+ 0x31,
+ 0x32,
+ 0x33,
+ 0x3d,
+ 0x3e,
+ 0x3f,
+ 0x42,
+ 0x44,
+ 0x4b,
+ 0x4c,
+ 0x4d,
+ 0x4e,
+ 0x4f,
+ 0x50,
+ 0x55,
+ 0x60,
+ 0x61,
+ 0x6a,
+ 0x6f,
+ 0x70,
+ 0x73,
+ 0x74,
+ 0x86,
+ 0x8a,
+ 0x8b,
+ 0x8d,
+ 0x8e,
+ 0x8f,
+ 0x90,
+ 0x92,
+ 0x93,
+ 0x94,
+ 0x95,
+ 0x96,
+ 0x97,
+ 0x98,
+ 0x99,
+};
+
+struct sp_reset {
+ struct reset_controller_dev rcdev;
+ struct notifier_block notifier;
+ void __iomem *base;
+};
+
+static inline struct sp_reset *to_sp_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct sp_reset, rcdev);
+}
+
+static int sp_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct sp_reset *reset = to_sp_reset(rcdev);
+ int index = sp_resets[id] / BITS_PER_HWM_REG;
+ int shift = sp_resets[id] % BITS_PER_HWM_REG;
+ u32 val;
+
+ val = (1 << (16 + shift)) | (assert << shift);
+ writel(val, reset->base + (index * 4));
+
+ return 0;
+}
+
+static int sp_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return sp_reset_update(rcdev, id, true);
+}
+
+static int sp_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return sp_reset_update(rcdev, id, false);
+}
+
+static int sp_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct sp_reset *reset = to_sp_reset(rcdev);
+ int index = sp_resets[id] / BITS_PER_HWM_REG;
+ int shift = sp_resets[id] % BITS_PER_HWM_REG;
+ u32 reg;
+
+ reg = readl(reset->base + (index * 4));
+
+ return !!(reg & BIT(shift));
+}
+
+static const struct reset_control_ops sp_reset_ops = {
+ .assert = sp_reset_assert,
+ .deassert = sp_reset_deassert,
+ .status = sp_reset_status,
+};
+
+static int sp_restart(struct notifier_block *nb, unsigned long mode,
+ void *cmd)
+{
+ struct sp_reset *reset = container_of(nb, struct sp_reset, notifier);
+
+ sp_reset_assert(&reset->rcdev, 0);
+ sp_reset_deassert(&reset->rcdev, 0);
+
+ return NOTIFY_DONE;
+}
+
+static int sp_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sp_reset *reset;
+ struct resource *res;
+ int ret;
+
+ reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL);
+ if (!reset)
+ return -ENOMEM;
+
+ reset->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(reset->base))
+ return PTR_ERR(reset->base);
+
+ reset->rcdev.ops = &sp_reset_ops;
+ reset->rcdev.owner = THIS_MODULE;
+ reset->rcdev.of_node = dev->of_node;
+ reset->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_HWM_REG;
+
+ ret = devm_reset_controller_register(dev, &reset->rcdev);
+ if (ret)
+ return ret;
+
+ reset->notifier.notifier_call = sp_restart;
+ reset->notifier.priority = 192;
+
+ return register_restart_handler(&reset->notifier);
+}
+
+static const struct of_device_id sp_reset_dt_ids[] = {
+ {.compatible = "sunplus,sp7021-reset",},
+ { /* sentinel */ },
+};
+
+static struct platform_driver sp_reset_driver = {
+ .probe = sp_reset_probe,
+ .driver = {
+ .name = "sunplus-reset",
+ .of_match_table = sp_reset_dt_ids,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(sp_reset_driver);
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
new file mode 100644
index 000000000000..e752594b6971
--- /dev/null
+++ b/drivers/reset/reset-sunxi.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Allwinner SoCs Reset Controller driver
+ *
+ * Copyright 2013 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/reset/sunxi.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+static int sunxi_reset_init(struct device_node *np)
+{
+ struct reset_simple_data *data;
+ struct resource res;
+ resource_size_t size;
+ int ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = of_address_to_resource(np, 0, &res);
+ if (ret)
+ goto err_alloc;
+
+ size = resource_size(&res);
+ if (!request_mem_region(res.start, size, np->name)) {
+ ret = -EBUSY;
+ goto err_alloc;
+ }
+
+ data->membase = ioremap(res.start, size);
+ if (!data->membase) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ spin_lock_init(&data->lock);
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.nr_resets = size * 8;
+ data->rcdev.ops = &reset_simple_ops;
+ data->rcdev.of_node = np;
+ data->active_low = true;
+
+ return reset_controller_register(&data->rcdev);
+
+err_alloc:
+ kfree(data);
+ return ret;
+};
+
+/*
+ * These are the reset controller we need to initialize early on in
+ * our system, before we can even think of using a regular device
+ * driver for it.
+ * The controllers that we can register through the regular device
+ * model are handled by the simple reset driver directly.
+ */
+static const struct of_device_id sunxi_early_reset_dt_ids[] __initconst = {
+ { .compatible = "allwinner,sun6i-a31-ahb1-reset", },
+ { /* sentinel */ },
+};
+
+void __init sun6i_reset_init(void)
+{
+ struct device_node *np;
+
+ for_each_matching_node(np, sunxi_early_reset_dt_ids)
+ sunxi_reset_init(np);
+}
diff --git a/drivers/reset/reset-th1520.c b/drivers/reset/reset-th1520.c
new file mode 100644
index 000000000000..fd32e991c4cb
--- /dev/null
+++ b/drivers/reset/reset-th1520.c
@@ -0,0 +1,983 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Author: Michal Wilczynski <m.wilczynski@samsung.com>
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/reset/thead,th1520-reset.h>
+
+ /* register offset in RSTGEN_R */
+#define TH1520_BROM_RST_CFG 0x0
+#define TH1520_C910_RST_CFG 0x4
+#define TH1520_CHIP_DBG_RST_CFG 0xc
+#define TH1520_AXI4_CPUSYS2_RST_CFG 0x10
+#define TH1520_X2H_CPUSYS_RST_CFG 0x18
+#define TH1520_AHB2_CPUSYS_RST_CFG 0x1c
+#define TH1520_APB3_CPUSYS_RST_CFG 0x20
+#define TH1520_MBOX0_RST_CFG 0x24
+#define TH1520_MBOX1_RST_CFG 0x28
+#define TH1520_MBOX2_RST_CFG 0x2c
+#define TH1520_MBOX3_RST_CFG 0x30
+#define TH1520_WDT0_RST_CFG 0x34
+#define TH1520_WDT1_RST_CFG 0x38
+#define TH1520_TIMER0_RST_CFG 0x3c
+#define TH1520_TIMER1_RST_CFG 0x40
+#define TH1520_PERISYS_AHB_RST_CFG 0x44
+#define TH1520_PERISYS_APB1_RST_CFG 0x48
+#define TH1520_PERISYS_APB2_RST_CFG 0x4c
+#define TH1520_GMAC0_RST_CFG 0x68
+#define TH1520_UART0_RST_CFG 0x70
+#define TH1520_UART1_RST_CFG 0x74
+#define TH1520_UART2_RST_CFG 0x78
+#define TH1520_UART3_RST_CFG 0x7c
+#define TH1520_UART4_RST_CFG 0x80
+#define TH1520_UART5_RST_CFG 0x84
+#define TH1520_QSPI0_RST_CFG 0x8c
+#define TH1520_QSPI1_RST_CFG 0x90
+#define TH1520_SPI_RST_CFG 0x94
+#define TH1520_I2C0_RST_CFG 0x98
+#define TH1520_I2C1_RST_CFG 0x9c
+#define TH1520_I2C2_RST_CFG 0xa0
+#define TH1520_I2C3_RST_CFG 0xa4
+#define TH1520_I2C4_RST_CFG 0xa8
+#define TH1520_I2C5_RST_CFG 0xac
+#define TH1520_GPIO0_RST_CFG 0xb0
+#define TH1520_GPIO1_RST_CFG 0xb4
+#define TH1520_GPIO2_RST_CFG 0xb8
+#define TH1520_PWM_RST_CFG 0xc0
+#define TH1520_PADCTRL0_APSYS_RST_CFG 0xc4
+#define TH1520_CPU2PERI_X2H_RST_CFG 0xcc
+#define TH1520_CPU2AON_X2H_RST_CFG 0xe4
+#define TH1520_AON2CPU_A2X_RST_CFG 0xfc
+#define TH1520_NPUSYS_AXI_RST_CFG 0x128
+#define TH1520_CPU2VP_X2P_RST_CFG 0x12c
+#define TH1520_CPU2VI_X2H_RST_CFG 0x138
+#define TH1520_BMU_C910_RST_CFG 0x148
+#define TH1520_DMAC_CPUSYS_RST_CFG 0x14c
+#define TH1520_SPINLOCK_RST_CFG 0x178
+#define TH1520_CFG2TEE_X2H_RST_CFG 0x188
+#define TH1520_DSMART_RST_CFG 0x18c
+#define TH1520_GPIO3_RST_CFG 0x1a8
+#define TH1520_I2S_RST_CFG 0x1ac
+#define TH1520_IMG_NNA_RST_CFG 0x1b0
+#define TH1520_PERI_APB3_RST_CFG 0x1dc
+#define TH1520_VP_SUBSYS_RST_CFG 0x1ec
+#define TH1520_PERISYS_APB4_RST_CFG 0x1f8
+#define TH1520_GMAC1_RST_CFG 0x204
+#define TH1520_GMAC_AXI_RST_CFG 0x208
+#define TH1520_PADCTRL1_APSYS_RST_CFG 0x20c
+#define TH1520_VOSYS_AXI_RST_CFG 0x210
+#define TH1520_VOSYS_X2X_RST_CFG 0x214
+#define TH1520_MISC2VP_X2X_RST_CFG 0x218
+#define TH1520_SUBSYS_RST_CFG 0x220
+
+ /* register offset in DSP_REGMAP */
+#define TH1520_DSPSYS_RST_CFG 0x0
+
+ /* register offset in MISCSYS_REGMAP */
+#define TH1520_EMMC_RST_CFG 0x0
+#define TH1520_MISCSYS_AXI_RST_CFG 0x8
+#define TH1520_SDIO0_RST_CFG 0xc
+#define TH1520_SDIO1_RST_CFG 0x10
+#define TH1520_USB3_DRD_RST_CFG 0x14
+
+ /* register offset in VISYS_REGMAP */
+#define TH1520_VISYS_RST_CFG 0x0
+#define TH1520_VISYS_2_RST_CFG 0x4
+
+ /* register offset in VOSYS_REGMAP */
+#define TH1520_GPU_RST_CFG 0x0
+#define TH1520_GPU_RST_CFG_MASK GENMASK(1, 0)
+#define TH1520_DPU_RST_CFG 0x4
+#define TH1520_DSI0_RST_CFG 0x8
+#define TH1520_DSI1_RST_CFG 0xc
+#define TH1520_HDMI_RST_CFG 0x14
+#define TH1520_AXI4_VO_DW_AXI_RST_CFG 0x18
+#define TH1520_X2H_X4_VOSYS_DW_RST_CFG 0x20
+
+/* register values */
+#define TH1520_GPU_SW_GPU_RST BIT(0)
+#define TH1520_GPU_SW_CLKGEN_RST BIT(1)
+#define TH1520_DPU_SW_DPU_HRST BIT(0)
+#define TH1520_DPU_SW_DPU_ARST BIT(1)
+#define TH1520_DPU_SW_DPU_CRST BIT(2)
+#define TH1520_DSI_SW_DSI_PRST BIT(0)
+#define TH1520_HDMI_SW_MAIN_RST BIT(0)
+#define TH1520_HDMI_SW_PRST BIT(1)
+
+ /* register offset in VPSYS_REGMAP */
+#define TH1520_AXIBUS_RST_CFG 0x0
+#define TH1520_FCE_RST_CFG 0x4
+#define TH1520_G2D_RST_CFG 0x8
+#define TH1520_VDEC_RST_CFG 0xc
+#define TH1520_VENC_RST_CFG 0x10
+
+struct th1520_reset_map {
+ u32 bit;
+ u32 reg;
+};
+
+struct th1520_reset_priv {
+ struct reset_controller_dev rcdev;
+ struct regmap *map;
+ const struct th1520_reset_map *resets;
+};
+
+struct th1520_reset_data {
+ const struct th1520_reset_map *resets;
+ size_t num;
+};
+
+static const struct th1520_reset_map th1520_resets[] = {
+ [TH1520_RESET_ID_GPU] = {
+ .bit = TH1520_GPU_SW_GPU_RST,
+ .reg = TH1520_GPU_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPU_CLKGEN] = {
+ .bit = TH1520_GPU_SW_CLKGEN_RST,
+ .reg = TH1520_GPU_RST_CFG,
+ },
+ [TH1520_RESET_ID_DPU_AHB] = {
+ .bit = TH1520_DPU_SW_DPU_HRST,
+ .reg = TH1520_DPU_RST_CFG,
+ },
+ [TH1520_RESET_ID_DPU_AXI] = {
+ .bit = TH1520_DPU_SW_DPU_ARST,
+ .reg = TH1520_DPU_RST_CFG,
+ },
+ [TH1520_RESET_ID_DPU_CORE] = {
+ .bit = TH1520_DPU_SW_DPU_CRST,
+ .reg = TH1520_DPU_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSI0_APB] = {
+ .bit = TH1520_DSI_SW_DSI_PRST,
+ .reg = TH1520_DSI0_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSI1_APB] = {
+ .bit = TH1520_DSI_SW_DSI_PRST,
+ .reg = TH1520_DSI1_RST_CFG,
+ },
+ [TH1520_RESET_ID_HDMI] = {
+ .bit = TH1520_HDMI_SW_MAIN_RST,
+ .reg = TH1520_HDMI_RST_CFG,
+ },
+ [TH1520_RESET_ID_HDMI_APB] = {
+ .bit = TH1520_HDMI_SW_PRST,
+ .reg = TH1520_HDMI_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOAXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_AXI4_VO_DW_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOAXI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_AXI4_VO_DW_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2H_DPU_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_X2H_X4_VOSYS_DW_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2H_DPU_AHB] = {
+ .bit = BIT(1),
+ .reg = TH1520_X2H_X4_VOSYS_DW_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2H_DPU1_AXI] = {
+ .bit = BIT(2),
+ .reg = TH1520_X2H_X4_VOSYS_DW_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2H_DPU1_AHB] = {
+ .bit = BIT(3),
+ .reg = TH1520_X2H_X4_VOSYS_DW_RST_CFG,
+ },
+};
+
+static const struct th1520_reset_map th1520_ap_resets[] = {
+ [TH1520_RESET_ID_BROM] = {
+ .bit = BIT(0),
+ .reg = TH1520_BROM_RST_CFG,
+ },
+ [TH1520_RESET_ID_C910_TOP] = {
+ .bit = BIT(0),
+ .reg = TH1520_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_NPU] = {
+ .bit = BIT(0),
+ .reg = TH1520_IMG_NNA_RST_CFG,
+ },
+ [TH1520_RESET_ID_WDT0] = {
+ .bit = BIT(0),
+ .reg = TH1520_WDT0_RST_CFG,
+ },
+ [TH1520_RESET_ID_WDT1] = {
+ .bit = BIT(0),
+ .reg = TH1520_WDT1_RST_CFG,
+ },
+ [TH1520_RESET_ID_C910_C0] = {
+ .bit = BIT(1),
+ .reg = TH1520_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_C910_C1] = {
+ .bit = BIT(2),
+ .reg = TH1520_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_C910_C2] = {
+ .bit = BIT(3),
+ .reg = TH1520_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_C910_C3] = {
+ .bit = BIT(4),
+ .reg = TH1520_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_CHIP_DBG_CORE] = {
+ .bit = BIT(0),
+ .reg = TH1520_CHIP_DBG_RST_CFG,
+ },
+ [TH1520_RESET_ID_CHIP_DBG_AXI] = {
+ .bit = BIT(1),
+ .reg = TH1520_CHIP_DBG_RST_CFG,
+ },
+ [TH1520_RESET_ID_AXI4_CPUSYS2_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_AXI4_CPUSYS2_RST_CFG,
+ },
+ [TH1520_RESET_ID_AXI4_CPUSYS2_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_AXI4_CPUSYS2_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2H_CPUSYS] = {
+ .bit = BIT(0),
+ .reg = TH1520_X2H_CPUSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_AHB2_CPUSYS] = {
+ .bit = BIT(0),
+ .reg = TH1520_AHB2_CPUSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_APB3_CPUSYS] = {
+ .bit = BIT(0),
+ .reg = TH1520_APB3_CPUSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_MBOX0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_MBOX0_RST_CFG,
+ },
+ [TH1520_RESET_ID_MBOX1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_MBOX1_RST_CFG,
+ },
+ [TH1520_RESET_ID_MBOX2_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_MBOX2_RST_CFG,
+ },
+ [TH1520_RESET_ID_MBOX3_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_MBOX3_RST_CFG,
+ },
+ [TH1520_RESET_ID_TIMER0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_TIMER0_RST_CFG,
+ },
+ [TH1520_RESET_ID_TIMER0_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_TIMER0_RST_CFG,
+ },
+ [TH1520_RESET_ID_TIMER1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_TIMER1_RST_CFG,
+ },
+ [TH1520_RESET_ID_TIMER1_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_TIMER1_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERISYS_AHB] = {
+ .bit = BIT(0),
+ .reg = TH1520_PERISYS_AHB_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERISYS_APB1] = {
+ .bit = BIT(0),
+ .reg = TH1520_PERISYS_APB1_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERISYS_APB2] = {
+ .bit = BIT(0),
+ .reg = TH1520_PERISYS_APB2_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GMAC0_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC0_AHB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GMAC0_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC0_CLKGEN] = {
+ .bit = BIT(2),
+ .reg = TH1520_GMAC0_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC0_AXI] = {
+ .bit = BIT(3),
+ .reg = TH1520_GMAC0_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART0_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART0_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART0_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART1_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART1_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART1_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART2_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART2_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART2_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART2_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART3_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART3_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART3_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART3_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART4_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART4_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART4_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART4_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART5_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_UART5_RST_CFG,
+ },
+ [TH1520_RESET_ID_UART5_IF] = {
+ .bit = BIT(1),
+ .reg = TH1520_UART5_RST_CFG,
+ },
+ [TH1520_RESET_ID_QSPI0_IF] = {
+ .bit = BIT(0),
+ .reg = TH1520_QSPI0_RST_CFG,
+ },
+ [TH1520_RESET_ID_QSPI0_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_QSPI0_RST_CFG,
+ },
+ [TH1520_RESET_ID_QSPI1_IF] = {
+ .bit = BIT(0),
+ .reg = TH1520_QSPI1_RST_CFG,
+ },
+ [TH1520_RESET_ID_QSPI1_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_QSPI1_RST_CFG,
+ },
+ [TH1520_RESET_ID_SPI_IF] = {
+ .bit = BIT(0),
+ .reg = TH1520_SPI_RST_CFG,
+ },
+ [TH1520_RESET_ID_SPI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_SPI_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C0_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C0_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C0_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C1_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C1_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C1_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C2_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C2_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C2_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C2_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C3_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C3_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C3_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C3_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C4_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C4_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C4_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C4_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C5_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2C5_RST_CFG,
+ },
+ [TH1520_RESET_ID_I2C5_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_I2C5_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO0_DB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GPIO0_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO0_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GPIO0_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO1_DB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GPIO1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO1_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GPIO1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO2_DB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GPIO2_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO2_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GPIO2_RST_CFG,
+ },
+ [TH1520_RESET_ID_PWM_COUNTER] = {
+ .bit = BIT(0),
+ .reg = TH1520_PWM_RST_CFG,
+ },
+ [TH1520_RESET_ID_PWM_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_PWM_RST_CFG,
+ },
+ [TH1520_RESET_ID_PADCTRL0_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_PADCTRL0_APSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_CPU2PERI_X2H] = {
+ .bit = BIT(1),
+ .reg = TH1520_CPU2PERI_X2H_RST_CFG,
+ },
+ [TH1520_RESET_ID_CPU2AON_X2H] = {
+ .bit = BIT(0),
+ .reg = TH1520_CPU2AON_X2H_RST_CFG,
+ },
+ [TH1520_RESET_ID_AON2CPU_A2X] = {
+ .bit = BIT(0),
+ .reg = TH1520_AON2CPU_A2X_RST_CFG,
+ },
+ [TH1520_RESET_ID_NPUSYS_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_NPUSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_NPUSYS_AXI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_NPUSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_CPU2VP_X2P] = {
+ .bit = BIT(0),
+ .reg = TH1520_CPU2VP_X2P_RST_CFG,
+ },
+ [TH1520_RESET_ID_CPU2VI_X2H] = {
+ .bit = BIT(0),
+ .reg = TH1520_CPU2VI_X2H_RST_CFG,
+ },
+ [TH1520_RESET_ID_BMU_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_BMU_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_BMU_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_BMU_C910_RST_CFG,
+ },
+ [TH1520_RESET_ID_DMAC_CPUSYS_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_DMAC_CPUSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DMAC_CPUSYS_AHB] = {
+ .bit = BIT(1),
+ .reg = TH1520_DMAC_CPUSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_SPINLOCK] = {
+ .bit = BIT(0),
+ .reg = TH1520_SPINLOCK_RST_CFG,
+ },
+ [TH1520_RESET_ID_CFG2TEE] = {
+ .bit = BIT(0),
+ .reg = TH1520_CFG2TEE_X2H_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSMART] = {
+ .bit = BIT(0),
+ .reg = TH1520_DSMART_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO3_DB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GPIO3_RST_CFG,
+ },
+ [TH1520_RESET_ID_GPIO3_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GPIO3_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERI_I2S] = {
+ .bit = BIT(0),
+ .reg = TH1520_I2S_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERI_APB3] = {
+ .bit = BIT(0),
+ .reg = TH1520_PERI_APB3_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERI2PERI1_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_PERI_APB3_RST_CFG,
+ },
+ [TH1520_RESET_ID_VPSYS_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_VP_SUBSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_PERISYS_APB4] = {
+ .bit = BIT(0),
+ .reg = TH1520_PERISYS_APB4_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_GMAC1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC1_AHB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GMAC1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC1_CLKGEN] = {
+ .bit = BIT(2),
+ .reg = TH1520_GMAC1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC1_AXI] = {
+ .bit = BIT(3),
+ .reg = TH1520_GMAC1_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_GMAC_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_GMAC_AXI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_GMAC_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_PADCTRL1_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_PADCTRL1_APSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOSYS_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_VOSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOSYS_AXI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_VOSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOSYS_AXI_X2X] = {
+ .bit = BIT(0),
+ .reg = TH1520_VOSYS_X2X_RST_CFG,
+ },
+ [TH1520_RESET_ID_MISC2VP_X2X] = {
+ .bit = BIT(0),
+ .reg = TH1520_MISC2VP_X2X_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSPSYS] = {
+ .bit = BIT(0),
+ .reg = TH1520_SUBSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS] = {
+ .bit = BIT(1),
+ .reg = TH1520_SUBSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VOSYS] = {
+ .bit = BIT(2),
+ .reg = TH1520_SUBSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VPSYS] = {
+ .bit = BIT(3),
+ .reg = TH1520_SUBSYS_RST_CFG,
+ },
+};
+
+static const struct th1520_reset_map th1520_dsp_resets[] = {
+ [TH1520_RESET_ID_X2X_DSP1] = {
+ .bit = BIT(0),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2X_DSP0] = {
+ .bit = BIT(1),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2X_SLAVE_DSP1] = {
+ .bit = BIT(2),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_X2X_SLAVE_DSP0] = {
+ .bit = BIT(3),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP0_CORE] = {
+ .bit = BIT(8),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP0_DEBUG] = {
+ .bit = BIT(9),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP0_APB] = {
+ .bit = BIT(10),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP1_CORE] = {
+ .bit = BIT(12),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP1_DEBUG] = {
+ .bit = BIT(13),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSP1_APB] = {
+ .bit = BIT(14),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DSPSYS_APB] = {
+ .bit = BIT(16),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_AXI4_DSPSYS_SLV] = {
+ .bit = BIT(20),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_AXI4_DSPSYS] = {
+ .bit = BIT(24),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_AXI4_DSP_RS] = {
+ .bit = BIT(26),
+ .reg = TH1520_DSPSYS_RST_CFG,
+ },
+};
+
+static const struct th1520_reset_map th1520_misc_resets[] = {
+ [TH1520_RESET_ID_EMMC_SDIO_CLKGEN] = {
+ .bit = BIT(0),
+ .reg = TH1520_EMMC_RST_CFG,
+ },
+ [TH1520_RESET_ID_EMMC] = {
+ .bit = BIT(1),
+ .reg = TH1520_EMMC_RST_CFG,
+ },
+ [TH1520_RESET_ID_MISCSYS_AXI] = {
+ .bit = BIT(0),
+ .reg = TH1520_MISCSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_MISCSYS_AXI_APB] = {
+ .bit = BIT(1),
+ .reg = TH1520_MISCSYS_AXI_RST_CFG,
+ },
+ [TH1520_RESET_ID_SDIO0] = {
+ .bit = BIT(0),
+ .reg = TH1520_SDIO0_RST_CFG,
+ },
+ [TH1520_RESET_ID_SDIO1] = {
+ .bit = BIT(1),
+ .reg = TH1520_SDIO1_RST_CFG,
+ },
+ [TH1520_RESET_ID_USB3_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_USB3_DRD_RST_CFG,
+ },
+ [TH1520_RESET_ID_USB3_PHY] = {
+ .bit = BIT(1),
+ .reg = TH1520_USB3_DRD_RST_CFG,
+ },
+ [TH1520_RESET_ID_USB3_VCC] = {
+ .bit = BIT(2),
+ .reg = TH1520_USB3_DRD_RST_CFG,
+ },
+};
+
+static const struct th1520_reset_map th1520_vi_resets[] = {
+ [TH1520_RESET_ID_ISP0] = {
+ .bit = BIT(0),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_ISP1] = {
+ .bit = BIT(4),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_CSI0_APB] = {
+ .bit = BIT(16),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_CSI1_APB] = {
+ .bit = BIT(17),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_CSI2_APB] = {
+ .bit = BIT(18),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_MIPI_FIFO] = {
+ .bit = BIT(20),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_ISP_VENC_APB] = {
+ .bit = BIT(24),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VIPRE_APB] = {
+ .bit = BIT(28),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VIPRE_AXI] = {
+ .bit = BIT(29),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_DW200_APB] = {
+ .bit = BIT(31),
+ .reg = TH1520_VISYS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS3_AXI] = {
+ .bit = BIT(8),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS2_AXI] = {
+ .bit = BIT(9),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS1_AXI] = {
+ .bit = BIT(10),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS_AXI] = {
+ .bit = BIT(12),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+ [TH1520_RESET_ID_VISYS_APB] = {
+ .bit = BIT(16),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+ [TH1520_RESET_ID_ISP_VENC_AXI] = {
+ .bit = BIT(20),
+ .reg = TH1520_VISYS_2_RST_CFG,
+ },
+};
+
+static const struct th1520_reset_map th1520_vp_resets[] = {
+ [TH1520_RESET_ID_VPSYS_AXI_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_AXIBUS_RST_CFG,
+ },
+ [TH1520_RESET_ID_VPSYS_AXI] = {
+ .bit = BIT(1),
+ .reg = TH1520_AXIBUS_RST_CFG,
+ },
+ [TH1520_RESET_ID_FCE_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_FCE_RST_CFG,
+ },
+ [TH1520_RESET_ID_FCE_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_FCE_RST_CFG,
+ },
+ [TH1520_RESET_ID_FCE_X2X_MASTER] = {
+ .bit = BIT(4),
+ .reg = TH1520_FCE_RST_CFG,
+ },
+ [TH1520_RESET_ID_FCE_X2X_SLAVE] = {
+ .bit = BIT(5),
+ .reg = TH1520_FCE_RST_CFG,
+ },
+ [TH1520_RESET_ID_G2D_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_G2D_RST_CFG,
+ },
+ [TH1520_RESET_ID_G2D_ACLK] = {
+ .bit = BIT(1),
+ .reg = TH1520_G2D_RST_CFG,
+ },
+ [TH1520_RESET_ID_G2D_CORE] = {
+ .bit = BIT(2),
+ .reg = TH1520_G2D_RST_CFG,
+ },
+ [TH1520_RESET_ID_VDEC_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_VDEC_RST_CFG,
+ },
+ [TH1520_RESET_ID_VDEC_ACLK] = {
+ .bit = BIT(1),
+ .reg = TH1520_VDEC_RST_CFG,
+ },
+ [TH1520_RESET_ID_VDEC_CORE] = {
+ .bit = BIT(2),
+ .reg = TH1520_VDEC_RST_CFG,
+ },
+ [TH1520_RESET_ID_VENC_APB] = {
+ .bit = BIT(0),
+ .reg = TH1520_VENC_RST_CFG,
+ },
+ [TH1520_RESET_ID_VENC_CORE] = {
+ .bit = BIT(1),
+ .reg = TH1520_VENC_RST_CFG,
+ },
+};
+
+static inline struct th1520_reset_priv *
+to_th1520_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct th1520_reset_priv, rcdev);
+}
+
+static int th1520_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct th1520_reset_priv *priv = to_th1520_reset(rcdev);
+ const struct th1520_reset_map *reset;
+
+ reset = &priv->resets[id];
+
+ return regmap_update_bits(priv->map, reset->reg, reset->bit, 0);
+}
+
+static int th1520_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct th1520_reset_priv *priv = to_th1520_reset(rcdev);
+ const struct th1520_reset_map *reset;
+
+ reset = &priv->resets[id];
+
+ return regmap_update_bits(priv->map, reset->reg, reset->bit,
+ reset->bit);
+}
+
+static const struct reset_control_ops th1520_reset_ops = {
+ .assert = th1520_reset_assert,
+ .deassert = th1520_reset_deassert,
+};
+
+static const struct regmap_config th1520_reset_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int th1520_reset_probe(struct platform_device *pdev)
+{
+ const struct th1520_reset_data *data;
+ struct device *dev = &pdev->dev;
+ struct th1520_reset_priv *priv;
+ void __iomem *base;
+ int ret;
+
+ data = device_get_match_data(dev);
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->map = devm_regmap_init_mmio(dev, base,
+ &th1520_reset_regmap_config);
+ if (IS_ERR(priv->map))
+ return PTR_ERR(priv->map);
+
+ if (of_device_is_compatible(dev->of_node, "thead,th1520-reset")) {
+ /* Initialize GPU resets to asserted state */
+ ret = regmap_update_bits(priv->map, TH1520_GPU_RST_CFG,
+ TH1520_GPU_RST_CFG_MASK, 0);
+ if (ret)
+ return ret;
+ }
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = data->num;
+ priv->rcdev.ops = &th1520_reset_ops;
+ priv->rcdev.of_node = dev->of_node;
+
+ priv->resets = data->resets;
+
+ return devm_reset_controller_register(dev, &priv->rcdev);
+}
+
+static const struct th1520_reset_data th1520_reset_data = {
+ .resets = th1520_resets,
+ .num = ARRAY_SIZE(th1520_resets),
+};
+
+static const struct th1520_reset_data th1520_ap_reset_data = {
+ .resets = th1520_ap_resets,
+ .num = ARRAY_SIZE(th1520_ap_resets),
+};
+
+static const struct th1520_reset_data th1520_dsp_reset_data = {
+ .resets = th1520_dsp_resets,
+ .num = ARRAY_SIZE(th1520_dsp_resets),
+};
+
+static const struct th1520_reset_data th1520_misc_reset_data = {
+ .resets = th1520_misc_resets,
+ .num = ARRAY_SIZE(th1520_misc_resets),
+};
+
+static const struct th1520_reset_data th1520_vi_reset_data = {
+ .resets = th1520_vi_resets,
+ .num = ARRAY_SIZE(th1520_vi_resets),
+};
+
+static const struct th1520_reset_data th1520_vp_reset_data = {
+ .resets = th1520_vp_resets,
+ .num = ARRAY_SIZE(th1520_vp_resets),
+};
+
+static const struct of_device_id th1520_reset_match[] = {
+ { .compatible = "thead,th1520-reset", .data = &th1520_reset_data },
+ { .compatible = "thead,th1520-reset-ap", .data = &th1520_ap_reset_data },
+ { .compatible = "thead,th1520-reset-dsp", .data = &th1520_dsp_reset_data },
+ { .compatible = "thead,th1520-reset-misc", .data = &th1520_misc_reset_data },
+ { .compatible = "thead,th1520-reset-vi", .data = &th1520_vi_reset_data },
+ { .compatible = "thead,th1520-reset-vp", .data = &th1520_vp_reset_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, th1520_reset_match);
+
+static struct platform_driver th1520_reset_driver = {
+ .driver = {
+ .name = "th1520-reset",
+ .of_match_table = th1520_reset_match,
+ },
+ .probe = th1520_reset_probe,
+};
+module_platform_driver(th1520_reset_driver);
+
+MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
+MODULE_DESCRIPTION("T-HEAD TH1520 SoC reset controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c
new file mode 100644
index 000000000000..1dc5b766aac1
--- /dev/null
+++ b/drivers/reset/reset-ti-sci.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Texas Instrument's System Control Interface (TI-SCI) reset driver
+ *
+ * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/idr.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+
+/**
+ * struct ti_sci_reset_control - reset control structure
+ * @dev_id: SoC-specific device identifier
+ * @reset_mask: reset mask to use for toggling reset
+ * @lock: synchronize reset_mask read-modify-writes
+ */
+struct ti_sci_reset_control {
+ u32 dev_id;
+ u32 reset_mask;
+ struct mutex lock;
+};
+
+/**
+ * struct ti_sci_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @dev: reset controller device pointer
+ * @sci: TI SCI handle used for communication with system controller
+ * @idr: idr structure for mapping ids to reset control structures
+ */
+struct ti_sci_reset_data {
+ struct reset_controller_dev rcdev;
+ struct device *dev;
+ const struct ti_sci_handle *sci;
+ struct idr idr;
+};
+
+#define to_ti_sci_reset_data(p) \
+ container_of((p), struct ti_sci_reset_data, rcdev)
+
+/**
+ * ti_sci_reset_set() - program a device's reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to toggle
+ * @assert: boolean flag to indicate assert or deassert
+ *
+ * This is a common internal function used to assert or deassert a device's
+ * reset using the TI SCI protocol. The device's reset is asserted if the
+ * @assert argument is true, or deasserted if @assert argument is false.
+ * The mechanism itself is a read-modify-write procedure, the current device
+ * reset register is read using a TI SCI device operation, the new value is
+ * set or un-set using the reset's mask, and the new reset value written by
+ * using another TI SCI device operation.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ const struct ti_sci_handle *sci = data->sci;
+ const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
+ struct ti_sci_reset_control *control;
+ u32 reset_state;
+ int ret;
+
+ control = idr_find(&data->idr, id);
+ if (!control)
+ return -EINVAL;
+
+ mutex_lock(&control->lock);
+
+ ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state);
+ if (ret)
+ goto out;
+
+ if (assert)
+ reset_state |= control->reset_mask;
+ else
+ reset_state &= ~control->reset_mask;
+
+ ret = dev_ops->set_device_resets(sci, control->dev_id, reset_state);
+out:
+ mutex_unlock(&control->lock);
+
+ return ret;
+}
+
+/**
+ * ti_sci_reset_assert() - assert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be asserted
+ *
+ * This function implements the reset driver op to assert a device's reset
+ * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
+ * with the corresponding parameters as passed in, but with the @assert
+ * argument set to true for asserting the reset.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ti_sci_reset_set(rcdev, id, true);
+}
+
+/**
+ * ti_sci_reset_deassert() - deassert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be deasserted
+ *
+ * This function implements the reset driver op to deassert a device's reset
+ * using the TI SCI protocol. This invokes the function ti_sci_reset_set()
+ * with the corresponding parameters as passed in, but with the @assert
+ * argument set to false for deasserting the reset.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return ti_sci_reset_set(rcdev, id, false);
+}
+
+/**
+ * ti_sci_reset_status() - check device reset status
+ * @rcdev: reset controller entity
+ * @id: ID of reset to be checked
+ *
+ * This function implements the reset driver op to return the status of a
+ * device's reset using the TI SCI protocol. The reset register value is read
+ * by invoking the TI SCI device operation .get_device_resets(), and the
+ * status of the specific reset is extracted and returned using this reset's
+ * reset mask.
+ *
+ * Return: 0 if reset is deasserted, or a non-zero value if reset is asserted
+ */
+static int ti_sci_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ const struct ti_sci_handle *sci = data->sci;
+ const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops;
+ struct ti_sci_reset_control *control;
+ u32 reset_state;
+ int ret;
+
+ control = idr_find(&data->idr, id);
+ if (!control)
+ return -EINVAL;
+
+ ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state);
+ if (ret)
+ return ret;
+
+ return reset_state & control->reset_mask;
+}
+
+static const struct reset_control_ops ti_sci_reset_ops = {
+ .assert = ti_sci_reset_assert,
+ .deassert = ti_sci_reset_deassert,
+ .status = ti_sci_reset_status,
+};
+
+/**
+ * ti_sci_reset_of_xlate() - translate a set of OF arguments to a reset ID
+ * @rcdev: reset controller entity
+ * @reset_spec: OF reset argument specifier
+ *
+ * This function performs the translation of the reset argument specifier
+ * values defined in a reset consumer device node. The function allocates a
+ * reset control structure for that device reset, and will be used by the
+ * driver for performing any reset functions on that reset. An idr structure
+ * is allocated and used to map to the reset control structure. This idr
+ * is used by the driver to do reset lookups.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_sci_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev);
+ struct ti_sci_reset_control *control;
+
+ if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells))
+ return -EINVAL;
+
+ control = devm_kzalloc(data->dev, sizeof(*control), GFP_KERNEL);
+ if (!control)
+ return -ENOMEM;
+
+ control->dev_id = reset_spec->args[0];
+ control->reset_mask = reset_spec->args[1];
+ mutex_init(&control->lock);
+
+ return idr_alloc(&data->idr, control, 0, 0, GFP_KERNEL);
+}
+
+static const struct of_device_id ti_sci_reset_of_match[] = {
+ { .compatible = "ti,sci-reset", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ti_sci_reset_of_match);
+
+static int ti_sci_reset_probe(struct platform_device *pdev)
+{
+ struct ti_sci_reset_data *data;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->sci = devm_ti_sci_get_handle(&pdev->dev);
+ if (IS_ERR(data->sci))
+ return PTR_ERR(data->sci);
+
+ data->rcdev.ops = &ti_sci_reset_ops;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.of_node = pdev->dev.of_node;
+ data->rcdev.of_reset_n_cells = 2;
+ data->rcdev.of_xlate = ti_sci_reset_of_xlate;
+ data->dev = &pdev->dev;
+ idr_init(&data->idr);
+
+ platform_set_drvdata(pdev, data);
+
+ return reset_controller_register(&data->rcdev);
+}
+
+static void ti_sci_reset_remove(struct platform_device *pdev)
+{
+ struct ti_sci_reset_data *data = platform_get_drvdata(pdev);
+
+ reset_controller_unregister(&data->rcdev);
+
+ idr_destroy(&data->idr);
+}
+
+static struct platform_driver ti_sci_reset_driver = {
+ .probe = ti_sci_reset_probe,
+ .remove = ti_sci_reset_remove,
+ .driver = {
+ .name = "ti-sci-reset",
+ .of_match_table = ti_sci_reset_of_match,
+ },
+};
+module_platform_driver(ti_sci_reset_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TI System Control Interface (TI SCI) Reset driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c
new file mode 100644
index 000000000000..23f86ddb8668
--- /dev/null
+++ b/drivers/reset/reset-ti-syscon.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI SYSCON regmap reset driver
+ *
+ * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ * Suman Anna <afd@ti.com>
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/ti-syscon.h>
+
+/**
+ * struct ti_syscon_reset_control - reset control structure
+ * @assert_offset: reset assert control register offset from syscon base
+ * @assert_bit: reset assert bit in the reset assert control register
+ * @deassert_offset: reset deassert control register offset from syscon base
+ * @deassert_bit: reset deassert bit in the reset deassert control register
+ * @status_offset: reset status register offset from syscon base
+ * @status_bit: reset status bit in the reset status register
+ * @flags: reset flag indicating how the (de)assert and status are handled
+ */
+struct ti_syscon_reset_control {
+ unsigned int assert_offset;
+ unsigned int assert_bit;
+ unsigned int deassert_offset;
+ unsigned int deassert_bit;
+ unsigned int status_offset;
+ unsigned int status_bit;
+ u32 flags;
+};
+
+/**
+ * struct ti_syscon_reset_data - reset controller information structure
+ * @rcdev: reset controller entity
+ * @regmap: regmap handle containing the memory-mapped reset registers
+ * @controls: array of reset controls
+ * @nr_controls: number of controls in control array
+ */
+struct ti_syscon_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+ struct ti_syscon_reset_control *controls;
+ unsigned int nr_controls;
+};
+
+#define to_ti_syscon_reset_data(_rcdev) \
+ container_of(_rcdev, struct ti_syscon_reset_data, rcdev)
+
+/**
+ * ti_syscon_reset_assert() - assert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of the reset to be asserted
+ *
+ * This function implements the reset driver op to assert a device's reset.
+ * This asserts the reset in a manner prescribed by the reset flags.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
+ struct ti_syscon_reset_control *control;
+ unsigned int mask, value;
+
+ if (id >= data->nr_controls)
+ return -EINVAL;
+
+ control = &data->controls[id];
+
+ if (control->flags & ASSERT_NONE)
+ return -ENOTSUPP; /* assert not supported for this reset */
+
+ mask = BIT(control->assert_bit);
+ value = (control->flags & ASSERT_SET) ? mask : 0x0;
+
+ return regmap_write_bits(data->regmap, control->assert_offset, mask, value);
+}
+
+/**
+ * ti_syscon_reset_deassert() - deassert device reset
+ * @rcdev: reset controller entity
+ * @id: ID of reset to be deasserted
+ *
+ * This function implements the reset driver op to deassert a device's reset.
+ * This deasserts the reset in a manner prescribed by the reset flags.
+ *
+ * Return: 0 for successful request, else a corresponding error value
+ */
+static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
+ struct ti_syscon_reset_control *control;
+ unsigned int mask, value;
+
+ if (id >= data->nr_controls)
+ return -EINVAL;
+
+ control = &data->controls[id];
+
+ if (control->flags & DEASSERT_NONE)
+ return -ENOTSUPP; /* deassert not supported for this reset */
+
+ mask = BIT(control->deassert_bit);
+ value = (control->flags & DEASSERT_SET) ? mask : 0x0;
+
+ return regmap_write_bits(data->regmap, control->deassert_offset, mask, value);
+}
+
+/**
+ * ti_syscon_reset_status() - check device reset status
+ * @rcdev: reset controller entity
+ * @id: ID of the reset for which the status is being requested
+ *
+ * This function implements the reset driver op to return the status of a
+ * device's reset.
+ *
+ * Return: 0 if reset is deasserted, true if reset is asserted, else a
+ * corresponding error value
+ */
+static int ti_syscon_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
+ struct ti_syscon_reset_control *control;
+ unsigned int reset_state;
+ int ret;
+
+ if (id >= data->nr_controls)
+ return -EINVAL;
+
+ control = &data->controls[id];
+
+ if (control->flags & STATUS_NONE)
+ return -ENOTSUPP; /* status not supported for this reset */
+
+ ret = regmap_read(data->regmap, control->status_offset, &reset_state);
+ if (ret)
+ return ret;
+
+ return !(reset_state & BIT(control->status_bit)) ==
+ !(control->flags & STATUS_SET);
+}
+
+static const struct reset_control_ops ti_syscon_reset_ops = {
+ .assert = ti_syscon_reset_assert,
+ .deassert = ti_syscon_reset_deassert,
+ .status = ti_syscon_reset_status,
+};
+
+static int ti_syscon_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct ti_syscon_reset_data *data;
+ struct regmap *regmap;
+ const __be32 *list;
+ struct ti_syscon_reset_control *controls;
+ int size, nr_controls, i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ list = of_get_property(np, "ti,reset-bits", &size);
+ if (!list || (size / sizeof(*list)) % 7 != 0) {
+ dev_err(dev, "invalid DT reset description\n");
+ return -EINVAL;
+ }
+
+ nr_controls = (size / sizeof(*list)) / 7;
+ controls = devm_kcalloc(dev, nr_controls, sizeof(*controls),
+ GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_controls; i++) {
+ controls[i].assert_offset = be32_to_cpup(list++);
+ controls[i].assert_bit = be32_to_cpup(list++);
+ controls[i].deassert_offset = be32_to_cpup(list++);
+ controls[i].deassert_bit = be32_to_cpup(list++);
+ controls[i].status_offset = be32_to_cpup(list++);
+ controls[i].status_bit = be32_to_cpup(list++);
+ controls[i].flags = be32_to_cpup(list++);
+ }
+
+ data->rcdev.ops = &ti_syscon_reset_ops;
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.of_node = np;
+ data->rcdev.nr_resets = nr_controls;
+ data->regmap = regmap;
+ data->controls = controls;
+ data->nr_controls = nr_controls;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static const struct of_device_id ti_syscon_reset_of_match[] = {
+ { .compatible = "ti,syscon-reset", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match);
+
+static struct platform_driver ti_syscon_reset_driver = {
+ .probe = ti_syscon_reset_probe,
+ .driver = {
+ .name = "ti-syscon-reset",
+ .of_match_table = ti_syscon_reset_of_match,
+ },
+};
+module_platform_driver(ti_syscon_reset_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-tn48m.c b/drivers/reset/reset-tn48m.c
new file mode 100644
index 000000000000..130027291b6e
--- /dev/null
+++ b/drivers/reset/reset-tn48m.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Delta TN48M CPLD reset driver
+ *
+ * Copyright (C) 2021 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/delta,tn48m-reset.h>
+
+#define TN48M_RESET_REG 0x10
+
+#define TN48M_RESET_TIMEOUT_US 125000
+#define TN48M_RESET_SLEEP_US 10
+
+struct tn48_reset_map {
+ u8 bit;
+};
+
+struct tn48_reset_data {
+ struct reset_controller_dev rcdev;
+ struct regmap *regmap;
+};
+
+static const struct tn48_reset_map tn48m_resets[] = {
+ [CPU_88F7040_RESET] = {0},
+ [CPU_88F6820_RESET] = {1},
+ [MAC_98DX3265_RESET] = {2},
+ [PHY_88E1680_RESET] = {4},
+ [PHY_88E1512_RESET] = {6},
+ [POE_RESET] = {7},
+};
+
+static inline struct tn48_reset_data *to_tn48_reset_data(
+ struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct tn48_reset_data, rcdev);
+}
+
+static int tn48m_control_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct tn48_reset_data *data = to_tn48_reset_data(rcdev);
+ unsigned int val;
+
+ regmap_update_bits(data->regmap, TN48M_RESET_REG,
+ BIT(tn48m_resets[id].bit), 0);
+
+ return regmap_read_poll_timeout(data->regmap,
+ TN48M_RESET_REG,
+ val,
+ val & BIT(tn48m_resets[id].bit),
+ TN48M_RESET_SLEEP_US,
+ TN48M_RESET_TIMEOUT_US);
+}
+
+static int tn48m_control_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct tn48_reset_data *data = to_tn48_reset_data(rcdev);
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, TN48M_RESET_REG, &regval);
+ if (ret < 0)
+ return ret;
+
+ if (BIT(tn48m_resets[id].bit) & regval)
+ return 0;
+ else
+ return 1;
+}
+
+static const struct reset_control_ops tn48_reset_ops = {
+ .reset = tn48m_control_reset,
+ .status = tn48m_control_status,
+};
+
+static int tn48m_reset_probe(struct platform_device *pdev)
+{
+ struct tn48_reset_data *data;
+ struct regmap *regmap;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = regmap;
+
+ data->rcdev.owner = THIS_MODULE;
+ data->rcdev.ops = &tn48_reset_ops;
+ data->rcdev.nr_resets = ARRAY_SIZE(tn48m_resets);
+ data->rcdev.of_node = pdev->dev.of_node;
+
+ return devm_reset_controller_register(&pdev->dev, &data->rcdev);
+}
+
+static const struct of_device_id tn48m_reset_of_match[] = {
+ { .compatible = "delta,tn48m-reset" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tn48m_reset_of_match);
+
+static struct platform_driver tn48m_reset_driver = {
+ .driver = {
+ .name = "delta-tn48m-reset",
+ .of_match_table = tn48m_reset_of_match,
+ },
+ .probe = tn48m_reset_probe,
+};
+module_platform_driver(tn48m_reset_driver);
+
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
+MODULE_DESCRIPTION("Delta TN48M CPLD reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-tps380x.c b/drivers/reset/reset-tps380x.c
new file mode 100644
index 000000000000..09d511f069ba
--- /dev/null
+++ b/drivers/reset/reset-tps380x.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * TI TPS380x Supply Voltage Supervisor and Reset Controller Driver
+ *
+ * Copyright (C) 2022 Pengutronix, Marco Felsch <kernel@pengutronix.de>
+ *
+ * Based on Simple Reset Controller Driver
+ *
+ * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reset-controller.h>
+
+struct tps380x_reset {
+ struct reset_controller_dev rcdev;
+ struct gpio_desc *reset_gpio;
+ unsigned int reset_ms;
+};
+
+struct tps380x_reset_devdata {
+ unsigned int min_reset_ms;
+ unsigned int typ_reset_ms;
+ unsigned int max_reset_ms;
+};
+
+static inline
+struct tps380x_reset *to_tps380x_reset(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct tps380x_reset, rcdev);
+}
+
+static int
+tps380x_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct tps380x_reset *tps380x = to_tps380x_reset(rcdev);
+
+ gpiod_set_value_cansleep(tps380x->reset_gpio, 1);
+
+ return 0;
+}
+
+static int
+tps380x_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct tps380x_reset *tps380x = to_tps380x_reset(rcdev);
+
+ gpiod_set_value_cansleep(tps380x->reset_gpio, 0);
+ msleep(tps380x->reset_ms);
+
+ return 0;
+}
+
+static const struct reset_control_ops reset_tps380x_ops = {
+ .assert = tps380x_reset_assert,
+ .deassert = tps380x_reset_deassert,
+};
+
+static int tps380x_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ /* No special handling needed, we have only one reset line per device */
+ return 0;
+}
+
+static int tps380x_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct tps380x_reset_devdata *devdata;
+ struct tps380x_reset *tps380x;
+
+ devdata = device_get_match_data(dev);
+ if (!devdata)
+ return -EINVAL;
+
+ tps380x = devm_kzalloc(dev, sizeof(*tps380x), GFP_KERNEL);
+ if (!tps380x)
+ return -ENOMEM;
+
+ tps380x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(tps380x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(tps380x->reset_gpio),
+ "Failed to get GPIO\n");
+
+ tps380x->reset_ms = devdata->max_reset_ms;
+
+ tps380x->rcdev.ops = &reset_tps380x_ops;
+ tps380x->rcdev.owner = THIS_MODULE;
+ tps380x->rcdev.dev = dev;
+ tps380x->rcdev.of_node = dev->of_node;
+ tps380x->rcdev.of_reset_n_cells = 0;
+ tps380x->rcdev.of_xlate = tps380x_reset_of_xlate;
+ tps380x->rcdev.nr_resets = 1;
+
+ return devm_reset_controller_register(dev, &tps380x->rcdev);
+}
+
+static const struct tps380x_reset_devdata tps3801_reset_data = {
+ .min_reset_ms = 120,
+ .typ_reset_ms = 200,
+ .max_reset_ms = 280,
+};
+
+static const struct of_device_id tps380x_reset_dt_ids[] = {
+ { .compatible = "ti,tps3801", .data = &tps3801_reset_data },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tps380x_reset_dt_ids);
+
+static struct platform_driver tps380x_reset_driver = {
+ .probe = tps380x_reset_probe,
+ .driver = {
+ .name = "tps380x-reset",
+ .of_match_table = tps380x_reset_dt_ids,
+ },
+};
+module_platform_driver(tps380x_reset_driver);
+
+MODULE_AUTHOR("Marco Felsch <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("TI TPS380x Supply Voltage Supervisor and Reset Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c
new file mode 100644
index 000000000000..a2a262bf6bfc
--- /dev/null
+++ b/drivers/reset/reset-uniphier-glue.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// reset-uniphier-glue.c - Glue layer reset driver for UniPhier
+// Copyright 2018 Socionext Inc.
+// Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/reset/reset-simple.h>
+
+#define MAX_CLKS 2
+#define MAX_RSTS 2
+
+struct uniphier_glue_reset_soc_data {
+ int nclks;
+ const char * const *clock_names;
+ int nrsts;
+ const char * const *reset_names;
+};
+
+struct uniphier_glue_reset_priv {
+ struct clk_bulk_data clk[MAX_CLKS];
+ struct reset_control_bulk_data rst[MAX_RSTS];
+ struct reset_simple_data rdata;
+ const struct uniphier_glue_reset_soc_data *data;
+};
+
+static void uniphier_clk_disable(void *_priv)
+{
+ struct uniphier_glue_reset_priv *priv = _priv;
+
+ clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
+}
+
+static int uniphier_glue_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct uniphier_glue_reset_priv *priv;
+ struct resource *res;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_device_get_match_data(dev);
+ if (WARN_ON(!priv->data || priv->data->nclks > MAX_CLKS ||
+ priv->data->nrsts > MAX_RSTS))
+ return -EINVAL;
+
+ priv->rdata.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(priv->rdata.membase))
+ return PTR_ERR(priv->rdata.membase);
+
+ for (i = 0; i < priv->data->nclks; i++)
+ priv->clk[i].id = priv->data->clock_names[i];
+ ret = devm_clk_bulk_get(dev, priv->data->nclks, priv->clk);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, uniphier_clk_disable, priv);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < priv->data->nrsts; i++)
+ priv->rst[i].id = priv->data->reset_names[i];
+ ret = devm_reset_control_bulk_get_shared_deasserted(dev,
+ priv->data->nrsts,
+ priv->rst);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->rdata.lock);
+ priv->rdata.rcdev.owner = THIS_MODULE;
+ priv->rdata.rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
+ priv->rdata.rcdev.ops = &reset_simple_ops;
+ priv->rdata.rcdev.of_node = dev->of_node;
+ priv->rdata.active_low = true;
+
+ return devm_reset_controller_register(dev, &priv->rdata.rcdev);
+}
+
+static const char * const uniphier_pro4_clock_reset_names[] = {
+ "gio", "link",
+};
+
+static const struct uniphier_glue_reset_soc_data uniphier_pro4_data = {
+ .nclks = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+ .clock_names = uniphier_pro4_clock_reset_names,
+ .nrsts = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+ .reset_names = uniphier_pro4_clock_reset_names,
+};
+
+static const char * const uniphier_pxs2_clock_reset_names[] = {
+ "link",
+};
+
+static const struct uniphier_glue_reset_soc_data uniphier_pxs2_data = {
+ .nclks = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+ .clock_names = uniphier_pxs2_clock_reset_names,
+ .nrsts = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+ .reset_names = uniphier_pxs2_clock_reset_names,
+};
+
+static const struct of_device_id uniphier_glue_reset_match[] = {
+ {
+ .compatible = "socionext,uniphier-pro4-usb3-reset",
+ .data = &uniphier_pro4_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-usb3-reset",
+ .data = &uniphier_pro4_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-usb3-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-usb3-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-usb3-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-nx1-usb3-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-ahci-reset",
+ .data = &uniphier_pro4_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-ahci-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-ahci-reset",
+ .data = &uniphier_pxs2_data,
+ },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, uniphier_glue_reset_match);
+
+static struct platform_driver uniphier_glue_reset_driver = {
+ .probe = uniphier_glue_reset_probe,
+ .driver = {
+ .name = "uniphier-glue-reset",
+ .of_match_table = uniphier_glue_reset_match,
+ },
+};
+module_platform_driver(uniphier_glue_reset_driver);
+
+MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
+MODULE_DESCRIPTION("UniPhier Glue layer reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c
new file mode 100644
index 000000000000..79c43c204d46
--- /dev/null
+++ b/drivers/reset/reset-uniphier.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+struct uniphier_reset_data {
+ unsigned int id;
+ unsigned int reg;
+ unsigned int bit;
+ unsigned int flags;
+#define UNIPHIER_RESET_ACTIVE_LOW BIT(0)
+};
+
+#define UNIPHIER_RESET_ID_END ((unsigned int)(-1))
+
+#define UNIPHIER_RESET_END \
+ { .id = UNIPHIER_RESET_ID_END }
+
+#define UNIPHIER_RESET(_id, _reg, _bit) \
+ { \
+ .id = (_id), \
+ .reg = (_reg), \
+ .bit = (_bit), \
+ }
+
+#define UNIPHIER_RESETX(_id, _reg, _bit) \
+ { \
+ .id = (_id), \
+ .reg = (_reg), \
+ .bit = (_bit), \
+ .flags = UNIPHIER_RESET_ACTIVE_LOW, \
+ }
+
+/* System reset data */
+static const struct uniphier_reset_data uniphier_ld4_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
+ UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (Ether, HSC, MIO) */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
+ UNIPHIER_RESETX(6, 0x2000, 12), /* Ether */
+ UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, MIO, RLE) */
+ UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (Ether, SATA, USB3) */
+ UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
+ UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
+ UNIPHIER_RESETX(28, 0x2000, 18), /* SATA0 */
+ UNIPHIER_RESETX(29, 0x2004, 18), /* SATA1 */
+ UNIPHIER_RESETX(30, 0x2000, 19), /* SATA-PHY */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
+ UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC) */
+ UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (PCIe, USB3) */
+ UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
+ UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
+ UNIPHIER_RESETX(24, 0x2008, 2), /* PCIe */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
+ UNIPHIER_RESETX(6, 0x2000, 12), /* Ether */
+ UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, RLE) */
+ UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
+ UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
+ UNIPHIER_RESETX(16, 0x2014, 4), /* USB30-PHY0 */
+ UNIPHIER_RESETX(17, 0x2014, 0), /* USB30-PHY1 */
+ UNIPHIER_RESETX(18, 0x2014, 2), /* USB30-PHY2 */
+ UNIPHIER_RESETX(20, 0x2014, 5), /* USB31-PHY0 */
+ UNIPHIER_RESETX(21, 0x2014, 1), /* USB31-PHY1 */
+ UNIPHIER_RESETX(28, 0x2014, 12), /* SATA */
+ UNIPHIER_RESET(30, 0x2014, 8), /* SATA-PHY (active high) */
+ UNIPHIER_RESETX(40, 0x2000, 13), /* AIO */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
+ UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
+ UNIPHIER_RESETX(6, 0x200c, 6), /* Ether */
+ UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC, MIO) */
+ UNIPHIER_RESETX(9, 0x200c, 9), /* HSC */
+ UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */
+ UNIPHIER_RESETX(41, 0x2008, 1), /* EVEA */
+ UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
+ UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
+ UNIPHIER_RESETX(6, 0x200c, 6), /* Ether */
+ UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC) */
+ UNIPHIER_RESETX(9, 0x200c, 9), /* HSC */
+ UNIPHIER_RESETX(14, 0x200c, 5), /* USB30 */
+ UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */
+ UNIPHIER_RESETX(17, 0x200c, 13), /* USB30-PHY1 */
+ UNIPHIER_RESETX(18, 0x200c, 14), /* USB30-PHY2 */
+ UNIPHIER_RESETX(19, 0x200c, 15), /* USB30-PHY3 */
+ UNIPHIER_RESETX(24, 0x200c, 4), /* PCIe */
+ UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */
+ UNIPHIER_RESETX(41, 0x2008, 1), /* EVEA */
+ UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
+ UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
+ UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
+ UNIPHIER_RESETX(6, 0x200c, 9), /* Ether0 */
+ UNIPHIER_RESETX(7, 0x200c, 10), /* Ether1 */
+ UNIPHIER_RESETX(8, 0x200c, 12), /* STDMAC */
+ UNIPHIER_RESETX(12, 0x200c, 4), /* USB30 link */
+ UNIPHIER_RESETX(13, 0x200c, 5), /* USB31 link */
+ UNIPHIER_RESETX(16, 0x200c, 16), /* USB30-PHY0 */
+ UNIPHIER_RESETX(17, 0x200c, 18), /* USB30-PHY1 */
+ UNIPHIER_RESETX(18, 0x200c, 20), /* USB30-PHY2 */
+ UNIPHIER_RESETX(20, 0x200c, 17), /* USB31-PHY0 */
+ UNIPHIER_RESETX(21, 0x200c, 19), /* USB31-PHY1 */
+ UNIPHIER_RESETX(24, 0x200c, 3), /* PCIe */
+ UNIPHIER_RESETX(28, 0x200c, 7), /* SATA0 */
+ UNIPHIER_RESETX(29, 0x200c, 8), /* SATA1 */
+ UNIPHIER_RESETX(30, 0x200c, 21), /* SATA-PHY */
+ UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */
+ UNIPHIER_RESETX(42, 0x2010, 2), /* EXIV */
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_nx1_sys_reset_data[] = {
+ UNIPHIER_RESETX(4, 0x2008, 8), /* eMMC */
+ UNIPHIER_RESETX(6, 0x200c, 0), /* Ether */
+ UNIPHIER_RESETX(12, 0x200c, 16), /* USB30 link */
+ UNIPHIER_RESETX(16, 0x200c, 24), /* USB30-PHY0 */
+ UNIPHIER_RESETX(17, 0x200c, 25), /* USB30-PHY1 */
+ UNIPHIER_RESETX(18, 0x200c, 26), /* USB30-PHY2 */
+ UNIPHIER_RESETX(24, 0x200c, 8), /* PCIe */
+ UNIPHIER_RESETX(52, 0x2010, 0), /* VOC */
+ UNIPHIER_RESETX(58, 0x2010, 8), /* HDMI-Tx */
+ UNIPHIER_RESET_END,
+};
+
+/* Media I/O reset data */
+#define UNIPHIER_MIO_RESET_SD(id, ch) \
+ UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 0)
+
+#define UNIPHIER_MIO_RESET_SD_BRIDGE(id, ch) \
+ UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 26)
+
+#define UNIPHIER_MIO_RESET_EMMC_HW_RESET(id, ch) \
+ UNIPHIER_RESETX((id), 0x80 + 0x200 * (ch), 0)
+
+#define UNIPHIER_MIO_RESET_USB2(id, ch) \
+ UNIPHIER_RESETX((id), 0x114 + 0x200 * (ch), 0)
+
+#define UNIPHIER_MIO_RESET_USB2_BRIDGE(id, ch) \
+ UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 24)
+
+#define UNIPHIER_MIO_RESET_DMAC(id) \
+ UNIPHIER_RESETX((id), 0x110, 17)
+
+static const struct uniphier_reset_data uniphier_ld4_mio_reset_data[] = {
+ UNIPHIER_MIO_RESET_SD(0, 0),
+ UNIPHIER_MIO_RESET_SD(1, 1),
+ UNIPHIER_MIO_RESET_SD(2, 2),
+ UNIPHIER_MIO_RESET_SD_BRIDGE(3, 0),
+ UNIPHIER_MIO_RESET_SD_BRIDGE(4, 1),
+ UNIPHIER_MIO_RESET_SD_BRIDGE(5, 2),
+ UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1),
+ UNIPHIER_MIO_RESET_DMAC(7),
+ UNIPHIER_MIO_RESET_USB2(8, 0),
+ UNIPHIER_MIO_RESET_USB2(9, 1),
+ UNIPHIER_MIO_RESET_USB2(10, 2),
+ UNIPHIER_MIO_RESET_USB2_BRIDGE(12, 0),
+ UNIPHIER_MIO_RESET_USB2_BRIDGE(13, 1),
+ UNIPHIER_MIO_RESET_USB2_BRIDGE(14, 2),
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pro5_sd_reset_data[] = {
+ UNIPHIER_MIO_RESET_SD(0, 0),
+ UNIPHIER_MIO_RESET_SD(1, 1),
+ UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1),
+ UNIPHIER_RESET_END,
+};
+
+/* Peripheral reset data */
+#define UNIPHIER_PERI_RESET_UART(id, ch) \
+ UNIPHIER_RESETX((id), 0x114, 19 + (ch))
+
+#define UNIPHIER_PERI_RESET_I2C(id, ch) \
+ UNIPHIER_RESETX((id), 0x114, 5 + (ch))
+
+#define UNIPHIER_PERI_RESET_FI2C(id, ch) \
+ UNIPHIER_RESETX((id), 0x114, 24 + (ch))
+
+#define UNIPHIER_PERI_RESET_SCSSI(id, ch) \
+ UNIPHIER_RESETX((id), 0x110, 17 + (ch))
+
+#define UNIPHIER_PERI_RESET_MCSSI(id) \
+ UNIPHIER_RESETX((id), 0x114, 14)
+
+static const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = {
+ UNIPHIER_PERI_RESET_UART(0, 0),
+ UNIPHIER_PERI_RESET_UART(1, 1),
+ UNIPHIER_PERI_RESET_UART(2, 2),
+ UNIPHIER_PERI_RESET_UART(3, 3),
+ UNIPHIER_PERI_RESET_I2C(4, 0),
+ UNIPHIER_PERI_RESET_I2C(5, 1),
+ UNIPHIER_PERI_RESET_I2C(6, 2),
+ UNIPHIER_PERI_RESET_I2C(7, 3),
+ UNIPHIER_PERI_RESET_I2C(8, 4),
+ UNIPHIER_PERI_RESET_SCSSI(11, 0),
+ UNIPHIER_RESET_END,
+};
+
+static const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = {
+ UNIPHIER_PERI_RESET_UART(0, 0),
+ UNIPHIER_PERI_RESET_UART(1, 1),
+ UNIPHIER_PERI_RESET_UART(2, 2),
+ UNIPHIER_PERI_RESET_UART(3, 3),
+ UNIPHIER_PERI_RESET_FI2C(4, 0),
+ UNIPHIER_PERI_RESET_FI2C(5, 1),
+ UNIPHIER_PERI_RESET_FI2C(6, 2),
+ UNIPHIER_PERI_RESET_FI2C(7, 3),
+ UNIPHIER_PERI_RESET_FI2C(8, 4),
+ UNIPHIER_PERI_RESET_FI2C(9, 5),
+ UNIPHIER_PERI_RESET_FI2C(10, 6),
+ UNIPHIER_PERI_RESET_SCSSI(11, 0),
+ UNIPHIER_PERI_RESET_SCSSI(12, 1),
+ UNIPHIER_PERI_RESET_SCSSI(13, 2),
+ UNIPHIER_PERI_RESET_SCSSI(14, 3),
+ UNIPHIER_PERI_RESET_MCSSI(15),
+ UNIPHIER_RESET_END,
+};
+
+/* Analog signal amplifiers reset data */
+static const struct uniphier_reset_data uniphier_ld11_adamv_reset_data[] = {
+ UNIPHIER_RESETX(0, 0x10, 6), /* EVEA */
+ UNIPHIER_RESET_END,
+};
+
+/* core implementaton */
+struct uniphier_reset_priv {
+ struct reset_controller_dev rcdev;
+ struct device *dev;
+ struct regmap *regmap;
+ const struct uniphier_reset_data *data;
+};
+
+#define to_uniphier_reset_priv(_rcdev) \
+ container_of(_rcdev, struct uniphier_reset_priv, rcdev)
+
+static int uniphier_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, int assert)
+{
+ struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev);
+ const struct uniphier_reset_data *p;
+
+ for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) {
+ unsigned int mask, val;
+
+ if (p->id != id)
+ continue;
+
+ mask = BIT(p->bit);
+
+ if (assert)
+ val = mask;
+ else
+ val = ~mask;
+
+ if (p->flags & UNIPHIER_RESET_ACTIVE_LOW)
+ val = ~val;
+
+ return regmap_write_bits(priv->regmap, p->reg, mask, val);
+ }
+
+ dev_err(priv->dev, "reset_id=%lu was not handled\n", id);
+ return -EINVAL;
+}
+
+static int uniphier_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return uniphier_reset_update(rcdev, id, 1);
+}
+
+static int uniphier_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return uniphier_reset_update(rcdev, id, 0);
+}
+
+static int uniphier_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev);
+ const struct uniphier_reset_data *p;
+
+ for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) {
+ unsigned int val;
+ int ret, asserted;
+
+ if (p->id != id)
+ continue;
+
+ ret = regmap_read(priv->regmap, p->reg, &val);
+ if (ret)
+ return ret;
+
+ asserted = !!(val & BIT(p->bit));
+
+ if (p->flags & UNIPHIER_RESET_ACTIVE_LOW)
+ asserted = !asserted;
+
+ return asserted;
+ }
+
+ dev_err(priv->dev, "reset_id=%lu was not found\n", id);
+ return -EINVAL;
+}
+
+static const struct reset_control_ops uniphier_reset_ops = {
+ .assert = uniphier_reset_assert,
+ .deassert = uniphier_reset_deassert,
+ .status = uniphier_reset_status,
+};
+
+static int uniphier_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct uniphier_reset_priv *priv;
+ const struct uniphier_reset_data *p, *data;
+ struct regmap *regmap;
+ struct device_node *parent;
+ unsigned int nr_resets = 0;
+
+ data = of_device_get_match_data(dev);
+ if (WARN_ON(!data))
+ return -EINVAL;
+
+ parent = of_get_parent(dev->of_node); /* parent should be syscon node */
+ regmap = syscon_node_to_regmap(parent);
+ of_node_put(parent);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to get regmap (error %ld)\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ for (p = data; p->id != UNIPHIER_RESET_ID_END; p++)
+ nr_resets = max(nr_resets, p->id + 1);
+
+ priv->rcdev.ops = &uniphier_reset_ops;
+ priv->rcdev.owner = dev->driver->owner;
+ priv->rcdev.of_node = dev->of_node;
+ priv->rcdev.nr_resets = nr_resets;
+ priv->dev = dev;
+ priv->regmap = regmap;
+ priv->data = data;
+
+ return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
+}
+
+static const struct of_device_id uniphier_reset_match[] = {
+ /* System reset */
+ {
+ .compatible = "socionext,uniphier-ld4-reset",
+ .data = uniphier_ld4_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-reset",
+ .data = uniphier_pro4_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-reset",
+ .data = uniphier_ld4_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-reset",
+ .data = uniphier_pro5_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-reset",
+ .data = uniphier_pxs2_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-reset",
+ .data = uniphier_ld11_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-reset",
+ .data = uniphier_ld20_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-reset",
+ .data = uniphier_pxs3_sys_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-nx1-reset",
+ .data = uniphier_nx1_sys_reset_data,
+ },
+ /* Media I/O reset, SD reset */
+ {
+ .compatible = "socionext,uniphier-ld4-mio-reset",
+ .data = uniphier_ld4_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-mio-reset",
+ .data = uniphier_ld4_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-mio-reset",
+ .data = uniphier_ld4_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-mio-reset",
+ .data = uniphier_ld4_mio_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-nx1-sd-reset",
+ .data = uniphier_pro5_sd_reset_data,
+ },
+ /* Peripheral reset */
+ {
+ .compatible = "socionext,uniphier-ld4-peri-reset",
+ .data = uniphier_ld4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-peri-reset",
+ .data = uniphier_ld4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-nx1-peri-reset",
+ .data = uniphier_pro4_peri_reset_data,
+ },
+ /* Analog signal amplifiers reset */
+ {
+ .compatible = "socionext,uniphier-ld11-adamv-reset",
+ .data = uniphier_ld11_adamv_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-adamv-reset",
+ .data = uniphier_ld11_adamv_reset_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, uniphier_reset_match);
+
+static struct platform_driver uniphier_reset_driver = {
+ .probe = uniphier_reset_probe,
+ .driver = {
+ .name = "uniphier-reset",
+ .of_match_table = uniphier_reset_match,
+ },
+};
+module_platform_driver(uniphier_reset_driver);
+
+MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier Reset Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/reset-zynq.c b/drivers/reset/reset-zynq.c
new file mode 100644
index 000000000000..688b512882ec
--- /dev/null
+++ b/drivers/reset/reset-zynq.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015, National Instruments Corp.
+ *
+ * Xilinx Zynq Reset controller driver
+ *
+ * Author: Moritz Fischer <moritz.fischer@ettus.com>
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct zynq_reset_data {
+ struct regmap *slcr;
+ struct reset_controller_dev rcdev;
+ u32 offset;
+};
+
+#define to_zynq_reset_data(p) \
+ container_of((p), struct zynq_reset_data, rcdev)
+
+static int zynq_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynq_reset_data *priv = to_zynq_reset_data(rcdev);
+
+ int bank = id / BITS_PER_LONG;
+ int offset = id % BITS_PER_LONG;
+
+ pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__,
+ bank, offset);
+
+ return regmap_update_bits(priv->slcr,
+ priv->offset + (bank * 4),
+ BIT(offset),
+ BIT(offset));
+}
+
+static int zynq_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynq_reset_data *priv = to_zynq_reset_data(rcdev);
+
+ int bank = id / BITS_PER_LONG;
+ int offset = id % BITS_PER_LONG;
+
+ pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__,
+ bank, offset);
+
+ return regmap_update_bits(priv->slcr,
+ priv->offset + (bank * 4),
+ BIT(offset),
+ ~BIT(offset));
+}
+
+static int zynq_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynq_reset_data *priv = to_zynq_reset_data(rcdev);
+
+ int bank = id / BITS_PER_LONG;
+ int offset = id % BITS_PER_LONG;
+ int ret;
+ u32 reg;
+
+ pr_debug("%s: %s reset bank %u offset %u\n", KBUILD_MODNAME, __func__,
+ bank, offset);
+
+ ret = regmap_read(priv->slcr, priv->offset + (bank * 4), &reg);
+ if (ret)
+ return ret;
+
+ return !!(reg & BIT(offset));
+}
+
+static const struct reset_control_ops zynq_reset_ops = {
+ .assert = zynq_reset_assert,
+ .deassert = zynq_reset_deassert,
+ .status = zynq_reset_status,
+};
+
+static int zynq_reset_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct zynq_reset_data *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->slcr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "syscon");
+ if (IS_ERR(priv->slcr)) {
+ dev_err(&pdev->dev, "unable to get zynq-slcr regmap");
+ return PTR_ERR(priv->slcr);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing IO resource\n");
+ return -ENODEV;
+ }
+
+ priv->offset = res->start;
+
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_LONG;
+ priv->rcdev.ops = &zynq_reset_ops;
+ priv->rcdev.of_node = pdev->dev.of_node;
+
+ return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
+}
+
+static const struct of_device_id zynq_reset_dt_ids[] = {
+ { .compatible = "xlnx,zynq-reset", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver zynq_reset_driver = {
+ .probe = zynq_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = zynq_reset_dt_ids,
+ },
+};
+builtin_platform_driver(zynq_reset_driver);
diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c
new file mode 100644
index 000000000000..c770ea3a1894
--- /dev/null
+++ b/drivers/reset/reset-zynqmp.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Xilinx, Inc.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+
+#define ZYNQMP_NR_RESETS (ZYNQMP_PM_RESET_END - ZYNQMP_PM_RESET_START)
+#define ZYNQMP_RESET_ID ZYNQMP_PM_RESET_START
+#define VERSAL_NR_RESETS 95
+#define VERSAL_NET_NR_RESETS 176
+
+struct zynqmp_reset_soc_data {
+ u32 reset_id;
+ u32 num_resets;
+};
+
+struct zynqmp_reset_data {
+ struct reset_controller_dev rcdev;
+ const struct zynqmp_reset_soc_data *data;
+};
+
+static inline struct zynqmp_reset_data *
+to_zynqmp_reset_data(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct zynqmp_reset_data, rcdev);
+}
+
+static int zynqmp_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
+
+ return zynqmp_pm_reset_assert(priv->data->reset_id + id,
+ PM_RESET_ACTION_ASSERT);
+}
+
+static int zynqmp_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
+
+ return zynqmp_pm_reset_assert(priv->data->reset_id + id,
+ PM_RESET_ACTION_RELEASE);
+}
+
+static int zynqmp_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
+ int err;
+ u32 val;
+
+ err = zynqmp_pm_reset_get_status(priv->data->reset_id + id, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+static int zynqmp_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev);
+
+ return zynqmp_pm_reset_assert(priv->data->reset_id + id,
+ PM_RESET_ACTION_PULSE);
+}
+
+static int zynqmp_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return reset_spec->args[0];
+}
+
+static const struct zynqmp_reset_soc_data zynqmp_reset_data = {
+ .reset_id = ZYNQMP_RESET_ID,
+ .num_resets = ZYNQMP_NR_RESETS,
+};
+
+static const struct zynqmp_reset_soc_data versal_reset_data = {
+ .reset_id = 0,
+ .num_resets = VERSAL_NR_RESETS,
+};
+
+static const struct zynqmp_reset_soc_data versal_net_reset_data = {
+ .reset_id = 0,
+ .num_resets = VERSAL_NET_NR_RESETS,
+};
+
+static const struct reset_control_ops zynqmp_reset_ops = {
+ .reset = zynqmp_reset_reset,
+ .assert = zynqmp_reset_assert,
+ .deassert = zynqmp_reset_deassert,
+ .status = zynqmp_reset_status,
+};
+
+static int zynqmp_reset_probe(struct platform_device *pdev)
+{
+ struct zynqmp_reset_data *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_device_get_match_data(&pdev->dev);
+ if (!priv->data)
+ return -EINVAL;
+
+ priv->rcdev.ops = &zynqmp_reset_ops;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.of_node = pdev->dev.of_node;
+ priv->rcdev.nr_resets = priv->data->num_resets;
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = zynqmp_reset_of_xlate;
+
+ return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
+}
+
+static const struct of_device_id zynqmp_reset_dt_ids[] = {
+ { .compatible = "xlnx,zynqmp-reset", .data = &zynqmp_reset_data, },
+ { .compatible = "xlnx,versal-reset", .data = &versal_reset_data, },
+ { .compatible = "xlnx,versal-net-reset", .data = &versal_net_reset_data, },
+ { /* sentinel */ },
+};
+
+static struct platform_driver zynqmp_reset_driver = {
+ .probe = zynqmp_reset_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = zynqmp_reset_dt_ids,
+ },
+};
+
+static int __init zynqmp_reset_init(void)
+{
+ return platform_driver_register(&zynqmp_reset_driver);
+}
+
+arch_initcall(zynqmp_reset_init);
diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig
new file mode 100644
index 000000000000..d832339f61bc
--- /dev/null
+++ b/drivers/reset/starfive/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config RESET_STARFIVE_JH71X0
+ bool
+
+config RESET_STARFIVE_JH7100
+ bool "StarFive JH7100 Reset Driver"
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ select RESET_STARFIVE_JH71X0
+ default ARCH_STARFIVE
+ help
+ This enables the reset controller driver for the StarFive JH7100 SoC.
+
+config RESET_STARFIVE_JH7110
+ bool "StarFive JH7110 Reset Driver"
+ depends on CLK_STARFIVE_JH7110_SYS
+ select AUXILIARY_BUS
+ select RESET_STARFIVE_JH71X0
+ default ARCH_STARFIVE
+ help
+ This enables the reset controller driver for the StarFive JH7110 SoC.
diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile
new file mode 100644
index 000000000000..7a44b66fb9d5
--- /dev/null
+++ b/drivers/reset/starfive/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_RESET_STARFIVE_JH71X0) += reset-starfive-jh71x0.o
+
+obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o
+obj-$(CONFIG_RESET_STARFIVE_JH7110) += reset-starfive-jh7110.o
diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c
new file mode 100644
index 000000000000..2a56f7fd4ba7
--- /dev/null
+++ b/drivers/reset/starfive/reset-starfive-jh7100.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Reset driver for the StarFive JH7100 SoC
+ *
+ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#include "reset-starfive-jh71x0.h"
+
+#include <dt-bindings/reset/starfive-jh7100.h>
+
+/* register offsets */
+#define JH7100_RESET_ASSERT0 0x00
+#define JH7100_RESET_ASSERT1 0x04
+#define JH7100_RESET_ASSERT2 0x08
+#define JH7100_RESET_ASSERT3 0x0c
+#define JH7100_RESET_STATUS0 0x10
+#define JH7100_RESET_STATUS1 0x14
+#define JH7100_RESET_STATUS2 0x18
+#define JH7100_RESET_STATUS3 0x1c
+
+/*
+ * Writing a 1 to the n'th bit of the m'th ASSERT register asserts
+ * line 32m + n, and writing a 0 deasserts the same line.
+ * Most reset lines have their status inverted so a 0 bit in the STATUS
+ * register means the line is asserted and a 1 means it's deasserted. A few
+ * lines don't though, so store the expected value of the status registers when
+ * all lines are asserted.
+ */
+static const u32 jh7100_reset_asserted[4] = {
+ /* STATUS0 */
+ BIT(JH7100_RST_U74 % 32) |
+ BIT(JH7100_RST_VP6_DRESET % 32) |
+ BIT(JH7100_RST_VP6_BRESET % 32),
+ /* STATUS1 */
+ BIT(JH7100_RST_HIFI4_DRESET % 32) |
+ BIT(JH7100_RST_HIFI4_BRESET % 32),
+ /* STATUS2 */
+ BIT(JH7100_RST_E24 % 32),
+ /* STATUS3 */
+ 0,
+};
+
+static int __init jh7100_reset_probe(struct platform_device *pdev)
+{
+ void __iomem *base = devm_platform_ioremap_resource(pdev, 0);
+
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ return reset_starfive_jh71x0_register(&pdev->dev, pdev->dev.of_node,
+ base + JH7100_RESET_ASSERT0,
+ base + JH7100_RESET_STATUS0,
+ jh7100_reset_asserted,
+ JH7100_RSTN_END,
+ THIS_MODULE);
+}
+
+static const struct of_device_id jh7100_reset_dt_ids[] = {
+ { .compatible = "starfive,jh7100-reset" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver jh7100_reset_driver = {
+ .driver = {
+ .name = "jh7100-reset",
+ .of_match_table = jh7100_reset_dt_ids,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver_probe(jh7100_reset_driver, jh7100_reset_probe);
diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c
new file mode 100644
index 000000000000..29a43f0f2ad6
--- /dev/null
+++ b/drivers/reset/starfive/reset-starfive-jh7110.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Reset driver for the StarFive JH7110 SoC
+ *
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/auxiliary_bus.h>
+
+#include <soc/starfive/reset-starfive-jh71x0.h>
+
+#include "reset-starfive-jh71x0.h"
+
+#include <dt-bindings/reset/starfive,jh7110-crg.h>
+
+struct jh7110_reset_info {
+ unsigned int nr_resets;
+ unsigned int assert_offset;
+ unsigned int status_offset;
+};
+
+static const struct jh7110_reset_info jh7110_sys_info = {
+ .nr_resets = JH7110_SYSRST_END,
+ .assert_offset = 0x2F8,
+ .status_offset = 0x308,
+};
+
+static const struct jh7110_reset_info jh7110_aon_info = {
+ .nr_resets = JH7110_AONRST_END,
+ .assert_offset = 0x38,
+ .status_offset = 0x3C,
+};
+
+static const struct jh7110_reset_info jh7110_stg_info = {
+ .nr_resets = JH7110_STGRST_END,
+ .assert_offset = 0x74,
+ .status_offset = 0x78,
+};
+
+static const struct jh7110_reset_info jh7110_isp_info = {
+ .nr_resets = JH7110_ISPRST_END,
+ .assert_offset = 0x38,
+ .status_offset = 0x3C,
+};
+
+static const struct jh7110_reset_info jh7110_vout_info = {
+ .nr_resets = JH7110_VOUTRST_END,
+ .assert_offset = 0x48,
+ .status_offset = 0x4C,
+};
+
+static int jh7110_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct jh7110_reset_info *info = (struct jh7110_reset_info *)(id->driver_data);
+ struct jh71x0_reset_adev *rdev = to_jh71x0_reset_adev(adev);
+ void __iomem *base = rdev->base;
+
+ if (!info || !base)
+ return -ENODEV;
+
+ return reset_starfive_jh71x0_register(&adev->dev, adev->dev.parent->of_node,
+ base + info->assert_offset,
+ base + info->status_offset,
+ NULL,
+ info->nr_resets,
+ NULL);
+}
+
+static const struct auxiliary_device_id jh7110_reset_ids[] = {
+ {
+ .name = "clk_starfive_jh7110_sys.rst-sys",
+ .driver_data = (kernel_ulong_t)&jh7110_sys_info,
+ },
+ {
+ .name = "clk_starfive_jh7110_sys.rst-aon",
+ .driver_data = (kernel_ulong_t)&jh7110_aon_info,
+ },
+ {
+ .name = "clk_starfive_jh7110_sys.rst-stg",
+ .driver_data = (kernel_ulong_t)&jh7110_stg_info,
+ },
+ {
+ .name = "clk_starfive_jh7110_sys.rst-isp",
+ .driver_data = (kernel_ulong_t)&jh7110_isp_info,
+ },
+ {
+ .name = "clk_starfive_jh7110_sys.rst-vo",
+ .driver_data = (kernel_ulong_t)&jh7110_vout_info,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids);
+
+static struct auxiliary_driver jh7110_reset_driver = {
+ .probe = jh7110_reset_probe,
+ .id_table = jh7110_reset_ids,
+};
+module_auxiliary_driver(jh7110_reset_driver);
+
+MODULE_AUTHOR("Hal Feng <hal.feng@starfivetech.com>");
+MODULE_DESCRIPTION("StarFive JH7110 reset driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c
new file mode 100644
index 000000000000..29ce3486752f
--- /dev/null
+++ b/drivers/reset/starfive/reset-starfive-jh71x0.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Reset driver for the StarFive JH71X0 SoCs
+ *
+ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+
+#include "reset-starfive-jh71x0.h"
+
+struct jh71x0_reset {
+ struct reset_controller_dev rcdev;
+ /* protect registers against concurrent read-modify-write */
+ spinlock_t lock;
+ void __iomem *assert;
+ void __iomem *status;
+ const u32 *asserted;
+};
+
+static inline struct jh71x0_reset *
+jh71x0_reset_from(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct jh71x0_reset, rcdev);
+}
+
+static int jh71x0_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
+ unsigned long offset = id / 32;
+ u32 mask = BIT(id % 32);
+ void __iomem *reg_assert = data->assert + offset * sizeof(u32);
+ void __iomem *reg_status = data->status + offset * sizeof(u32);
+ u32 done = data->asserted ? data->asserted[offset] & mask : 0;
+ u32 value;
+ unsigned long flags;
+ int ret;
+
+ if (!assert)
+ done ^= mask;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ value = readl(reg_assert);
+ if (assert)
+ value |= mask;
+ else
+ value &= ~mask;
+ writel(value, reg_assert);
+
+ /* if the associated clock is gated, deasserting might otherwise hang forever */
+ ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+ return ret;
+}
+
+static int jh71x0_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return jh71x0_reset_update(rcdev, id, true);
+}
+
+static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return jh71x0_reset_update(rcdev, id, false);
+}
+
+static int jh71x0_reset_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = jh71x0_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ return jh71x0_reset_deassert(rcdev, id);
+}
+
+static int jh71x0_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
+ unsigned long offset = id / 32;
+ u32 mask = BIT(id % 32);
+ void __iomem *reg_status = data->status + offset * sizeof(u32);
+ u32 value = readl(reg_status);
+
+ if (!data->asserted)
+ return !(value & mask);
+
+ return !((value ^ data->asserted[offset]) & mask);
+}
+
+static const struct reset_control_ops jh71x0_reset_ops = {
+ .assert = jh71x0_reset_assert,
+ .deassert = jh71x0_reset_deassert,
+ .reset = jh71x0_reset_reset,
+ .status = jh71x0_reset_status,
+};
+
+int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,
+ void __iomem *assert, void __iomem *status,
+ const u32 *asserted, unsigned int nr_resets,
+ struct module *owner)
+{
+ struct jh71x0_reset *data;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->rcdev.ops = &jh71x0_reset_ops;
+ data->rcdev.owner = owner;
+ data->rcdev.nr_resets = nr_resets;
+ data->rcdev.dev = dev;
+ data->rcdev.of_node = of_node;
+
+ spin_lock_init(&data->lock);
+ data->assert = assert;
+ data->status = status;
+ data->asserted = asserted;
+
+ return devm_reset_controller_register(dev, &data->rcdev);
+}
+EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register);
diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h
new file mode 100644
index 000000000000..db7d39a87f87
--- /dev/null
+++ b/drivers/reset/starfive/reset-starfive-jh71x0.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
+ */
+
+#ifndef __RESET_STARFIVE_JH71X0_H
+#define __RESET_STARFIVE_JH71X0_H
+
+int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,
+ void __iomem *assert, void __iomem *status,
+ const u32 *asserted, unsigned int nr_resets,
+ struct module *owner);
+
+#endif /* __RESET_STARFIVE_JH71X0_H */
diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig
new file mode 100644
index 000000000000..0b599f7cf6ed
--- /dev/null
+++ b/drivers/reset/sti/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if ARCH_STI || COMPILE_TEST
+
+config STIH407_RESET
+ bool "STIH407 Reset Driver" if COMPILE_TEST
+
+endif
diff --git a/drivers/reset/sti/Makefile b/drivers/reset/sti/Makefile
new file mode 100644
index 000000000000..5e833496cee3
--- /dev/null
+++ b/drivers/reset/sti/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_STIH407_RESET) += reset-stih407.o reset-syscfg.o
diff --git a/drivers/reset/sti/reset-stih407.c b/drivers/reset/sti/reset-stih407.c
new file mode 100644
index 000000000000..c3bf9a918dbd
--- /dev/null
+++ b/drivers/reset/sti/reset-stih407.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2014 STMicroelectronics (R&D) Limited
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/reset/stih407-resets.h>
+#include "reset-syscfg.h"
+
+/* STiH407 Peripheral powerdown definitions. */
+static const char stih407_core[] = "st,stih407-core-syscfg";
+static const char stih407_sbc_reg[] = "st,stih407-sbc-reg-syscfg";
+static const char stih407_lpm[] = "st,stih407-lpm-syscfg";
+
+#define STIH407_PDN_0(_bit) \
+ _SYSCFG_RST_CH(stih407_core, SYSCFG_5000, _bit, SYSSTAT_5500, _bit)
+#define STIH407_PDN_1(_bit) \
+ _SYSCFG_RST_CH(stih407_core, SYSCFG_5001, _bit, SYSSTAT_5501, _bit)
+#define STIH407_PDN_ETH(_bit, _stat) \
+ _SYSCFG_RST_CH(stih407_sbc_reg, SYSCFG_4032, _bit, SYSSTAT_4520, _stat)
+
+/* Powerdown requests control 0 */
+#define SYSCFG_5000 0x0
+#define SYSSTAT_5500 0x7d0
+/* Powerdown requests control 1 (High Speed Links) */
+#define SYSCFG_5001 0x4
+#define SYSSTAT_5501 0x7d4
+
+/* Ethernet powerdown/status/reset */
+#define SYSCFG_4032 0x80
+#define SYSSTAT_4520 0x820
+#define SYSCFG_4002 0x8
+
+static const struct syscfg_reset_channel_data stih407_powerdowns[] = {
+ [STIH407_EMISS_POWERDOWN] = STIH407_PDN_0(1),
+ [STIH407_NAND_POWERDOWN] = STIH407_PDN_0(0),
+ [STIH407_USB3_POWERDOWN] = STIH407_PDN_1(6),
+ [STIH407_USB2_PORT1_POWERDOWN] = STIH407_PDN_1(5),
+ [STIH407_USB2_PORT0_POWERDOWN] = STIH407_PDN_1(4),
+ [STIH407_PCIE1_POWERDOWN] = STIH407_PDN_1(3),
+ [STIH407_PCIE0_POWERDOWN] = STIH407_PDN_1(2),
+ [STIH407_SATA1_POWERDOWN] = STIH407_PDN_1(1),
+ [STIH407_SATA0_POWERDOWN] = STIH407_PDN_1(0),
+ [STIH407_ETH1_POWERDOWN] = STIH407_PDN_ETH(0, 2),
+};
+
+/* Reset Generator control 0/1 */
+#define SYSCFG_5128 0x200
+#define SYSCFG_5131 0x20c
+#define SYSCFG_5132 0x210
+
+#define LPM_SYSCFG_1 0x4 /* Softreset IRB & SBC UART */
+
+#define STIH407_SRST_CORE(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih407_core, _reg, _bit)
+
+#define STIH407_SRST_SBC(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih407_sbc_reg, _reg, _bit)
+
+#define STIH407_SRST_LPM(_reg, _bit) \
+ _SYSCFG_RST_CH_NO_ACK(stih407_lpm, _reg, _bit)
+
+static const struct syscfg_reset_channel_data stih407_softresets[] = {
+ [STIH407_ETH1_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 4),
+ [STIH407_MMC1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 3),
+ [STIH407_USB2_PORT0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 28),
+ [STIH407_USB2_PORT1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 29),
+ [STIH407_PICOPHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 30),
+ [STIH407_IRB_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 6),
+ [STIH407_PCIE0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 6),
+ [STIH407_PCIE1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 15),
+ [STIH407_SATA0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 7),
+ [STIH407_SATA1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 16),
+ [STIH407_MIPHY0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 4),
+ [STIH407_MIPHY1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 13),
+ [STIH407_MIPHY2_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 22),
+ [STIH407_SATA0_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 5),
+ [STIH407_SATA1_PWR_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 14),
+ [STIH407_DELTA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 3),
+ [STIH407_BLITTER_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 10),
+ [STIH407_HDTVOUT_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 11),
+ [STIH407_HDQVDP_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 12),
+ [STIH407_VDP_AUX_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 14),
+ [STIH407_COMPO_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 15),
+ [STIH407_HDMI_TX_PHY_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 21),
+ [STIH407_JPEG_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 23),
+ [STIH407_VP8_DEC_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 24),
+ [STIH407_GPU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 30),
+ [STIH407_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 0),
+ [STIH407_ERAM_HVA_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5132, 1),
+ [STIH407_LPM_SOFTRESET] = STIH407_SRST_SBC(SYSCFG_4002, 2),
+ [STIH407_KEYSCAN_SOFTRESET] = STIH407_SRST_LPM(LPM_SYSCFG_1, 8),
+ [STIH407_ST231_AUD_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 26),
+ [STIH407_ST231_DMU_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 27),
+ [STIH407_ST231_GP0_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5131, 28),
+ [STIH407_ST231_GP1_SOFTRESET] = STIH407_SRST_CORE(SYSCFG_5128, 2),
+};
+
+/* PicoPHY reset/control */
+#define SYSCFG_5061 0x0f4
+
+static const struct syscfg_reset_channel_data stih407_picophyresets[] = {
+ [STIH407_PICOPHY0_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 5),
+ [STIH407_PICOPHY1_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 6),
+ [STIH407_PICOPHY2_RESET] = STIH407_SRST_CORE(SYSCFG_5061, 7),
+};
+
+static const struct syscfg_reset_controller_data stih407_powerdown_controller = {
+ .wait_for_ack = true,
+ .nr_channels = ARRAY_SIZE(stih407_powerdowns),
+ .channels = stih407_powerdowns,
+};
+
+static const struct syscfg_reset_controller_data stih407_softreset_controller = {
+ .wait_for_ack = false,
+ .active_low = true,
+ .nr_channels = ARRAY_SIZE(stih407_softresets),
+ .channels = stih407_softresets,
+};
+
+static const struct syscfg_reset_controller_data stih407_picophyreset_controller = {
+ .wait_for_ack = false,
+ .nr_channels = ARRAY_SIZE(stih407_picophyresets),
+ .channels = stih407_picophyresets,
+};
+
+static const struct of_device_id stih407_reset_match[] = {
+ {
+ .compatible = "st,stih407-powerdown",
+ .data = &stih407_powerdown_controller,
+ },
+ {
+ .compatible = "st,stih407-softreset",
+ .data = &stih407_softreset_controller,
+ },
+ {
+ .compatible = "st,stih407-picophyreset",
+ .data = &stih407_picophyreset_controller,
+ },
+ { /* sentinel */ },
+};
+
+static struct platform_driver stih407_reset_driver = {
+ .probe = syscfg_reset_probe,
+ .driver = {
+ .name = "reset-stih407",
+ .of_match_table = stih407_reset_match,
+ },
+};
+
+static int __init stih407_reset_init(void)
+{
+ return platform_driver_register(&stih407_reset_driver);
+}
+
+arch_initcall(stih407_reset_init);
diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c
new file mode 100644
index 000000000000..2324060b747c
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ *
+ * Inspired by mach-imx/src.c
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "reset-syscfg.h"
+
+/**
+ * struct syscfg_reset_channel - Reset channel regmap configuration
+ *
+ * @reset: regmap field for the channel's reset bit.
+ * @ack: regmap field for the channel's ack bit (optional).
+ */
+struct syscfg_reset_channel {
+ struct regmap_field *reset;
+ struct regmap_field *ack;
+};
+
+/**
+ * struct syscfg_reset_controller - A reset controller which groups together
+ * a set of related reset bits, which may be located in different system
+ * configuration registers.
+ *
+ * @rst: base reset controller structure.
+ * @active_low: are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @channels: An array of reset channels for this controller.
+ */
+struct syscfg_reset_controller {
+ struct reset_controller_dev rst;
+ bool active_low;
+ struct syscfg_reset_channel *channels;
+};
+
+#define to_syscfg_reset_controller(_rst) \
+ container_of(_rst, struct syscfg_reset_controller, rst)
+
+static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev,
+ unsigned long idx, int assert)
+{
+ struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
+ const struct syscfg_reset_channel *ch;
+ u32 ctrl_val = rst->active_low ? !assert : !!assert;
+ int err;
+
+ if (idx >= rcdev->nr_resets)
+ return -EINVAL;
+
+ ch = &rst->channels[idx];
+
+ err = regmap_field_write(ch->reset, ctrl_val);
+ if (err)
+ return err;
+
+ if (ch->ack) {
+ u32 ack_val;
+
+ err = regmap_field_read_poll_timeout(ch->ack, ack_val, (ack_val == ctrl_val),
+ 100, USEC_PER_SEC);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int syscfg_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, true);
+}
+
+static int syscfg_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ return syscfg_reset_program_hw(rcdev, idx, false);
+}
+
+static int syscfg_reset_dev(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ int err;
+
+ err = syscfg_reset_assert(rcdev, idx);
+ if (err)
+ return err;
+
+ return syscfg_reset_deassert(rcdev, idx);
+}
+
+static int syscfg_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev);
+ const struct syscfg_reset_channel *ch;
+ u32 ret_val = 0;
+ int err;
+
+ if (idx >= rcdev->nr_resets)
+ return -EINVAL;
+
+ ch = &rst->channels[idx];
+ if (ch->ack)
+ err = regmap_field_read(ch->ack, &ret_val);
+ else
+ err = regmap_field_read(ch->reset, &ret_val);
+ if (err)
+ return err;
+
+ return rst->active_low ? !ret_val : !!ret_val;
+}
+
+static const struct reset_control_ops syscfg_reset_ops = {
+ .reset = syscfg_reset_dev,
+ .assert = syscfg_reset_assert,
+ .deassert = syscfg_reset_deassert,
+ .status = syscfg_reset_status,
+};
+
+static int syscfg_reset_controller_register(struct device *dev,
+ const struct syscfg_reset_controller_data *data)
+{
+ struct syscfg_reset_controller *rc;
+ int i, err;
+
+ rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
+ if (!rc)
+ return -ENOMEM;
+
+ rc->channels = devm_kcalloc(dev, data->nr_channels,
+ sizeof(*rc->channels), GFP_KERNEL);
+ if (!rc->channels)
+ return -ENOMEM;
+
+ rc->rst.ops = &syscfg_reset_ops;
+ rc->rst.of_node = dev->of_node;
+ rc->rst.nr_resets = data->nr_channels;
+ rc->active_low = data->active_low;
+
+ for (i = 0; i < data->nr_channels; i++) {
+ struct regmap *map;
+ struct regmap_field *f;
+ const char *compatible = data->channels[i].compatible;
+
+ map = syscon_regmap_lookup_by_compatible(compatible);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].reset);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].reset = f;
+
+ if (!data->wait_for_ack)
+ continue;
+
+ f = devm_regmap_field_alloc(dev, map, data->channels[i].ack);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ rc->channels[i].ack = f;
+ }
+
+ err = reset_controller_register(&rc->rst);
+ if (!err)
+ dev_info(dev, "registered\n");
+
+ return err;
+}
+
+int syscfg_reset_probe(struct platform_device *pdev)
+{
+ struct device *dev = pdev ? &pdev->dev : NULL;
+ const void *data;
+
+ if (!dev || !dev->driver)
+ return -ENODEV;
+
+ data = device_get_match_data(&pdev->dev);
+ if (!data)
+ return -EINVAL;
+
+ return syscfg_reset_controller_register(dev, data);
+}
diff --git a/drivers/reset/sti/reset-syscfg.h b/drivers/reset/sti/reset-syscfg.h
new file mode 100644
index 000000000000..4e3e69de1070
--- /dev/null
+++ b/drivers/reset/sti/reset-syscfg.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013 STMicroelectronics (R&D) Limited
+ * Author: Stephen Gallimore <stephen.gallimore@st.com>
+ */
+#ifndef __STI_RESET_SYSCFG_H
+#define __STI_RESET_SYSCFG_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+/**
+ * Reset channel description for a system configuration register based
+ * reset controller.
+ *
+ * @compatible: Compatible string of the syscon regmap containing this
+ * channel's control and ack (status) bits.
+ * @reset: Regmap field description of the channel's reset bit.
+ * @ack: Regmap field description of the channel's acknowledge bit.
+ */
+struct syscfg_reset_channel_data {
+ const char *compatible;
+ struct reg_field reset;
+ struct reg_field ack;
+};
+
+#define _SYSCFG_RST_CH(_c, _rr, _rb, _ar, _ab) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), \
+ .ack = REG_FIELD(_ar, _ab, _ab), }
+
+#define _SYSCFG_RST_CH_NO_ACK(_c, _rr, _rb) \
+ { .compatible = _c, \
+ .reset = REG_FIELD(_rr, _rb, _rb), }
+
+/**
+ * Description of a system configuration register based reset controller.
+ *
+ * @wait_for_ack: The controller will wait for reset assert and de-assert to
+ * be "ack'd" in a channel's ack field.
+ * @active_low: Are the resets in this controller active low, i.e. clearing
+ * the reset bit puts the hardware into reset.
+ * @nr_channels: The number of reset channels in this controller.
+ * @channels: An array of reset channel descriptions.
+ */
+struct syscfg_reset_controller_data {
+ bool wait_for_ack;
+ bool active_low;
+ int nr_channels;
+ const struct syscfg_reset_channel_data *channels;
+};
+
+/**
+ * syscfg_reset_probe(): platform device probe function used by syscfg
+ * reset controller drivers. This registers a reset
+ * controller configured by the OF match data for
+ * the compatible device which should be of type
+ * "struct syscfg_reset_controller_data".
+ *
+ * @pdev: platform device
+ */
+int syscfg_reset_probe(struct platform_device *pdev);
+
+#endif /* __STI_RESET_SYSCFG_H */
diff --git a/drivers/reset/tegra/Kconfig b/drivers/reset/tegra/Kconfig
new file mode 100644
index 000000000000..4a2d26d1210a
--- /dev/null
+++ b/drivers/reset/tegra/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config RESET_TEGRA_BPMP
+ bool "Tegra BPMP Reset Driver" if COMPILE_TEST
+ default TEGRA_BPMP
diff --git a/drivers/reset/tegra/Makefile b/drivers/reset/tegra/Makefile
new file mode 100644
index 000000000000..eccba896aa1b
--- /dev/null
+++ b/drivers/reset/tegra/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_RESET_TEGRA_BPMP) += reset-bpmp.o
diff --git a/drivers/reset/tegra/reset-bpmp.c b/drivers/reset/tegra/reset-bpmp.c
new file mode 100644
index 000000000000..4c5bba52b105
--- /dev/null
+++ b/drivers/reset/tegra/reset-bpmp.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 NVIDIA Corporation
+ */
+
+#include <linux/reset-controller.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+static struct tegra_bpmp *to_tegra_bpmp(struct reset_controller_dev *rstc)
+{
+ return container_of(rstc, struct tegra_bpmp, rstc);
+}
+
+static int tegra_bpmp_reset_common(struct reset_controller_dev *rstc,
+ enum mrq_reset_commands command,
+ unsigned int id)
+{
+ struct tegra_bpmp *bpmp = to_tegra_bpmp(rstc);
+ struct mrq_reset_request request;
+ struct tegra_bpmp_message msg;
+ int err;
+
+ memset(&request, 0, sizeof(request));
+ request.cmd = command;
+ request.reset_id = id;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_RESET;
+ msg.tx.data = &request;
+ msg.tx.size = sizeof(request);
+
+ err = tegra_bpmp_transfer(bpmp, &msg);
+ if (err)
+ return err;
+ if (msg.rx.ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_bpmp_reset_module(struct reset_controller_dev *rstc,
+ unsigned long id)
+{
+ return tegra_bpmp_reset_common(rstc, CMD_RESET_MODULE, id);
+}
+
+static int tegra_bpmp_reset_assert(struct reset_controller_dev *rstc,
+ unsigned long id)
+{
+ return tegra_bpmp_reset_common(rstc, CMD_RESET_ASSERT, id);
+}
+
+static int tegra_bpmp_reset_deassert(struct reset_controller_dev *rstc,
+ unsigned long id)
+{
+ return tegra_bpmp_reset_common(rstc, CMD_RESET_DEASSERT, id);
+}
+
+static const struct reset_control_ops tegra_bpmp_reset_ops = {
+ .reset = tegra_bpmp_reset_module,
+ .assert = tegra_bpmp_reset_assert,
+ .deassert = tegra_bpmp_reset_deassert,
+};
+
+int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
+{
+ bpmp->rstc.ops = &tegra_bpmp_reset_ops;
+ bpmp->rstc.owner = THIS_MODULE;
+ bpmp->rstc.of_node = bpmp->dev->of_node;
+ bpmp->rstc.nr_resets = bpmp->soc->num_resets;
+
+ return devm_reset_controller_register(bpmp->dev, &bpmp->rstc);
+}