As of Figure 41.1 (Power Domain and Power Supply) and Table 41.1 (Power Domain to which Power Supply Pins Belong) the USB and PCIE belongs to PD_ISOVCC power domain controlled though PMIC [B]. The USB, PCI signals are also reference in HW manual in the low power consumption chapter describing the transition to different power modes. E.g., chapter 41.6.1 (ALL_ON to VBATT), Table 41.8 (Example Transition Flow Outline from ALL_ON Mode to VBATT Mode) says at steps 6 and 7: 6. USB PHY PWRRDY signal control (if using USB) SYS_USB_PWRRDY 7. PCIe RST_RSM_B signal control (if using PCIe) SYS_PCIE_RST_RSM_B Meaning these signals need to be controlled before going to VBATT power mode (where the power supply to USB is turned off) [C]. Due to [A], [B] and [C] I chosed to have the implementation of these signals though a reset control driver. Other option I explored was though power domains as follows: 1/ registering one domain for USB, one of PCIE 2/ passed the domain ID to USB though device tree 3/ attach from USB PHY control driver to the USB power domain with dev_pm_domain_attach_by_name() 4/ and controlling the SYSC registers with pm_runtime_resume_and_get(usb_sysc_domain). Please let me know what do you think about this! Thank you, Claudiu Beznea [1] https://www.renesas.com/us/en/products/microcontrollers-microprocessors/rz-mpus/rzg3s-general-purpose-microprocessors-single-core-arm-cortex-a55-11-ghz-cpu-and-dual-core-cortex-m33-250 drivers/reset/Kconfig | 7 + drivers/reset/Makefile | 1 + drivers/reset/reset-rzg3s-sysc.c | 140 +++++++++++++++++++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/rzg3s-sysc.c | 113 +++++++++++++++ include/linux/soc/renesas/rzg3s-sysc-reset.h | 24 ++++ 6 files changed, 286 insertions(+) create mode 100644 drivers/reset/reset-rzg3s-sysc.c create mode 100644 drivers/soc/renesas/rzg3s-sysc.c create mode 100644 include/linux/soc/renesas/rzg3s-sysc-reset.h diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 67bce340a87e..fbdf860b2293 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -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 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 27b0bbdfcc04..ee5ca21acc44 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -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 diff --git a/drivers/reset/reset-rzg3s-sysc.c b/drivers/reset/reset-rzg3s-sysc.c new file mode 100644 index 000000000000..56af03f1d8a2 --- /dev/null +++ b/drivers/reset/reset-rzg3s-sysc.c @@ -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); diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 734f8f8cefa4..74c72ac46f91 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -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 diff --git a/drivers/soc/renesas/rzg3s-sysc.c b/drivers/soc/renesas/rzg3s-sysc.c new file mode 100644 index 000000000000..e664d29ce5c3 --- /dev/null +++ b/drivers/soc/renesas/rzg3s-sysc.c @@ -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@xxxxxxxxxxxxxx>"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/soc/renesas/rzg3s-sysc-reset.h b/include/linux/soc/renesas/rzg3s-sysc-reset.h new file mode 100644 index 000000000000..813cbe82a68a --- /dev/null +++ b/include/linux/soc/renesas/rzg3s-sysc-reset.h @@ -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 -- 2.39.2