diff options
Diffstat (limited to 'drivers/reset')
31 files changed, 2377 insertions, 326 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index ccd59ddd7610..d85be5899da6 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -66,6 +66,29 @@ config RESET_BRCMSTB_RESCAL This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on BCM7216. +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 + 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 @@ -73,6 +96,13 @@ config RESET_HSDK 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 @@ -82,6 +112,14 @@ config RESET_IMX7 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 @@ -94,9 +132,9 @@ config RESET_INTEL_GW config RESET_K210 bool "Reset controller driver for Canaan Kendryte K210 SoC" - depends on (SOC_CANAAN || COMPILE_TEST) && OF + depends on (SOC_CANAAN_K210 || COMPILE_TEST) && OF select MFD_SYSCON - default SOC_CANAAN + 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 @@ -115,30 +153,17 @@ config RESET_LPC18XX This enables the reset controller driver for NXP LPC18xx/43xx SoCs. config RESET_MCHP_SPARX5 - bool "Microchip Sparx5 reset driver" - depends on ARCH_SPARX5 || SOC_LAN966 || COMPILE_TEST + tristate "Microchip Sparx5 reset driver" + depends on ARCH_SPARX5 || 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_MESON - tristate "Meson Reset Driver" - depends on ARCH_MESON || COMPILE_TEST - default ARCH_MESON - help - This enables the reset driver for Amlogic Meson 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 - 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. @@ -200,6 +225,13 @@ config RESET_RZG2L_USBPHY_CTRL 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 @@ -213,7 +245,7 @@ config RESET_SCMI config RESET_SIMPLE bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT - default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC + 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 @@ -228,6 +260,7 @@ config RESET_SIMPLE - 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) @@ -253,6 +286,16 @@ config RESET_SUNXI help This enables the reset driver for Allwinner SoCs. +config RESET_TH1520 + tristate "T-HEAD 1520 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) @@ -318,6 +361,13 @@ config RESET_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" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 8270da8a4baa..91e6348e3351 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core.o +obj-y += amlogic/ obj-y += hisilicon/ obj-y += starfive/ -obj-$(CONFIG_ARCH_STI) += sti/ -obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-y += sti/ +obj-y += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o obj-$(CONFIG_RESET_ATH79) += reset-ath79.o obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o @@ -11,15 +12,17 @@ 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_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_LANTIQ) += reset-lantiq.o obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o -obj-$(CONFIG_RESET_MESON) += reset-meson.o -obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o obj-$(CONFIG_RESET_NPCM) += reset-npcm.o obj-$(CONFIG_RESET_NUVOTON_MA35D1) += reset-ma35d1.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o @@ -28,11 +31,13 @@ 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_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 @@ -40,4 +45,4 @@ 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_ARCH_ZYNQMP) += reset-zynqmp.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/reset-meson-audio-arb.c b/drivers/reset/amlogic/reset-meson-audio-arb.c index 7891d52fa899..6ec253976bed 100644 --- a/drivers/reset/reset-meson-audio-arb.c +++ b/drivers/reset/amlogic/reset-meson-audio-arb.c @@ -120,7 +120,7 @@ static const struct of_device_id meson_audio_arb_of_match[] = { }; MODULE_DEVICE_TABLE(of, meson_audio_arb_of_match); -static int meson_audio_arb_remove(struct platform_device *pdev) +static void meson_audio_arb_remove(struct platform_device *pdev) { struct meson_audio_arb_data *arb = platform_get_drvdata(pdev); @@ -128,10 +128,6 @@ static int meson_audio_arb_remove(struct platform_device *pdev) spin_lock(&arb->lock); writel(0, arb->regs); spin_unlock(&arb->lock); - - clk_disable_unprepare(arb->clk); - - return 0; } static int meson_audio_arb_probe(struct platform_device *pdev) @@ -150,7 +146,7 @@ static int meson_audio_arb_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, arb); - arb->clk = devm_clk_get(dev, NULL); + 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"); @@ -170,11 +166,6 @@ static int meson_audio_arb_probe(struct platform_device *pdev) * In the initial state, all memory interfaces are disabled * and the general bit is on */ - ret = clk_prepare_enable(arb->clk); - if (ret) { - dev_err(dev, "failed to enable arb clock\n"); - return ret; - } writel(BIT(ARB_GENERAL_BIT), arb->regs); /* Register reset controller */ 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, ®map_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 4d5a78d3c085..22f67fc77ae5 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -5,14 +5,19 @@ * Copyright 2013 Philipp Zabel, Pengutronix */ #include <linux/atomic.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/kref.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/idr.h> #include <linux/module.h> #include <linux/of.h> #include <linux/acpi.h> +#include <linux/platform_device.h> #include <linux/reset.h> #include <linux/reset-controller.h> #include <linux/slab.h> @@ -23,6 +28,11 @@ static LIST_HEAD(reset_controller_list); static DEFINE_MUTEX(reset_lookup_mutex); static LIST_HEAD(reset_lookup_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 @@ -63,6 +73,16 @@ struct reset_control_array { 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 + * @list: list entry for the reset_gpio_lookup_list + */ +struct reset_gpio_lookup { + struct of_phandle_args of_args; + struct list_head list; +}; + static const char *rcdev_name(struct reset_controller_dev *rcdev) { if (rcdev->dev) @@ -71,6 +91,9 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->of_node) return rcdev->of_node->full_name; + if (rcdev->of_args) + return rcdev->of_args->np->full_name; + return NULL; } @@ -99,6 +122,9 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ 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; @@ -747,12 +773,19 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_release); static struct reset_control * __reset_control_get_internal(struct reset_controller_dev *rcdev, - unsigned int index, bool shared, bool acquired) + 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) { /* @@ -786,6 +819,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, kref_init(&rstc->refcnt); rstc->acquired = acquired; rstc->shared = shared; + get_device(rcdev->dev); return rstc; } @@ -800,6 +834,7 @@ static void __reset_control_release(struct kref *kref) module_put(rstc->rcdev->owner); list_del(&rstc->list); + put_device(rstc->rcdev->dev); kfree(rstc); } @@ -813,12 +848,165 @@ static void __reset_control_put_internal(struct reset_control *rstc) kref_put(&rstc->refcnt, __reset_control_release); } +static int __reset_add_reset_gpio_lookup(int id, struct device_node *np, + unsigned int gpio, + unsigned int of_flags) +{ + const struct fwnode_handle *fwnode = of_fwnode_handle(np); + unsigned int lookup_flags; + const char *label_tmp; + + /* + * 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. + */ + if (of_flags > GPIO_ACTIVE_LOW) { + pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", + of_flags, gpio); + return -EINVAL; + } + + struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode); + if (!gdev) + return -EPROBE_DEFER; + + label_tmp = gpio_device_get_label(gdev); + if (!label_tmp) + return -EINVAL; + + char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL); + if (!label) + return -ENOMEM; + + /* Size: one lookup entry plus sentinel */ + struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id); + if (!lookup->dev_id) + return -ENOMEM; + + lookup_flags = GPIO_PERSISTENT; + lookup_flags |= of_flags & GPIO_ACTIVE_LOW; + lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset", + lookup_flags); + + /* Not freed on success, because it is persisent subsystem data. */ + gpiod_add_lookup_table(no_free_ptr(lookup)); + + return 0; +} + +/* + * @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 reset_gpio_lookup *rgpio_dev; + struct platform_device *pdev; + 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); + + 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 */ + } + } + + 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; + } + + ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0], + args->args[1]); + if (ret < 0) + goto err_kfree; + + 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); + pdev = platform_device_register_data(NULL, "reset-gpio", id, + &rgpio_dev->of_args, + sizeof(rgpio_dev->of_args)); + ret = PTR_ERR_OR_ZERO(pdev); + if (ret) + goto err_put; + + list_add(&rgpio_dev->list, &reset_gpio_lookup_list); + + return 0; + +err_put: + of_node_put(rgpio_dev->of_args.np); +err_kfree: + 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, - bool shared, bool optional, bool acquired) + 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 *r, *rcdev; + struct reset_controller_dev *rcdev; struct of_phandle_args args; int rstc_id; int ret; @@ -839,39 +1027,54 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, index, &args); if (ret == -EINVAL) return ERR_PTR(ret); - if (ret) - return optional ? NULL : ERR_PTR(ret); + if (ret) { + if (!IS_ENABLED(CONFIG_RESET_GPIO)) + return optional ? NULL : ERR_PTR(ret); - mutex_lock(&reset_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; } } + mutex_lock(&reset_list_mutex); + rcdev = __reset_find_rcdev(&args, gpio_fallback); if (!rcdev) { rstc = ERR_PTR(-EPROBE_DEFER); - goto out; + goto out_unlock; } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { rstc = ERR_PTR(-EINVAL); - goto out; + goto out_unlock; } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out; + goto out_unlock; } + flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; + /* reset_list_mutex also protects the rcdev's reset_control list */ - rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired); + rstc = __reset_control_get_internal(rcdev, rstc_id, flags); -out: +out_unlock: mutex_unlock(&reset_list_mutex); +out_put: of_node_put(args.np); return rstc; @@ -898,8 +1101,9 @@ __reset_controller_by_name(const char *name) static struct reset_control * __reset_control_get_from_lookup(struct device *dev, const char *con_id, - bool shared, bool optional, bool acquired) + enum reset_control_flags flags) { + bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; const struct reset_control_lookup *lookup; struct reset_controller_dev *rcdev; const char *dev_id = dev_name(dev); @@ -923,9 +1127,11 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id, return ERR_PTR(-EPROBE_DEFER); } + flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; + rstc = __reset_control_get_internal(rcdev, lookup->index, - shared, acquired); + flags); mutex_unlock(&reset_list_mutex); break; } @@ -940,30 +1146,29 @@ __reset_control_get_from_lookup(struct device *dev, const char *con_id, } struct reset_control *__reset_control_get(struct device *dev, const char *id, - int index, bool shared, bool optional, - bool acquired) + int index, enum reset_control_flags flags) { + bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED; + bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; + if (WARN_ON(shared && acquired)) return ERR_PTR(-EINVAL); if (dev->of_node) - return __of_reset_control_get(dev->of_node, id, index, shared, - optional, acquired); + return __of_reset_control_get(dev->of_node, id, index, flags); - return __reset_control_get_from_lookup(dev, id, shared, optional, - acquired); + return __reset_control_get_from_lookup(dev, id, flags); } EXPORT_SYMBOL_GPL(__reset_control_get); int __reset_control_bulk_get(struct device *dev, int num_rstcs, struct reset_control_bulk_data *rstcs, - bool shared, bool optional, bool acquired) + 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, - shared, optional, acquired); + 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; @@ -1031,23 +1236,46 @@ static void devm_reset_control_release(struct device *dev, void *res) reset_control_put(*(struct reset_control **)res); } +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, - bool shared, bool optional, bool acquired) + 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, index, shared, optional, acquired); + 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); @@ -1067,24 +1295,45 @@ static void devm_reset_control_bulk_release(struct device *dev, void *res) reset_control_bulk_put(devres->num_rstcs, devres->rstcs); } +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, - bool shared, bool optional, bool acquired) + enum reset_control_flags flags) { struct reset_control_bulk_devres *ptr; + bool deasserted = flags & RESET_CONTROL_FLAGS_BIT_DEASSERTED; int ret; - ptr = devres_alloc(devm_reset_control_bulk_release, sizeof(*ptr), + ptr = devres_alloc(deasserted ? devm_reset_control_bulk_release_deasserted : + devm_reset_control_bulk_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; - ret = __reset_control_bulk_get(dev, num_rstcs, rstcs, shared, optional, acquired); + 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); @@ -1105,6 +1354,7 @@ EXPORT_SYMBOL_GPL(__devm_reset_control_bulk_get); */ int __device_reset(struct device *dev, bool optional) { + enum reset_control_flags flags; struct reset_control *rstc; int ret; @@ -1120,7 +1370,8 @@ int __device_reset(struct device *dev, bool optional) } #endif - rstc = __reset_control_get(dev, NULL, 0, 0, optional, true); + 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); @@ -1163,17 +1414,14 @@ static int of_reset_control_get_count(struct device_node *node) * device node. * * @np: device node for the device that requests the reset controls array - * @shared: whether reset controls are shared or not - * @optional: whether it is optional to get the reset controls - * @acquired: only one reset control may be acquired for a given controller - * and ID + * @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, bool shared, bool optional, - bool acquired) +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; @@ -1188,8 +1436,7 @@ of_reset_control_array_get(struct device_node *np, bool shared, bool optional, resets->num_rstcs = num; for (i = 0; i < num; i++) { - rstc = __of_reset_control_get(np, NULL, i, shared, optional, - acquired); + rstc = __of_reset_control_get(np, NULL, i, flags); if (IS_ERR(rstc)) goto err_rst; resets->rstc[i] = rstc; @@ -1214,8 +1461,7 @@ 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 - * @shared: whether reset controls are shared or not - * @optional: whether it is optional to get the 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 @@ -1224,7 +1470,7 @@ EXPORT_SYMBOL_GPL(of_reset_control_array_get); * Returns pointer to allocated reset_control on success or error on failure */ struct reset_control * -devm_reset_control_array_get(struct device *dev, bool shared, bool optional) +devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags) { struct reset_control **ptr, *rstc; @@ -1233,7 +1479,7 @@ devm_reset_control_array_get(struct device *dev, bool shared, bool optional) if (!ptr) return ERR_PTR(-ENOMEM); - rstc = of_reset_control_array_get(dev->of_node, shared, optional, true); + rstc = of_reset_control_array_get(dev->of_node, flags); if (IS_ERR_OR_NULL(rstc)) { devres_free(ptr); return rstc; diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c index 5c3267acd2b1..65aa5ff5ed82 100644 --- a/drivers/reset/hisilicon/hi6220_reset.c +++ b/drivers/reset/hisilicon/hi6220_reset.c @@ -219,4 +219,5 @@ static int __init hi6220_reset_init(void) postcore_initcall(hi6220_reset_init); +MODULE_DESCRIPTION("Hisilicon Hi6220 reset controller driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/reset/reset-berlin.c b/drivers/reset/reset-berlin.c index 2537ec05ecee..578fe867080c 100644 --- a/drivers/reset/reset-berlin.c +++ b/drivers/reset/reset-berlin.c @@ -68,13 +68,14 @@ static int berlin_reset_xlate(struct reset_controller_dev *rcdev, static int berlin2_reset_probe(struct platform_device *pdev) { - struct device_node *parent_np = of_get_parent(pdev->dev.of_node); + 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)) diff --git a/drivers/reset/reset-eyeq.c b/drivers/reset/reset-eyeq.c new file mode 100644 index 000000000000..02d50041048b --- /dev/null +++ b/drivers/reset/reset-eyeq.c @@ -0,0 +1,570 @@ +// 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 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; + + /* + * 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..2290b25b6703 --- /dev/null +++ b/drivers/reset/reset-gpio.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/gpio/consumer.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.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 platform_device *pdev) +{ + struct device *dev = &pdev->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; + + platform_set_drvdata(pdev, &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 platform_device_id reset_gpio_ids[] = { + { .name = "reset-gpio", }, + {} +}; +MODULE_DEVICE_TABLE(platform, reset_gpio_ids); + +static struct platform_driver reset_gpio_driver = { + .probe = reset_gpio_probe, + .id_table = reset_gpio_ids, + .driver = { + .name = "reset-gpio", + }, +}; +module_platform_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-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-imx8mp-audiomix.c b/drivers/reset/reset-imx8mp-audiomix.c new file mode 100644 index 000000000000..6b357adfe646 --- /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(1) +#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(2) + +#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-k210.c b/drivers/reset/reset-k210.c index b62a2fd44e4e..e77e4cca377d 100644 --- a/drivers/reset/reset-k210.c +++ b/drivers/reset/reset-k210.c @@ -90,7 +90,7 @@ static const struct reset_control_ops k210_rst_ops = { static int k210_rst_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *parent_np = of_get_parent(dev->of_node); + struct device_node *parent_np; struct k210_rst *ksr; dev_info(dev, "K210 reset controller\n"); @@ -99,6 +99,7 @@ static int k210_rst_probe(struct platform_device *pdev) 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)) diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c index 28fb85772b3e..e42b2f24a93d 100644 --- a/drivers/reset/reset-lpc18xx.c +++ b/drivers/reset/reset-lpc18xx.c @@ -150,29 +150,15 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) if (IS_ERR(rc->base)) return PTR_ERR(rc->base); - rc->clk_reg = devm_clk_get(&pdev->dev, "reg"); - if (IS_ERR(rc->clk_reg)) { - dev_err(&pdev->dev, "reg clock not found\n"); - return PTR_ERR(rc->clk_reg); - } - - rc->clk_delay = devm_clk_get(&pdev->dev, "delay"); - if (IS_ERR(rc->clk_delay)) { - dev_err(&pdev->dev, "delay clock not found\n"); - return PTR_ERR(rc->clk_delay); - } - - ret = clk_prepare_enable(rc->clk_reg); - if (ret) { - dev_err(&pdev->dev, "unable to enable reg clock\n"); - return ret; - } + 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"); - ret = clk_prepare_enable(rc->clk_delay); - if (ret) { - dev_err(&pdev->dev, "unable to enable delay clock\n"); - goto dis_clk_reg; - } + 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; @@ -189,10 +175,8 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) rc->rcdev.of_node = pdev->dev.of_node; ret = reset_controller_register(&rc->rcdev); - if (ret) { - dev_err(&pdev->dev, "unable to register device\n"); - goto dis_clks; - } + 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, @@ -201,13 +185,6 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "failed to register restart handler\n"); return 0; - -dis_clks: - clk_disable_unprepare(rc->clk_delay); -dis_clk_reg: - clk_disable_unprepare(rc->clk_reg); - - return ret; } static const struct of_device_id lpc18xx_rgu_match[] = { diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c deleted file mode 100644 index f78be97898bc..000000000000 --- a/drivers/reset/reset-meson.c +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -/* - * Amlogic Meson Reset Controller driver - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong <narmstrong@baylibre.com> - */ -#include <linux/err.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/reset-controller.h> -#include <linux/slab.h> -#include <linux/types.h> - -#define BITS_PER_REG 32 - -struct meson_reset_param { - int reg_count; - int level_offset; -}; - -struct meson_reset { - void __iomem *reg_base; - const struct meson_reset_param *param; - struct reset_controller_dev rcdev; - spinlock_t lock; -}; - -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 bank = id / BITS_PER_REG; - unsigned int offset = id % BITS_PER_REG; - void __iomem *reg_addr = data->reg_base + (bank << 2); - - writel(BIT(offset), reg_addr); - - return 0; -} - -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 bank = id / BITS_PER_REG; - unsigned int offset = id % BITS_PER_REG; - void __iomem *reg_addr; - unsigned long flags; - u32 reg; - - reg_addr = data->reg_base + data->param->level_offset + (bank << 2); - - spin_lock_irqsave(&data->lock, flags); - - reg = readl(reg_addr); - if (assert) - writel(reg & ~BIT(offset), reg_addr); - else - writel(reg | BIT(offset), reg_addr); - - spin_unlock_irqrestore(&data->lock, flags); - - return 0; -} - -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 const struct reset_control_ops meson_reset_ops = { - .reset = meson_reset_reset, - .assert = meson_reset_assert, - .deassert = meson_reset_deassert, -}; - -static const struct meson_reset_param meson8b_param = { - .reg_count = 8, - .level_offset = 0x7c, -}; - -static const struct meson_reset_param meson_a1_param = { - .reg_count = 3, - .level_offset = 0x40, -}; - -static const struct meson_reset_param meson_s4_param = { - .reg_count = 6, - .level_offset = 0x40, -}; - -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}, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, meson_reset_dt_ids); - -static int meson_reset_probe(struct platform_device *pdev) -{ - struct meson_reset *data; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->reg_base)) - return PTR_ERR(data->reg_base); - - data->param = of_device_get_match_data(&pdev->dev); - if (!data->param) - return -ENODEV; - - spin_lock_init(&data->lock); - - data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = data->param->reg_count * BITS_PER_REG; - data->rcdev.ops = &meson_reset_ops; - data->rcdev.of_node = pdev->dev.of_node; - - return devm_reset_controller_register(&pdev->dev, &data->rcdev); -} - -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_LICENSE("Dual BSD/GPL"); diff --git a/drivers/reset/reset-microchip-sparx5.c b/drivers/reset/reset-microchip-sparx5.c index 636e85c388b0..6d3e75b33260 100644 --- a/drivers/reset/reset-microchip-sparx5.c +++ b/drivers/reset/reset-microchip-sparx5.c @@ -8,6 +8,7 @@ */ #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> @@ -62,6 +63,36 @@ 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, ®map_config); +} + static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name, struct regmap **target) { @@ -72,7 +103,18 @@ static int mchp_sparx5_map_syscon(struct platform_device *pdev, char *name, syscon_np = of_parse_phandle(pdev->dev.of_node, name, 0); if (!syscon_np) return -ENODEV; - regmap = syscon_node_to_regmap(syscon_np); + + /* + * 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); @@ -121,6 +163,7 @@ static int mchp_sparx5_reset_probe(struct platform_device *pdev) 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; @@ -158,6 +201,7 @@ static const struct of_device_id mchp_sparx5_reset_of_match[] = { }, { } }; +MODULE_DEVICE_TABLE(of, mchp_sparx5_reset_of_match); static struct platform_driver mchp_sparx5_reset_driver = { .probe = mchp_sparx5_reset_probe, @@ -180,3 +224,4 @@ 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 index 7f3fb2d472f4..574e59db83a4 100644 --- a/drivers/reset/reset-mpfs.c +++ b/drivers/reset/reset-mpfs.c @@ -8,9 +8,11 @@ */ #include <linux/auxiliary_bus.h> #include <linux/delay.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/reset-controller.h> #include <dt-bindings/clock/microchip,mpfs-clock.h> #include <soc/microchip/mpfs.h> @@ -28,20 +30,30 @@ /* block concurrent access to the soft reset register */ static DEFINE_SPINLOCK(mpfs_reset_lock); +struct mpfs_reset { + void __iomem *base; + 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); unsigned long flags; u32 reg; spin_lock_irqsave(&mpfs_reset_lock, flags); - reg = mpfs_reset_read(rcdev->dev); + reg = readl(rst->base); reg |= BIT(id); - mpfs_reset_write(rcdev->dev, reg); + writel(reg, rst->base); spin_unlock_irqrestore(&mpfs_reset_lock, flags); @@ -50,14 +62,15 @@ static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id) static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id) { + struct mpfs_reset *rst = to_mpfs_reset(rcdev); unsigned long flags; u32 reg; spin_lock_irqsave(&mpfs_reset_lock, flags); - reg = mpfs_reset_read(rcdev->dev); + reg = readl(rst->base); reg &= ~BIT(id); - mpfs_reset_write(rcdev->dev, reg); + writel(reg, rst->base); spin_unlock_irqrestore(&mpfs_reset_lock, flags); @@ -66,7 +79,8 @@ static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id) static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id) { - u32 reg = mpfs_reset_read(rcdev->dev); + struct mpfs_reset *rst = to_mpfs_reset(rcdev); + u32 reg = readl(rst->base); /* * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit @@ -121,11 +135,15 @@ static int mpfs_reset_probe(struct auxiliary_device *adev, { struct device *dev = &adev->dev; struct reset_controller_dev *rcdev; + struct mpfs_reset *rst; - rcdev = devm_kzalloc(dev, sizeof(*rcdev), GFP_KERNEL); - if (!rcdev) + rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL); + if (!rst) return -ENOMEM; + rst->base = (void __iomem *)adev->dev.platform_data; + + rcdev = &rst->rcdev; rcdev->dev = dev; rcdev->dev->parent = dev->parent; rcdev->ops = &mpfs_reset_ops; @@ -137,9 +155,68 @@ static int mpfs_reset_probe(struct auxiliary_device *adev, return devm_reset_controller_register(dev, rcdev); } +static void mpfs_reset_unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static void mpfs_reset_adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static struct auxiliary_device *mpfs_reset_adev_alloc(struct device *clk_dev) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + adev->name = "reset-mpfs"; + adev->dev.parent = clk_dev; + adev->dev.release = mpfs_reset_adev_release; + adev->id = 666u; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ERR_PTR(ret); + } + + return adev; +} + +int mpfs_reset_controller_register(struct device *clk_dev, void __iomem *base) +{ + struct auxiliary_device *adev; + int ret; + + adev = mpfs_reset_adev_alloc(clk_dev); + if (IS_ERR(adev)) + return PTR_ERR(adev); + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + adev->dev.platform_data = (__force void *)base; + + return devm_add_action_or_reset(clk_dev, mpfs_reset_unregister_adev, adev); +} +EXPORT_SYMBOL_NS_GPL(mpfs_reset_controller_register, "MCHP_CLK_MPFS"); + static const struct auxiliary_device_id mpfs_reset_ids[] = { { - .name = "clk_mpfs.reset-mpfs", + .name = "reset_mpfs.reset-mpfs", }, { } }; @@ -154,4 +231,4 @@ module_auxiliary_driver(mpfs_reset_driver); MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver"); MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); -MODULE_IMPORT_NS(MCHP_CLK_MPFS); +MODULE_IMPORT_NS("MCHP_CLK_MPFS"); diff --git a/drivers/reset/reset-npcm.c b/drivers/reset/reset-npcm.c index 8935ef95a2d1..e5b6127783a7 100644 --- a/drivers/reset/reset-npcm.c +++ b/drivers/reset/reset-npcm.c @@ -1,6 +1,7 @@ // 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> @@ -10,11 +11,14 @@ #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) @@ -89,6 +93,7 @@ struct npcm_rc_data { const struct npcm_reset_info *info; struct regmap *gcr_regmap; u32 sw_reset_number; + struct device *dev; void __iomem *base; spinlock_t lock; }; @@ -372,6 +377,67 @@ static const struct reset_control_ops npcm_rc_ops = { .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; @@ -392,6 +458,7 @@ static int npcm_rc_probe(struct platform_device *pdev) 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) { @@ -405,15 +472,22 @@ static int npcm_rc_probe(struct platform_device *pdev) 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, + rc->restart_nb.priority = 192; + rc->restart_nb.notifier_call = npcm_rc_restart; ret = register_restart_handler(&rc->restart_nb); - if (ret) + if (ret) { dev_warn(&pdev->dev, "failed to register restart handler\n"); + return ret; + } } } - 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 = { diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index a8dde4606360..8a7f167e405e 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -10,10 +10,12 @@ #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> #define RESET 0x000 +#define VBENCTL 0x03c #define RESET_SEL_PLLRESET BIT(12) #define RESET_PLLRESET BIT(8) @@ -32,6 +34,7 @@ struct rzg2l_usbphy_ctrl_priv { struct reset_controller_dev rcdev; struct reset_control *rstc; void __iomem *base; + struct platform_device *vdev; spinlock_t lock; }; @@ -100,10 +103,19 @@ static const struct reset_control_ops rzg2l_usbphy_ctrl_reset_ops = { .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 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; @@ -116,6 +128,10 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) 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); + priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(priv->rstc)) return dev_err_probe(dev, PTR_ERR(priv->rstc), @@ -125,25 +141,14 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) if (error) return error; - 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) - 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) { - pm_runtime_disable(&pdev->dev); - reset_control_assert(priv->rstc); - return dev_err_probe(&pdev->dev, error, "pm_runtime_resume_and_get failed"); + 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 */ @@ -153,18 +158,49 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) 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 int rzg2l_usbphy_ctrl_remove(struct platform_device *pdev) +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); - - return 0; } static struct platform_driver rzg2l_usbphy_ctrl_driver = { @@ -173,7 +209,7 @@ static struct platform_driver rzg2l_usbphy_ctrl_driver = { .of_match_table = rzg2l_usbphy_ctrl_match_table, }, .probe = rzg2l_usbphy_ctrl_probe, - .remove = rzg2l_usbphy_ctrl_remove, + .remove = rzg2l_usbphy_ctrl_remove, }; module_platform_driver(rzg2l_usbphy_ctrl_driver); 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-simple.c b/drivers/reset/reset-simple.c index 818cabcc9fb7..276067839830 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -151,6 +151,8 @@ static const struct of_device_id reset_simple_dt_ids[] = { { .compatible = "snps,dw-high-reset" }, { .compatible = "snps,dw-low-reset", .data = &reset_simple_active_low }, + { .compatible = "sophgo,sg2042-reset", + .data = &reset_simple_active_low }, { /* sentinel */ }, }; diff --git a/drivers/reset/reset-th1520.c b/drivers/reset/reset-th1520.c new file mode 100644 index 000000000000..7874f0693e1b --- /dev/null +++ b/drivers/reset/reset-th1520.c @@ -0,0 +1,135 @@ +// 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 VOSYS_REGMAP */ +#define TH1520_GPU_RST_CFG 0x0 +#define TH1520_GPU_RST_CFG_MASK GENMASK(1, 0) + +/* register values */ +#define TH1520_GPU_SW_GPU_RST BIT(0) +#define TH1520_GPU_SW_CLKGEN_RST BIT(1) + +struct th1520_reset_priv { + struct reset_controller_dev rcdev; + struct regmap *map; +}; + +struct th1520_reset_map { + u32 bit; + u32 reg; +}; + +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, + } +}; + +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 = &th1520_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 = &th1520_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, + .fast_io = true, +}; + +static int th1520_reset_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct th1520_reset_priv *priv; + void __iomem *base; + int ret; + + 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); + + /* 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 = ARRAY_SIZE(th1520_resets); + priv->rcdev.ops = &th1520_reset_ops; + priv->rcdev.of_node = dev->of_node; + + return devm_reset_controller_register(dev, &priv->rcdev); +} + +static const struct of_device_id th1520_reset_match[] = { + { .compatible = "thead,th1520-reset" }, + { /* 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 index cc01fa5b0bea..1dc5b766aac1 100644 --- a/drivers/reset/reset-ti-sci.c +++ b/drivers/reset/reset-ti-sci.c @@ -235,15 +235,13 @@ static int ti_sci_reset_probe(struct platform_device *pdev) return reset_controller_register(&data->rcdev); } -static int ti_sci_reset_remove(struct platform_device *pdev) +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); - - return 0; } static struct platform_driver ti_sci_reset_driver = { diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c index 5f9f2f7994c0..a2a262bf6bfc 100644 --- a/drivers/reset/reset-uniphier-glue.c +++ b/drivers/reset/reset-uniphier-glue.c @@ -35,13 +35,6 @@ static void uniphier_clk_disable(void *_priv) clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); } -static void uniphier_rst_assert(void *_priv) -{ - struct uniphier_glue_reset_priv *priv = _priv; - - reset_control_bulk_assert(priv->data->nrsts, priv->rst); -} - static int uniphier_glue_reset_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -68,13 +61,6 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) 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(dev, priv->data->nrsts, - priv->rst); - if (ret) - return ret; - ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk); if (ret) return ret; @@ -83,11 +69,11 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev) if (ret) return ret; - ret = reset_control_bulk_deassert(priv->data->nrsts, priv->rst); - if (ret) - return ret; - - ret = devm_add_action_or_reset(dev, uniphier_rst_assert, priv); + 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; diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c index 55bbbd2de52c..29ce3486752f 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.c +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -94,6 +94,9 @@ static int jh71x0_reset_status(struct reset_controller_dev *rcdev, 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); } diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig index a2622e146b8b..0b599f7cf6ed 100644 --- a/drivers/reset/sti/Kconfig +++ b/drivers/reset/sti/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -if ARCH_STI +if ARCH_STI || COMPILE_TEST config STIH407_RESET - bool + bool "STIH407 Reset Driver" if COMPILE_TEST endif diff --git a/drivers/reset/tegra/Kconfig b/drivers/reset/tegra/Kconfig index e4a9a389e98c..4a2d26d1210a 100644 --- a/drivers/reset/tegra/Kconfig +++ b/drivers/reset/tegra/Kconfig @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only config RESET_TEGRA_BPMP - def_bool TEGRA_BPMP + bool "Tegra BPMP Reset Driver" if COMPILE_TEST + default TEGRA_BPMP |