@@ -218,6 +218,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_RZG3S_SYSC
+ tristate "Renesas RZ/G3S SYSC reset driver"
+ depends on ARCH_R9A08G045 || COMPILE_TEST
+ help
+ Support for SYSC reset found on RZ/G3S family. It mainly
+ controls reset on USB and PCIE.
+
config RESET_SCMI
tristate "Reset driver controlled via ARM SCMI interface"
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
@@ -30,6 +30,7 @@ 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_RZG3S_SYSC) += reset-rzg3s-sysc.o
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
new file mode 100644
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G3S SYSC reset driver
+ *
+ * Copyright (C) 2024 Renesas Electronics Corp.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/soc/renesas/rzg3s-sysc-reset.h>
+
+#include <dt-bindings/reset/renesas,r9a08g045-sysc.h>
+
+#define RZG3S_SYSC_USB_PWRRDY 0xd70
+#define RZG3S_SYSC_PCIE_RST_RSM_B 0xd74
+#define RZG3S_SYSC_RESET_MASK 0x1
+
+/**
+ * struct rzg3s_sysc_reset_info - SYSC reset information
+ * @offset: offset to configure the reset
+ * @assert_val: value to write to register on assert
+ * @deassert_val: value to write to register on de-assert
+ */
+struct rzg3s_sysc_reset_info {
+ u16 offset;
+ u8 assert_val;
+ u8 deassert_val;
+};
+
+/**
+ * struct rzg3s_sysc_reset - SYSC reset
+ * @info: SYSC reset information
+ * @radev: SYSC reset auxiliary device
+ * @rcdev: reset controller device
+ */
+struct rzg3s_sysc_reset {
+ const struct rzg3s_sysc_reset_info *info;
+ struct rzg3s_sysc_reset_adev *radev;
+ struct reset_controller_dev rcdev;
+};
+
+#define to_rzg3s_sysc_reset(r) container_of(r, struct rzg3s_sysc_reset, rcdev)
+
+static int rzg3s_sysc_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct rzg3s_sysc_reset *reset = to_rzg3s_sysc_reset(rcdev);
+ struct rzg3s_sysc_reset_adev *radev = reset->radev;
+ struct rzg3s_sysc_reset_info info = reset->info[id];
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(radev->lock, flags);
+ tmp = readl(radev->base + info.offset);
+ tmp &= ~RZG3S_SYSC_RESET_MASK;
+ tmp |= assert ? info.assert_val : info.deassert_val;
+ writel(tmp, radev->base + info.offset);
+ spin_unlock_irqrestore(radev->lock, flags);
+
+ return 0;
+}
+
+static int rzg3s_sysc_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return rzg3s_sysc_reset_set(rcdev, id, true);
+}
+
+static int rzg3s_sysc_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return rzg3s_sysc_reset_set(rcdev, id, false);
+}
+
+static int rzg3s_sysc_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rzg3s_sysc_reset *reset = to_rzg3s_sysc_reset(rcdev);
+ const struct rzg3s_sysc_reset_info info = reset->info[id];
+ struct rzg3s_sysc_reset_adev *radev = reset->radev;
+ u32 tmp;
+
+ tmp = readl(radev->base + info.offset);
+ tmp = !!(tmp & RZG3S_SYSC_RESET_MASK);
+
+ return info.assert_val ? tmp : !tmp;
+}
+
+static const struct reset_control_ops rzg3s_sysc_reset_ops = {
+ .assert = rzg3s_sysc_reset_assert,
+ .deassert = rzg3s_sysc_reset_deassert,
+ .status = rzg3s_sysc_reset_status,
+};
+
+static const struct rzg3s_sysc_reset_info rzg3s_sysc_reset_info[] = {
+ [R9A08G045_SYSC_RESET_USB] = {
+ .offset = RZG3S_SYSC_USB_PWRRDY, .assert_val = 1, .deassert_val = 0
+ },
+ [R9A08G045_SYSC_RESET_PCIE] = {
+ .offset = RZG3S_SYSC_PCIE_RST_RSM_B, .assert_val = 0, .deassert_val = 1
+ },
+};
+
+static int rzg3s_sysc_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct rzg3s_sysc_reset_adev *reset_adev = to_rzg3s_sysc_reset_adev(adev);
+ struct device *dev = &adev->dev;
+ struct rzg3s_sysc_reset *reset;
+
+ reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL);
+ if (!reset)
+ return -ENOMEM;
+
+ reset->radev = reset_adev;
+ reset->info = rzg3s_sysc_reset_info;
+
+ reset->rcdev.ops = &rzg3s_sysc_reset_ops;
+ reset->rcdev.of_reset_n_cells = 1;
+ reset->rcdev.nr_resets = ARRAY_SIZE(rzg3s_sysc_reset_info);
+ reset->rcdev.of_node = dev->parent->of_node;
+ reset->rcdev.dev = dev;
+
+ return devm_reset_controller_register(dev, &reset->rcdev);
+}
+
+static const struct auxiliary_device_id rzg3s_sysc_reset_ids[] = {
+ { .name = "rzg3s_sysc.reset" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, rzg3s_sysc_reset_ids);
+
+static struct auxiliary_driver rzg3s_sysc_reset_driver = {
+ .probe = rzg3s_sysc_reset_probe,
+ .id_table = rzg3s_sysc_reset_ids,
+};
+module_auxiliary_driver(rzg3s_sysc_reset_driver);
@@ -6,6 +6,7 @@ obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o
ifdef CONFIG_SMP
obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o
endif
+obj-$(CONFIG_ARCH_R9A08G045) += rzg3s-sysc.o
# Family
obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o
new file mode 100644
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G3S System controller driver
+ *
+ * Copyright (C) 2024 Renesas Electronics Corp.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/platform_device.h>
+
+#include <linux/soc/renesas/rzg3s-sysc-reset.h>
+
+/**
+ * struct rzg3s_sysc - SYSC private data structure
+ * @base: base address
+ * @dev: device
+ * @lock: lock
+ */
+struct rzg3s_sysc {
+ void __iomem *base;
+ struct device *dev;
+ spinlock_t lock;
+};
+
+static void rzg3s_sysc_reset_adev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+ struct rzg3s_sysc_reset_adev *reset_adev = to_rzg3s_sysc_reset_adev(adev);
+
+ kfree(reset_adev);
+}
+
+static void rzg3s_sysc_reset_unregister_adev(void *adev)
+{
+ auxiliary_device_delete(adev);
+ auxiliary_device_uninit(adev);
+}
+
+static int rzg3s_sysc_reset_probe(struct rzg3s_sysc *sysc, const char *adev_name,
+ u32 adev_id)
+{
+ struct rzg3s_sysc_reset_adev *radev;
+ struct auxiliary_device *adev;
+ int ret;
+
+ radev = kzalloc(sizeof(*radev), GFP_KERNEL);
+ if (!radev)
+ return -ENOMEM;
+
+ radev->base = sysc->base;
+ radev->lock = &sysc->lock;
+
+ adev = &radev->adev;
+ adev->name = adev_name;
+ adev->dev.parent = sysc->dev;
+ adev->dev.release = rzg3s_sysc_reset_adev_release;
+ adev->id = adev_id;
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(sysc->dev, rzg3s_sysc_reset_unregister_adev, adev);
+}
+
+static int rzg3s_sysc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzg3s_sysc *sysc;
+
+ sysc = devm_kzalloc(dev, sizeof(*sysc), GFP_KERNEL);
+ if (!sysc)
+ return -ENOMEM;
+
+ sysc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sysc->base))
+ return PTR_ERR(sysc->base);
+
+ sysc->dev = dev;
+ spin_lock_init(&sysc->lock);
+
+ return rzg3s_sysc_reset_probe(sysc, "reset", 0);
+}
+
+static const struct of_device_id rzg3s_sysc_match[] = {
+ { .compatible = "renesas,r9a08g045-sysc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rzg3s_sysc_match);
+
+static struct platform_driver rzg3s_sysc_driver = {
+ .driver = {
+ .name = "renesas-rzg3s-sysc",
+ .of_match_table = rzg3s_sysc_match
+ },
+ .probe = rzg3s_sysc_probe
+};
+
+static int __init rzg3s_sysc_init(void)
+{
+ return platform_driver_register(&rzg3s_sysc_driver);
+}
+subsys_initcall(rzg3s_sysc_init);
+
+MODULE_DESCRIPTION("Renesas RZ/G3S System Controller Driver");
+MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __SOC_RENESAS_SYSC_RESET_RZG3S_H
+#define __SOC_RENESAS_SYSC_RESET_RZG3S_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/spinlock_types.h>
+#include <linux/container_of.h>
+
+/**
+ * struct rzg3s_sysc_reset_adev - SYSC reset auxiliary device
+ * @base: base address
+ * @lock: lock
+ * @adev: auxiliary device
+ */
+struct rzg3s_sysc_reset_adev {
+ void __iomem *base;
+ spinlock_t *lock;
+ struct auxiliary_device adev;
+};
+
+#define to_rzg3s_sysc_reset_adev(a) container_of(a, struct rzg3s_sysc_reset_adev, adev)
+
+#endif